Author Topic: Help with sleep & wake on Sparky85  (Read 785 times)

surveyranger

  • Newbie
  • *
  • Posts: 5
  • I'm more of a learn-as-you-go type person
Help with sleep & wake on Sparky85
« on: February 09, 2019, 04:26:38 pm »
Trying to make some NeoPixels cycle through modes when the user presses a button.  Then, I want it to go to sleep after 10 minutes of being idle (no one pressed a button) and wake on button press.  I need to minimize power consumption because I want to power it with a CR2032 attached to an energy scavenger module (.9-4V to 5V). 
I got the Pixels to cycle as desired, but ran into a snag when I tried to add a sleep mode to wake on an external interrupt only on pin 2(PCINT0).
When I added the extra code, ol' Sparky85 does nothing...visibly.  My laptop starts cycling through recognizing Sparky85 & disconnecting.  Tried using a USB power bank, but same results.  Button does not "wake up" or cycle through the patterns.  The power LED is on though (until I remove it). 

The code is a the offspring of the NeoPixel buttoncycler sketch, Nick Gammon's sketches on interrupts & low power threads, an assist from a user on another board turning for loops into state machines, and a dash of stuff from different websites.

Picture of the simple setup:


The amalgam of code:
Code: [Select]
#include <avr/sleep.h>         // library for sleep modes
#include <avr/power.h>         // library for power management
#include <avr/wdt.h>           // disable/enable WDT
#include <avr/interrupt.h>     // library for interrupts NEEDED???
#include <Adafruit_NeoPixel.h>

#ifdef __AVR__
 #include <avr/power.h>
#endif

#define PIXEL_PIN    0    // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT  9

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

unsigned long idleMillis;  // Measure idle time between button presses
unsigned long patternInterval = 20 ; // time between steps in the pattern
unsigned long lastUpdate = 0;  // for millis() when last update occurred
unsigned long showSpeed [] = { 30, 30, 30, 10, 10, 30, 60, 60, 60 } ; // speed for each pattern - add here when adding more cases
const byte BUTTON_PIN = 2;   // Pin 2 / PCINT0 / externalInturrupt
                             
// Function to put Digispark to sleep
void goToSleep ()
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sets the mode for power consumption during sleep
  pinMode(PIXEL_PIN, INPUT);            // Change pin to input
  ADCSRA = 0;                           // Turn off ADC if not already done in setup()
  power_all_disable();                  // power off ADC, Timer 0 and 1, serial interface
  wdt_disable();                        // Disable Watchdog Timer so only button wakes up MCU
  sleep_enable();                       // Enables the sleep bit so that the processor can actually be put to sleep with sleep_mode ()
  sleep_bod_disable();                  // Disable brown out detection to reduce power consumption in sleep mode. Auto-enables on wake
  noInterrupts();                       // Turn other interrupts OFF while attaching interrupt
  attachInterrupt(0, awakeNow, LOW);    // 0 = PCINT0 = P2 on button press, fires the ISR to wake up processor
  interrupts();                         // Turn interrupts ON
  sleep_mode();                         // Should now be sleeping like a baby
  sleep_disable();                      // Program returns to this point when the ISR fires
  noInterrupts();                       // Turn other interrupts OFF
  detachInterrupt(0);                   // Stop MCU from registering BUTTON_PIN press to LOW
  interrupts();                         // Turn interrupts ON
  wdt_enable(WDTO_8S);                  // Enable Watchdog Timer to 8S
  power_all_enable();                   // power everything back on
  ADCSRA = 0;                           // Turn off ADC because I don't need it
  pinMode(PIXEL_PIN, OUTPUT);           // Change pin to output
}

// Interrupt Service Routine (ISR) to wake up the Digispark
void awakeNow ()
{
  // ISR blank or add detachInterrupt(0);?
}

// Set unused pins to INPUT and LOW to save power
void inputsLow ()
{
  digitalWrite(1, INPUT); 
  digitalWrite(3, INPUT);
  digitalWrite(4, INPUT);
  digitalWrite(4, INPUT);
  digitalWrite(1, LOW); 
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(4, LOW);
}

void setup()
{
  pinMode(PIXEL_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);
  digitalWrite(BUTTON_PIN, HIGH);    // activate pull-up resistor to set pin HIGH
  inputsLow();                               // Set other pins to LOW
 
  strip.begin();
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show();             // Initialize all pixels to 'off'

  idleMillis = millis();    // Start idle time

  // pin change interrupt
  PCMSK  |= bit (PCINT0);   // want P2 on Digispark
  GIFR   |= bit (PCIF);     // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);     // enable pin change interrupts
 
  ADCSRA = 0;               // Turn off ADC because I don't need it
}

