Microchip AVR-IoT WG Dev Board - Review

Table of contents

RoadTest: Microchip AVR-IoT WG Dev Board

Author: florinescu

Creation date:

Evaluation Type: Development Boards & Tools

Did you receive all parts the manufacturer stated would be included in the package?: True

What other parts do you consider comparable to this product?: Arduino MKR WiFi, ESP-32, Arduino Uno WiFi

What were the biggest problems encountered?: connection reliability

Detailed Review:

Introduction

 

Hello! My name is Florin, I am a major in Systems Engineering currently employed in an automotive company and here is my review of the Microchip AVR-IOT WG Development Board. This review is more of an analysis of the board and its capabilities, than a walkthrough of a demo project using it. I try to answer the questions "How can it help me develop an IoT solution? What differentiates it from similar products?".

My first concern with reviewing this board is power consumption and whether it would be useful for battery powered projects where it would last weeks without a charge.

 

Unboxing

 

I received a small box containing just the board. It is a bit disappointing that they didn't include a microUSB cable to connect the board to the PC, but since most likely everyone has one lying around, it's not that big of an issue and it helps bring costs down for Microchip. There is no manual inside which, again, is not necessary as everything you need can be found on the manufacturer's page.

 

Demo App

 

First thing when you connect the device to your PC is the Demo app. The debugger enumerates as a USB storage with some files with board info. There is a CLICK-ME.html file which opens a page in the avr-iot.com domain showing you how to set up your device to use your wifi credentials and showing you data retrieved from Google Cloud once the device is successfully connected

With regards to the guy who had his PC destroyed by this board, the autorun.inf file only specifies the icon to display for the drive. Even if your device has been tampered with and a malicious .exe has been added to the storage, since windows 7 & up, the OS DOES NOT automatically execute what the autorun file points to.

 

When connection is successful, the web page redirected to from CLICK-ME.html looks like this:

image

 

The data looks ok, even if I don't have a second device to compare brightness or temperature. It is updated in real time once every second. The measurement units aren't displayed. For the light sensor, it's lux, and for temperature it's °C. Sorry USA!

A big downside of the board: it gets a bit warm. When thinking of a development board with light & temperature sensors, the first thing that comes to mind is using it for ambient measurements. This however is not possible since the temperature sensor warms up to the board's 33-35 °C. In some projects board temperature can be relevant, however if you want to monitor room temperature you would need to connect a separate sensor unless it's summer and over 35 degrees outside. Even with proper sleep mode to reduce current, the board was hovering around 26 °, a few degrees above room temperature.

 

Support documents

 

Microchip's device page contains plenty of documentation, the first being the User Guide. It shows you how to first use the board and how to set up your environment to be able to build for the device. I am not a particular fan of splitting the documentation in several files, as is the case here. The Technical Summary duplicates some of the information in the User Guide, but provides more in-depth information such as power generation for the board, some particularities regarding power supply and current sensing and what all the on-board LEDs do in the default project. There are also some documents like "Development Board Overview" and "Pocket Guide" which also show very basic information like board pinout. There is also a schematic, a must for any kind of modification. It's very nice that they also provide full datasheets for everything relevant on the board.

It can get a bit confusing with so many documents if you are looking for that one bit of info that you can't remember where you've read, but once you read through all of them a few times you get used with where to find what.

 

Hardware

 

The board is comparable in size and features to other embedded development boards like the Arduino MKR WiFi or the ESP-8266. It appears as if it was designed to allow you to cut off the debugger and power supply side for maybe an end-product where no programming would be necessary and power is supplied some other way.

 

Reasons why this board is preferable over other solutions:

  • Size. It reduces clutter with prototyping wires and can you can more easily make a DIY housing for your projects (3d printing, cardboard box, whatever). It is noteworthy that this one doesn't come with any kind of pins attached. Added flexibility, but you need to buy pin headers separately.
  • On-board debugger. No external debugger to take up extra space (e.g. PicKit or AVR ICE), but no limited functionality (e.g. debugging possible, unlike for the classic Arduino)
  • Battery connector AND battery charger. You can use this board to test actual real-life usage scenarios of your end product.

Things that could be improved with the hardware design:

  • No reset button. The reset pin is connected to the SW0 button and even if the reset were enabled by fuse, there is no external pull up on board and obviously the internal one can't be used.
  • Battery charges very slow. Charge current could be easily increased for this design.
  • Not all microcontroller pins are exposed.
  • No pin headers and battery connector packaged, no USB cable.

 

Power Management

 

The board has a very nice power supply system, definitely designed with low power applications in mind. It uses a battery charge IC to provide either USB or battery voltage to the 3.3V buck regulator which powers the system (microcontroller, debugger and all other ICs). 3.3V, not 5V as is the case with the more popular development boards.

