Moteino M0
The MoteinoM0 (release notes) is a SAMD21 Cortex M0+ based Moteino featuring these general specifications:
- ATSAMD21G18A microcontroller (48pin LQFP) running at 48Mhz, 3.3V core
- Input voltage: 3.6-6V
- Digital pins: 23 (11 PWM)
- Analog ADC pins: 6 (12bit)
- Analog DAC pins: 1 (10bit)
- Flash Program memory: 256KB (8KB used by bootloader)
- SRAM: 32KB
- Transceivers: RFM69HCW, RFM95, RFM96
- Low power: ~8uA in standby mode (FLASH and transceiver in deep sleep)
Memory & Bootloader
The MoteinoM0 runs the standard Arduino Zero bootloader – enhanced to allow flashing the memory from the external FLASH-MEM memory. This is similar/compatible with Dualoptiboot for AVR Moteinos and OTA/Wireless programming. The booloader is located in the first 8KB of internal flash memory and is protected by the NVM user byte fuse.
There is no dedicated EEPROM on the SAMD21 microcontroller but EEPROM can be emulated up to 16kb and the Arduino EEPROM library can be used to access this emulated EEPROM.
The M0 bootloader is available here for download if you wish to reinstall in on a M0 board using a SWD programmer (ex. Atmel ICE).
Pin reference
Schematic
Input voltage – VIN
Please note that on the first batch of PCBs, there is an error on the silkscreen next to the VIN pin, the input voltage when the MoteinoM0 is powered from the VIN pin, should be 3-6V (the limiting factor is the MCP73831 charger which can take no more than 6V). This error will be corrected in a future revision of the PCB, and current boards will ship with a small marking to cover the “1” in 3-16V:
Example reading the VIN
The sketch below illustrates how to read the input voltage (USB or battery). The MoteinoM0 has a simple voltage monitor connected to A5 throug a half (50%) voltage divider (see schematic). When the reading is around 1 schottky diode below 5V it means USB is connected. When the battery is connected in addition to USB, the reading is typically around 4.2V. When only the battery is connected, then A5 will read the actual battery voltage. Below is a sample of code to correctly read the voltage monitor through pin A5:
#if defined (MOTEINO_M0) && defined(SERIAL_PORT_USBVIRTUAL) #define Serial SERIAL_PORT_USBVIRTUAL // Required for Serial on Zero based boards #endif // the setup function runs once when you press reset or power the board void setup() { Serial.begin(115200); pinMode(A5, INPUT); } // the loop function runs over and over again forever void loop() { float vin = analogRead (A5) * 2 * 0.003226; // 0.003226 = 3.3 / 1023 Serial.println(vin); delay(1000); // wait for a second }
Solder Jumpers
There are 4 solder jumpers on the bottom of MoteinoM0. You can see each one in the schematic. Here’s a short explanation of each jumper’s function and (default state):
- LED_EN (shorted): cut this open and the onboard LED is disconnected – a measn to save a little extra power, or use pin D13 for other purposes
- DIODE_BYPASS (open): solder this jumper and you will short the Schottky diode that blocks current to flow back into the USB connector. This is experimental and should be left open.
- VMON_EN A & B (both shorted): these two jumpers allow you to disconnect the onboard battery monitoring voltage divider circuit. Both should be the same state – either open or shorted. If you open them, then pin A5 will be available for other purposes.
Hardware / Driver install
To use MoteinoM0 with ArduinoIDE you will need to install the latest Arduino SAMD Boards package and then also the Moteino SAMD Boards package by LowPowerLab.
First add the Moteino core json definition URL to your Board Manager.
Then in Board Manager you will see AVR and SAMD boards by LowPowerLab.
If you’re on MAC or Linux you should not need any drivers. Also Windows 10 will probably detect the board as a virtual serial port and assign it a COM port by default without the need of installing a driver. Older Windows versions are likely going to fail to install a driver when MoteinoM0 is first plugged in. Click below to get the USB driver and unzip it to your Desktop or another folder. The ZIP file contains a screenshot walkthrough of how to install the driver.
Using with the ArduinoIDE
It’s important to understand how the board operates and how to enter the bootloader to ensure you can program it regardless what state the board is in. The bootloader behaves identically to the Arduino Zero samba bootloader, so if you’re familiar with that board, you should be all set.
If you load a low power sketch, or turn off the USB-Device (serial port) in your sketch, or even using certain sketches will cause the board to appear unresponsive to an Upload command while it’s in sleep mode or if it’s caught up doing other things. In such cases an Upload will fail from the IDE, and it’s necessary to do a fast double-tap on the RST button to put the board in bootloader mode. In some even more precarious situations where the board does to sleep immediately after boot, you might need to first do a single tap, then shortly after do the double-tap. You know you have entered the bootloader when the onboard LED is in a breathing pattern and a new serial port appears. Once there, select the new serial port, and try an Upload. There will be a delay until the board receives the sketch, or if you’re in a hurry you can jump start the upload by doing another double-tap. It’s only confusing until you manage to do this a few times!
Example deep sleep sketch
The sketch below illustrates how to put the MoteinoM0 to standby sleep (lowest power sleep), and optionally wake it from an external interrupt (active low on digital pin 0). It also puts the FLASH-MEM and transceiver (if any) to sleep.
//Standby sleep example for MoteinoM0 with external interrupt wakeup //expected current in standby sleep: 7.5uA const int pin = 0; //pin to use as interrupt (can be any digital pin) //#define WITHRFM69 //comment this out if you don't have a radio installed on your MoteinoM0 #ifdef WITHRFM69 #include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69 #define NODEID 2 //must be unique for each node on same network (range up to 254, 255 is used for broadcast) #define NETWORKID 100 //the same on all nodes that talk to each other (range up to 255) #define GATEWAYID 1 #define FREQUENCY RF69_915MHZ #define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W! RFM69 radio; #endif #include <SPIFlash.h> //get it here: https://www.github.com/lowpowerlab/spiflash SPIFlash flash(SS_FLASHMEM, 0xEF30); //EF30 for 4mbit Windbond chip (W25X40CL) //replace Serial with SerialUSB #if defined (MOTEINO_M0) #if defined(SERIAL_PORT_USBVIRTUAL) #define Serial SERIAL_PORT_USBVIRTUAL // Required for Serial on Zero based boards #endif #endif void setup() { Serial.begin(115200); delay(500); pinMode(LED_BUILTIN, OUTPUT); pinMode(0, INPUT_PULLUP); //using internal pullup, makes pin 0 active low //Uses internal pullup eliminates the need for external resistor and makes interupt pin active low //If you need active high, then attach to HIGH and declare pin as INPUT without PULLUP, then use external resistor from GND to pin to keep it from floating attachInterrupt(pin, wakeupISR, LOW); //note: RISING and FALLING do not seem to work, use LOW or HIGH instead if (flash.initialize()) { Serial.println("flash.init() OK, sleeping it..."); flash.sleep(); } else Serial.println("flash.init() FAIL"); #ifdef WITHRFM69 if (!radio.initialize(FREQUENCY,NODEID,NETWORKID)) Serial.println("radio.init() FAIL"); else Serial.println("radio.init() SUCCESS"); #ifdef IS_RFM69HW radio.setHighPower(); //uncomment only for RFM69HW! #endif radio.sleep(); #endif for (byte count=0; count < 3; count++) { Serial.print("."); blink(500); } Serial.println("Entering standby sleep mode..."); delay(100); } void loop() { standbySleep(); //interrupt happened: WAKE UP and blink the LED! blink(50); } void blink(int ms) { digitalWrite(LED_BUILTIN, HIGH); delay(ms); digitalWrite(LED_BUILTIN, LOW); delay(ms); } byte toggle=true; void wakeupISR(void) { //nothing here, just a placeholder } void standbySleep() { //Disable USB (optional) //USBDevice.detach(); //Standby - lowest power sleep mode SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; __DSB(); __WFI(); // Wait For Interrupt call //Sleep until woken by interrupt... //Enable USB //USBDevice.attach(); }
Example periodic sleep using RTCZero library
The sketch below illustrates how to put the MoteinoM0 to sleep and wake it up periodically using the RTC (Real Time Clock timer). On a bare MoteinoM0 this sketch should achieve ~8.7uA while sleeping.
#include <RTCZero.h> RTCZero zerortc; // Set how often alarm goes off here const byte alarmSeconds = 3; const byte alarmMinutes = 0; const byte alarmHours = 0; volatile bool alarmFlag = false; // Start awake #if defined (MOTEINO_M0) #if defined(SERIAL_PORT_USBVIRTUAL) #define Serial SERIAL_PORT_USBVIRTUAL // Required for Serial on Zero based boards #endif #endif void setup() { Serial.begin(115200); delay(1000); // Wait for console pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); zerortc.begin(); // Set up clocks and such resetAlarm(); // Set alarm zerortc.attachInterrupt(alarmMatch); // Set up a handler for the alarm } void loop() { if (alarmFlag == true) { alarmFlag = false; // Clear flag digitalWrite(LED_BUILTIN, HIGH); Serial.println("Alarm went off - I'm awake!"); } resetAlarm(); // Reset alarm before returning to sleep Serial.println("Alarm set, going to sleep now."); digitalWrite(LED_BUILTIN, LOW); zerortc.standbyMode(); // Sleep until next alarm match } void alarmMatch(void) { alarmFlag = true; // Set flag } void resetAlarm(void) { byte seconds = 0; byte minutes = 0; byte hours = 0; byte day = 1; byte month = 1; byte year = 1; zerortc.setTime(hours, minutes, seconds); zerortc.setDate(day, month, year); zerortc.setAlarmTime(alarmHours, alarmMinutes, alarmSeconds); zerortc.enableAlarm(zerortc.MATCH_HHMMSS); }
USB Device
MoteinoM0 can act like any USB device. It can emulate a keyboard or a joystick, a mouse or even a USB stick. You could prank a friend and plug a MoteinoM0 into their PC that simulates a keyboard, and remotely have another MoteinoM0 that hosts a real keyboard on which you can type (in that case it would act as a USB-Device, see below) and remotely type keys on your friend’s computer through the radio link, that would make quite a prank!
Check out the standard Arduino examples which provide samples to make the MoteinoM0 be a keyboard, mouse, or a combination of these:
USB Host
MoteinoM0 can also act like any USB-Host. It can host another USB-device on its USB-micro connector, via a USB-OTG adapter. Since USB devices require 5V, powering both the M0 and the attached USB-device from a LiPo battery (4v) is not enough, hence it is required to provide a stable 5V on the VUSB pin. The photo below illustrates how this wiring is achieved. The Keyboard could be replaced with any other USB device:
Here is a sample sketch that reads the inputs from the attached keyboard, and outputs them to the Serial port (pins 30,31):
/* Keyboard Controller Example Shows the output of a USB Keyboard connected to the USB-micro port (through USB-OTG adapter) on a MoteinoM0 board. See https://lowpowerlab.com/guide/moteino/moteinoM0/ for wiring Output is provided on the SERIAL_PORT_MONITOR, ie Serial port on pins 30,31 created 8 Oct 2012 by Cristian Maglie adapted for MoteinoM0 by Felix Rusu (LowPowerLab.com) http://arduino.cc/en/Tutorial/KeyboardController This sample code is part of the public domain. */ // Require keyboard control library #include <KeyboardController.h> // Initialize USB Controller USBHost usb; #define LED LED_BUILTIN // Attach keyboard controller to USB KeyboardController keyboard(usb); void printKey(); // This function intercepts key press void keyPressed() { SERIAL_PORT_MONITOR.print("Pressed: "); printKey(); } // This function intercepts key release void keyReleased() { SERIAL_PORT_MONITOR.print("Released: "); printKey(); } void printKey() { digitalWrite(LED, HIGH); delay(10); // getOemKey() returns the OEM-code associated with the key SERIAL_PORT_MONITOR.print(" key:"); SERIAL_PORT_MONITOR.print(keyboard.getOemKey()); // getModifiers() returns a bits field with the modifiers-keys int mod = keyboard.getModifiers(); SERIAL_PORT_MONITOR.print(" mod:"); SERIAL_PORT_MONITOR.print(mod); SERIAL_PORT_MONITOR.print(" => "); if (mod & LeftCtrl) SERIAL_PORT_MONITOR.print("L-Ctrl "); if (mod & LeftShift) SERIAL_PORT_MONITOR.print("L-Shift "); if (mod & Alt) SERIAL_PORT_MONITOR.print("Alt "); if (mod & LeftCmd) SERIAL_PORT_MONITOR.print("L-Cmd "); if (mod & RightCtrl) SERIAL_PORT_MONITOR.print("R-Ctrl "); if (mod & RightShift) SERIAL_PORT_MONITOR.print("R-Shift "); if (mod & AltGr) SERIAL_PORT_MONITOR.print("AltGr "); if (mod & RightCmd) SERIAL_PORT_MONITOR.print("R-Cmd "); // getKey() returns the ASCII translation of OEM key // combined with modifiers. SERIAL_PORT_MONITOR.write(keyboard.getKey()); SERIAL_PORT_MONITOR.println(); digitalWrite(LED, LOW); } void setup() { SERIAL_PORT_MONITOR.begin( 115200 ); while (!SERIAL_PORT_MONITOR); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection SERIAL_PORT_MONITOR.println("Keyboard Controller Program started"); if (usb.Init() == -1) SERIAL_PORT_MONITOR.println("OSC did not start."); delay( 20 ); } void loop() { // Process USB tasks usb.Task(); }
Eagle library components
You can now use the Moteino Eagle library (Moteino.lbr) and drop-in Moteino layouts including MoteinoM0, straight into your design. Import the Moteino.lbr library in Eagle and add the recommended routing to accomodate the radio module mounted on the MoteinoM0 bottom (if any). This way you can flush-mount MoteinoM0 directly on your PCB, here is an example: