Go Down

Topic: button cycled NeoPixel on Digispark ATtiny85 (Read 762 times) previous topic - next topic

surveyranger

I believe I may be suffering paralysis by analysis.  I modified the Adafruit_Neopixel example for 'buttoncycler' to use with a digispark ATtiny 85.  I plan to eventually make it battery powered hence the need for low power consumption.  I broke it down into phases to help focus my efforts:

Phase 1 = Get buttoncycler example/circuit to work and customize showTypes [complete]
Phase 2 = Have current showType stop (even in the middle of running) when button pressed and advance to next showType (case)
Phase 3 = Have current showType repeat until button pressed or uC goes to sleep from 5-min idle timer
Phase 4 = Add a sleep mode to wake on external interrupt (button on P2)
Phase 5 = Optimize to reduce uAh and mAh

I've tried various things after reading different posts on the forums and websites.  I have the Arduino Reference, Robin2's post Demonstration code..., UKHelibob's post on Using millis()..., and Nick Gammon's Interrupts opened in their own tabs trying to piece something together.

Where I'm stuck at now is getting the button press recognized when a showType is running.  For instance, in case (1) the colorWipe runs through the different cycles then stops at the end of the function even if I tap the button repeatedly.  I tried using a $1 WAVGAT Pro Mini clone, but it did not like the Neopixel library so I switched to the Sparky85. 

Code to follow in next post due to size




surveyranger

Code: [Select]
/* Cycle NeoPixels with button using Digispark ATtiny85 (microUSB version)
 *  Button on P2 (PCINT0) 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 5 minutes.  uC wakes on button press on P2 (PCINT0) and continues program.
 *  User can turn off circuit with switched attached to VIN.  User needs to press the
 *  button once to start the first animation!
 *  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 5V and GND pins connected to Digispark ATtiny85 power rail 5V and GND pins, respectively.
 *  Program will eventually include SLEEP_MODE_PWR_DOWN & wdt_disable so it only awakens with PCINT0
 *  
 *  ISSUE LOG:
 *  1. Button press does not stop showType in middle of function
 *  2. showType runs once and stops rather than keeps running until button pressed or 5-min sleep timer
 *  3. Minimize power draw when awake
 *  4. Remove delay() in showType functions
 *  5. case(0) is skipped when first initialized and setup() finishes. Pressing button changes LEDs from green
 *     to case(1) colorWipe
 */
 
// #include <avr/sleep.h>         // library for sleep modes
// #include <avr/power.h>         // library for power managements
// #include <avr/wdt.h>           // Needed to disable/enable WDT??
// #include <avr/interrupt.h>     // library for interrupts

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

#define BUTTON_PIN   2   // P2 (PCINT0) pin for button
                          

#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);

bool oldState = HIGH;
bool newState;
byte showType = 0;        //changed from (16bit)int to (8bit)byte to reduce size

unsigned long startMillis;
unsigned long currentMillis;

void debounce() {
const byte debouncePeriod = 20;  // number of MS for button debounce [changed from (32bit)const unsigned long to (8 bit)const byte]
boolean debouncing = false;  
  unsigned long debounceStartMillis = currentMillis;  //save the time that the state change occured
  debouncing = true;  //flag that debouncing in progress
    if (currentMillis - debounceStartMillis >= debouncePeriod)  //if the debounce period has elapsed
    {
      if (debouncing == true)    //debouncing taking place
      {
        if (digitalRead(BUTTON_PIN) == LOW)     //if the button is currently pressed
        {
          debouncing = false;    //debouncing is finished
        }
      }
    }  
}

void setup() {
/*  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code
*/  
  pinMode(PIXEL_PIN, OUTPUT);
  digitalWrite(BUTTON_PIN, HIGH);  // activate pull-up resistor to set pin HIGH
  pinMode(1, INPUT);  // Turn unused pins to INPUTs to conserve battery
  pinMode(3, INPUT);  // when in sleep mode. Should be INPUT by default??
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  
  strip.begin();
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show(); // Initialize all pixels to 'off'
  // Neopixel color test
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 0, 255), 50); // Blue  
  colorWipe(strip.Color(0, 255, 0), 50); // Green

  startMillis = millis();  //initial start 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
}