void loop()
{
  static byte idleTime = 1;  // Sets idle time before sleep mode
  if (millis() - idleMillis >= (1000UL*60*idleTime))   // 1000(unsigned long)ms * 60(sec/min) * # minutes
  {
    goToSleep();       // Will uncomment once sleep/wake functions correct
  }

  // Start with case 0
  static byte showType = 0;
  static bool oldState;
 
  // Get current button state
  bool newState = digitalRead(BUTTON_PIN);
 
  // Check if state changed from high to low (button press)
  if (newState == LOW && oldState == HIGH)
  {
    showType++;                // Move to next showType
    idleMillis = millis();     // Reset idle timer
    if (showType > 8)          // If showType exceeds number of shows
    {
    showType = 0;              // Reset showType to case 0
    }
    patternInterval = showSpeed[showType];  // Set speed for this pattern
    delay(20);                 // Debounce delay
  }
  oldState = newState;         // Update state
  if (millis() - lastUpdate > patternInterval)
  {
    startShow(showType);
  }
}

Function with switch/case with different patterns

surveyranger

  • Newbie
  • *
  • Posts: 5
  • I'm more of a learn-as-you-go type person
Re: Help with sleep & wake on Sparky85
« Reply #1 on: February 21, 2019, 06:19:20 pm »
Wanted to close this out in case someone runs into a similar issue.  I got rid of the INT0 interrupt and went with PCINT2 interrupt.

Final circuit diagram:


End product:


Final code:
Code: [Select]
/*  Cycle NeoPixels with button
 *  Digispark ATtiny85 (microUSB version)
 *  2019-02-12
 * 
 *  Button on P2 (PCINT2/INT0) cycles through different modes (cases). Mode stops and switches to the
 *  next mode when button is pressed.  Current mode plays until button is pressed or goes into
 *  low power mode after 10 minutes.  uC wakes on button press on P2 (PCINT2/INT0) and continues program.
 *  User can turn off circuit with switch attached to VIN. 
 *  Momentary push button is wired to connect P2 to ground. P2 kept high by internal pullup.
 *  NeoPixel data in connected in series with a 330 ohm resistor to P0.
 *  NeoPixel VCC pin connected to Digispark ATtiny85 power rail 5V pin.
 *  Transistor (2N2222) base connected to P1 with 1800 ohm series resistor.
 *  Transistor collector connected to GND on NeoPixels.
 *  Transistor emitter connected to Digispark ATtiny85 GND pin.
 *  Current configuration allows the circuit to be powered from a CR2032 or the microUSB on the Digispark. 
 */
 
#include <avr/sleep.h>         // library for sleep modes
#include <avr/power.h>         // library for power management
#include <avr/wdt.h>           // disable/enable WDT
#include <avr/interrupt.h>     // library for interrupts

#include <Adafruit_NeoPixel.h>

#define PIXEL_PIN    0    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT  9

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

unsigned long idleMillis;            // Measure idle time between button presses
unsigned long patternInterval = 20 ; // time between steps in the pattern
unsigned long lastUpdate = 0;        // for millis() when last update occurred
unsigned long showSpeed [] = { 30, 30, 30, 10, 10, 30, 60, 60, 60 } ; // speed for each pattern - add here when adding more cases
int fadeStep = 0;                    // state variable for fade function
const byte BUTTON_PIN = 2;           // Pin 2 / PCINT0 / externalInturrupt
const byte pixelPowerPin = 1;        // Pin 1 connected to 2N2222 transistor with 1800ohm resistor
                             
// Function to put Digispark to sleep
void sleep()
{
    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
   
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Call for deepest sleep mode
    ADCSRA = 0;                             // ADC off
    power_all_disable();                    // Power off ADC, Timer 0 and 1, serial interface. avr/power.h
    sleep_enable();                         // Sets the Sleep Enable (SE) bit in the MCUCR Register (SE BIT)
    wdt_disable();                          // Disable watchdog timer to conserve power
    sleep_bod_disable();                    // Disable brown out detection to conserve power. Auto-enables on wake
    sei();                                  // Enable interrupts
    sleep_cpu();                            // Sleep
    // ISR starts here when button pressed
    sleep_disable();                        // Clear SE bit
    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
    sei();                                  // Enable interrupts
    power_all_enable();                     // Power everything on. avr/power.h
    ADCSRA = 0;                             // Disable ADC because I don't need it
    }

EMPTY_INTERRUPT(PCINT0_vect);               // Alternate use of ISR(PCINT0_vect){} since
                                            // no action needed in the interrupt
/*
 * ISR(PCINT0_vect) // PCINT0 does not refer to the pin number. Refers to register PCINT2 location
{
    // This is called when the interrupt occurs, but I don't need to do anything in it
}
 */
   
