2

I've been messing with the Adafruit Neopixel lib, and I'm trying to replace some of the delay functions with mills ( so that I don't block the serial port ). So far I have the following code;

void loop() {  
  currentMillis = millis();

  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
}

void colorWipe(uint32_t c, uint8_t wait) {
  if (currentMillis - previousMillis >= 200) {
    for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      //delay(wait);
      Serial.println("Light");
      previousMillis = currentMillis;
    }
   Serial.println("Done");
  }
}

The issue I'm running into ( and I do understand this is fundamental concept in Arduino programming ) is how to run sequential functions, like the ones in loop, while still not blocking the main loop. The way this currently functions, it runs through the first colorWhipe loop and then just keeps iterating.

3 Answers 3

3

I'm assuming you want to run each colorWipe for 200 ms, one after another.

You need a state machine to timing each colorWipe, like this:

void loop()
{
    static int state = 0;
    static unsigned long previousMillis = 0;
    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis >= 200) {
        previousMillis = currentMillis;

        switch(state) {
        case 0 :
            colorWipe(strip.Color(255, 0, 0), 50); // Red
            state++;
            break;

        case 1 :
            colorWipe(strip.Color(0, 255, 0), 50); // Green
            state++;
            break;

        case 2 :
            colorWipe(strip.Color(0, 0, 255), 50); // Blue
            state++;
            break;

        case 3 :
            colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
            Serial.println("Done");
            state = 0;
        }
    }
}

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);
        Serial.println("Light");
    }
}

In each loop you will be waiting 200 ms before doing anything. When the times comes, you will enter the if with state==0 and you will execute the first 'colorWipe'. After that, you advance the state by adding 1 to state variable.

Then you will be entering loop and doing nothing for another 200 ms. When that time pass, you will enter the ìf with state==1. Now the second colorWipe will be executed.

When the last colorWipe is executed, you set state to zero and begin again.

1

Assuming the commented out delay in colorWipe should still happen then you need to nest the state machines:

void loop()
{
    static bool waiting=false;
    static int state = 0;
    static unsigned long previousMillis = 0;
    unsigned long currentMillis = millis();
    if(waiting){
        if(currentMillis - previousMillis >= 200){
            waiting = false;
        }
    }


    if(!waiting  && currentMillis - previousMillis >= 50) {
        previousMillis = currentMillis;

        switch(state) {
        case 0 :
            if(colorWipe(strip.Color(255, 0, 0))){ // Red
                state++;
                waiting = true;
            }
            break;

        case 1 :
            if(colorWipe(strip.Color(0, 255, 0))){ // Green
                state++;
                waiting = true;
            }
            break;

        case 2 :
            if(colorWipe(strip.Color(0, 0, 255))){ // Blue
                state++;
                waiting = true;
            }
            break;

        case 3 :
            if(colorWipe(strip.Color(0, 0, 0, 255))){ // White RGBW
                state++;
                state = 0;
            }
            break;
        }
    }
}

int wipeState = 0;
bool colorWipe(uint32_t c)
{

    strip.setPixelColor(wipeState , c);
    strip.show();

    Serial.println("Light");
    wipeState++;
    if(wipeState == strip.numPixels()){
        wipeState = 0;
        return true;
    }
    return false;
}
1

Here's an OOP solution. (It seems a lot of Arduino programmers shy away from C++ classes, but I use them all the time).

Since you don't want to block loop(), you need to create a function that will set the pixel (if time has expired) and return immediately. I would create a class to encapsulate the pixel strip.

The way this works is the class has a loop() function that needs to be called every time in the main program's loop() function. The class's loop() will either return (almost) immediately or it will update the pixel. Which will also be very quick. This allows you to do other things in the main loop().

Disclaimer: I just whipped this up, there may be bugs.

// A class to encapsulate the neo pixel strip.
class color_strip {
    protected:
    Adafruit_NeoPixel pixels;
    uint32_t last_ms;
    uint32_t color;
    uint32_t duration = 0;
    int current_pixel;
    bool first_time;

    public:
    // Constructor
    color_strip(int num_pixels, int pin) {
        strip = Adafruit_NeoPixel(num_pixels, pin, NEO_GRB + NEO_KHZ800);
    }

    // Call this from setup()
    void setup() {
       strip.begin();
    }

    // Call this to start a wipe
    // param duration_ms is how long the entire wipe should take
    void wipe(RGB color, uint32_t duration_ms) {
        // Calculate how long to light each pixel
        duration = duration_ms / strip.numPixels()
        // Initialize this data for loop()
        last_ms = millis();
        first_time = true;
        this->color = strip.Color(color.red, color.green, color.blue);
        current_pixel = 0;
    }

    void loop() {
        // Is there something to do? Only if duration != 0
        if (0 != duration) {
            // Time to change pixel?
            uint32_t current_ms = millis();
            if (current_ms - last_ms >= duration || first_time) {
                first_time = false;
                // Finished the entire wipe?
                if (current_pixel == strip.numPixels()) {
                    duration = 0;
                }
                else {
                    // Set the pixel
                    strip.setPixelColor(current_pixel++, color);
                    strip.show();
                    // Save
                    last_ms = ms;
                }
            }
        }
    }

    // Returns true if doing a wipe, else false
    bool working() {return duration != 0;}
};

Usage: you can create an array of colors. You then cycle through all these colors. If you want to add more colors, simply add to the array.

// Helper struct to store rgb
struct RGB {
    byte red, green, blue;
};
RGB all_colors[] = {
    {255, 0, 0},
    {0, 255, 0},
    {0, 0, 255},
    {0, 0, 0}
    // Add more colors here, if needed
};
// Need to keep track of the current color being displayed
byte current_color = 0;
const byte num_colors = sizeof(all_colors) / sizeof(all_colors[0]);

// Here's the actual object
// Change the parameters to whatever they need to be in your setup
color_strip strip(number_of_pixels, pin_number);

void setup()
    // Call the class setup function
    strip.setup();
}

void loop()
{
    // If the strip has completed its wipe, set it to next color
    if (!strip.working()) {
        current_color = (current_color + 1) % num_colors;
        strip.wipe(all_colors[current_color], 1000);
    }
    // Call the class loop(). It will return quickly so won't block
    // the rest of the program
    strip.loop();

    // Do other stuff here...    
}
7
  • Well, the OP have problems with a simple algorithm. I don't think using classes will be the best approach for him. Using C++ produces brain damage.
    – user31481
    Oct 26, 2017 at 16:02
  • By adding duration to millis, you introduce the rollover problem.
    – Jot
    Oct 27, 2017 at 21:01
  • @LookAlterno Not sure if that's a joke or not....Anyway, OP needs to be able to handle classes. Adafruit_NeoPixel is a C++ class.
    – 001
    Oct 27, 2017 at 21:55
  • @Jot Rollover should not be an issue with unsigned variables. See the answer here: arduino.stackexchange.com/questions/12587/…
    – 001
    Oct 27, 2017 at 22:02
  • @JohnnyMopp. I use classes and I wrote them too. C++ i's like an enema, something you have to do, but not mean to be enjoyed.
    – user31481
    Oct 27, 2017 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.