What if you could prototype your IoT hardware device and build your cloud application at the same time? Good news. My team and I spent months stumbling through an embedded-to-cloud journey. We fell into time traps and discovered pitfalls so that you don't need to! We believe we have finally curated an ideal way to get you started developing your own applications.
View our complete guided journey on GitHub
In this project, you will get hands-on experience programming an embedded device (an AVR-IoT or PIC-IoT board) and building a cloud application using AWS and the MQTT pub/sub messaging protocol.
By the end of the tutorial, you'll be able to view button press information from the dev board in the cloud. You will also have created an application where the dev board's LEDs are configured to blink whenever the cloud receives information about button presses.
You can follow along using one or multiple dev boards. If you use two or more you will get a special treat – the cloud application will blink the LEDs on each connected dev board when a button is pressed on any of them. Maybe you can give one to a friend and use the blinking LEDs as a secret messaging system?
- Check out the sandbox demo that comes with the IoT board by connecting it to your computer, opening the
CURIOSITY
drive, and clicking on theCLICK-ME.HTM
file. - Download and install the latest version of MPLAB X IDE.
- Download and install the latest version of the XC8 (AVR-IoT) and/or XC16 (PIC-IoT) compiler.
If you are unfamiliar with the MPLAB X integrated developer environment (IDE), you can check out the following guide: Get Started with MPLAB® X IDE and Microchip ToolsStep 1: Provision your IoT board
If you haven't yet migrated from the sandbox to a private AWS account, you will need to do that using Microchip's IoT provisioning tool. If you're not sure, you probably haven't - so check out this tutorial to set up your board-to-cloud connection.
In the aforementioned tutorial, the IoT boards are provisioned using the IoT Provisioning Tool. This generates an AWS IoT Core Policy that determines which permissions the boards have when they interact with AWS resources. By default, this policy is configured to only grant an IoT board the right to publish and subscribe to MQTT topics containing the board's thing name.
In this project, we will send and receive MQTT messages over the buttonPresses
topic. We must, therefore, expand the permissions to also include this topic:
- Open the IoT Core module in AWS and select Secure -> Policies in the menu on the left-hand side.
- Open zt_policy and scroll down to the Policy document section.
- Click Edit policy document and perform the changes described below:
- Update the
iot:Publish
andiot:Receive
permissions to include thebuttonPresses
topic, as shown below. Note that############
in the code below is a placeholder for your unique AWS resource identifier. Remember to replace this with the identifier found in your original policy document.
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:us-east-2:############:topic/${iot:Connection.Thing.ThingName}/*",
"arn:aws:iot:us-east-2:############:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/*",
"arn:aws:iot:us-east-2:############:topic/buttonPresses"
]
},
- Update the
iot:Publish
andiot:Receive
permissions to include thebuttonPresses
topic, as shown below. Note that############
in the code below is a placeholder for your unique AWS resource identifier. Remember to replace this with the identifier found in your original policy document.
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:us-east-2:############:topicfilter/${iot:Connection.Thing.ThingName}/*",
"arn:aws:iot:us-east-2:############:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/*",
"arn:aws:iot:us-east-2:############:topicfilter/buttonPresses"
]
},
- Click Save as new version.
The permissions have now been updated, and the board should be able to send and receive data over the buttonPresses
MQTT topic.
The starting point for our application is an unmodified copy of the firmware that is compatible with your device's microcontroller family:
- GitHub repository for the PIC-IoT Development Boards
- GitHub repository for the AVR-IoT Development Boards
Download the correct repository and open the PICIoT.X
and/or AVRIoT.X
project(s) in MPLAB X.
We will make several modifications to the firmware in the next couple of steps. If you get stuck, you can check out these fully modified versions of the firmware on GitHub:
- Modified firmware for the PIC-IoT Development Boards
- Modified firmware for the AVR-IoT Development boards
We want to send a message to AWS whenever the SW0
button is pressed, which we will detect using interrupts.
The first part of the procedures for the AVR-IoT boards differs somewhat from the procedures for the PIC-IoT boards. Follow the procedures for your device's microcontroller family below:
Procedures for AVR-IoT boards
In the MPLAB X project, open application_manager.c
(Source Files -> MCC Generated Files) and add the following code just after the SYSTEM_Initialize()
call in the application_init
function:
SW0_EnableInterruptForFallingEdge();
PORTF_SW0_SetInterruptHandler(sendButtonPressToCloud);
The first line enables falling edge interrupt detection for the SW0
button on the AVR-IoT boards, and a function handler for this interrupt is then assigned on the second line.
Procedures for PIC-IoT boards
In pin_manager.c
, perform the following edits:
- In the
PIN_MANAGER_initialize
function, enable interrupts for theSW0
button (which is connected to RA7) and clear its interrupt flag by including these two lines:
IOCNAbits.IOCNA7 = 1; //Pin : RA7
IOCFAbits.IOCFA7 = 0; //Pin : RA7
- Add another variable below the
INT_InterruptHandler
variable to store the interrupt handler for theSW0
hardware button:
void (*SW0_InterruptHandler)(void) = NULL;
- Add a function that sets the variable we just created (place it just after the
INT_SetInterruptHandler
function):
void SW0_SetInterruptHandler(void (* InterruptHandler)(void))
{
IEC1bits.IOCIE = 0; //Disable IOCI interrupt
SW0_InterruptHandler = InterruptHandler;
IEC1bits.IOCIE = 1; //Enable IOCI interrupt
}
Modify the _IOCInterrupt
interrupt service routine to also handle the SW0
button presses (the interrupt service routine is located near line 155 in pin_manager.c
). The fully modified interrupt service routine is provided below. Either copy and replace _IOCInterrupt
in its entirety or add the second nested if
statement to your project.
void __attribute__ (( interrupt, no_auto_psv )) _IOCInterrupt ( void )
{
if(IFS1bits.IOCIF == 1)
{
// Clear the flag
IFS1bits.IOCIF = 0;
if(IOCFAbits.IOCFA12 == 1)
{
IOCFAbits.IOCFA12 = 0; //Clear flag for Pin - RA12
if(INT_InterruptHandler)
{
INT_InterruptHandler();
}
}
// Handle SW0 button presses
if(IOCFAbits.IOCFA7 == 1)
{
IOCFAbits.IOCFA7 = 0; //Clear flag for Pin - RA7
if(SW0_InterruptHandler)
{
SW0_InterruptHandler();
}
}
}
}
In pin_manager.h
, add a declaration of the SW0_SetInterruptHandler
function we just added to make it available in other files, for example after the declaration of the INT_SetInterruptHandler
function:
void SW0_SetInterruptHandler(void (* InterruptHandler)(void));
In application_manager.c
, set the SW0
interrupt handler just after the call to the SYSTEM_Initialize()
in the application_init
function:
// Set interrupt handler for button presses
SW0_SetInterruptHandler(sendButtonPressToCloud);
Procedures for both AVR-IoT and PIC-IoT boards
The above procedures ensure that the sendButtonPressToCloud
function will be called whenever the SW0
button on the PIC-IoT or AVR-IoT board is pressed.
The IoT boards communicate with AWS using the MQTT protocol, which uses a publish-subscribe model. Messages are not sent directly between devices but are rather published to specific topics. Devices can subscribe to various topics and AWS keeps track of how the messages should be distributed.
Let us declare a variable to keep track of the MQTT topic we will use. Add the following declaration to application_manager.c
(e.g., below the declaration of the mqttSubscribeTopic
variable):
char tutorialMqttTopic[SUBSCRIBE_TOPIC_SIZE];
Implement the aforementioned function handler by adding the following code to application_manager.c
:
static void sendButtonPressToCloud(){
// Ensure that we have a valid cloud connection
if (shared_networking_params.haveAPConnection)
{
static char tutorialPayload[PAYLOAD_SIZE];
int tutorialLen = 0;
// Set MQTT topic
memset((void*)tutorialMqttTopic, 0, sizeof(tutorialMqttTopic));
sprintf(tutorialMqttTopic, "buttonPresses");
// Construct payload
tutorialLen = sprintf(tutorialPayload,"{\"thing_name\":\"%s\"}", cid);
// Publish data to cloud
CLOUD_publishData((uint8_t*)tutorialMqttTopic ,(uint8_t*)tutorialPayload, tutorialLen);
}
}
This function closely resembles the sendToCloud
function that we mentioned earlier and will publish an MQTT message to the buttonPresses
topic. The content of the message will be a JSON object that contains the name of the thing/device that sent the message.
Build the modified project and program it onto the device using MPLAB X. This is done by clicking on the Make and Program Device Main Project button on the MPLAB X toolbar.
Verify that messages are successfully being sent to AWS
When the device has been successfully programmed, let us make sure that we are receiving the messages in AWS:
- Sign in to the AWS Management Console and select the IoT Core service.
- Select Test in the menu on the left-hand side
- In the Subscription topic field, enter
buttonPresses
. - Click the Subscribe to topic button.
- Press the
SW0
button on the board and observe that the button press is successfully registered in the cloud.
Now that we have successfully modified our project to send messages to a custom topic, we must also find a way to subscribe to this topic:
- Change the definition of
NUM_TOPICS_SUBSCRIBE
inmqtt_config.h
(Header Files -> MCC Generated Files -> config) to allow up to two simultaneous MQTT topic subscriptions:
#define NUM_TOPICS_SUBSCRIBE 2
- Edit the
subscribeToCloud
function inapplication_manager.c
to include a subscription to thebuttonPresses
topic. The fully modified function is provided below. Either copy and replace thesubscribeToCloud
function in its entirety or add the last two lines of the code below in your MPLAB X project.
static void subscribeToCloud(void)
{
sprintf(mqttSubscribeTopic, "$aws/things/%s/shadow/update/delta", cid);
CLOUD_registerSubscription((uint8_t*)mqttSubscribeTopic,receivedFromCloud);
sprintf(tutorialMqttTopic, "buttonPresses");
CLOUD_registerSubscription((uint8_t*)tutorialMqttTopic,receiveButtonPressFromCloud);
}
The second parameter of theCLOUD_registerSubscription
function is a handler that dictates which function will be run when a message is received to the specified topic. We, therefore, need to implement thereceiveButtonPressFromCloud
function to handle any received messages.
- Add the following function definition to
application_manager.c
(somewhere above thesubscribeToCloud
function) to make the device's LEDs blink twice when a message is received:
static void receiveButtonPressFromCloud(uint8_t *topic, uint8_t *payload){
LED_test();
LED_test();
}
- Build the project and program the device in MPLAB X. If you have multiple AVR-IoT or PIC-IoT devices available, you can try to program all of them using the same project.
If you are using both AVR-IoT and PIC-IoT devices: To use devices from two different device families together, it is necessary to complete this tutorial individually for the AVR-IoT and PIC-IoT repositories on GitHub and program the devices with the compatible firmware.
Your device(s) should now be configured correctly. If the SW0
button is pressed on any of the configured IoT kits, the LEDs on all configured IoT kits should blink twice.
For more tutorials on how to use the IoT boards with AWS, check out the Microchip IoT Developer Guides for AWS on GitHub.
Comments