The new Meadow F7 microcontrollers are a really exciting development in the embedded .NET ecosystem.
Around this time 6 years ago, I started my university honours project - "Remote Data Acquisition and Transmission using Low Cost Computing Platforms" - using Netduino and Netduino 2 Plus microcontrollers.
The original Netduino was based around a 48MHz ARM7 processor and had 64Kb RAM - not much to play with! The Netduino 2 Plus was a bit beefier with a 168MHz ARM Cortex-M4 and 100Kb RAM, and also had onboard microSD and Ethernet connections.
Both of these boards were built to match the Arduino form factor, and could mount Arduino "shields". Each had about 1MB flash storage for code. They ran the .NET Micro Framework - a subset of the .NET Framework originally designed for smart watches, and later open sourced and marketed toward use on resource-constrained devices in general.
The .NET Micro Framework came with it's own set of challenges - it was a greatly reduced subset of the full framework and many commonly used .NET APIs were missing. While the .NET Framework on the desktop features a just-in-time compiler and a generational garbage collector, .NET MF is an interpeted runtime with a simple(r) mark-and-sweep GC.
Unsurprisingly, I found that the .NET runtime is unforgiving on such a constrained device and far greater attention needs to be paid to performance - in particular, garbage collection is very costly and allocating short-lived objects will result in frequent GC runs which halt program execution for significant periods. While, of course, it is good engineering practice to consider performance and not squander resources in any programming environment, it becomes absolutely critical on platforms such as the Netduino as there simply isn't any spare resource to waste.
I stumbled across the Meadow F7 Micro kickstarter campaign in November 2018 and pretty quickly became backer #453. It was already almost 200% funded and still had 28 days left to run.
The Meadow F7 Micro promised a significant upgrade over the Netduino series - a 216MHz ARM Cortex-M7, 16MB RAM (256x more than the original Netduino!) and 32MB flash storage. Although the onboard Ethernet and microSD connections had been dropped, the F7 Micro packed WiFi, Bluetooth Low Energy (BLE) and, perhaps most excitingly (for me), provided a .NET Standard 2.0 compliant runtime!
Switching from the niche .NET Micro Framework to mainstream .NET Standard massively increases the API surface available - not only is the .NET Standard 2.0 base class library extensive, but there are also thousands of pre-exising Nuget packages that can be installed at the click of a button.
The original delivery estimate was February 2019, although this proved to be optimistic - I eventually received my Meadow in November.
The F7 Micro proved really easy to get started with, simply by following the instructions provided by Wilderness Labs.
Assembly was straightforward, although I am always a bit nervous when soldering pins onto a (relatively) expensive board in case I cook it!
The board runs "Meadow OS", a minimalist real-time operating system based around the Linux kernel that provided "just enough" OS to host a .NET runtime environment (currently Mono).
Meadow OS is still very much in active development - at time of writing Beta 3 is the latest version. This means that the first thing you'll want to do with a new Meadow board is flash the latest Meadow OS build to it.
Note: The linked guide is pretty comprehensive - although it is important to take read the section regarding the
"Cannot open DFU device 0483:df11" error on Windows. Replacing the default Windows USB driver with
WinUSB using Zadig (as recommended) worked for me - although I initially got some
DEVICE_DESCRIPTOR_FAILURE errors due to a faulty USB cable.
Once the firmware is up-to-date, it's time to write some code!
I used Visual Studio 2019, but VS Code is also supported. Install the appropriate "Tools for Meadow" extension (in VS2019, go to
Manage Extensions and then search under the
Online tab). This provides deployment and debugging support. You'll probably need to restart Visual Studio to complete installation.
Once the extension is installed, you'll see a new "Meadow Application" project type:
Creating a project using this project type will pre-configure a basic, Hello World application that cycles the built in RGB LED through different colours. Simply hit F5 to download the program onto the Meadow device and run it - be patient, it can take a little while to start.
It's Still in Beta
At this point, I thought it would be interesting to write a short program to connect to my WiFi network and then use
HttpClient to retrieve some data from the web. Unfortunately, this was when I discovered that networking support will not be available until Beta 4 is released - there doesn't seem to be any information about when this might be.
A Simple Thermometer
As I had volunteered to deliver a talk inroducing the Meadow F7 Micro at the Aberdeen .NET Developers User Group I needed to come up with a demonstration that was slightly more than the "blinky" app but still simple enough to talk about in 15 minutes or so.
I decided to build a digital thermometer using a TMP36 temperature sensor and a MAX7219 LED display. I initially wanted to use a ST7735 1.44" TFT display as the Meadow.Foundation libraries already included support for this, but I couldn't get one delivered in time.
TMP36 Temperature Sensor
The TMP36 is a cheap and simple analog temperature sensor with an operating range of -40°C to 150°C. It only has three pins - power, ground, and signal. The sensor can be powered by 3.3v or 5v - which makes it useful in the largely 3.3v world of Netduino and Meadow.
The left pin should be connected to power and the right pin to ground. The middle pin provides an analog voltage output which varies linearly with temperature, which makes it pretty easy to work with.
The Meadow has 5 analog input pins and the API for accessing these is really simple - the following code will read the voltage currently applied to pin
IAnalogInputPort analogPort = device.CreateAnalogInputPort(Device.Pins.A00); float volts = await analogPort.Read();
From the TMP36 datasheet, we can see that the sensor provides a reading of 750mV at 25°C, meaning that voltage is offset from the temperature (i.e. 0v does not mean 0°C), and that the voltage varies by 10mV per degree celsius.
The equation of a straight line is
y = mx + c. We know that
y = 750 when
x = 25, and that 10mV = 1°C (
m = 10)
Subsitituting these values,
750 = (10 * 25) + c, therefore
c = 750 - 250 = 500.
This gives a calculation of
y = 10x + 500 - using
T for temperature and
V for voltage, we have
V = 10T + 500.
Since we know voltage and want to find temperature, we can rearrange to get
T = (V - 500) / 10.
Enough maths - in C#, this looks like:
IAnalogInputPort analogPort = device.CreateAnalogInputPort(Device.Pins.A00); float millivolts = await analogPort.Read() * 1000; // Read() returns volts float temperature = (millivolts - 500) / 10;
Note that the Meadow.Foundation libraries provide an AnalogTemperature class which abstracts away this maths. However, at time of writing it does not work correctly for the TMP36. TMP35 users may have more luck.
One of my colleagues kindly gave me a MAX7219 display for this demo (thanks Peter!)
The MAX7219 is a shift-register chip which drives a bank of 7-segment LEDs. This means it is quite limited in terms of the characters that can be displayed.
It has 5 pins which need to be connected - power (5V), ground, data in, load (or chip-select) and clock. Ignoring the power and ground pins, the remainder are all digital inputs - i.e. they are either on (high) or off (low).
The Meadow.Foundation libraries do not include any driver for the MAX7219 - luckily, the datasheet is quite comprehensive. I was also able to take inspiration from the LedControl Arduino library.
The MAX7219 is driven by a simple set of 16-bit commands - the first byte is the command, the following byte is the operand. A list of commands can be found in the MAX7219 datasheet.
Conceptually, the MAX7219 is straightforward to operate - pull the
load pin low, then repeatedly "bit-bang" the command bytes into the shift-register by pulling the
clock pin low, setting the
data pin high or low to match the current bit being sent (i.e. 1 or 0), and then pulling the
clock pin high again. Once the command has been sent, pull the
load pin high to execute.
The manual overhead of bit-banging can be avoided by using the SPI bus - this is also typically an order of magnitude faster as it has dedicated hardware available.
I found that bit-banging on the Meadow F7 is currently very slow - averaging around 62Hz in my tests. This meant that it took ~800 - 900ms to send a 16-bit command to the display, e.g. to set a single digit.
I had just finished implementing the two drivers for the TMP36 and MAX7219 individually, and written a program that would use both of them to update the display with the current temperature, when disaster struck! The Meadow stopped accepting deployments from Visual Studio.
I got a couple of different error messages, and in some cases Visual Studio hung for almost 5 minutes:
1>------ Deploy started: Project: HelloMeadow, Configuration: Debug Any CPU ------ 1>Deploying to Meadow on COM3... 1>Initializing Meadow 1>SerialPort cannot be null ========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========
1>------ Deploy started: Project: HelloMeadow, Configuration: Debug Any CPU ------ 1>Deploying to Meadow on COM3... 1>Initializing Meadow 1>Checking files on device (may take several seconds) 1>Writing App.exe 1>The semaphore timeout period has expired. 1> ========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========
1>------ Deploy started: Project: HelloMeadow, Configuration: Debug Any CPU ------ 1>Deploying to Meadow on COM3... 1>Initializing Meadow 1>Can't read Meadow device 1>Failed to initialize Meadow. Trying reconnecting the device. ========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========
I went through the usual sequence of debugging steps, but without success:
- Reboot Meadow
- Restart Visual Studio
- Reboot PC
- Try new USB cable
- Re-flash Meadow firmware (the DFU bootloader still worked)
- Reinstall USB-Serial device in Windows Device Manager
- Try another PC
I posted a thread on the Wilderness Labs Community forums. This didn't get any response for a couple of days, but then another user replied to report the same (unresolved) issue.
With the date of my talk getting closer and still no response from the Meadow board, I turned to the Wilderness Labs Slack channel. A number of Wilderness Labs employees picked up on my issue and did their best to help diagnose - I am immensely grateful for the time they put into it!
We went through some more advanced diagnostics using the Meadow CLI:
- Attempt to erase the flash using
Meadow.CLI --EraseFlash --SerialPort COM3
- Attempt to confirm flash erased
Meadow.CLI --VerifyErasedFlash --SerialPort COM3
- Attempt to confirm communication with the board using
Meadow.CLI --SetTraceLevel --SerialPort Com3 --TraceLevel 1(expected to return a string almost instantly)
The eventual conclusion was that the Meadow board was bricked - the Meadow was not responding to communications. Wilderness Labs very kindly offered to post a new board next-day delivery in order to try to get it to me in time for my talk.
I received the new Meadow the next morning and all seemed well. However, within a few minutes, it too had bricked! After some more discussion and diagnostics with Wilderness Labs, there turned out to be two issues:
- The Meadow F7 only supports running assemblies called
App.exe. Changing the
AssemblyName(and therefore the output filename) will cause a file to be written to the board, but it will not execute and will leave the board in a broken state.
- The public download of the Meadow CLI tools does not appear to correctly erase the flash when called with
--EraseFlash. The latest binaries direct from the developers were able to successfully erase the flash.
With both Meadow boards resurrected, I'm ready for my talk!
The Meadow F7 Micro is a big step forward from the Netduino series and it will be really exciting to watch the technology mature. There are still a number of rough edges and missing features, and it is still a bit too easy to accidentally brick the device!
I'm really looking forward to Beta 4, and eventually the v1.0 release!