// Set unused pins to INPUT and LOW to save power
void inputsLow ()
{
  pinMode(0, INPUT);
  pinMode(1, INPUT); 
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  digitalWrite(0, LOW);
  digitalWrite(1, LOW); 
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
}

// ***************************** RUNS ONCE ***************************** //
void setup()
{
  pinMode(PIXEL_PIN, OUTPUT);
  pinMode(pixelPowerPin, OUTPUT);
  digitalWrite(pixelPowerPin, HIGH); // Control base on 2N2222
  pinMode(BUTTON_PIN, INPUT);
  digitalWrite(BUTTON_PIN, HIGH);    // activate pull-up resistor to set pin HIGH
   
  strip.begin();
//  strip.setBrightness(50);  // dim output to conserve battery  ***added to functions
  strip.show();             // Initialize all pixels to 'off'
  wipe();                   // Clear buffers on pixels

  idleMillis = millis();    // Start idle time

  ADCSRA = 0;               // Turn off ADC because I don't need it
}

// ***************************** RUNS CONTINUOUSLY ***************************** //
void loop()
{
  static byte idleTime = 10;  // Sets idle time (in minutes) before sleep mode
  if (millis() - idleMillis >= (1000UL*60*idleTime))   // 1000(unsigned long)ms * 60(sec/min) * # minutes
  {
    wipe();                            // Clear pixel buffers
    inputsLow();                       // Change pins to input to conserve battery
    sleep();                           // Call sleep function
    pinMode(pixelPowerPin, OUTPUT);    // Change pixel power pin to output
    digitalWrite(pixelPowerPin, HIGH); // Control base on 2N2222
    pinMode(PIXEL_PIN, OUTPUT);        // Change pixel pin to output
  }

  // Start with case 0
  static byte showType = 0;
  static bool oldState;
 
  // Get current button state
  bool newState = digitalRead(BUTTON_PIN);
 
  // Check if state changed from high to low (button press)
  if (newState == LOW && oldState == HIGH)
  {
    showType++;                // Move to next showType
    fadeStep = 0;              // Reset fade state variable
    idleMillis = millis();     // Reset idle timer
    if (showType > 8)          // If showType exceeds number of shows
    {
    showType = 0;              // Reset showType to case 0
    }
    patternInterval = showSpeed[showType];  // Set speed for this pattern
    wipe();                    // Clear out pixel buffer
    delay(20);                 // Debounce delay
  }
  oldState = newState;         // Update state
  if (millis() - lastUpdate > patternInterval)
  {
    startShow(showType);
  }
}

void startShow(int showPattern)
{
  switch(showPattern)
  {
    //All colors
    case 0: fade(0,255, 0,0, 0,255, 1600);             // fade from black to purple and back
            fade(0,255, 0,0, 0,153, 1600);             // fade from black to pink and back
            fade(0,127, 0,127, 0,127, 1600);           // fade from black to white and back
            fade(0,200, 0,64, 0,0, 1600);              // fade from black to orange and back
            fade(0,255, 0,0, 0,0, 1600);               // fade from black to red and back
            fade(0,255, 0,200, 0,0, 1600);             // fade from black to yellow and back
            break;
    //All colors, but in reverse order???
    case 1: fade(0,255, 0,0, 0,255, 1200);             // fade from black to purple and back
            fade(0,255, 0,0, 0,153, 1200);             // fade from black to pink and back
            fade(0,127, 0,127, 0,127, 1200);           // fade from black to white and back
            fade(0,200, 0,64, 0,0, 1200);              // fade from black to orange and back
            fade(0,255, 0,0, 0,0, 1200);               // fade from black to red and back
            fade(0,255, 0,200, 0,0, 1200);             // fade from black to yellow and back
            break;
    //All colors
    case 2: fade(0,255, 0,0, 0,255, 400);             // fade from black to purple and back
            fade(0,255, 0,0, 0,153, 400);             // fade from black to pink and back
            fade(0,127, 0,127, 0,127, 400);           // fade from black to white and back
            fade(0,200, 0,64, 0,0, 400);              // fade from black to orange and back
            fade(0,255, 0,0, 0,0, 400);               // fade from black to red and back
            fade(0,255, 0,200, 0,0, 400);             // fade from black to yellow and back
            break;
    case 3: rainbow();                                // Quickly cycle
            break;   
    case 4: rainbowCycle();                           // Quickly cycle
            break;
    case 5: theaterChaseRainbow();                    // Quickly cycle
            break;
    case 6: rainbow();                                // Slowly cycle
            break;   
    case 7: rainbowCycle();                           // Slowly cycle
            break;
    case 8: theaterChaseRainbow();                    // Slowly cycle
            break;
  }
}

