Azure Sphere Banner

Unboxing the Azure Sphere MT3620 Development Kit

Please follow and share!
Twitter
Facebook
LinkedIn
RSS

While doing some IoT writing, I became increasingly curious about the new MCU on the block – Azure Sphere. It is being touted as being very powerful and as the most secure board available in the market today. It also boasts a lifetime of fully managed patches and security updates.

Note: Azure Sphere needs to be linked to Azure Active Directory (AAD) – I have one through my personal business’ Office 365 subscription. There are multiple ways of creating an AAD instance through this guidance.

Procuring the dev kit

I ordered my dev kit and received it a few weeks later – I also added the Grove starter kit so that I could start tinkering with my new board immediately.

Download and install the SDK

In order to develop for the Azure Sphere, you’ll need to download and install the SDK – at the time of this writing it is currently in preview.

Claiming the device

Hook up the Sphere to your PC using the USB cable that was included in the box. Open the Azure Sphere Developer Command Prompt and log in with your Microsoft account:

azshpere login

This will open a familiar browser login screen to authenticate your account. You may be shown a message indicating that there are no Azure Sphere Tenants found under the selected AAD user – this is expected. If you need to create a tenant, execute a command to create one (replace the name argument with the name of your choosing, I named mine ‘TrilliumSphere’) – you will be prompted to authenticate once more:

azsphere tenant create --name TrilliumSphere

Then claim the device using the following command:

azsphere device claim
Create Tenant and Claim Device
Create Tenant and Claim Device

Performing initial setup

Configure the device to utilize your Wi-Fi by issuing the following command:

azsphere device wifi add --ssid <YOURSSID> --key <YOURKEY>

When the device first boots, or connects to Wi-Fi, it will automatically seek out and install any updates. These updates should not be interrupted. Check the status of any updates by issuing the following command:

azsphere device show-ota-status
Add WiFi and Device Update Status
Add WiFi and Device Update Status

HelloWorld MCU Edition == Blink

The default Hello World program for a microcontroller is to blink an LED. I followed the following walkthrough to perform just that!

By default, the Sphere ships to you in a locked state, meaning you can’t load or debug programs from a PC. Execute the following command to unlock the device:

azsphere device prep-debug

You will then see output similar to the following:

Unlock Sphere for Debugging
Unlock Sphere for Debugging

As indicated in the console window – when a device is unlocked, it will not receive over-the-air updates. To lock a device for production (and to start receiving updates once more) you would execute the following command:

azsphere device prep-field

In order to run the blink sample, keep the device in the unlocked/debug mode. Ensure that you are running the latest build of Visual Studio 2017 – at the time of this writing it is v.15.8.9 – this will save you a whole lot of headaches!!!

Open Visual Studio, create a new project – then select the ‘Blink Sample for MT3620 RDB (Azure Sphere)’ template (on my machine this is located under Other Languages -> Visual C++ -> Cross Platform -> Azure Sphere).

New MT3620 Blink Sample Project
New MT3620 Blink Sample Project

Build and run the project – then press the ‘A’ button to change the speed of the blinking on LED 1.

Azure Sphere MT3620 Blink Sample Project

Sample Source file listing is as follows (copied from template) Source Files\main.c :

#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

// applibs_versions.h defines the API struct versions to use for applibs APIs.
#include "applibs_versions.h"
#include "epoll_timerfd_utilities.h"

#include <applibs/gpio.h>
#include <applibs/log.h>

#include "mt3620_rdb.h"

// This sample C application for the MT3620 Reference Development Board (Azure Sphere)
// blinks an LED.
// The blink rate can be changed through a button press.
//
// It uses the API for the following Azure Sphere application libraries:
// - gpio (digital input for button)
// - log (messages shown in Visual Studio's Device Output window during debugging)

// File descriptors - initialized to invalid value
static int gpioButtonFd = -1;
static int gpioButtonTimerFd = -1;
static int gpioLedFd = -1;
static int gpioLedTimerFd = -1;
static int epollFd = -1;

// Button state variables
static GPIO_Value_Type buttonState = GPIO_Value_High;
static GPIO_Value_Type ledState = GPIO_Value_High;

// Blink interval variables
static const int numBlinkIntervals = 3;
static const struct timespec blinkIntervals[] = {{0, 125000000}, {0, 250000000}, {0, 500000000}};
static int blinkIntervalIndex = 0;

// Termination state
static 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>
///     Handle LED timer event: blink LED.
/// </summary>
static void LedTimerEventHandler()
{
    if (ConsumeTimerFdEvent(gpioLedTimerFd) != 0) {
        terminationRequired = true;
        return;
    }

    // The blink interval has elapsed, so toggle the LED state
    // The LED is active-low so GPIO_Value_Low is on and GPIO_Value_High is off
    ledState = (ledState == GPIO_Value_Low ? GPIO_Value_High : GPIO_Value_Low);
    int result = GPIO_SetValue(gpioLedFd, ledState);
    if (result != 0) {
        Log_Debug("ERROR: Could not set LED output value: %s (%d).\n", strerror(errno), errno);
        terminationRequired = true;
    }
}

/// <summary>
///     Handle button timer event: if the button is pressed, change the LED blink rate.
/// </summary>
static void ButtonTimerEventHandler()
{
    if (ConsumeTimerFdEvent(gpioButtonTimerFd) != 0) {
        terminationRequired = true;
        return;
    }

    // Check for a button press
    GPIO_Value_Type newButtonState;
    int result = GPIO_GetValue(gpioButtonFd, &newButtonState);
    if (result != 0) {
        Log_Debug("ERROR: Could not read button GPIO: %s (%d).\n", strerror(errno), errno);
        terminationRequired = true;
        return;
    }

    // If the button has just been pressed, change the LED blink interval
    // The button has GPIO_Value_Low when pressed and GPIO_Value_High when released
    if (newButtonState != buttonState) {
        if (newButtonState == GPIO_Value_Low) {
            blinkIntervalIndex = (blinkIntervalIndex + 1) % numBlinkIntervals;
            if (SetTimerFdInterval(gpioLedTimerFd, &blinkIntervals[blinkIntervalIndex]) != 0) {
                terminationRequired = true;
            }
        }
        buttonState = newButtonState;
    }
}

/// <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;
    }

    // Open button GPIO as input, and set up a timer to poll it
    Log_Debug("Opening MT3620_RDB_BUTTON_A as input\n");
    gpioButtonFd = GPIO_OpenAsInput(MT3620_RDB_BUTTON_A);
    if (gpioButtonFd < 0) {
        Log_Debug("ERROR: Could not open button GPIO: %s (%d).\n", strerror(errno), errno);
        return -1;
    }
    struct timespec buttonPressCheckPeriod = {0, 1000000};
    gpioButtonTimerFd = CreateTimerFdAndAddToEpoll(epollFd, &buttonPressCheckPeriod,
                                                   &ButtonTimerEventHandler, EPOLLIN);
    if (gpioButtonTimerFd < 0) {
        return -1;
    }

    // Open LED GPIO, set as output with value GPIO_Value_High (off), and set up a timer to poll it
    Log_Debug("Opening MT3620_RDB_LED1_RED\n");
    gpioLedFd = GPIO_OpenAsOutput(MT3620_RDB_LED1_RED, GPIO_OutputMode_PushPull, GPIO_Value_High);
    if (gpioLedFd < 0) {
        Log_Debug("ERROR: Could not open LED GPIO: %s (%d).\n", strerror(errno), errno);
        return -1;
    }
    gpioLedTimerFd = CreateTimerFdAndAddToEpoll(epollFd, &blinkIntervals[blinkIntervalIndex],
                                                &LedTimerEventHandler, EPOLLIN);
    if (gpioLedTimerFd < 0) {
        return -1;
    }

    return 0;
}

/// <summary>
///     Close peripherals and handlers.
/// </summary>
static void ClosePeripheralsAndHandlers(void)
{
    // Leave the LED off
    if (gpioLedFd >= 0) {
        GPIO_SetValue(gpioLedFd, GPIO_Value_High);
    }

    Log_Debug("Closing file descriptors\n");
    CloseFdAndPrintError(gpioLedTimerFd, "LedTimer");
    CloseFdAndPrintError(gpioLedFd, "GpioLed");
    CloseFdAndPrintError(gpioButtonTimerFd, "ButtonTimer");
    CloseFdAndPrintError(gpioButtonFd, "GpioButton");
    CloseFdAndPrintError(epollFd, "Epoll");
}

/// <summary>
///     Main entry point for this application.
/// </summary>
int main(int argc, char *argv[])
{
    Log_Debug("Blink 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;
        }
    }

    ClosePeripheralsAndHandlers();
    Log_Debug("Application exiting\n");
    return 0;
}

First impressions

My first impressions with this device has overall been positive. It was easy to add it to the domain and claim it – though I did have to disconnect and reconnect it in order for the tenant creation command to see it. In Visual Studio – I was getting an application error when opening the project, and if I tried to build the project, it would fail but not give any errors in the error pane. After updating Visual Studio – the project started working fine!

I guess it’s time to brush up on my C programming!!!

Please follow and share!
Twitter
Facebook
LinkedIn
RSS

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.