Friday, December 9, 2016

Eliminating Switch Bounce with a Debounce Circuit

In video we discuss what is switch bounce and how to implement a simple and low cost debounce circuit to eliminate switch bounce.



Debounce circuit used in video

Wednesday, November 23, 2016

Creating a Sensor Network that Connects to the Cloud Part 3

In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part three we look at how to access the sensor data from the cloud with a PC or Android device.


GitHub link to access code from the series: https://github.com/ForceTronics/nRF24L01-Sensor-Network-that-Connects-to-the-Cloud/

Wednesday, November 9, 2016

Unboxing the Anaren A20737A BLE Module and Atmosphere IDE

In this video we take a look at Anaren's A20737A BLE Module and the innovative Atmoshere IDE. If you want to try out the IDE for yourself before investing in the hardware use the following link: https://atmosphere.anaren.com/


Thursday, November 3, 2016

Creating a Sensor Network that Connects to the Cloud Part 2

In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part two we look at how to add time stamps to our sensor data and track the battery state of our nodes.



GitHub: https://github.com/ForceTronics/nRF24L01-Sensor-Network-that-Connects-to-the-Cloud/tree/master

Wednesday, October 26, 2016

Creating a Sensor Network that Connects to the Cloud Part 1

In this three part series we look at how to create a wireless sensor mesh network that stores data on the cloud using the Arduino platform. In part one we will look at the architecture of the network and how to get started sending sensor data to the cloud. 


Link to GitHub library and sketch code from video https://github.com/ForceTronics/nRF24L01-Sensor-Network-that-Connects-to-the-Cloud


Friday, October 14, 2016

Utilizing Advanced ADC Capabilities on Arduino’s with the SAMD21 (Zero, MKR1000, etc) Part 1

We are all familiar with the Arduino "analogRead()" function, but there is a lot more to the SAMD21 ADC then just taking simple readings. In this video series we look at some of the more advanced ADC capabilities of the SAMD21 and how to use them. In part 1 we look at how to use the window monitoring capability of the ADC.



//*******************Arduino code from the video*********************
//This sketch is from a tutorial on the ForceTronics YouTube Channel called 
//Utilizing Advanced ADC Capabilities on Arduino’s with the SAMD21 (Zero, MKR1000, etc)
//This code is public domain and free to anyone to use or modify at your own risk

//declare const for window mode settings
const byte DISABLE = 0;
const byte MODE1 = 1;
const byte MODE2 = 2;
const byte MODE3 = 3;
const byte MODE4 = 4;

void setup() {
  //call this function to start the ADC in window and define the window parameters
  ADCWindowBegin(MODE1, 512, 750); //Do not use the Arduino analog functions until you call ADCWindowEnd()
  Serial.begin(57600);
}

void loop() {
  delay(1500);
  Serial.println(readADC()); //the "readADC()" function can be used to get ADC readings while in Window mode
  Serial.println();
}

//This is the interrupt service routine (ISR) that is called 
//if an ADC measurement falls out of the range of the window 
void ADC_Handler() {
    digitalWrite(LED_BUILTIN, HIGH); //turn LED off
    ADC->INTFLAG.reg = ADC_INTFLAG_WINMON; //Need to reset interrupt
}

//this function sets up the ADC window mode with interrupt
void ADCWindowBegin(byte mode, int upper, int lower) {
  setMeasPin(); //function sets up ADC pin A0 as input
  setGenClock(); //setup ADC clock, using internal 8MHz clock
  setUPADC(); //configure ADC
  setADCWindow(mode, upper, lower); //setup ADC window mode 
  setUpInterrupt(0); //setup window mode interrupt with highest priority
  enableADC(1); //enable ADC 
}

void ADCWindowEnd() {
  NVIC_DisableIRQ(ADC_IRQn); //turn off interrupt
  enableADC(0); //disable ADC 
}

//setup measurement pin, using Arduino ADC pin A3
void setMeasPin() {
  // Input pin for ADC Arduino A3/PA04
  REG_PORT_DIRCLR1 = PORT_PA04;

  // Enable multiplexing on PA04
  PORT->Group[0].PINCFG[4].bit.PMUXEN = 1;
  PORT->Group[0].PMUX[1].reg = PORT_PMUX_PMUXE_B | PORT_PMUX_PMUXO_B;
}

