Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
Energy management system comprises of generation, transmission and distribution of power. Efficiency of whole system depends on efficient functioning of each individual stage. For increasing the efficiency of the energy management system and to cope up with the increasing demand of electricity, both technical and non-technical losses need to be minimized.
Whilst, the technical losses are improved upon by the utilities. This project tries to minimize various non-technical losses such as:
a) Tampering with Energy Meter
b) Double-feed and bypass of Energy Meter
c) Ignoring unpaid bills
d) Human-error and delay in meter reading and billing
Anti Tamper feature in Energy Meters is one of the ways to bring down the incidence of energy-theft tampering of meters. Use of Intelligent and smart meters with Anti tamper feature makes electricity theft more difficult and easily detectable.
NOTE: The initial device setup (Azure Sphere MT3620), is indeed a not too-complicated project in itself, hence it wouldn't be summarized here. But still, one can get a detailed info regarding Azure Sphere dry-test by Cabe Atwell's
Project OverviewMeasuring Energy consumption
The project involves the measurement of current consumption based on the output values from ACS758 current sensor, that is tweaked for 2.5V output (default 5V) through resistor-divider circuit, before feeding to the analog pin AN of MT3620 (mikroBUS-1), this is done to compensate for the ADC Voltage reference of MT3620 board. Voltage levels and power-factor have been averaged-out to standard 230V and 0.8 respectively due to minor fluctuations in our energy supply region as well as for the sake of simplicity of the project.
Anti-tamper methodology
As soon as the device registers the 'Tamper-state', it would shutdown the connected output load through load-relay, and log the device details to the azure cloud.
Over-loading and ignoring energy bills for a set duration would also perform load-shutdown and log the specific details to the azure cloud.
Physical abuse to the azure sphere powered smart meter would also trigger output load-shutdown and log the device ID details to the concerned utility for theft penalty.
Azure Sphere - Goals fulfilled...
-Use onboard mikroBUS-1 AN pin for reading the ACS758 current sensor.
-Use onboard mikroBUS-2 CS and PWM pin for controlling relays.
-Use onboard ambient-environment sensor for detecting temperature, pressure and lux levels.
-Use onboard IMU unit for preventing tampering to the application product.
-Use onboard wifi for secure communication with end-user via android through azure cloud services(to be implemented...)
-Use onboard wifi for OTA updates and periodic sync with azure cloud services.
Azure Sphere - Programming & Code ModificationsThe project involves the use of handy code provided by AVNET and Azure Sphere Github workspace that has been modified, with reference to specific Mikroe Click boards. Starting with the Relay-Click and the Current-Click and moving further to the PWM, ADC(hardcore) and the IntercoreComms. Getting on-board sensor data was fun especially the Inertial Measurement Unit and the environment sensor, where I combined the codes from toggling on-board RGB LED, and relay module due to change in gyroscopic values.
The light sensor ADC value was visualized in Hterm application through the use of USB to Serial converter that was connected to azure sphere's J11 pins, that needs to be soldered manually. This provided a basic start point for the ACS758 measurement, through tuned 2.5V peak output.
Building the Circuit and AssemblyAzure Sphere MT3620 exposes every interface required for the project, wherein the Relay-I is used solely for switching the output connected load, Relay-II is used for switching the 12V exhaust fan powered through mini SMPS, the fan would only be used at times of excessive heat build-up within the enclosure from the bare-processor SOM and to keep the debris out(to be tested yet).
Similarly, the ACS758(hall-effect current sensor), is connected in series to the output AC load, that registers the analog values from the linear-proportional sensor and is further used in energy monitoring algorithm.
Initially, the connections to the relay, current sensor and the Sphere MT3620 is made with jumper cables, but soon I would be posting the PCB design files that can be used as a shield for the Azure Sphere device.
The entire setup is tried and tested barely, and soon would be revamped with 3D printed custom ABS enclosure with an OLED display.
To do...-Blockchainless Secure Bill-Payment Schema for end-user via Azure Service.
-Certificate-based device authentication to user-defined web service.
-Threat detection via failure reporting.
-Automating the security updates.
- Azure Onboard sensor and tamper detection(main)
- Azure Onboard sensor and tamper detection(app manifest)
- Azure Onboard sensor and tamper detection(build options)
- Azure Relay control through mikroBUS-IO(main)
- Azure Relay control through mikroBUS-IO(app manifest)
- Azure sphere current measurement through ACS758
Azure Onboard sensor and tamper detection(main)
C/C++The result is shown in the console of inbuilt GDB Debugger Session console.
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
/************************************************************************************************
Name: AvnetStarterKitReferenceDesign
Sphere OS: 19.02
This file contains the 'main' function. Program execution begins and ends there
Authors:
Peter Fenn (Avnet Engineering & Technology)
Brian Willess (Avnet Engineering & Technology)
Purpose:
Using the Avnet Azure Sphere Starter Kit demonstrate the following features
1. Read X,Y,Z accelerometer data from the onboard LSM6DSO device using the I2C Interface
2. Read X,YZ Angular rate data from the onboard LSM6DSO device using the I2C Interface
3. Read the barometric pressure from the onboard LPS22HH device using the I2C Interface
4. Read the temperature from the onboard LPS22HH device using the I2C Interface
5. Read the state of the A and B buttons
6. Read BSSID address, Wi-Fi AP SSID, Wi-Fi Frequency
*************************************************************************************************
Connected application features: When connected to Azure IoT Hub or IoT Central
*************************************************************************************************
7. Send X,Y,Z accelerometer data to Azure
8. Send barometric pressure data to Azure
9. Send button state data to Azure
10. Send BSSID address, Wi-Fi AP SSID, Wi-Fi Frequency data to Azure
11. Send the application version string to Azure
12. Control user RGB LEDs from the cloud using device twin properties
13. Control optional Relay Click relays from the cloud using device twin properties
14. Send Application version up as a device twin property
15. Stop application using "haltApplication" Direct Method call from the cloud
16. Modify sensor polling time using "setSensorPollTinme" Direct Method from the cloud
TODO
1. Add support for a OLED display
2. Add support for on-board light sensor
*************************************************************************************************/
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
// applibs_versions.h defines the API struct versions to use for applibs APIs.
#include "applibs_versions.h"
#include "epoll_timerfd_utilities.h"
#include "i2c.h"
#include "mt3620_avnet_dev.h"
#include "deviceTwin.h"
#include "azure_iot_utilities.h"
#include "connection_strings.h"
#include "build_options.h"
#include <applibs/log.h>
#include <applibs/i2c.h>
#include <applibs/gpio.h>
#include <applibs/wificonfig.h>
#include <azureiot/iothub_device_client_ll.h>
// Provide local access to variables in other files
extern twin_t twinArray[];
extern int twinArraySize;
extern IOTHUB_DEVICE_CLIENT_LL_HANDLE iothubClientHandle;
extern int accelTimerFd;
// Support functions.
static void TerminationHandler(int signalNumber);
static int InitPeripheralsAndHandlers(void);
static void ClosePeripheralsAndHandlers(void);
// File descriptors - initialized to invalid value
int epollFd = -1;
static int buttonPollTimerFd = -1;
static int buttonAGpioFd = -1;
static int buttonBGpioFd = -1;
int userLedRedFd = -1;
int userLedGreenFd = -1;
int userLedBlueFd = -1;
int appLedFd = -1;
int wifiLedFd = -1;
int clickSocket1Relay1Fd = -1;
int clickSocket1Relay2Fd = -1;
// Button state variables, initilize them to button not-pressed (High)
static GPIO_Value_Type buttonAState = GPIO_Value_High;
static GPIO_Value_Type buttonBState = GPIO_Value_High;
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
bool versionStringSent = false;
#endif
// Define the Json string format for the accelerator button press data
static const char cstrButtonTelemetryJson[] = "{\"%s\":\"%d\"}";
// Termination state
volatile sig_atomic_t terminationRequired = false;
/// <summary>
/// Signal handler for termination requests. This handler must be async-signal-safe.
/// </summary>
static void TerminationHandler(int signalNumber)
{
// Don't use Log_Debug here, as it is not guaranteed to be async-signal-safe.
terminationRequired = true;
}
/// <summary>
/// Allocates and formats a string message on the heap.
/// </summary>
/// <param name="messageFormat">The format of the message</param>
/// <param name="maxLength">The maximum length of the formatted message string</param>
/// <returns>The pointer to the heap allocated memory.</returns>
static void *SetupHeapMessage(const char *messageFormat, size_t maxLength, ...)
{
va_list args;
va_start(args, maxLength);
char *message =
malloc(maxLength + 1); // Ensure there is space for the null terminator put by vsnprintf.
if (message != NULL) {
vsnprintf(message, maxLength, messageFormat, args);
}
va_end(args);
return message;
}
/// <summary>
/// Direct Method callback function, called when a Direct Method call is received from the Azure
/// IoT Hub.
/// </summary>
/// <param name="methodName">The name of the method being called.</param>
/// <param name="payload">The payload of the method.</param>
/// <param name="responsePayload">The response payload content. This must be a heap-allocated
/// string, 'free' will be called on this buffer by the Azure IoT Hub SDK.</param>
/// <param name="responsePayloadSize">The size of the response payload content.</param>
/// <returns>200 HTTP status code if the method name is reconginized and the payload is correctly parsed;
/// 400 HTTP status code if the payload is invalid;</returns>
/// 404 HTTP status code if the method name is unknown.</returns>
static int DirectMethodCall (const char *methodName, const char *payload, size_t payloadSize, char **responsePayload, size_t *responsePayloadSize)
{
Log_Debug("\nDirect Method called %s\n", methodName);
int result = 404; // HTTP status code.
if (payloadSize < 32) {
// Declare a char buffer on the stack where we'll operate on a copy of the payload.
char directMethodCallContent[payloadSize + 1];
// Prepare the payload for the response. This is a heap allocated null terminated string.
// The Azure IoT Hub SDK is responsible of freeing it.
*responsePayload = NULL; // Reponse payload content.
*responsePayloadSize = 0; // Response payload content size.
// Look for the haltApplication method name. This direct method does not require any payload, other than
// a valid Json argument such as {}.
if (strcmp(methodName, "haltApplication") == 0) {
// Log that the direct method was called and set the result to reflect success!
Log_Debug("haltApplication() Direct Method called\n");
result = 200;
// Construct the response message. This response will be displayed in the cloud when calling the direct method
static const char resetOkResponse[] =
"{ \"success\" : true, \"message\" : \"Halting Application\" }";
size_t responseMaxLength = sizeof(resetOkResponse);
*responsePayload = SetupHeapMessage(resetOkResponse, responseMaxLength);
if (*responsePayload == NULL) {
Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n");
abort();
}
*responsePayloadSize = strlen(*responsePayload);
// Set the terminitation flag to true. When in Visual Studio this will simply halt the application.
// If this application was running with the device in field-prep mode, the application would halt
// and the OS services would resetart the application.
terminationRequired = true;
return result;
}
// Check to see if the setSensorPollTime direct method was called
else if (strcmp(methodName, "setSensorPollTime") == 0) {
// Log that the direct method was called and set the result to reflect success!
Log_Debug("setSensorPollTime() Direct Method called\n");
result = 200;
// The payload should contain a JSON object such as: {"pollTime": 20}
if (directMethodCallContent == NULL) {
Log_Debug("ERROR: Could not allocate buffer for direct method request payload.\n");
abort();
}
// Copy the payload into our local buffer then null terminate it.
memcpy(directMethodCallContent, payload, payloadSize);
directMethodCallContent[payloadSize] = 0; // Null terminated string.
JSON_Value *payloadJson = json_parse_string(directMethodCallContent);
// Verify we have a valid JSON string from the payload
if (payloadJson == NULL) {
goto payloadError;
}
// Verify that the payloadJson contains a valid JSON object
JSON_Object *pollTimeJson = json_value_get_object(payloadJson);
if (pollTimeJson == NULL) {
goto payloadError;
}
// Pull the Key: value pair from the JSON object, we're looking for {"pollTime": <integer>}
// Verify that the new timer is < 0
int newPollTime = (int)json_object_get_number(pollTimeJson, "pollTime");
if (newPollTime < 1) {
goto payloadError;
}
else {
Log_Debug("New PollTime %d\n", newPollTime);
// Construct the response message. This will be displayed in the cloud when calling the direct method
static const char newPollTimeResponse[] =
"{ \"success\" : true, \"message\" : \"New Sensor Poll Time %d seconds\" }";
size_t responseMaxLength = sizeof(newPollTimeResponse) + strlen(payload);
*responsePayload = SetupHeapMessage(newPollTimeResponse, responseMaxLength, newPollTime);
if (*responsePayload == NULL) {
Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n");
abort();
}
*responsePayloadSize = strlen(*responsePayload);
// Define a new timespec variable for the timer and change the timer period
struct timespec newAccelReadPeriod = { .tv_sec = newPollTime,.tv_nsec = 0 };
SetTimerFdToPeriod(accelTimerFd, &newAccelReadPeriod);
return result;
}
}
else {
result = 404;
Log_Debug("INFO: Direct Method called \"%s\" not found.\n", methodName);
static const char noMethodFound[] = "\"method not found '%s'\"";
size_t responseMaxLength = sizeof(noMethodFound) + strlen(methodName);
*responsePayload = SetupHeapMessage(noMethodFound, responseMaxLength, methodName);
if (*responsePayload == NULL) {
Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n");
abort();
}
*responsePayloadSize = strlen(*responsePayload);
return result;
}
}
else {
Log_Debug("Payload size > 32 bytes, aborting Direct Method execution\n");
goto payloadError;
}
// If there was a payload error, construct the
// response message and send it back to the IoT Hub for the user to see
payloadError:
result = 400; // Bad request.
Log_Debug("INFO: Unrecognised direct method payload format.\n");
static const char noPayloadResponse[] =
"{ \"success\" : false, \"message\" : \"request does not contain an identifiable "
"payload\" }";
size_t responseMaxLength = sizeof(noPayloadResponse) + strlen(payload);
responseMaxLength = sizeof(noPayloadResponse);
*responsePayload = SetupHeapMessage(noPayloadResponse, responseMaxLength);
if (*responsePayload == NULL) {
Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n");
abort();
}
*responsePayloadSize = strlen(*responsePayload);
return result;
}
/// <summary>
/// Handle button timer event: if the button is pressed, report the event to the IoT Hub.
/// </summary>
static void ButtonTimerEventHandler(EventData *eventData)
{
bool sendTelemetryButtonA = false;
bool sendTelemetryButtonB = false;
if (ConsumeTimerFdEvent(buttonPollTimerFd) != 0) {
terminationRequired = true;
return;
}
// Check for button A press
GPIO_Value_Type newButtonAState;
int result = GPIO_GetValue(buttonAGpioFd, &newButtonAState);
if (result != 0) {
Log_Debug("ERROR: Could not read button GPIO: %s (%d).\n", strerror(errno), errno);
terminationRequired = true;
return;
}
// If the A button has just been pressed, send a telemetry message
// The button has GPIO_Value_Low when pressed and GPIO_Value_High when released
if (newButtonAState != buttonAState) {
if (newButtonAState == GPIO_Value_Low) {
Log_Debug("Button A pressed!\n");
sendTelemetryButtonA = true;
}
else {
Log_Debug("Button A released!\n");
}
// Update the static variable to use next time we enter this routine
buttonAState = newButtonAState;
}
// Check for button B press
GPIO_Value_Type newButtonBState;
result = GPIO_GetValue(buttonBGpioFd, &newButtonBState);
if (result != 0) {
Log_Debug("ERROR: Could not read button GPIO: %s (%d).\n", strerror(errno), errno);
terminationRequired = true;
return;
}
// If the B button has just been pressed/released, send a telemetry message
// The button has GPIO_Value_Low when pressed and GPIO_Value_High when released
if (newButtonBState != buttonBState) {
if (newButtonBState == GPIO_Value_Low) {
// Send Telemetry here
Log_Debug("Button B pressed!\n");
sendTelemetryButtonB = true;
}
else {
Log_Debug("Button B released!\n");
}
// Update the static variable to use next time we enter this routine
buttonBState = newButtonBState;
}
// If either button was pressed, then enter the code to send the telemetry message
if (sendTelemetryButtonA || sendTelemetryButtonB) {
char *pjsonBuffer = (char *)malloc(JSON_BUFFER_SIZE);
if (pjsonBuffer == NULL) {
Log_Debug("ERROR: not enough memory to send telemetry");
}
if (sendTelemetryButtonA) {
// construct the telemetry message for Button A
snprintf(pjsonBuffer, JSON_BUFFER_SIZE, cstrButtonTelemetryJson, "buttonA", newButtonAState);
Log_Debug("\n[Info] Sending telemetry %s\n", pjsonBuffer);
AzureIoT_SendMessage(pjsonBuffer);
}
if (sendTelemetryButtonB) {
// construct the telemetry message for Button B
snprintf(pjsonBuffer, JSON_BUFFER_SIZE, cstrButtonTelemetryJson, "buttonB", newButtonBState);
Log_Debug("\n[Info] Sending telemetry %s\n", pjsonBuffer);
AzureIoT_SendMessage(pjsonBuffer);
}
free(pjsonBuffer);
}
}
// event handler data structures. Only the event handler field needs to be populated.
static EventData buttonEventData = { .eventHandler = &ButtonTimerEventHandler };
/// <summary>
/// Set up SIGTERM termination handler, initialize peripherals, and set up event handlers.
/// </summary>
/// <returns>0 on success, or -1 on failure</returns>
static int InitPeripheralsAndHandlers(void)
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = TerminationHandler;
sigaction(SIGTERM, &action, NULL);
epollFd = CreateEpollFd();
if (epollFd < 0) {
return -1;
}
if (initI2c() == -1) {
return -1;
}
// Traverse the twin Array and for each GPIO item in the list open the file descriptor
for (int i = 0; i < twinArraySize; i++) {
// Verify that this entry is a GPIO entry
if (twinArray[i].twinGPIO != NO_GPIO_ASSOCIATED_WITH_TWIN) {
*twinArray[i].twinFd = -1;
// For each item in the data structure, initialize the file descriptor and open the GPIO for output. Initilize each GPIO to its specific inactive state.
*twinArray[i].twinFd = (int)GPIO_OpenAsOutput(twinArray[i].twinGPIO, GPIO_OutputMode_PushPull, twinArray[i].active_high ? GPIO_Value_Low : GPIO_Value_High);
if (*twinArray[i].twinFd < 0) {
Log_Debug("ERROR: Could not open LED %d: %s (%d).\n", twinArray[i].twinGPIO, strerror(errno), errno);
return -1;
}
}
}
// Open button A GPIO as input
Log_Debug("Opening Starter Kit Button A as input.\n");
buttonAGpioFd = GPIO_OpenAsInput(MT3620_RDB_BUTTON_A);
if (buttonAGpioFd < 0) {
Log_Debug("ERROR: Could not open button A GPIO: %s (%d).\n", strerror(errno), errno);
return -1;
}
// Open button B GPIO as input
Log_Debug("Opening Starter Kit Button B as input.\n");
buttonBGpioFd = GPIO_OpenAsInput(MT3620_RDB_BUTTON_B);
if (buttonBGpioFd < 0) {
Log_Debug("ERROR: Could not open button B GPIO: %s (%d).\n", strerror(errno), errno);
return -1;
}
// Set up a timer to poll the buttons
struct timespec buttonPressCheckPeriod = { 0, 1000000 };
buttonPollTimerFd =
CreateTimerFdAndAddToEpoll(epollFd, &buttonPressCheckPeriod, &buttonEventData, EPOLLIN);
if (buttonPollTimerFd < 0) {
return -1;
}
// Tell the system about the callback function that gets called when we receive a device twin update message from Azure
AzureIoT_SetDeviceTwinUpdateCallback(&deviceTwinChangedHandler);
// Tell the system about the callback function to call when we receive a Direct Method message from Azure
AzureIoT_SetDirectMethodCallback(&DirectMethodCall);
return 0;
}
/// <summary>
/// Close peripherals and handlers.
/// </summary>
static void ClosePeripheralsAndHandlers(void)
{
Log_Debug("Closing file descriptors.\n");
closeI2c();
CloseFdAndPrintError(epollFd, "Epoll");
CloseFdAndPrintError(buttonPollTimerFd, "buttonPoll");
CloseFdAndPrintError(buttonAGpioFd, "buttonA");
CloseFdAndPrintError(buttonBGpioFd, "buttonB");
// Traverse the twin Array and for each GPIO item in the list the close the file descriptor
for (int i = 0; i < twinArraySize; i++) {
// Verify that this entry has an open file descriptor
if (twinArray[i].twinGPIO != NO_GPIO_ASSOCIATED_WITH_TWIN) {
CloseFdAndPrintError(*twinArray[i].twinFd, twinArray[i].twinKey);
}
}
}
/// <summary>
/// Main entry point for this application.
/// </summary>
int main(int argc, char *argv[])
{
// Variable to help us send the version string up only once
bool networkConfigSent = false;
char ssid[128];
uint32_t frequency;
char bssid[20];
// Clear the ssid array
memset(ssid, 0, 128);
Log_Debug("Version String: %s\n", argv[1]);
Log_Debug("Avnet Starter Kit Simple Reference Application starting.\n");
if (InitPeripheralsAndHandlers() != 0) {
terminationRequired = true;
}
// Use epoll to wait for events and trigger handlers, until an error or SIGTERM happens
while (!terminationRequired) {
if (WaitForEventAndCallHandler(epollFd) != 0) {
terminationRequired = true;
}
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
// Setup the IoT Hub client.
// Notes:
// - it is safe to call this function even if the client has already been set up, as in
// this case it would have no effect;
// - a failure to setup the client is a fatal error.
if (!AzureIoT_SetupClient()) {
Log_Debug("ERROR: Failed to set up IoT Hub client\n");
break;
}
#endif
WifiConfig_ConnectedNetwork network;
int result = WifiConfig_GetCurrentNetwork(&network);
if (result < 0) {
// Log_Debug("INFO: Not currently connected to a WiFi network.\n");
}
else {
frequency = network.frequencyMHz;
snprintf(bssid, JSON_BUFFER_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
network.bssid[0], network.bssid[1], network.bssid[2],
network.bssid[3], network.bssid[4], network.bssid[5]);
if ((strncmp(ssid, (char*)&network.ssid, network.ssidLength)!=0) || !networkConfigSent) {
memset(ssid, 0, 128);
strncpy(ssid, network.ssid, network.ssidLength);
Log_Debug("SSID: %s\n", ssid);
Log_Debug("Frequency: %dMHz\n", frequency);
Log_Debug("bssid: %s\n", bssid);
networkConfigSent = true;
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
// Note that we send up this data to Azure if it changes, but the IoT Central Properties elements only
// show the data that was currenet when the device first connected to Azure.
checkAndUpdateDeviceTwin("ssid", &ssid, TYPE_STRING, false);
checkAndUpdateDeviceTwin("freq", &frequency, TYPE_INT, false);
checkAndUpdateDeviceTwin("bssid", &bssid, TYPE_STRING, false);
#endif
}
}
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
if (iothubClientHandle != NULL && !versionStringSent) {
checkAndUpdateDeviceTwin("versionString", argv[1], TYPE_STRING, false);
versionStringSent = true;
}
// AzureIoT_DoPeriodicTasks() needs to be called frequently in order to keep active
// the flow of data with the Azure IoT Hub
AzureIoT_DoPeriodicTasks();
#endif
}
ClosePeripheralsAndHandlers();
Log_Debug("Application exiting.\n");
return 0;
}
Azure Onboard sensor and tamper detection(app manifest)
C/C++{
"SchemaVersion": 1,
"Name": "Avnet-Starter-Kit-reference-V1.0",
"ComponentId": "685f13af-25a5-40b2-8dd8-8cbc253ecbd8",
"EntryPoint": "/bin/app",
"CmdArgs": [ "Avnet-Starter-Kit-reference-V2.0" ],
"Capabilities": {
"AllowedConnections": [],
"AllowedTcpServerPorts": [],
"AllowedUdpServerPorts": [],
"Gpio": [ 0, 4, 5, 8, 9, 10, 12, 13, 34 ],
"Uart": [],
"I2cMaster": [ "ISU2" ],
"SpiMaster": [],
"WifiConfig": true,
"NetworkConfig": true,
"SystemTime": true
}
}
Azure Onboard sensor and tamper detection(build options)
C/C++#pragma once
// If your application is going to connect to an IoT Central Application, then enable this define. When
// enabled Device Twin JSON updates will conform to what IoT Central expects to confirm Device Twin settings
//#define IOT_CENTRAL_APPLICATION
// If your application is going to connect straight to a IoT Hub, then enable this define.
//#define IOT_HUB_APPLICATION
#if (defined(IOT_CENTRAL_APPLICATION) && defined(IOT_HUB_APPLICATION))
#error "Can not define both IoT Central and IoT Hub Applications at the same time only define one."
#endif
#if (!defined(IOT_CENTRAL_APPLICATION) && !defined(IOT_HUB_APPLICATION))
#warning "Building application for no cloud connectivity"
#endif
#ifdef IOT_CENTRAL_APPLICATION
#warning "Building for IoT Central Application"
#endif
#ifdef IOT_HUB_APPLICATION
#warning "Building for IoT Hub Application"
#endif
// Defines how quickly the accelerator data is read and reported
#define ACCEL_READ_PERIOD_SECONDS 1
#define ACCEL_READ_PERIOD_NANO_SECONDS 0
// Enables I2C read/write debug
//#define ENABLE_READ_WRITE_DEBUG
Azure Relay control through mikroBUS-IO(main)
C/C++Relay-2 is used for switching 12V Ext. powered fan at desired periods.
Run-time can be modified for according to the user()
/**
* copyright (c) 2018, James Flynn
* SPDX-License-Identifier: MIT
*/
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
// applibs_versions.h defines the API struct versions to use for applibs APIs.
#include "applibs_versions.h"
#include <applibs/log.h>
#include "mt3620_rdb.h"
#include "relay.h"
#define MIKROE_PWM MT3620_GPIO1 //click#1=GPIO0; click#2=GPIO1
#define MIKROE_CS MT3620_GPIO35 //click#1=GPIO34; click#2=GPIO35
static int r1PinFd; //relay #1
static GPIO_Value_Type relay1Pin;
static int r2PinFd; //relay #2
static GPIO_Value_Type relay2Pin;
void init(void)
{
r1PinFd = GPIO_OpenAsOutput(MIKROE_PWM, relay1Pin, GPIO_Value_Low);
r2PinFd = GPIO_OpenAsOutput(MIKROE_CS, relay2Pin, GPIO_Value_Low);
}
void state(RELAY* ptr)
{
if (ptr->relay1_status == 1)
GPIO_SetValue(r1PinFd, GPIO_Value_High);
else
GPIO_SetValue(r1PinFd, GPIO_Value_Low);
if (ptr->relay2_status == 1)
GPIO_SetValue(r2PinFd, GPIO_Value_High);
else
GPIO_SetValue(r2PinFd, GPIO_Value_Low);
}
void usage(void)
{
Log_Debug(" The 'relay_demo' program can be started with several options:\n");
Log_Debug(" -r X: Set the run-time for 'X' seconds\n");
Log_Debug(" -? : Display usage info\n");
}
int main(int argc, char *argv[])
{
int i, run_time = 6; //default to 30 second run time
RELAY* rptr;
while ((i = getopt(argc, argv, "tr:?")) != -1)
switch (i) {
case 'r':
sscanf(optarg, "%x", &run_time);
Log_Debug(">> run-time set to %d seconds ", run_time);
break;
case '?':
usage();
exit(EXIT_SUCCESS);
default:
Log_Debug(">> unknown option character `\\x%x'.\n", optopt);
exit(EXIT_FAILURE);
}
Log_Debug("\n\n");
Log_Debug(" ####\r\n");
Log_Debug(" ** ** SW reuse using C example\r\n");
Log_Debug(" ## ## for the Relay Click\r\n");
Log_Debug(" ** '''' **\r\n");
Log_Debug("\r\n");
Log_Debug("This demo simply alternates the relays through different\n");
Log_Debug("states at a 1 second interval. This demo requires using Socket #1.\r\n");
rptr = open_relay(state, init);
sleep(1);
i = 0;
while (i++ < run_time) {
relaystate(rptr, (i & 1) ? relay1_set : relay1_clr);
relaystate(rptr, (i & 2) ? relay2_set : relay2_clr);
Log_Debug("(%d) relay2 %s, relay1 %s\n", i,
relaystate(rptr, relay2_rd) ? "ON" : "OFF",
relaystate(rptr, relay1_rd) ? "ON" : "OFF");
sleep(1);
}
int fd = GPIO_OpenAsOutput(8, GPIO_OutputMode_PushPull, GPIO_Value_High);
int fdd = GPIO_OpenAsOutput(9, GPIO_OutputMode_PushPull, GPIO_Value_High);
const struct timespec sleepTime = { 1, 0 };
while (true) {
GPIO_SetValue(fd, GPIO_Value_Low);
GPIO_SetValue(fdd, GPIO_Value_Low);
nanosleep(&sleepTime, NULL);
GPIO_SetValue(fd, GPIO_Value_High);
GPIO_SetValue(fdd, GPIO_Value_High);
nanosleep(&sleepTime, NULL);
}
close_relay(rptr);
GPIO_SetValue(r1PinFd, GPIO_Value_Low);
GPIO_SetValue(r2PinFd, GPIO_Value_Low);
Log_Debug("relay2 OFF, relay1 oFF\nDONE...\n");
exit(EXIT_SUCCESS);
}
{
"SchemaVersion": 1,
"Name" : "relay_demo",
"ComponentId" : "3b468583-4290-44cd-a5cc-857419588546",
"EntryPoint": "/bin/app",
"CmdArgs": [],
"Capabilities": {
"AllowedConnections": [],
"AllowedTcpServerPorts": [],
"AllowedUdpServerPorts": [],
"Gpio": [1, 8, 9, 35],
"Uart": [],
"I2cMaster": [],
"SpiMaster": [],
"WifiConfig": false,
"NetworkConfig": false,
"SystemTime": false
}
}
Azure sphere current measurement through ACS758
C/C++/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
#include <ctype.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include "mt3620-baremetal.h"
#include "mt3620-intercore.h"
#include "mt3620-uart-poll.h"
#include "mt3620-adc.h"
extern uint32_t StackTop; // &StackTop == end of TCM0
static _Noreturn void DefaultExceptionHandler(void);
static void PrintBytes(const uint8_t *buf, int start, int end);
static void PrintGuid(const uint8_t *guid);
static _Noreturn void RTCoreMain(void);
// ARM DDI0403E.d SB1.5.2-3
// From SB1.5.3, "The Vector table must be naturally aligned to a power of two whose alignment
// value is greater than or equal to (Number of Exceptions supported x 4), with a minimum alignment
// of 128 bytes.". The array is aligned in linker.ld, using the dedicated section ".vector_table".
// The exception vector table contains a stack pointer, 15 exception handlers, and an entry for
// each interrupt.
#define INTERRUPT_COUNT 100 // from datasheet
#define EXCEPTION_COUNT (16 + INTERRUPT_COUNT)
#define INT_TO_EXC(i_) (16 + (i_))
static const uintptr_t ExceptionVectorTable[EXCEPTION_COUNT]
__attribute__((section(".vector_table"))) __attribute__((used)) = {
[0] = (uintptr_t)&StackTop, // Main Stack Pointer (MSP)
[1] = (uintptr_t)RTCoreMain, // Reset
[2] = (uintptr_t)DefaultExceptionHandler, // NMI
[3] = (uintptr_t)DefaultExceptionHandler, // HardFault
[4] = (uintptr_t)DefaultExceptionHandler, // MPU Fault
[5] = (uintptr_t)DefaultExceptionHandler, // Bus Fault
[6] = (uintptr_t)DefaultExceptionHandler, // Usage Fault
[11] = (uintptr_t)DefaultExceptionHandler, // SVCall
[12] = (uintptr_t)DefaultExceptionHandler, // Debug monitor
[14] = (uintptr_t)DefaultExceptionHandler, // PendSV
[15] = (uintptr_t)DefaultExceptionHandler, // SysTick
[INT_TO_EXC(0)... INT_TO_EXC(INTERRUPT_COUNT - 1)] = (uintptr_t)DefaultExceptionHandler};
static _Noreturn void DefaultExceptionHandler(void)
{
for (;;) {
// empty.
}
}
static void PrintBytes(const uint8_t *buf, int start, int end)
{
int step = (end >= start) ? +1 : -1;
for (/* nop */; start != end; start += step) {
Uart_WriteHexBytePoll(buf[start]);
}
Uart_WriteHexBytePoll(buf[end]);
}
static void PrintGuid(const uint8_t *guid)
{
PrintBytes(guid, 3, 0); // 4-byte little-endian word
Uart_WriteStringPoll("-");
PrintBytes(guid, 5, 4); // 2-byte little-endian half
Uart_WriteStringPoll("-");
PrintBytes(guid, 7, 6); // 2-byte little-endian half
Uart_WriteStringPoll("-");
PrintBytes(guid, 8, 9); // 2 bytes
Uart_WriteStringPoll("-");
PrintBytes(guid, 10, 15); // 6 bytes
}
static _Noreturn void RTCoreMain(void)
{
union Analog_data
{
uint32_t u32;
uint8_t u8[4];
} analog_data;
// SCB->VTOR = ExceptionVectorTable
WriteReg32(SCB_BASE, 0x08, (uint32_t)ExceptionVectorTable);
Uart_Init();
Uart_WriteStringPoll("--------------------------------\r\n");
Uart_WriteStringPoll("IntercoreCommsADC_RTApp_MT3620_BareMetal\r\n");
Uart_WriteStringPoll("App built on: " __DATE__ ", " __TIME__ "\r\n");
//// ADC Communication
EnableAdc();
BufferHeader *outbound, *inbound;
uint32_t sharedBufSize = 0;
if (GetIntercoreBuffers(&outbound, &inbound, &sharedBufSize) == -1) {
for (;;) {
// empty.
}
}
static const size_t payloadStart = 20;
for (;;)
{
uint8_t buf[256];
uint32_t dataSize = sizeof(buf);
uint8_t j = 0;
uint32_t mV;
// On success, dataSize is set to the actual number of bytes which were read.
int r = DequeueData(outbound, inbound, sharedBufSize, buf, &dataSize);
if (r == -1 || dataSize < payloadStart)
{
continue;
}
// For debug prrposes
Uart_WriteStringPoll("Received message of ");
Uart_WriteIntegerPoll(dataSize);
Uart_WriteStringPoll("bytes:\r\n");
// Print the Component Id (A7 Core)
Uart_WriteStringPoll(" Component Id (16 bytes): ");
PrintGuid(buf);
Uart_WriteStringPoll("\r\n");
// Print reserved field as little-endian 4-byte integer.
Uart_WriteStringPoll(" Reserved (4 bytes): ");
PrintBytes(buf, 19, 16);
Uart_WriteStringPoll("\r\n");
// Print message as hex.
size_t payloadBytes = dataSize - payloadStart;
Uart_WriteStringPoll(" Payload (");
Uart_WriteIntegerPoll(payloadBytes);
Uart_WriteStringPoll(" bytes as hex): ");
for (size_t i = payloadStart; i < dataSize; ++i)
{
Uart_WriteHexBytePoll(buf[i]);
if (i != dataSize - 1) {
Uart_WriteStringPoll(":");
}
}
Uart_WriteStringPoll("\r\n");
// Print message as text.
Uart_WriteStringPoll(" Payload (");
Uart_WriteIntegerPoll(payloadBytes);
Uart_WriteStringPoll(" bytes as text): ");
for (size_t i = payloadStart; i < dataSize; ++i)
{
char c[2];
c[0] = isprint(buf[i]) ? buf[i] : '.';
c[1] = '\0';
Uart_WriteStringPoll(c);
}
Uart_WriteStringPoll("\r\n");
// Read ADC channel 0
analog_data.u32 = ReadAdc(1);
mV = (analog_data.u32 * 2500) / 0xFFF;
Uart_WriteStringPoll("ADC channel 0: ");
Uart_WriteIntegerPoll(mV / 1000);
Uart_WriteStringPoll(".");
Uart_WriteIntegerWidthPoll(mV % 1000, 3);
Uart_WriteStringPoll(" V");
Uart_WriteStringPoll("\r\n");
j = 0;
for (int i = payloadStart; i < payloadStart + 4; i++)
{
// Put ADC data to buffer
buf[i] = analog_data.u8[j++];
}
// Send buffer to A7 Core
EnqueueData(inbound, outbound, sharedBufSize, buf, payloadStart + 4);
}
}
Comments