void loop() {
  //get the current "time" (actually the number of milliseconds since the program started)
  currentMillis = millis();  
  
  // Get current button state.
  newState = digitalRead(BUTTON_PIN);
  
  // Check if state changed from high to low (button press).
  if (newState != oldState)  // was (newState == LOW && oldState == HIGH)
  {
    // Run button debounce function
    debounce();
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW)   // Verifies it really is a button press
    {
      showType++;
      if (showType > 6)
        showType=0;
      startShow(showType);
    }
  }
  
  // Set the last button state to the old state.
  oldState = newState;
}

void startShow(int i) {
  switch(i) {
    case 0: colorWipe(strip.Color(0, 0, 0), 50);         // Black/off
            break;
    case 1: colorWipe(strip.Color(153, 0, 255), 200);    // Purple: Signifies pain, suffering, grief, and mourning
            colorWipe(strip.Color(255, 0, 153), 200);    // Pink: Celebration
            colorWipe(strip.Color(127, 127, 127), 200);  // White: Purity and hope
            colorWipe(strip.Color(200, 51, 0), 200);     // Orange: Sun
            colorWipe(strip.Color(255, 0, 0), 200);      // Red: The blood of life
            colorWipe(strip.Color(255, 200, 0), 200);    // Yellow: Cempazuchitl are marigolds that symbolize death.
            break;
    case 2: theaterChase(strip.Color(153, 0, 255), 200);    // Purple
            theaterChase(strip.Color(255, 0, 153), 200);    // Pink
            theaterChase(strip.Color(127, 127, 127), 200);  // White
            theaterChase(strip.Color(200, 51, 0), 200);     // Orange
            theaterChase(strip.Color(255, 0, 0), 200);      // Red
            theaterChase(strip.Color(255, 200, 0), 200);    // Yellow
            break;
    case 3: rainbow(50);
            break;
    case 4: rainbowCycle(20);
            break;
    case 5: theaterChaseRainbow(50);
            break;
    case 6: colorWipe(strip.Color(0, 0, 0), 50);    // Black/off
            break;
  }
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t showSpeed) {
  for(uint16_t i=0; i<strip.numPixels(); i++)
  {
    strip.setPixelColor(i, c);
    strip.show();
   if (digitalRead(BUTTON_PIN) == LOW)   // Check if button pressed
    {
      break;                              // If button pressed, move to next showType
    }
    delay(showSpeed);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t showSpeed) {
  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(showSpeed);

      for (int i=0; i < strip.numPixels(); i=i+3)
      {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

void rainbow(uint8_t showSpeed) {
  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(showSpeed);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t showSpeed) {
  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(showSpeed);
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t showSpeed) {
  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(showSpeed);

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

surveyranger

Not sure if I ask really good questions or really bad questions since I never received any replies.  Pretty sure it's the latter.
 
Phase 1 = Get buttoncycler example/circuit to work and customize showTypes (complete)
Phase 2 = Have current showType stop (even in the middle of running) when button pressed and advance to next showType (case)
Phase 3a = Have current showType repeat until button pressed (complete)
Phase 3b = Have current showType repeat until uC goes to sleep from 5-min idle timer
Phase 4 = Add a sleep mode to wake on external interrupt (button on P2)
Phase 5 = Optimize to reduce uAh and mAh

Made some changes to the code that got me some progress after 2-3 days of reading/editing.  Any suggestions on how to stop a "for loop"  or "for loops" nested together when I press a button?  I've tried while, do/while, goto, if, break, return, continue....  but nothing seems to work as intended (although I'm sure it's working as written).  I attached screencaps of a function (showType) that I wanted to interrupt.  The examples/references in the IDE and on the website do not normally involve "for loops."  I'm hesitant to try an interrupt because I want to use the button pin as an external interrupt when Sparky is asleep.  Can I have 1 pin do 2 interrupts??



Code: [Select]

/* Cycle NeoPixels with button using Digispark ATtiny85 (microUSB version)
 *  Button on P2 (PCINT0) 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 5 minutes.  uC wakes on button press on P2 (PCINT0) and continues program.
 *  User can turn off circuit with switched attached to VIN.  User needs to press the
 *  button once to start the first animation!
 *  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 5V and GND pins connected to Digispark ATtiny85 power rail 5V and GND pins, respectively.
 *  Program will eventually include SLEEP_MODE_PWR_DOWN & wdt_disable so it only awakens with PCINT0
 * 
 *  ISSUE LOG:
 *  1. Button press does not stop showType in middle of function
 *  2. showType runs until button pressed nut NOT 5-min sleep timer
 *  3. Minimize power draw when awake
 *  4. Remove delay() in showType functions
 */
 
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h>
#endif

#define BUTTON_PIN   2    // Digital IO pin connected to the button. 

#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);

bool buttonPressed = false;
bool oldState = HIGH;
bool newState = HIGH;
int showType = 0;

unsigned long startMillis;
unsigned long currentMillis;
const int debouncePeriod = 20; 
unsigned long debounceStartMillis;

// Neopixel color test
void testPixels()
{
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 0, 255), 50); // Blue 
  colorWipe(strip.Color(0, 255, 0), 50); // Green
}

// Check state of button
void checkButton()
{
    // Get current button state
  newState = digitalRead(BUTTON_PIN);
 
  // Check if state changed from high to low (button press)
  if (newState != oldState)
  {
    if (currentMillis - debounceStartMillis >= debouncePeriod)
    {
      if (digitalRead(BUTTON_PIN) == LOW)    // if button still pressed
      {
        newState = LOW;
        oldState += newState;                // Update oldState to last button state (newState)
        buttonPressed = true;                // trying to set flag that button is pressed
      }
    }
  }
  else
  {
    newState = HIGH;
    oldState += newState;
    buttonPressed = false;
  }                                             
}

void setup()
{
/*  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code
*/ 
  pinMode(PIXEL_PIN, OUTPUT);
  digitalWrite(BUTTON_PIN, HIGH);  // activate pull-up resistor to set pin HIGH
 
  strip.begin();
  strip.setBrightness(50);  // dim output to conserve battery
  strip.show(); // Initialize all pixels to 'off'

  testPixels();
 
  startMillis = millis();  //initial start time

}

void loop()
{
  //get the current "time"
  currentMillis = millis(); 

  // Call to checkButton function
  checkButton();                               
     
  if (buttonPressed == true)
  {
    showType++;
    if (showType > 8)
    {
    showType = 0;
    }
  }
  startShow(showType);

//  oldState = newState;
}

void startShow(int i) {
  switch(i) {
    case 0: checkButton();
            colorWipe(strip.Color(0, 0, 0), 50);         // Black/off
            break;
    case 1: checkButton();
            colorWipe(strip.Color(153, 0, 255), 200);    // Purple: Signifies pain, suffering, grief, and mourning
            colorWipe(strip.Color(255, 0, 153), 200);    // Pink: Celebration
            colorWipe(strip.Color(127, 127, 127), 200);  // White: Purity and hope
            colorWipe(strip.Color(200, 51, 0), 200);     // Orange: Sun
            colorWipe(strip.Color(255, 0, 0), 200);      // Red: The blood of life
            colorWipe(strip.Color(255, 200, 0), 200);    // Yellow: Cempazuchitl are marigolds that symbolize death.
            break;
    case 2: checkButton();
            theaterChase(strip.Color(153, 0, 255), 200);    // Purple
            theaterChase(strip.Color(255, 0, 153), 200);    // Pink
            theaterChase(strip.Color(127, 127, 127), 200);  // White
            theaterChase(strip.Color(200, 51, 0), 200);     // Orange
            theaterChase(strip.Color(255, 0, 0), 200);      // Red
            theaterChase(strip.Color(255, 200, 0), 200);    // Yellow
            break;
    case 3: checkButton();
            rainbow(10);
            break;   
    case 4: checkButton();
            rainbowCycle(20);
            break;
    case 5: checkButton();
            theaterChaseRainbow(50);
            break;
    case 6: checkButton();
            rainbow(50);
            break;
    case 7: checkButton();
            rainbowCycle(50);
            break;
    case 8: checkButton();
            theaterChaseRainbow(100);
            break;
  }
}


void colorWipe(uint32_t c, uint8_t showSpeed)
{
  for(uint16_t i=0; i<strip.numPixels(); i++)
  {
    strip.setPixelColor(i, c);
    strip.show();
    delay(showSpeed);
  }
}

void theaterChase(uint32_t c, uint8_t showSpeed)
{
  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(showSpeed);

      for (int i=0; i < strip.numPixels(); i=i+3)
      {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

void rainbow(uint8_t showSpeed) {
  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(showSpeed);
  }
}

void rainbowCycle(uint8_t showSpeed) {
  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(showSpeed);
  }
}

void theaterChaseRainbow(uint8_t showSpeed) {
  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(showSpeed);

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

Grumpy_Mike

#3
Feb 05, 2019, 07:03 am Last Edit: Feb 05, 2019, 07:07 am by Grumpy_Mike
Quote
Any suggestions on how to stop a "for loop"  or "for loops" nested together when I press a button?
Yes you don't write them in the first place, unless you want to do a lot of things in one step and there is no delay in it. Nesting them is bad.

You need to write your code as a state machine, what you have at the moment is not one because it contains for loops.

This examples shows you how to do this for all the standard Adafruit patterns.

Code: [Select]

// StrandTest from AdaFruit implemented as a state machine
// pattern change by push button
// By Mike Cook Jan 2016
// Fade function added Sept 2017

#define PINforControl   4 // pin connected to the small NeoPixels strip
#define NUMPIXELS1      64 // number of LEDs on strip

#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS1, PINforControl, NEO_GRB + NEO_KHZ800);

unsigned long patternInterval = 20 ; // time between steps in the pattern
unsigned long lastUpdate = 0 ; // for millis() when last update occoured
unsigned long intervals [] = { 20, 20, 50, 100, 30 } ; // speed for each pattern - add here when adding more cases
int fadeStep = 0; // stste variable for fade function
int numberOfCases = 4; // how many case statements or patterns you have
const byte button = 3; // pin to connect button switch to between pin and ground

void setup() {
  strip.begin(); // This initializes the NeoPixel library.
  wipe(); // wipes the LED buffers
  pinMode(button, INPUT_PULLUP); // change pattern button
}

void loop() {
  static int pattern = 4, lastReading; // start with the fade function
  int reading = digitalRead(button);
  if(lastReading == HIGH && reading == LOW){
    pattern++ ; // change pattern number
    fadeStep = 0; // reset the fade state variable
    if(pattern > numberOfCases) pattern = 0; // wrap round if too big
    patternInterval = intervals[pattern]; // set speed for this pattern
    wipe(); // clear out the buffer
    delay(50); // debounce delay
  }
  lastReading = reading; // save for next time

if(millis() - lastUpdate > patternInterval) updatePattern(pattern);
}

void  updatePattern(int pat){ // call the pattern currently being created
  switch(pat) {
    case 0:
        rainbow();
        break;
    case 1:
        rainbowCycle();
        break;
    case 2:
        theaterChaseRainbow();
        break;
    case 3:
         colorWipe(strip.Color(255, 0, 0)); // red
         break;
        
    case 4:
         fade(0,255, 0,64, 0,0, 400); // fade from black to oraange and back
         break;                  
  }
}

void fade(int redStartValue, int redEndValue, int greenStartValue, int greenEndValue, int blueStartValue, int blueEndValue, int totalSteps) {
static float redIncrement, greenIncrement, blueIncrement;
static float red, green, blue;
static boolean 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 (int i = 0; i < strip.numPixels(); i++) {
  strip.setPixelColor(i, strip.Color((int)red,(int)green,(int)blue));
  }
 // now display it
  strip.show();
fadeStep += 1; // go on to next step
if(fadeStep >= totalSteps) { // finished fade
  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
}
 }
}

void rainbow() { // modified from Adafruit example to make it a state machine
  static uint16_t j=0;
    for(int i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
     j++;
  if(j >= 256) j=0;
  lastUpdate = millis(); // time for next change to the display
 
}
void rainbowCycle() { // modified from Adafruit example to make it a state machine
  static uint16_t j=0;
    for(int i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
  j++;
  if(j >= 256*5) j=0;
  lastUpdate = millis(); // time for next change to the display
}

void theaterChaseRainbow() { // modified from Adafruit example to make it a state machine
  static int j=0, q = 0;
  static boolean on = true;
     if(on){
            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
                 }
      }
     on = !on; // toggel pixelse on or off for next time
      strip.show(); // display
      q++; // update the q variable
      if(q >=3 ){ // if it overflows reset it and update the J variable
        q=0;
        j++;
        if(j >= 256) j = 0;
      }
  lastUpdate = millis(); // time for next change to the display  
}

void colorWipe(uint32_t c) { // modified from Adafruit example to make it a state machine
  static int i =0;
    strip.setPixelColor(i, c);
    strip.show();
  i++;
  if(i >= strip.numPixels()){
    i = 0;
    wipe(); // blank out strip
  }
  lastUpdate = millis(); // time for next change to the display
}


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

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



Quote
Not sure if I ask really good questions or really bad questions
Very bad, difficult to read - used a Fritzing diagram (always bad), long and rambling difficult to understand - showed code as a screen dump meaning impossible for us to recreate

surveyranger

Yes you don't write them in the first place, unless you want to do a lot of things in one step and there is no delay in it. Nesting them is bad.
Very good distinction on for loops.  I'll remember that moving forward.

You need to write your code as a state machine, what you have at the moment is not one because it contains for loops.

This examples shows you how to do this for all the standard Adafruit patterns.
Thank you for the example.  I'll look it over to see how I can get rid of those for loops and complete Phase 2.

Very bad, difficult to read - used a Fritzing diagram (always bad), long and rambling difficult to understand - showed code as a screen dump meaning impossible for us to recreate
Thanks for the feedback.

surveyranger

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).  This is my first attempt at sleep functions and ISRs.

Since it doesn't even start blinking the pixels, then I think it's in the setup().  Anybody see where the hangup is?

Code added to sleep & wake (hypothetically)
Code: [Select]

// 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);?
}

void setup
{
  // 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
  }

if (newState == LOW && oldState == HIGH)
  {
    showType++;                // Move to next showType
    idleMillis = millis();     // Reset idle timer
  }
}


Most of the code (without all the functions to blink the pixels)
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



Phase 1 = Get buttoncycler example/circuit to work and customize showTypes (complete)
Phase 2 = Have current showType stop (even in the middle of running) when button pressed and advance to next showType (case) (complete)
Phase 3a = Have current showType repeat until button pressed (complete)
Phase 3b = Have current showType repeat until uC goes to sleep from 1/5/10-min idle timer(in progress)
Phase 4 = Add a sleep mode to wake on external interrupt (button on P2) (in progress)
Phase 5 = Optimize to reduce mAh to uAh

surveyranger

Made a few sacrifices in the coding and wiring, but I'm finished with this project.  Final code below if anyone is interested.



.ino File attached because code exceeded 7000 characters.

Go Up