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 bootloader (compatible with Arduino/Genuino Zero). The bootloader is enhanced to allow flashing the memory from the external FLASH-MEM memory. This is compatible with Dualoptiboot 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.

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:

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:

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();
}