Highlights:

  • Battery charge manager
    • Provides output to the system from either the 5V from USB and the 3.7V (typ.) from the Li-Ion/Li-Po battery, based on which is connected, prioritizing the 5V if both are present.
    • Has outputs to signal low battery and charge states, which are connected to on-board LEDs. The Low Battery output is active when the battery voltage drops under 3.1 V. Not all battery chargers have this output and for most projects it eliminates the need to monitor the battery voltage via ADC of the microcontroller.
    • Power draw specifications:
      • It is limited by the board design to draw up to 500 mA from USB. A pity since it could've been configured to draw up to 1.8 A from a wall plug, and provide up to 1.65 A to the system to enable more power hungry applications. This won't be needed if you don't have a large motor or some power LEDs connected.
      • It provides 100 mA to the connected battery. Be careful to not connect really small batteries since it will fry them if they can't handle this current. The charge current can be changed by replacing the R104 resistor according to the IC's datasheet, but it will not exceed 500mA.
    • Has a safety timer to stop the charging if it's been ongoing for over 6 hours and only resumes charging if battery is disconnected and reconnected. This prevents overheating and may indicate a faulty battery.
    • Has the possibility to connect a thermistor which can cause the charge to halt while the battery gets too hot. There is a 10k resistor on board (R103) which can be desoldered and an actual thermistor can be wired to the pads if you want to use this. The datasheet provides an example of resistor network to connect in series & parallel to the thermistor to set the charge temperature range to 0-50°C.
  • Voltage regulator
    • I will not detail its internal construction, but it is a very good switching regulator using very little board space for a buck. It is well suited for extremely low current consumptions.
    • Theoretically provides up to 600 mA at 3.3V, but this will only happen when connected to a battery that can provide that. It will be less on USB due to the max board input current.
    • Can handle very low current modes, consuming max. 32 μA at 0mA output (in operating mode, not in shut down!). Most switching regulators need a minimum load in the order of a few mA to function properly which wouldn't work in a battery powered device.

Sensors

 

The light sensor is a phototransistor which the development board technical summary says will draw 10-50 uA between 20-100 lux. In full daylight you can expect over 1000 lux which increases the current consumption by quite a lot more.

The temperature sensor is a bit more sophisticated:

  • digital, provides data as 16 bit words via I2C. Only 12 bits + a sign bit are actually used out of those, and the data is in two's complement format
  • 0.25°C typical accuracy with 0.5°C max.
  • max. resolution of 0.0625°C/bit. Averaging a number of reads with max. resolution can provide close to this level of accuracy if needed.
  • conversion time between 30-250 ms, depending on accuracy
  • provides an alarm signal on a separate pin if temperature goes above or under a threshold

 

Microcontroller

 

The ATMega4808, an 8bit microcontroller part of the megaAVR 0 series, is the brain of this board. Highlight of features compared to the ATMega328P (and other older AVRs) from the Arduino Uno:

  • Configuration Change Protection, a feature which requires you to write a certain key to a special register before writing to certain registers. A failsafe so that runaway code will not write crap all over your registers.
  • Brown-out detection with a choice of EIGHT voltage levels. The usual ATMegas have 2 options only.
    • can either be permanently enabled or periodically sampled, with the ability to select either of these modes independently for when the CPU is active and in sleep
    • has a voltage level monitor to trigger an interrupt at a voltage level x% above the BOD one
  • Internal oscillator configurable for 16 or 20 MHz and ANOTHER internal low power 32kHz oscillator. Usual ATMegas have an 8 MHz which from factory is scaled down via fuse to 1 MHz.
    • It is only guaranteed to operate up to around 10 MHz at 3.3V supply, so the internal clock prescaler must be configured to at least 1:2 for proper operation.
    • Interesting design that the controller only supports a 32KHz crystal oscillator, no external high speed oscillator, but external high speed clock signal is possible.
    • The oscillator also has configurable temperature drift compensation. You don't see this on every controller and it's important for timekeeping over long periods of time.
  • Reset not on independent pin. The pin is PF6, connected to the unboard button SW0 and the debugger DGI. It is configurable via fuse as either GPIO or RESET input.
  • Controller has an instruction to trigger a software reset. For the 328P this is done by temporarily enabling watchdog, waiting for it to reset system and disabling it.
  • All pins have interrupts configurable for one edge, both edges or level interrupt trigger. On the 328P, only 2 pins have the possibility to configure the edges.
  • Remappable peripherals to add flexibility to connecting stuff to the board.
    • digital input buffers can be disabled for each pin individually
  • Event system used to signal stuff from one peripheral to others. Think of it like linking an ADC start conversion to a timer compare match, only extendable to more peripherals with up to 8 simultaneously configured Events (pair of trigger signal - one or more consumer peripheral action). This allows the controller to do stuff while in sleep, huge power saver and code simplifier.
  • Configurable Custom Logic peripheral. The datasheet explains it very well: "The CCL can serve as "glue logic" between the device peripherals and external devices. The CCL can eliminate the need for external logic components, and can also help the designer to overcome real-time constraints by combining core independent peripherals to handle the most time-critical parts of the application independent of the CPU.". Button debouncing or input signal filtering becomes trivial with this one.
  • 0.55-4.3 V 5 level configurable ADC and AC (analog comparator) voltage reference. Other ATMegas have a 1.1 V and sometimes a 2.5 V reference.
  • Watchdog with windowed mode. Compared to the Normal mode, the Window mode can catch situations where a code error causes constant watchdog resets. In practice, it forces you to be smart with regards to watchdog usage and not constantly reset it at the end of the while (1).
  • TCA - 16 bit timers/counters that is more output oriented, with no input capture support
    • buffered period and compare registers mean you can write anytime during a PWM period to set a new period or duty cycle and it won't update until the period finishes. No more funky shorter or wrong % intermediary periods or waiting for period interrupt request to write the new values.
    • also the waveform generation modes are simplified vs. the 328. Single/dual slope PWM, while having the same functionality, are much clearer names to understand compared to Fast PWM, Phase/Frequency correct PWM. They also got rid of the 8/9/10 bit counting choices.
    • FINALLY, long gone are the horrible days of using input capture or output compare registers for PWM period instead of having a dedicated period register. It is MUCH clearer how the timer functions and to write code for it.
  • TCB - 16 bit timers/counters, the input capture-oriented complement to TCA. It has several input capture modes and simpler, 8 bit PWM only.
    • input capture can be used with the Event controller only, which means that way more pins are usable than on the 328P, but other peripheral output signals can be measured as well.
    • one interesting mode is the time-out check. This is a regular input-capture which measures the duration of an event pulse, but can trigger an interrupt if the pulse exceeds a certain duration. My ultrasonic project below is using this.
  • RTC clocked from internal 32KHz low power clock
    • 15 bit prescaler which can support resolutions up to 1 second, overflowing at over 18 hours.
  • Periodic Interrupt Timer (PIT) permanently running during sleep which can provide a time base for your application (e.g. for scheduler)
  • USART is pretty much the same, with 2 noteworthy additions:
    • fractional baud rate generator - the baud rate register has 10 integer and 6 fractional bits for configuring the baud rate. No need for external oscillator with certain frequencies (7.38 MHz) or oscillator calibration on per-device basis.
    • infrared pulse modulation/demodulation
  • simultaneous and independent master&slave I2C (TWI) operation, each with its own set of registers
  • ADC with hardware accumulator. Instead of sampling 4 times and averaging those samples in SW, with this you can just get the sum of 4 consecutive samples in the result register and an interrupt only at the end.
    • there are no differential inputs, which is something I miss from the 328P.
  • I accidentally fed a 5V signal into the controller for more than half an hour before realizing it. Shame on me! But the controller was fine due to the internal protection diodes. I have fried some ARM controllers this way, so it's nice to see Microchip keeping this life saver for newer controllers.

 

Overall this controller feels really great to work with. It has many obvious improvements: some directly targeted at low power usage, some at simplifying programming for beginners, some as proof that they have learned from their experiences with older ATMegas and are bringing new stuff to the table. It is built around IoT. The Core Independent features (Event Controller and Configurable Custom Logic) allow for great power saving and move lots of not-so-trivial SW to the HW.

Doesn't take much to get used to the differences if coming from older ATMegas.

 

Board pinout

 

As previously mentioned, the board doesn't come with any kind of pin header. You need to mount them yourself, based on what you may need). Should go for female straight headers if you want to use MikroElektronika click boards.

The board advertisement placed a lot of emphasis on it having a pinout called mikroBUS compatible with MikroElektronika click boards. For those that never heard of these before, they are similar to the arduino shields in that they are a secondary board attaching to all of your board's pins, even if not using all of them. The reasoning behind these click boards is that you just plug&play the board to add features. For a board trying to be the IoT equivalent to beginners' Arduino, I am sure it is a most welcome feature. But I am not a fan of this design since you cannot access pins not used by the click board, and I do not have any of these boards around.

Also if you put a click board over this one, it completely covers the ambient light sensor. image Bit of a slip on the board design.

 

Anyway, what pins are available? As it turns out, not all of them. All pins of the microcontroller are exposed (including the I2C and SPI busses) except some connected to other modules.

The microcontroller pins not exposed on the board are:

  • PD0 - PD3 - connected to the status LEDs
  • PD5 - connected to the output of the light sensor
  • PC2 - interrupt output of temperature sensor
  • PF5, PF6 - connected to the onboard buttons and the debugger's DGI as GPIOs. The buttons require internal pull-ups from the controller. The one connected to PF6 can act as a reset button.
  • UPDI - connected to debugger for one-wire programming
  • PF2 - PF4 - connected to wifi module
  • PF0, PF1 - connected to the debugger, used to forward UART from microcontroller to host PC via USB CDC

 

The 5V supply to the mikroBUS header is not connected by default. To enable 5V to the header, solder in a 0-ohms resistor (0603) or a solder blob over the footprint under the 5V pin. It's worth mentioning that this pin is connected to the output of the battery charge circuit, so it is 5V only while powered from USB, otherwise the battery voltage will be present here.

 

The fact that it includes battery management and a battery connector is amazing and uncommon for development boards. The connector is tiny and I always feel like I will rip it from the board when disconnecting the battery. I had to do a little research to find the right crimp terminals and connector housing, since Microchip only provides the connector part number in the BOM. Here are the manufacturer codes to spare other people some trouble: JST PHR-2 and SPH-002T-P0.5S.

 

The board has two straps that can be cut to measure current usage for the entire system (3V3 rail) excluding the debugger, and for the debugger separately. It could've been prettier to have a solder blob joining two pads from the factory, a removable 0 ohm resistor, or even a pin header with a jumper. For me it's a slight annoyance that the 2 pads, once cut, don't have a 0.1" spacing, but there are 2mm jumpers or 0805 SMD 0 ohm resistors instead to connect things back in a pretty way.

 

Default power usage

 

For a board marketed for IoT, obviously the biggest concern is power usage. Based on the datasheets for all of the devices, I have estimated the current draw as follows:

 

ComponentStand-by current (if applicable)Typical on currentMax. on current
Battery Charger

28 uA battery only

28 uA battery only

180 uA USB only

250 uA USB + battery charged

2.5 mA USB + battery charging

50 uA battery only

300 uA USB only

350 uA USB + battery charged

3.8 mA USB + battery charging

Buck converter20 uA20 uA32 uA
Debugger0 A if disabled3.4 mA3.7 mA
Microcontroller0.1 uA2.3 mA5 mA
Temperature sensor0.1 uA200 uA400 uA
Light sensor5 uA50  uA1 mA full sunlight
Secure element2 uA if device is sent to sleep2 mA14 mA processing
WiFi380 uA100 mA290 mA while transmitting
Status LEDs0 A (no LED on)

0.4 mA blue LED+

1 mA green LED

2.4 mA (3 LEDs on)
Battery charge LEDs0 A

0 A no battery connected or battery charged

1 mA USB + battery connected

2 mA charged for more than 6 h
Debugger power LED0 A1 mA1 mA
Total104 uA

111 mA battery only, USB only or USB + battery charged

113 mA USB + battery charging

315-320 mA

 

To measure the actual current, I have done the following:

     1) Mount a battery connector to my battery. The battery I used is a Cellevia LP573450 with 980mAh capacity and 5C rated discharge current. It has its own protection circuit, which disconnects the battery when it discharges past 3.3 V. I remember having a battery from MikroElektronika with this particular connector but I cannot find it.

     I used regular pliers to tighten the crimp terminals to the wire and the insulation since every terminal type comes with its own crimp tool. A thing I do in this case to make sure the terminal makes a good connection to the wire is to add a bit of solder to where the terminal crimps the wire conductor. You need to be very quick with this or the terminal heats and melts the insulation.

     You can see in the pictures what the connectors look like, how ugly I crimped them, and the board finally powered from the battery.

     imageimageimage

     2) Next I cut off the straps for target and debugger supply and soldered wires to them. My intention is to use aligator clips to connect them to my multimeters for current measurement and connect the wires to each other to re-do the straps when this is not needed.

     Here I also soldered pin headers to the board and mounted some screws so I wouldn't risk ripping the wires from moving the board around the desk.

    imageimage

     3) Here is the final board with the aligator clips connected to the wires.

     image

     4) Current measurement running off battery only. Left multimeter is target current, right one is nEDBG current. All following current measurements are done with default app running.

     image

     5) Current measurement running off USB. First picture is before WiFi chip is initialized and communicating, second one is after WiFi is running

     imageimage

     6) Battery fully charged

     image

     7) 6 hour timer kicks in. For my 980 mAh battery, 100 mA current for charging is too slow.

     Contrary to the charger datasheet which says that if the timer kicks in you must disconnect and reconnect the battery, I simply reconnected the USB cable leaving the battery as-is and it started a new charge cycle. Funny.

     image

 

As we can see I was off by a lot in my estimates, especially regarding WiFi. To sum up my findings:

  • debugger eats up ~14 mA permanently while connected to USB. It does not enter a low power mode when debugging isn't ongoing as I had hoped.
  • debugger drops slightly to ~9 mA while battery powered.It does not enter a low power mode either. My guess is that it's constantly sampling the USB supply line to detect when USB is plugged in. The lower power draw could be because the LED is off (1 mA) as well as the USB peripheral being turned off (possibly some others as well).
  • the system (microcontroller+other ICs) eat up ~17 mA while WiFi is not yet initialized.
  • current consumption from the system jumps to ~110 mA while connected to WiFi. It jumps ~10 mA higher while transmitting data, but i expect an instantaneous current while transmitting to be way higher, just too quick so the multimeter averages it out.

 

This is huge and the default application kills my 980mAh battery in around 10-11 hours.

Battery charge time was similar, taking around 10-11 hours for a full charge. This however can be improved by replacing the R104 resistor according to the charger datasheet.

Unfortunately my battery protection circuit kicks in at 3.3 V open circuit, way before the charger's 3.1 V threshold for the Low Battery indication. I have seen the low battery LED on occasion. Also the battery sometimes got charged to 4.25V and still the red LED was on, as the charger thought it wasn't yet done. This may also be because of the battery protection which would draw more current than the charge termination logic would expect.

 

As you can see, the debugger, LEDs and WiFi play a major role in optimizing board power consumption.

The 4 status LEDs are connected to the microcontroller and therefore can be disabled in software to save power. The battery LEDs do not bother during normal usage but you can remove the series resistor R102 so the low battery LED doesn't draw power while the signal is active. 1mA may not seem much, but compared to the sleep low power consumption, that is a big increase! You could warn about a low battery via WiFi once instead of having an LED on continuously.

The debugger supply is separate and can be cut off by cutting the VCC nEDBG strap. The debugger LED turns off while only battery powered, but the debugger does not enter any low power state. Therefore i recommend leaving its supply cut off. Have a jumper there so you can easily switch between debugging and battery testing. Very important to set all microcontroller pins connected to the debugger as output LOW, and do not connect the USB while the debugger supply is disconnected. Otherwise the debugger gets powered parasitically via its I/Os and will be destroyed by this.

 

Google Cloud setup

 

The web page showing you device data streamed has a section to help you migrate your device to your Google Cloud Platform account, which is actually a github repo which needs to be cloned into some folder into your console account. A complicated process to be honest, but it is something you need to do once and then it's done. From now on all the data resides in my Google account and I can view it in Firebase Console in the Database menu. The hashed things are my project ID and device ID. The timestamp is an epoch timestamp (seconds since 1 January 1970).

image

Extending the data displayed to further fields is surprisingly easy. I just modified the sendToCloud() function with a new JSON field for human detection in hopes that it will show up in firebase and it did, updated in real time. Also the application linked to the private account (slightly different to the sandbox one) automatically started displaying a chart for the new field.