//Function sets up generic clock for ADC
//Uses built-in 8MHz clock
void setGenClock() {
   // Enable the APBC clock for the ADC
  REG_PM_APBCMASK |= PM_APBCMASK_ADC;

  configOSC8M(); //this function sets up the internal 8MHz clock that we will use for the ADC
  
  // Setup clock GCLK3 for no div factor
   GCLK->GENDIV.reg |= GCLK_GENDIV_ID(3)| GCLK_GENDIV_DIV(1);
   while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);  

  //configure the generator of the generic clock, which is 8MHz clock
  GCLK->GENCTRL.reg |= GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSC8M | GCLK_GENCTRL_ID(3) | GCLK_GENCTRL_DIVSEL;
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);
  
  //enable clock, set gen clock number, and ID to where the clock goes (30 is ADC)
  GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(3) | GCLK_CLKCTRL_ID(30);
  while (GCLK->STATUS.bit.SYNCBUSY);
}

//Function that does general settings for ADC
//sets it for a single sample
//Uses internal voltage reference
//sets gain factor to 1/2
void setUPADC() {
  // Select reference, internal VCC/2
  ADC->REFCTRL.reg |= ADC_REFCTRL_REFSEL_INTVCC1; // VDDANA/2, combine with gain DIV2 for full VCC range

  // Average control 1 sample, no right-shift
  ADC->AVGCTRL.reg |= ADC_AVGCTRL_ADJRES(0) | ADC_AVGCTRL_SAMPLENUM_1;

  // Sampling time, no extra sampling half clock-cycles
  REG_ADC_SAMPCTRL |= ADC_SAMPCTRL_SAMPLEN(0);

  // Input control: set gain to div by two so ADC has measurement range of VCC, no diff measurement so set neg to gnd, pos input set to pin 0 or A0
  ADC->INPUTCTRL.reg |= ADC_INPUTCTRL_GAIN_DIV2 | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN4;
  while (REG_ADC_STATUS & ADC_STATUS_SYNCBUSY);

  // PS16, 8 MHz, ADC_CLK = 500 kHz, ADC sampling rate, single eded, 12 bit, free running, DIV2 gain, 7 ADC_CLKs, 14 usec
  ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV16 | ADC_CTRLB_RESSEL_10BIT | ADC_CTRLB_FREERUN; // Run ADC continously, 7 ADC_CLKs, 14 usec
  while (REG_ADC_STATUS & ADC_STATUS_SYNCBUSY);
}

//This function is used to setup the ADC windowing mode
//inputs are the mode, upper window value, and lower window value
//
void setADCWindow(byte mode, int upper, int lower) {
  ADC->WINCTRL.reg = mode; //set window mode
  while (ADC->STATUS.bit.SYNCBUSY);

   ADC->WINUT.reg = upper; //set upper threshold
   while (ADC->STATUS.bit.SYNCBUSY);

   ADC->WINLT.reg = lower; //set lower threshold
   while (ADC->STATUS.bit.SYNCBUSY);
}

//This function sets up an ADC interrupt that is triggered 
//when an ADC value is out of range of the window
//input argument is priority of interrupt (0 is highest priority)
void setUpInterrupt(byte priority) {
  
  ADC->INTENSET.reg |= ADC_INTENSET_WINMON; // enable ADC window monitor interrupt
   while (ADC->STATUS.bit.SYNCBUSY);

   NVIC_EnableIRQ(ADC_IRQn); // enable ADC interrupts
   NVIC_SetPriority(ADC_IRQn, priority); //set priority of the interrupt
}

//function allows you to enable or disable ADC
void enableADC(bool en) {
  if(en) ADC->CTRLA.reg = 2; //2 is binary 010 which is register bit to enable ADC
  else ADC->CTRLA.reg = 0; //0 disables ADC
}

//This function will return the latest ADC reading made during free run window mode
//must first start the ADC before calling this function
unsigned int readADC() {
  // Free running, wait for conversion to complete
  while (!(REG_ADC_INTFLAG & ADC_INTFLAG_RESRDY));
  // Wait for synchronization before reading RESULT
  while (REG_ADC_STATUS & ADC_STATUS_SYNCBUSY);
  
  return REG_ADC_RESULT;
}

//function enables the 8MHz clock used for the ADC
void configOSC8M() 
{
  SYSCTRL->OSC8M.reg |= SYSCTRL_OSC8M_ENABLE;
}


Saturday, September 24, 2016

Reducing Power Consumption on Arduino Zero, MKR1000, or any SAMD21 Arduino Part 1

In this multiple part series we look at how to reduce power consumption for battery powered designs that utilize Arduino's with the Atmel SAMD21 MCU (Zero, MKR1000, etc). In part one we look at how to put the SAMD21 to sleep and wake it up with either the real time clock (RTC) or an external event on an input pin.