// clear all LEDs
void wipe()
{
  for(byte i=0;i<strip.numPixels();i++)
  {
    strip.setPixelColor(i, strip.Color(0,0,0));
    strip.show();
  }
}

void rainbow()
{
  static uint16_t j = 0;
  for(int i=0; i<strip.numPixels(); i++)
  {
    strip.setPixelColor(i, Wheel((i+j) & 255));
  }
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show();
  j++;
  if (j >= 256)
  {
    j = 0;
  }
  // time for next change to the display
  lastUpdate = millis();
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle()
{
  static uint16_t j=0;
  for(int i=0; i < strip.numPixels(); i++)
  {
    strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
  }
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show();
  j++;
  if(j >= 256*5)
  {
    j=0;
  }
  // time for next change to the display
  lastUpdate = millis();
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow()
{
  static int j = 0;
  static byte q = 0;
  static bool onLED = true;
  if (onLED)
  {
    for (int i=0; i < strip.numPixels(); i=i+3)
    {
      strip.setPixelColor(i+q, Wheel((i+j) % 255));    //turn every third pixel on
    }
  }
  else
  {
    for (int i=0; i < strip.numPixels(); i=i+3)
    {
      strip.setPixelColor(i+q, 0);        //turn every third pixel off
    }
  }
  onLED = !onLED; // Toggle pixels on/off for next time
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show();   // Display on LEDs
  q++;            // Increase q
  if (q >= 3)     // If q greater than 2
  {
    q = 0;        // Reset q
    j++;          // Increase j
    if (j >= 256) // If j greater than 255
    {
      j = 0;      // Reset j
    }
  }
  // time for next change to the display   
  lastUpdate = millis();
}

// 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);
  }
  if(WheelPos < 170)
  {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

void fade(byte redStartValue, byte redEndValue, byte greenStartValue, byte greenEndValue, byte blueStartValue, byte blueEndValue, int totalSteps)
{
  static float redIncrement, greenIncrement, blueIncrement;
  static float red, green, blue;
  static bool fadeUp = false;

  if (fadeStep == 0) // first step is to initialise the initial colour and increments
  {
    red = redStartValue;
    green = greenStartValue;
    blue = blueStartValue;
    fadeUp = false;

    redIncrement = (float)(redEndValue - redStartValue) / (float)totalSteps;
    greenIncrement = (float)(greenEndValue - greenStartValue) / (float)totalSteps;
    blueIncrement = (float)(blueEndValue - blueStartValue) / (float)totalSteps;
    fadeStep = 1; // next time the function is called start the fade
  }
  else  // all other steps make a new colour and display it
  {
    // make new colour
    red += redIncrement;
    green +=  greenIncrement;
    blue += blueIncrement;
 
    // set up the pixel buffer
    for (byte i = 0; i < strip.numPixels(); i++)
    {
    strip.setPixelColor(i, strip.Color((byte)red,(byte)green,(byte)blue));
    }
 
    // now display it
    strip.setBrightness(50);  // dim output to conserve battery
    strip.show();
 
    // go on to next step
    fadeStep += 1;

    // finished fade
    if(fadeStep >= totalSteps)
    {
      if(fadeUp) // finished fade up and back
      {
        fadeStep = 0;
        return; // so next call recalabrates the increments
      }
 
      // now fade back
      fadeUp = true;
      redIncrement = -redIncrement;
      greenIncrement = -greenIncrement;
      blueIncrement = -blueIncrement;
      fadeStep = 1; // don't calculate the increments again but start at first change
    } 
  }
}

CNRom

  • Newbie
  • *
  • Posts: 5
Re: Help with sleep & wake on Sparky85
« Reply #2 on: April 29, 2019, 10:23:44 am »
Hi,
Thank you very much for sharing the code, i found it so helpful.

Can you please tell me how can i make the interrupt work when pin gets from LOW to HIGH ?

Thanks!

surveyranger

  • Newbie
  • *
  • Posts: 5
  • I'm more of a learn-as-you-go type person
Re: Help with sleep & wake on Sparky85
« Reply #3 on: April 29, 2019, 02:01:29 pm »
In void setup(), delete:
 digitalWrite(BUTTON_PIN, HIGH);    // activate pull-up resistor to set pin HIGH

In the wiring, use a pull-down resistor to make the pin LOW.  Then, when the button is pressed, it should go HIGH.

In void loop(), change:

if (newState == LOW && oldState == HIGH)

to:

if (newState == HIGH && oldState == LOW)

CNRom

  • Newbie
  • *
  • Posts: 5
Re: Help with sleep & wake on Sparky85
« Reply #4 on: April 29, 2019, 03:06:17 pm »
Thanks a lot :-)