int len = sprintf(json, "{\"Light\":%d,\"Temp\":\"%d.%02d\",\"Human\":\"1\"}", light, rawTemperature / 100, abs(rawTemperature) % 100);

                                                       imageimage

The demo project also shows you how to use a toggle on the website to send data TO the board and react to it. Unfortunately, once switching over to my private account, adding toggles or fields to the web app is gone from the interface. There is not much help available with regards to adding functionality to the web application and there is a lot of digging necessary here. The bottom of the page is full however with messages telling you to get in touch with Leverege, the web developer behind the interface, to have them design it for you. Not much help from them or Microchip for the casual hobbyist. image

 

Debugger communication

 

There are 3 methods for flashing/communicating with the device:

  1. Drag'n'drop
    • you can drop a .hex file into the flash storage enumerated by the debugger, but keep in mind that this is not actually flash storage, so don't store random files there.
    • I used this to push the latest firmware hex (v. 1.1.0) to the device. Windows appeared stuck initializing the copy and it instantly finished with the on-board LEDs lighting up with the new SW being flashed while windows was "copying". The hex file still appeared in the mass storage device until i replugged the USB cable.
    • you can add text files with commands inside them in the format of e.g. "CMD:ERASE" which erases the controller flash. This is also the mechanism by which the WiFi config is sent. The syntax is identical to the CDC one. After you configure your WiFi SSID and password once, the controller saves them to its EEPROM.
  2. Commands via CDC
    • this works only if you enable CLI in your application and isn't very useful past configuring your WiFi in the default state. It provides however a basic framework that can be extended to allow for diagnostics and other quick configuration for when your project will be on the field.
  3. Regular programming/debugging via 1 pin UPDI interface.

 

Programming and improving power consumption

 

Writing code for the controller is a bit different in C than for older ATMegas. The first and most striking is the fact that the registers are not defined individually, but based on their associated peripheral, defined via C structures. This means that the initial address of registers of a peripheral is defined as the address of the struct, and all struct members follow as offsets of that address. This is done in the iom4808.h header, which is the controller specific header assigned when you include <avr/io.h>. The datasheet also refers to the peripherals in this manner. Neat trick to keep things organised. For instance, PORTD's registers on ATMega328P are DDRD for direction, PORTD for output, PIND for input. On the ATMega4808, PORTD.DIR is for direction, PORTD.OUT and PORTD.in for output and input, respectively.

 

There is another trick implemented in hardware regarding GPIOs. There are certain registers which allow you to configure a pin without disturbing the other pins. If you write value 0x02 to PORTB.DIR to set pin B1 as output, you also automatically reconfigure other pins in port B as inputs. Instead, you can write a 0x02 to PORTB.DIRSET to only set bit 1 in PORTB.DIR (pin B1 as output), without changing other registers. Similarly, writing 0x02 to PORTB.DIRCLR will clear bit 1 in PORTB.DIR (B1 as an input). There are similar registers called PORTx.DIRTGL, OUTSET, OUTCLR and OUTTGL which are pretty self explanatory. I can see this being useful as a fail-safe for beginners, but good luck porting that to older controllers!

 

This controller also sets the notion of virtual port. This means that the basic port registers (not the strobe command ones like DIRSET, DIRCLR), normally located in the extended I/O memory space are also mapped to the bit-accessible I/O space. This may make the compiler turn a 8bit write to PORT.DIR into a 1bit write to VPORT.DIR. It was a bit confusing for me at first when I saw that the code generated by the Atmel Start configurator used VPORTD but also includes functions for PORTD. Again, using VPORT will decrease portability.

 

You can modify fuses from Atmel Studio and presumably from MPLAB as well.

 

To get used to programming the board, I thought I could work on the demo app to try to improve its power consumption as much as possible.

First I just wrote a quick demo function to put the controller in sleep mode resulted in a 600 uA current draw. Not bad!

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

void sleep()
{
     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
     //Enable interrupts to be able to wake up again
     sei();
     //Go in sleep mode and clear sleep enable bit when woken up
     sleep_mode();
}

int main(void)
{
     ...
    while (1) 
    {
          ...
         sleep();
    }
}

image

 

Then I switched to the demo application and started modifying. First, only init the I2C, nothing else, and inserted the sleep function call into while (1). Modified sleep function to also disable I2C. Goal here is to later talk to the temp sensor and secure element to disable them.

void sleep()
{
     TWI0.MCTRLA &= ~TWI_ENABLE_bm;
     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
     //Enable interrupts to be able to wake up again
     sei();
     //Go in sleep mode and clear sleep enable bit when woken up
     sleep_mode();
}

imageimage

This should have just enabled the TWI, then disable it and enter sleep. Why is then the consumption so much worse? 2 things happening here:

  1. There is a function mcu_init() that is supposed to disable all peripherals to save power. This actually doesn't do anything, and way worse: it enables pull ups on all pins of the controller. Removing this cut the current in half.
  2. Something is pulling the I2C SCL and SDA lines low. Turns out the Atmel Configurator code initializes the PA2, PA3 GPIOS as outputs with low state. This does not affect I2C while communicating, but as soon as I disable the TWI peripheral, the GPIOs kick in and pull the line low. Fixing this drops the current to 600-700 uA, closer to what I saw before with sleep only mode.

 

I also noticed that the current usage would randomly move from ~400 to 700+ uA. Turns out it was a cloudy day and the sun was 5 minutes brighter, 5 minutes behind clouds. At this point the light sensor is starting to become a big power drain.

I tried to put some black tape over it but it didn't do much. I wouldn't want to remove its series resistor, so I did the most sensible thing to do: cover the board with something big and thick. image

image

Now I put the temperature sensor into shutdown mode by adapting the existing sensors_handling file with the following sleep function which will be called by my sleep routine. I also included a wakeup function for later.

void SENSORS_sleep(void)
{
     uint16_t regValue = I2C_0_read2ByteRegister(MCP9809_ADDR, MCP9809_REG_CONFIG);
     I2C_0_write2ByteRegister(MCP9809_ADDR, MCP9809_REG_CONFIG, regValue | 0x0100);
}

void SENSORS_wakeup(void)
{
     uint16_t regValue = I2C_0_read2ByteRegister(MCP9809_ADDR, MCP9809_REG_CONFIG);
     I2C_0_write2ByteRegister(MCP9809_ADDR, MCP9809_REG_CONFIG, regValue & ~0x0100);
}

Now we are starting to see real results!

image

Next I thought hey, since the badly designed mcu_init() is there, why don't I use it to disable all pin digital input buffers at reset, and later enable only the ones that are needed?

The results are amazing! The input buffer current draw obviously depends on what is connected to the pin and the pin state.

void mcu_init(void)
{
     /* On AVR devices all peripherals are enable from power on reset, this
     * disables all peripherals to save power. Driver shall enable
     * peripheral if used */

     /* Disable input buffers for all pins */

     for (uint8_t i = 0; i < 8; i++)
     {
          *((uint8_t *)&PORTA + 0x10 + i) |= PORT_ISC_INPUT_DISABLE_gc;
     }

     //...
     //similarly for all ports
}

This photo shows the lowest possible current I have achieved with this board (WiFi disabled though):

image

Next, set the ATECC608 to sleep. I found the library function for this, called atcab_sleep(). Needed to uncomment cryptoauth lib init and add sleep command to our sleep function. No difference in power consumption which means that the ATEC was anyways sleeping while not yet initialized.

Also no power difference when enabling CLI (command line interface) and its corresponding UART.

 

For WiFi, things get a little more complicated. You can either downright disable it, which means reconnecting every time it wakes up, or you can set it to manage its sleep mode by itself, while keeping the WIFi connection alive. I chose the auto deep sleep mode that must be configured once after the module is initialized, before connecting. Added the following portion to the reInit() function in cloud_service.c, while also enabling the scheduler and everything back. Also increased the data send interval to 10 seconds via MAIN_DATATASK_INTERVAL and the M2M_LISTEN_INTERVAL to 10 (WiFi router can queue up to 10 beacon intervals of data before actually sending it).

static uint8_t reInit(void)
{
     ...
     //Configure WiFi sleep mode
     tstrM2mLsnInt strM2mLsnInt;
     strM2mLsnInt.u16LsnInt = M2M_LISTEN_INTERVAL;
     m2m_wifi_set_sleep_mode(M2M_PS_DEEP_AUTOMATIC, true);
     m2m_wifi_set_lsn_int(&strM2mLsnInt);
     ...
}

 

Finally by integrating all of the above I have achieved a 0.7 mA sleep current while keeping WiFi connected. To test the sleep current I forced the microcontroller to remain stuck in sleep mode after communication to Google Cloud was established. Otherwise my multimeter couldn't sample accurate data because of the WiFi communicating for DTIM sync once every second. There were some times where getting into sleep mode for good still caused a power draw of ~12 mA, which I think is because I was abruptly interrupting the WiFi module, but it was pretty sporadic and just for the sake of demonstration, I didn't look into it further.

My results with different transmission intervals can be seen in the following tables. They assume my 980mAh battery.

 

No power save

average current~110 mA
battery life9 h

 

Sending data every second

StateDuration (ms)Current (mA)Charge (mA * ms)
sleep8350,7584,5
WiFi DTIM sync151001500
actively sending data15025037500
total charge39584,5 mA * ms
average current~40 mA
battery life24,5 h

 

Sending data every 10 seconds

StateDuration (ms)Current (mA)Charge (mA * ms)
sleep98350,76884,5
WiFi DTIM sync15010015000
actively sending data15025037500
total charge59290 mA * ms
average current~5,9 mA
battery life165 h (1 week)

 

Sending data every minute

StateDuration (ms)Current (mA)Charge (mA * ms)
sleep598350,741884,5
WiFi DTIM sync90010090000
actively sending data15025037500
total charge168765 mA * ms
average current~2,8 mA
battery life348 h (2 weeks)

 

As you can see, choosing your timing strategy and implementing proper sleep is very important to improving your project. Just by implementing proper sleep modes, my project's battery life increased by 275%, up to 1 day. By transmitting 10 times less often, I had around a 7x increase in battery life. By further increasing transmission interval to once a minute, which is completely realistic for a light&temperature sensor, it is able to last for 2 weeks. All in all, an almost 15 times lifetime increase! For receiving data there shouldn't be major power increases, as this would happen in one of the DTIM sync windows.

 

I have generated a graph showing power draw based on send interval data from the previous tables.

There are however 2 tipping points. One at 25 sec transmission interval, where maintaining the WiFi connection and syncing with the router takes up as much power as sending a single packet. And another point at 55 seconds, where sleep mode starts to eat up more than actually sending the data once. After this point you wouldn't see much benefit by increasing the transmit interval. Following chart demonstrates battery life and power draw for intervals between 1 second and 24 hours.

