Posted in Arduino | 0 comments

At today’s workshop, we’ll follow a series of small projects using parts from our kit. We’ll copy and paste code to see what it does. Then later, we’ll dive into the inner working of the code. Finally, we’ll make something on our own with the combined parts.

If you prefer to download all the files at once, please download this .zip file.

Installing the Software

Review the Hardware Kit

Let’s take a look at the parts we have:

Learn More:

 

Configure Arduino IDE

There are a few settings we need to change to use the Gemmas.

First, choose Tools >> Board >> Adafruit Gemma 8MHz

screen-shot-2016-10-27-at-6-52-20-pm

 

Next, choose Tools >> Programmer >> USBtinyISPscreen-shot-2016-10-27-at-6-53-53-pm

 

When you are ready to upload code, you’ll need to trigger the bootloader. Press the little push button on the Gemma. The LED will pulse showing it’s ready to accept code.

 

Hardware Test

Use the USB cable to connect the Gemma to your computer. You don’t need to connect anything else.

blink_solo_bb

Copy and paste the following code (all code, use select all) into the Arduino IDE software.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.

  To upload to your  or Trinket:
  1) Select the proper board from the Tools->Board Menu (Arduino Gemma if
     teal, Adafruit Gemma if black)
  2) Select the uploader from the Tools->Programmer ("Arduino Gemma" if teal, 
   "USBtinyISP" if black Gemma)
  3) Plug in the Gemma into USB, make sure you see the green LED lit
  4) For windows, make sure you install the right Gemma drivers
  5) Press the button on the Gemma/Trinket - verify you see
     the red LED pulse. This means it is ready to receive data
  6) Click the upload button above within 10 seconds
*/
 
int led = 1; // blink 'digital' pin 1 - AKA the built in red LED

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);

}

// the loop routine runs over and over again forever:
void loop() {
    digitalWrite(led, HIGH); 
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

 

  • Press the reset button on the Arduino to start the bootloader (it will slowly flash for up to 10 seconds)
  • Press the upload button upload in the Arduino IDE. Watch the dialog to make sure it upload properly.
    compiling
  • See what happens

Blink Take 2

Let’s make our first circuit. Unplug your Gemma. Then use your hardware kit to match the following diagram. This is using a 270 ohm resistor (it has a purple line on it).

blink_bb

No need to change your code. Just plug in the Gemma to power it.

 

Did it work? Now try unplugging your Gemma, and switch out the LED for the motor. Plug back in the Gemma? Does the motor work too?

 

NeoPixels

Next, we’ll try upgrading the light output experience to NeoPixels. Unplug your Gemma. Take out one NeoPixel. Look for the arrow direction on the NeoPixel. Orient the NeoPixel, and create this circuit. Then upload the new code.

strandtest1_bb

#include <Adafruit_NeoPixel.h>

#define PIN 0
const uint8_t pxls = 1;       // number of pixels in the neopixel ring
const int maxBright = 64;        // max brightness (0-255)

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(pxls, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

void setup() {
  strip.begin();
  strip.setBrightness(maxBright);
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(127,   0,   0), 50); // Red
  theaterChase(strip.Color(  0,   0, 127), 50); // Blue

  rainbow(20);
  rainbowCycle(20);
  theaterChaseRainbow(50);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();
     
      delay(wait);
     
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
        }
        strip.show();
       
        delay(wait);
       
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, 0);        //turn every third pixel off
        }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else if(WheelPos < 170) {
    WheelPos -= 85;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}


 

Next, try adding more pixels in series. You’ll need to change the number of pixels in the code, line 4.

strandtest2_bb

Control Color Hue

potcolorhue_bb

/* 
 *  Code created by Chris Evans @drumminhands
 *  Used to map a potentiometer to color hue
 *    
*/

// sensor input from  http://www.arduino.cc/en/Tutorial/AnalogInput
// NeoPixels from here: http://learn.adafruit.com/adafruit-neopixel-uberguide/overview

#include <Adafruit_NeoPixel.h>

//constansts won't change
const uint8_t neoPin = 0;      // Neopixels Pin
const uint8_t pxls = 4;       // number of pixels in the neopixel ring
const uint8_t maxBright = 64; // the default brightness between 0 and 255

// variables that will change
int sensorPin = 1;             // select the input pin for the potentiometer (analog 1 is digital 2)
int sensorValue = 0;           // variable to store the value coming from the sensor
int neoPixelHue = 0;           // stores current hue of the neoPixels during the rainbow cycles

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(pxls, neoPin, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

void setup() {  
  strip.begin();
  strip.setBrightness(maxBright); // initialize brightness
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
    sensorValue = analogRead(sensorPin); // read the value from the sensor
    sensorValue = map(sensorValue, 0, 1023, 0, 255); // change value porportionally to be between 0 and 255
    colorSet(Wheel(sensorValue)); // set the color based on the pot
}

// sets all pixels to one given color
void colorSet(uint32_t c){
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
  }
  strip.show();
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else if(WheelPos < 170) {
    WheelPos -= 85;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}


 

BREAK?

Morse Code

Now, let’s unplug everything and wire up a circuit using the motor.

morsecode_bb

// by chris@drumminhands.com
// send morse code signals to the motor
// find morse code charts here: https://en.wikipedia.org/wiki/Morse_code

int m = 1; // motor pin
int dotDuration = 250; // duration of a morse code dot in miliseconds
int dashDuration = 3*dotDuration; // duration of a morse code dash in miliseconds
int delayBetweenChars = 250; // pause duration between buzzing each character

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(m, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
    // s
    dot();
    dot();
    dot();
    
    delay(500);

    // o
    dash();
    dash();
    dash();
    
    delay(500);

    // s
    dot();
    dot();
    dot();  
    
    delay(2000); 
}

void buzz(int p, int d){
  // buzzes the motor connected to pin p, for duration d (in miliseconds)
    digitalWrite(p, HIGH); 
    delay(d);
    digitalWrite(p, LOW);
    delay(delayBetweenChars);
}

void dot(){
  // buzzes the motor for a short pulse
  buzz(m,dotDuration);
}

void dash(){
  buzz(m,dashDuration); // three times a dash
}


 

Mindfulness Bracelet

We’ve done enough little tests with your Arduino kit. It’s time to do a bigger project. If you want to follow the official instructions, see the Adafruit tutorial on making a Mindfulness Bracelet.

Start by trying the circuit with your alligator clips. Careful to wire everything correctly. And the wires on the buzzer are tiny, so double-check that you have a good connection.

This code is set to buzz every five seconds while we’re testing. Change the value in line 8 from 5 to a more reasonable duration. Say 60 minutes. Or 120.

bracelet_bb

// Mindfulness Bracelet sketch for Adafruit/Arduino Gemma.  Briefly runs
// vibrating motor (connected through transistor) at regular intervals.
// This code is not beginner-friendly, it does a lot of esoteric low-level
// hardware shenanigans in order to conserve battery power.

const uint32_t           // These may be the only lines you need to edit...
  onTime   =  2 * 1000L, // Vibration motor run time, in milliseconds
  interval = 5 * 1000L; // Time between reminders, in milliseconds
                         // It gets progressively geekier from here...

// Additional power savings can optionally be realized by disabling the
// power-on LED, either by desoldering or by cutting the trace from 3Vo
// on the component side of the board.

// This sketch spends nearly all its time in a low-power sleep state...
#include <avr/power.h>
#include <avr/sleep.h>

// The chip's 'watchdog timer' (WDT) is used to wake up the CPU when needed.
// WDT runs on its own 128 KHz clock source independent of main CPU clock.
// Uncalibrated -- it's "128 KHz-ish" -- thus not reliable for extended
// timekeeping.  To compensate, immediately at startup the WDT is run for
// one maximum-duration cycle (about 8 seconds...ish) while keeping the CPU
// awake, the actual elapsed time is noted and used as a point of reference
// when calculating sleep times.  Still quite sloppy -- the WDT only has a
// max resolution down to 16 ms -- this may drift up to 30 seconds per hour,
// but is an improvement over the 'raw' WDT clock and is adequate for this
// casual, non-medical, non-Mars-landing application.  Alternatives would
// require keeping the CPU awake, draining the battery much quicker.

uint16_t          maxSleepInterval;  // Actual ms in '8-ish sec' WDT interval
volatile uint32_t sleepTime     = 1; // Total milliseconds remaining in sleep
volatile uint16_t sleepInterval = 1; // ms to subtract in current WDT cycle
volatile uint8_t  tablePos      = 0; // Index into WDT configuration table

void setup() {

  // Unused pins can be set to INPUT w/pullup -- most power-efficient state
  pinMode(0, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // LED shenanigans.  Rather that setting pin 1 to an output and using
  // digitalWrite() to turn the LED on or off, the internal pull-up resistor
  // (about 10K) is enabled or disabled, dimly lighting the LED with much
  // less current.
  pinMode(1, INPUT);               // LED off to start

  // AVR peripherals that are NEVER used by the sketch are disabled to save
  // tiny bits of power.  Some have side-effects, don't do this willy-nilly.
  // If using analogWrite() to for different motor levels, timer 0 and/or 1
  // must be enabled -- for power efficiency they could be turned off in the
  // ubersleep() function and re-enabled on wake.
  power_adc_disable();             // Knocks out analogRead()
  power_timer1_disable();          // May knock out analogWrite()
  power_usi_disable();             // Knocks out TinyWire library
  DIDR0 = _BV(AIN1D) | _BV(AIN0D); // Digital input disable on analog pins
  // Timer 0 isn't disabled yet...it's needed for one thing first...

  // The aforementioned watchdog timer calibration...
  uint32_t t = millis();                       // Save start time
  noInterrupts();                              // Timing-critical...
  MCUSR &= ~_BV(WDRF);                         // Watchdog reset flag
  WDTCR  =  _BV(WDCE) | _BV(WDE);              // WDT change enable
  WDTCR  =  _BV(WDIE) | _BV(WDP3) | _BV(WDP0); // 8192-ish ms interval
  interrupts();
  while(sleepTime);                            // Wait for WDT
  maxSleepInterval  = millis() - t;            // Actual ms elapsed
  maxSleepInterval += 64;                      // Egyptian constant
  power_timer0_disable();  // Knocks out millis(), delay(), analogWrite()
}

const uint32_t offTime = interval - onTime; // Duration motor is off, ms

void loop() {
  pinMode(1, INPUT_PULLUP); // LED on (using internal pullup)
  ubersleep(onTime);        // Delay while LED/motor on
  pinMode(1, INPUT);        // LED off
  ubersleep(offTime);       // Delay while off
}

// WDT timer operates only in specific intervals based on a prescaler.
// CPU wakes on each interval, prescaler is adjusted as needed to pick off
// the longest setting possible on each pass, until requested milliseconds
// have elapsed.
const uint8_t cfg[] PROGMEM = { // WDT config bits for different intervals
  _BV(WDIE) | _BV(WDP3) |                         _BV(WDP0), // ~8192 ms
  _BV(WDIE) | _BV(WDP3)                                    , // ~4096 ms
  _BV(WDIE) |             _BV(WDP2) | _BV(WDP1) | _BV(WDP0), // ~2048 ms
  _BV(WDIE) |             _BV(WDP2) | _BV(WDP1)            , // ~1024 ms
  _BV(WDIE) |             _BV(WDP2) |             _BV(WDP0), //  ~512 ms
  _BV(WDIE) |             _BV(WDP2)                        , //  ~256 ms
  _BV(WDIE) |                         _BV(WDP1) | _BV(WDP0), //  ~128 ms
  _BV(WDIE) |                         _BV(WDP1)            , //   ~64 ms
  _BV(WDIE) |                                     _BV(WDP0), //   ~32 ms
  _BV(WDIE)                                                  //   ~16 ms
}; // Remember, WDT clock is uncalibrated, times are "ish"

void ubersleep(uint32_t ms) {
  if(ms == 0) return;
  tablePos      = 0;                   // Reset WDT config stuff to
  sleepInterval = maxSleepInterval;    // longest interval to start
  configWDT(ms);                       // Set up for requested time
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deepest sleep mode
  sleep_enable();
  while(sleepTime && (tablePos < sizeof(cfg))) sleep_mode();
  noInterrupts();                      // WDT off (timing critical)...
  MCUSR &= ~_BV(WDRF);
  WDTCR  = 0;
  interrupts();
}

static void configWDT(uint32_t newTime) {
  sleepTime = newTime; // Total sleep time remaining (ms)
  // Find next longest WDT interval that fits within remaining time...
  while(sleepInterval > newTime) {
    sleepInterval /= 2;                          // Each is 1/2 previous
    if(++tablePos >= sizeof(cfg)) return;        // No shorter intervals
  }
  uint8_t bits = pgm_read_byte(&cfg[tablePos]);  // WDT config bits for time
  noInterrupts();                                // Timing-critical...
  MCUSR &= ~_BV(WDRF);
  WDTCR  =  _BV(WDCE) | _BV(WDE);                // WDT change enable
  WDTCR  =  bits;                                // Interrupt + prescale
  interrupts();
}

ISR(WDT_vect) { // Watchdog timeout interrupt
  configWDT(sleepTime - sleepInterval); // Subtract, setup next cycle...
}

 

BREAK?

 

Remix Ideas

  • Change the color of the pixels based on the light sensor.
  • Pimp out your name badge with an LED sequence.
  • Try using both the button and potentiometer with the NeoPixels. See code in the “other” folder in the download.

Finish Your Product

  • When you have a curcuit working they way you want it to, now it’s time to solder or sew.
  • Leave plenty of time to cut, glue, solder, sew, etc.
  • Try cutting a table tennis ball in half and use it as a diffuser on your LEDs.
  • Cut felt to make a bracelet or modify your name badge
  • Finish up your mindfulness bracelet and wear it for the rest of the conference. Remember to get up and move.

Featured image by Allan on Flickr