Digistump Forums
The Digispark => Digispark (Original) Support => Topic started by: surveyranger 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:
(https://i.imgur.com/qWRoPwP.jpg)
The amalgam of code:
#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
-
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:
(https://i.imgur.com/6sg2CJE.jpg)
End product:
(https://i.imgur.com/v3FqDTH.jpg?1)
Final code:
/* 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
}
}
}
-
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!
-
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)
-
Thanks a lot :-)