image

 

Now what if you were to fully disable WiFi during sleep (via enable pin) and reconnect to router, send a message, then sleep again? This way the sleep current diminishes to 100 uA so it must be good, right? As it turns out, that doesn't make much sense for really low send intervals, since it takes 5-10 seconds to connect to the router. Only if you send really rarely, at around once every 20 minutes, it does actually break even with having kept the WiFi connected all that time. Increasing the connect&send interval past that point begins to increase battery life exponentially. It probably won't be possible to reach a month of battery life due to the battery leakage, temperature and etc, so I cut the graph at the 1 hour mark. You can see that even when sending data once an hour, it takes almost 90% of total power just to connect to the router.

 

image

Of course this is just theory and real life values will be different, but the trend is still clear. If you can get away with transmitting data and reacting to received data more rarely, do it. Around the "communication once every 20 minutes" mark, it starts to make sense to turn off WiFi completely between transmissions. If I were to, say, monitor light&temperature in a field, getting minute updates wouldn't even be that relevant. Throw in a small palm-size solar panel and I may only need to change the battery once every few years when it won't hold a charge any more.

 

I tried to test the setup for transmission once every 10 sec to see the real life behavior, but somewhere around 12 hours I noticed the connection dropped and I am not sure how much earlier that happened. This random disconnect and lock up has happened repeatedly while trying to retest. I suspect this is due to the way my SW sometimes enters sleep mode while the WiFi was supposed to be doing something. Still, usually after 8 hours the battery voltage dropped from 4.2 V to 4.0 V, and after 12 hours, the battery voltage dropped to 3.9 V, so it should have way over half its capacity left, somewhat better than my estimates. This is accounting for the fact that battery capacity isn't linearly proportional to voltage.

 

Test project

 

I have come up with a basic project to test the board overall functionality. It is an automated porch light which uses an HC-SR04 sensor to sense if someone is present, and if there is, turn on an LED but PWM it so the room doesn't get too "bright". The device gets the current time from NTP and will not turn the light on if it is still day (between sunrise and sunset). There is a button to completely kill the lights. Everything is output to the cloud, where it could be used in whatever way. The idea behind this is to use as much of the core independent functionality as possible.

 

Here is the code I have made for this, only showing the relevant parts that I have added:

 

The NTP part is done automatically by the WiFi stack, so all I need to do is extract the saved timestamp, set my geo location for sunrise&sunset calculations and get those values.

 

void time_init()
{
     set_position(45.7489 * ONE_DEGREE, 21.2087 * ONE_DEGREE);
}

{
     time(&timestamp);
     sunrise = sun_rise(&timestamp);
     sunset = sun_set(&timestamp);
     timestamp += 2 * ONE_HOUR;
     sunrise += 2 * ONE_HOUR;
     sunset += 2 * ONE_HOUR;
     memcpy(&currentTime, localtime(&timestamp), sizeof(struct tm));
     memcpy(&sunriseTime, localtime(&sunrise), sizeof(struct tm));
     memcpy(&sunsetTime, localtime(&sunset), sizeof(struct tm));

     if (difftime(timestamp, sunrise) > 0 && difftime(sunset, timestamp) > 0)
     {
          //Day
          TCA0.SPLIT.HCMP1 = 0x00;
     }
     else
     {
          //Night
          if (humanPresent)
          {
               if (light < 500)
                    TCA0.SINGLE.CMP0BUF = TCA0.SINGLE.PER / 100 * ((500 - light) / 5);
               else
                    TCA0.SINGLE.CMP0BUF = 0;
          }
          else
          {
               TCA0.SINGLE.CMP0BUF = 0x00;
          }
     }
}

 

For the ultrasonic, I periodically pulse the trigger line and use TCB (Timer/Counter B) in pulse width input mode to measure the duration of the sensor's output pulse. Since TCB does not directly have an input pin, the pin state must be routed through the EVCTRL (Event Controller), also demonstrating this functionality.

 

void ultrasonic_init()
{
     EVSYS.CHANNEL2 = EVSYS_GENERATOR_PORT0_PIN0_gc;
     EVSYS.USERTCB0 = EVSYS_CHANNEL_CHANNEL2_gc;

     PORTC.PIN0CTRL = PORT_ISC_INTDISABLE_gc;
     TCB0.INTCTRL = TCB_CAPT_bm;
     TCB0.CTRLA = TCB_RUNSTDBY_bm | TCB_CLKSEL_CLKDIV2_gc;
     TCB0.CTRLB = TCB_CNTMODE_PW_gc;
     TCB0.EVCTRL = TCB_CAPTEI_bm;

     TCB0.CTRLA |= TCB_ENABLE_bm;
}

void ultrasonic_trigger()
{
     PORTC.OUTSET = 1 << 1;
     _delay_us(10);
     PORTC.OUTCLR = 1 << 1;
}

ISR(TCB0_INT_vect)
{
     ultrasonicResult = TCB0.CCMP / 29;
}
             

 

The LED uses TCA for PWM output and its duty cycle is adjusted once a second according to the light sensor. For the button I used the CCL to debounce it and produce an output that toggles once for each button press. This button state is fed by the event system into the TCA to enable/disable counting. One button press allows the PWM, another one blocks it. Only time the CPU has to run for this is the small bugfix CCL interrupt and changing the duty cycle.

 

void bulb_init()
{
     //Bulb PWM pin
     PORTA.OUTCLR = 1 << 0;
     PORTA.DIRSET = 1 << 0;

     //Event 0: CCL LUT0 out -> TCA0 count enable
     EVSYS.CHANNEL0 = EVSYS_GENERATOR_CCL_LUT0_gc;
     EVSYS.USERTCA0 = EVSYS_CHANNEL_CHANNEL0_gc;

     TCA0.SINGLE.EVCTRL = TCA_SINGLE_EVACT_HIGHLVL_gc | TCA_SINGLE_CNTEI_bm;
     TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
     TCA0.SINGLE.CTRLC = 0 << TCA_SINGLE_CMP0OV_bp;
     TCA0.SINGLE.PER = 10000;
     TCA0.SINGLE.DBGCTRL = TCA_SINGLE_DBGRUN_bm;

     TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
}

ISR(CCL_CCL_vect)
{
     //Lights were turned off from button
     //Set TCA0 output low (timer is stopped but output is stuck to whatever its last state)
     TCA0.SINGLE.CTRLESET = TCA_SINGLE_CMD_RESTART_gc;
     //CCL interrupt flag isn't cleared automatically
     CCL.INTFLAGS |= CCL_INT0_bm;
}

void button_init()
{
     //Light button pin
     PORTD.DIRCLR = 1 << 6;
     PORTD.PIN6CTRL = PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc;

     //Event 3: PD6 -> CCL LUT0A & LUT1A
     EVSYS.CHANNEL3 = EVSYS_GENERATOR_PORT1_PIN6_gc;
     EVSYS.USERCCLLUT0A = EVSYS_CHANNEL_CHANNEL3_gc;
     EVSYS.USERCCLLUT1A = EVSYS_CHANNEL_CHANNEL3_gc;

     //Configure CCL LUT 0 & 1 as inputs into JK
     CCL.SEQCTRL0 = CCL_SEQSEL0_JK_gc;

     //CCL LUT0 is fed into J
     CCL.LUT0CTRLB = CCL_INSEL1_MASK_gc | CCL_INSEL0_EVENTA_gc;
     CCL.LUT0CTRLC = CCL_INSEL2_MASK_gc;
     CCL.TRUTH0 = 0x01;
     CCL.LUT0CTRLA = CCL_EDGEDET_EN_gc | CCL_ENABLE_bm;

     //CCL LUT1 is fed into K
     CCL.LUT1CTRLB = CCL_INSEL1_EVENTB_gc | CCL_INSEL0_EVENTA_gc;
     CCL.LUT1CTRLC = CCL_INSEL2_MASK_gc;
     CCL.TRUTH1 = 0x0d;
     CCL.LUT1CTRLA = CCL_EDGEDET_EN_gc | CCL_ENABLE_bm;

     //Configure CCL JK output to interrupt on falling edge (lights off)
     CCL.INTCTRL0 = CCL_INTMODE0_FALLING_gc;

     CCL.CTRLA = CCL_ENABLE_bm;
}

 

This is what it looks like assembled and running (sorry no video):

imageimage

 

And this is what it looks like on the website:

image

 

What I learned about IoT development with this board

 

  1. You can connect the microcontroller to the battery management IC's status pins. Enable microcontroller internal pull-ups and solder wires between 2 GPIOs and the R101 & R102 resistors. You can also try a third jumper wire on the PG pin if you have a steady hand. This can provide you with great battery state indications. Useful if the device is in a remote location and you need to know when its battery is getting low, as you probably wouldn't want to go there to check on the battery LED.
  2. Regarding previous example, remove resistor R102 so the low battery state won't waste power by turning on an LED.
  3. Always use power save mode! Disable unneeded peripherals at application init (after reset). Disable ALL peripherals when entering low power mode, except the ones used to wake up. Even set unneeded output pins as inputs and disable all unused input pin buffers.
  4. Gather and transmit data as rarely as possible. If you are for instance measuring room temperature, you don't need to measure and report it once every second, or even worse, in a while (1). You can also average over a number of samples and transmit only the average to further save power.
  5. RTC or PIT can be used in sleep to wake up the device. Do not expect too precise wake up timing or timekeeping for days because the low power 32kHZ internal oscillator frequency can drift by anywhere between ±5% to ±30%. The board demo software synchronizes system clock with an NTP server. Use this instead.
  1. Always use a watchdog! For operation in remote locations there can be nothing more annoying than seeing your device has stopped communicating overnight and you have to drive there to hit the reset button.
  2. Check out AN1416 Low-Power Design Guide. Lots of helpful advice on designing as well as testing power consumption.
  3. Some peripherals (TCA, TWI) have a DBGCTRL register where you can configure the peripheral to keep running/halt during a break in debugging. Good to not stop your timers and screw up your timing logic.

 

Final impressions

 

I have tried to highlight as many of the board's quirks I could find. It has its flaws, obviously, but it is incredibly well designed to support its target field. Development and prototyping is easy and the board is a pleasure to work with.

It is an interesting product and a great step toward providing better IoT development possibilities to enthusiasts. The Google Cloud integration supports scaling your product so it is not just something for hobbyists.

 

I definitely recommend buying this board if you are interested in this field and I will probably buy a few more myself.

 

Thank you to element14 for giving me this great opportunity to further my insight and give something back to the community of enthusiasts!

Anonymous