//***************Arduino Sketch from the video*********************.
//This code was used for a tutorial on the ForceTronics YouTube channel. It shows how to save power
//by putting Arduino's based on the SAMD21 MCU (MKR1000, Zero, etc) to sleep and how to wake them
//This code is public domain for anybody to use or modify

//#include "RTCZero.h"
#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 00;
const byte hours = 00;

/* Change these values to set the current initial date */
const byte day = 24;
const byte month = 9;
const byte year = 16;

void setup() 
{
  delay(5000); //delay so we can see normal current draw
   pinMode(LED_BUILTIN, OUTPUT); //set LED pin to output
  digitalWrite(LED_BUILTIN, LOW); //turn LED off

  rtc.begin(); //Start RTC library, this is where the clock source is initialized

  rtc.setTime(hours, minutes, seconds); //set time
  rtc.setDate(day, month, year); //set date

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(ISR); //creates an interrupt that wakes the SAMD21 which is triggered by a FTC alarm
  //comment out the below line if you are using RTC alarm for interrupt
 // extInterrupt(A1); //creates an interrupt source on external pin
  
  //puts SAMD21 to sleep
  rtc.standbyMode(); //library call
  //samSleep(); //function to show how call works
}

void loop() 
{
  //do nothing in main loop
}

//interrupt service routine (ISR), called when interrupt is triggered 
//executes after MCU wakes up
void ISR()
{
  digitalWrite(LED_BUILTIN, HIGH);
}


//function that sets up external interrupt
void extInterrupt(int interruptPin) {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(interruptPin, ISR, LOW);
}

//function to show how to put the 
void samSleep()
{
  // Set the sleep mode to standby
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
  // SAMD sleep
  __WFI();
}

//**********************Changed "begin" function from RTCZero Library**************
void RTCZero::begin(bool resetTime)
{
  uint16_t tmp_reg = 0;
  
  PM->APBAMASK.reg |= PM_APBAMASK_RTC; // turn on digital interface clock
  //config32kOSC();

  // If the RTC is in clock mode and the reset was
  // not due to POR or BOD, preserve the clock time
  // POR causes a reset anyway, BOD behaviour is?
  bool validTime = false;
  RTC_MODE2_CLOCK_Type oldTime;

  if ((!resetTime) && (PM->RCAUSE.reg & (PM_RCAUSE_SYST | PM_RCAUSE_WDT | PM_RCAUSE_EXT))) {
    if (RTC->MODE2.CTRL.reg & RTC_MODE2_CTRL_MODE_CLOCK) {

      validTime = true;
      oldTime.reg = RTC->MODE2.CLOCK.reg;
    }
  }
  // Setup clock GCLK2 with OSC32K divided by 32
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2)|GCLK_GENDIV_DIV(4);
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;                                                         /*XOSC32K*/
  GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL );
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;
  GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos)));
  while (GCLK->STATUS.bit.SYNCBUSY)
    ;

  RTCdisable();

  RTCreset();

  tmp_reg |= RTC_MODE2_CTRL_MODE_CLOCK; // set clock operating mode
  tmp_reg |= RTC_MODE2_CTRL_PRESCALER_DIV1024; // set prescaler to 1024 for MODE2
  tmp_reg &= ~RTC_MODE2_CTRL_MATCHCLR; // disable clear on match
  
  //According to the datasheet RTC_MODE2_CTRL_CLKREP = 0 for 24h
  tmp_reg &= ~RTC_MODE2_CTRL_CLKREP; // 24h time representation

  RTC->MODE2.READREQ.reg &= ~RTC_READREQ_RCONT; // disable continuously mode

  RTC->MODE2.CTRL.reg = tmp_reg;
  while (RTCisSyncing())
    ;

  NVIC_EnableIRQ(RTC_IRQn); // enable RTC interrupt 
  NVIC_SetPriority(RTC_IRQn, 0x00);

  RTC->MODE2.INTENSET.reg |= RTC_MODE2_INTENSET_ALARM0; // enable alarm interrupt
  RTC->MODE2.Mode2Alarm[0].MASK.bit.SEL = MATCH_OFF; // default alarm match is off (disabled)
  
  while (RTCisSyncing())
    ;

  RTCenable();
  RTCresetRemove();

  // If desired and valid, restore the time value
  if ((!resetTime) && (validTime)) {
    RTC->MODE2.CLOCK.reg = oldTime.reg;
    while (RTCisSyncing())
      ;
  }

  _configured = true;
}