Caveman code blynk photon neopixel non blocking

[demo video][1]

A project for my mother’s Christmas tree that I was determined to do one way or another. I will post the photon code, nano code and blynk setup if anybody is remotely interested.
The photon connects to the nano over I2C so purely receives and responds to blynk commands. The nano is then instructed what to do.
It is a sledgehammer / nut approach but that is all I know and it works really nicely and responds instantly to commands.
In fact I am sure that the photon could handle the entire workload, perhaps somebody who knows what they are doing might want to take that challenge on. Not sure whether it would be so responsive if it was all running on the photon, ideas?
[1]: https://www.youtube.com/watch?v=FDPCYvVqSec

3 Likes

Awesome!

Photon code:`

#include "blynk/blynk.h"
#define brightness 127
int paramint;
#define BLYNK_PRINT Serial
//DANGER - DO NOT SHARE!!!!
char auth[] = "YOUR_BLYNK_TOKEN"; // Put your blynk token here, to create a token open blynk on your smart device, create a new project, generate a token, send it to your email and paste it in
//DANGER - DO NOT SHARE!!!!
#define OTHER_ADDRESS 0x09//slave I2C address

int last_state = HIGH;


void setup() {
  Wire.begin();
    Serial.begin(115200);
    Serial.println("booted up");
  Blynk.begin(auth);
}

BLYNK_WRITE(V0) {
    if (param.asInt() > 0) {
        Serial.println("hsv");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("l");
        Wire.write(param.asInt());
        Wire.endTransmission();
    }
}

BLYNK_WRITE(V1) {
    if (param[0].asInt() > 0) {
        Serial.println("zergba");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("s");
        Wire.write(param[0].asInt());
        Wire.write("h");
        Wire.write(param[1].asInt());
        Wire.write("c");
        Wire.write(param[2].asInt());
        Wire.endTransmission();
        Serial.println("ZERGBA2 pressed");
    }
}

BLYNK_WRITE(V2) {
    if (param.asInt() > 0) {     
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(2);
        Wire.endTransmission();
        Serial.println("button v2 pressed");
    }
}

BLYNK_WRITE(V3) {
    if (param.asInt() > 0) {        
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("v");
        Wire.write(param.asInt());
        Wire.endTransmission();
        Serial.println("button v3 pressed");
    }
}

BLYNK_WRITE(V4) {
    if (param.asInt() == 1) {       
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(4);
        Wire.endTransmission();
        Serial.println("button v4 pressed");
    }
}

BLYNK_WRITE(V5) {
    if (param.asInt() == 1) {       
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(5);
        Wire.endTransmission();
        Serial.println("button v5 pressed");
    }
}

BLYNK_WRITE(V6) {
    if (param.asInt() == 1) {
        Serial.println("theater chase");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(6);
        Wire.endTransmission();
        Serial.println("button v6 pressed");
    }
}

BLYNK_WRITE(V7) {
    if (param.asInt() == 1) {
        Serial.println("scanner");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(7);
        Wire.endTransmission();
        Serial.println("button v7 pressed");
    }
}

BLYNK_WRITE(V8) {
    if (param.asInt() == 1) {
        Serial.println("snowing");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(8);
        Wire.endTransmission();
        Serial.println("button v8 pressed");
    }
}

BLYNK_WRITE(V9) {
    if (param.asInt() == 1) {
        Serial.println("fade");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(9);
        Wire.endTransmission();
        Serial.println("button v9 pressed");
    }
}

BLYNK_WRITE(V10) {
    if (param.asInt() == 1) {
        Serial.println("fade");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("m");
        Wire.write(0);
        Wire.endTransmission();
        Serial.println("button v10 pressed");
    }
}

BLYNK_WRITE(V11) {
    if (param[0].asInt() > 0) {
        Serial.println("zergba");
        Serial.println(param.asInt());
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write("r");
        Wire.write(param[0].asInt());
        Wire.write("g");
        Wire.write(param[1].asInt());
        Wire.write("b");
        Wire.write(param[2].asInt());
        Wire.endTransmission();
        Serial.println("ZERGBA1 pressed");
    }
}

void loop() {
 Blynk.run(); 
}

Arduino nano code:

    #include <Wire.h>

#include <Adafruit_NeoPixel.h>

int i1;
int i2;
// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, SNOWING, ALLWHITE, SPARKLE };
// Patern directions supported:
enum  direction { FORWARD, REVERSE };

// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
  public:

    // Member Variables:
    pattern  ActivePattern;  // which pattern is running
    direction Direction;     // direction to run the pattern

    unsigned long Interval;   // milliseconds between updates
    unsigned long lastUpdate; // last update of position

    uint32_t Color1, Color2, Color3;  // What colors are in use
    uint16_t TotalSteps;  // total number of steps in the pattern
    uint16_t Index;  // current step within the pattern

    void (*OnComplete)();  // Callback on completion of pattern

    // Constructor - calls base-class constructor to initialize strip
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
      : Adafruit_NeoPixel(pixels, pin, type)
    {
      OnComplete = callback;
    }

    // Update the pattern
    void Update()
    {
      if ((millis() - lastUpdate) > Interval) // time to update
      {
        lastUpdate = millis();
        switch (ActivePattern)
        {
          case RAINBOW_CYCLE:
            RainbowCycleUpdate();
            break;
          case THEATER_CHASE:
            TheaterChaseUpdate();
            break;
          case COLOR_WIPE:
            ColorWipeUpdate();
            break;
          case SCANNER:
            ScannerUpdate();
            break;
          case FADE:
            FadeUpdate();
            break;
          case SNOWING:
            SnowUpdate();
            break;
          case ALLWHITE:
            AllwhiteUpdate();
            break;
          case SPARKLE:
            SparkleUpdate();
            break;
          default:
            break;
        }
      }
    }

    // Increment the Index and reset at the end
    void Increment()
    {
      if (Direction == FORWARD)
      {
        Index++;
        if (Index >= TotalSteps)
        {
          Index = 0;
          if (OnComplete != NULL)
          {
            OnComplete(); // call the comlpetion callback
          }
        }
      }
      else // Direction == REVERSE
      {
        --Index;
        if (Index <= 0)
        {
          Index = TotalSteps - 1;
          if (OnComplete != NULL)
          {
            OnComplete(); // call the comlpetion callback
          }
        }
      }
    }

    // Reverse pattern direction
    void Reverse()
    {
      if (Direction == FORWARD)
      {
        Direction = REVERSE;
        Index = TotalSteps - 1;

        //     Index = TotalSteps;
      }
      else
      {
        Direction = FORWARD;
        Index = 0;
      }
    }

    // Initialize for a RainbowCycle
    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
      ActivePattern = RAINBOW_CYCLE;
      Interval = interval;
      TotalSteps = 255;
      Index = 0;
      Direction = dir;
    }

    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
      for (int i = 0; i < numPixels(); i++)
      {
        setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
      }
      show();
      // setBrightness(brightness);
      Increment();
    }

    // Initialize for a Theater Chase
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
      ActivePattern = THEATER_CHASE;
      Interval = interval;
      TotalSteps = numPixels();
      Color1 = color1;
      Color2 = color2;
      Index = 0;
      Direction = dir;
    }

    // Update the Theater Chase Pattern
    void TheaterChaseUpdate()
    {
      for (int i = 0; i < numPixels(); i++)
      {
        if ((i + Index) % 3 == 0)
        {
          setPixelColor(i, Color1);
        }
        else
        {
          setPixelColor(i, Color2);
        }
      }
      show();
      Increment();
    }

    // Initialize for a ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
      ActivePattern = COLOR_WIPE;
      Interval = interval;
      TotalSteps = numPixels();
      Color1 = color;
      Index = 0;
      Direction = dir;
    }

    // Update the Color Wipe Pattern
    void ColorWipeUpdate()
    {
      setPixelColor(Index, Color1);
      show();
      Increment();
    }

    // Initialize for a SCANNNER
    void Scanner(uint32_t color1, uint8_t interval)
    {
      ActivePattern = SCANNER;
      Interval = interval;
      TotalSteps = (numPixels() - 1) * 2;
      Color1 = color1;
      Index = 0;
    }

    // Update the Scanner Pattern
    void ScannerUpdate()
    {
      for (int i = 0; i < numPixels(); i++)
      {
        if (i == Index)  // Scan Pixel to the right
        {
          setPixelColor(i, Color1);
        }
        else if (i == TotalSteps - Index) // Scan Pixel to the left
        {
          setPixelColor(i, Color1);
        }
        else // Fading tail
        {
          setPixelColor(i, DimColor(getPixelColor(i)));
        }
      }
      show();
      Increment();
    }

    void Snowing(uint32_t color3, uint8_t interval)
    {
      ActivePattern = SNOWING;
      Interval = interval;
      TotalSteps = numPixels();
      Color3 = color3;
      Index = 0;
    }
    void SnowUpdate()
    {
      i1 = random(numPixels());
      setPixelColor(i1, Color3);
      i2 = random(numPixels());
      while (i1 == i2)
      {
        i2 = random(numPixels());
      }
      setPixelColor(i2, Color3);
      show();
      //delay(50);
      setPixelColor(i1, (0, 0, 0));
      setPixelColor(i2, (0, 0, 0));
      //setPixelColor[i2] = CRGB::Black;
      show();
      Increment();
    }
    void Sparkle( uint8_t interval)
    {
      ActivePattern = SPARKLE;
      Interval = interval;
      TotalSteps = numPixels();
      Index = 0;
    }

    void SparkleUpdate()
    {
      i1 = random(numPixels());
      setPixelColor(i1, Wheel(random(255)));
      i2 = random(numPixels());
      while (i1 == i2)
      {
        i2 = random(numPixels());
      }

      setPixelColor(i2, Wheel(random(255)));
      show();
      //delay(50);
      setPixelColor(i1, (0, 0, 0));
      setPixelColor(i2, (0, 0, 0));
      //setPixelColor[i2] = CRGB::Black;
      show();
      Increment();
    }
    void Allwhite(uint32_t color3, uint8_t interval)
    {
      ActivePattern = ALLWHITE;
      Interval = interval;
      TotalSteps = numPixels();
      Color3 = color3;
      Index = 0;
    }

    void AllwhiteUpdate()
    {
      for (int i = 0; i < numPixels(); i++)
      {
        setPixelColor(i, Color3);
      }
      show();
      Increment();
    }

    // Initialize for a Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
      ActivePattern = FADE;
      Interval = interval;
      TotalSteps = steps;
      Color1 = color1;
      Color2 = color2;
      Index = 0;
      Direction = dir;
    }

    // Update the Fade Pattern
    void FadeUpdate()
    {
      // Calculate linear interpolation between Color1 and Color2
      // Optimise order of operations to minimize truncation error
      uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
      uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
      uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

      ColorSet(Color(red, green, blue));
      show();
      Increment();
    }

    // Calculate 50% dimmed version of a color (used by ScannerUpdate)
    uint32_t DimColor(uint32_t color)
    {
      // Shift R, G and B components one bit to the right
      uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
      return dimColor;
    }

    // Set all pixels to a color (synchronously)
    void ColorSet(uint32_t color)
    {
      for (int i = 0; i < numPixels(); i++)
      {
        setPixelColor(i, color);
      }
      show();
    }

    // Returns the Red component of a 32-bit color
    uint8_t Red(uint32_t color)
    {
      return (color >> 16) & 0xFF;
    }

    // Returns the Green component of a 32-bit color
    uint8_t Green(uint32_t color)
    {
      return (color >> 8) & 0xFF;
    }

    // Returns the Blue component of a 32-bit color
    uint8_t Blue(uint32_t color)
    {
      return color & 0xFF;
    }

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

NeoPatterns Stick(64, A2, NEO_GRB + NEO_KHZ800, &StickComplete);

#define THIS_ADDRESS 0x09
int brightness = 120;
int currentmode = 0;
char incharacter;
int ininteger;
boolean brightnesschanged = false;
boolean modechanged = false;
boolean colourchanged = false;
int currentred = 255;
int currentgreen = 255;
int currentblue = 0;

int currentred2 = 0;
int currentgreen2 = 0;
int currentblue2 = 25;

// Initialize everything and prepare to start
void setup()
{
  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
  Serial.begin(115200);
  Stick.begin();
  Stick.Allwhite(Stick.Color(120, 120, 120), 10);
}

// Main loop
void loop()
{
  Stick.Update();
  if (modechanged == true)
  {
    //    if (currentmode == 1) // Button v1 pressed
    //    {
    //
    //      Stick.Allwhite(Stick.Color(255, 0, 0), 10);
    //      Serial.println("red set");
    //
    //    }
    if (currentmode == 2) // Button v2 pressed
    {
      int randomred = random(255);
      int randomblue = random(255);
      int randomgreen = random(255);
      Stick.Sparkle( 10);
      Serial.println("sparkle set");
    }
    //    else if (currentmode == 3)// button v3 pressed
    //    {
    //      Stick.Allwhite(Stick.Color(0, 0, 255), 10);
    //      Serial.println("blue set");
    //    }
    else if (currentmode == 4)// white
    {
      Stick.Allwhite(Stick.Color(currentred, currentgreen, currentblue), 10);
      Serial.println("allwhite set");
    }
    else if (currentmode == 5)// rainbow
    {
      Stick.RainbowCycle(random(0, 10));
      Serial.println("rainbow set");
    }
    else if (currentmode == 6)// theatrechase
    {
      Stick.TheaterChase(Stick.Color(currentred, currentgreen, currentblue), Stick.Color(currentred2, currentgreen2, currentblue2), 100);
      Serial.println("theater chase set");
    }
    else if (currentmode == 7)// scanner
    {
      Stick.Scanner(Stick.Color(currentred, currentgreen, currentblue), 10);
      Serial.println("scanner set");
    }
    else if (currentmode == 8)// snowing
    {
      Stick.Snowing(Stick.Color(currentred, currentgreen, currentblue), 10);
      Serial.println("snowing set");
    }
    else if (currentmode == 9)// fade
    {
      Stick.Fade(Stick.Color(currentred, currentgreen, currentblue), Stick.Color(currentred2, currentgreen2, currentblue2), 255, 40, FORWARD);
      Serial.println("fade set");
    }
    else if (currentmode == 0)// colour wipe        {

      Stick.ColorWipe(Stick.Wheel(random(255)), 40, FORWARD);
      Serial.println("colour wipe set");
    }
    modechanged = false;
  }
  if (brightnesschanged == true)
  {
    Stick.setBrightness(brightness);
    brightnesschanged = false;
    Serial.println("brightness set");
  }
  if (colourchanged == true)
  {
    colourchanged = false;
    if (Stick.ActivePattern == ALLWHITE)
    {
      Stick.Allwhite(Stick.Color(currentred, currentgreen, currentblue), 10);
    }
    else if (Stick.ActivePattern == SCANNER)
    {
      Stick.Scanner(Stick.Color(currentred, currentgreen, currentblue), 10);
    }
    else if (Stick.ActivePattern == SNOWING)
    {
      Stick.Snowing(Stick.Color(currentred, currentgreen, currentblue), 10);
    }
    else if (Stick.ActivePattern == FADE)
    {
      Stick.Fade(Stick.Color(currentred, currentgreen, currentblue), Stick.Color(currentred2, currentgreen2, currentblue2), 255, 40, FORWARD);
    }
    else if (Stick.ActivePattern == THEATER_CHASE)
    {
      Stick.TheaterChase(Stick.Color(currentred, currentgreen, currentblue), Stick.Color(currentred2, currentgreen2, currentblue2), 100);
    }
  }
}

void receiveEvent(int howMany) {
  while (Wire.available() > 0)
  {
    incharacter = Wire.read(); // receive byte as a character
    if (String(incharacter) == "m")
    {
      ininteger = Wire.read();
      currentmode = ininteger;
      modechanged = true;
    }
    else if (String(incharacter) == "l")
    {
      ininteger = Wire.read();
      brightness = ininteger;
      brightnesschanged = true;
    }
    else if (String(incharacter) == "r")
    {
      ininteger = Wire.read();
      currentred = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "g")
    {
      ininteger = Wire.read();
      currentgreen = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "b")
    {
      ininteger = Wire.read();
      currentblue = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "s")
    {
      ininteger = Wire.read();
      currentred2 = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "h")
    {
      ininteger = Wire.read();
      currentgreen2 = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "c")
    {
      ininteger = Wire.read();
      currentblue2 = ininteger;
      colourchanged = true;
    }
    else if (String(incharacter) == "i")
    {
      ininteger = Wire.read();
      currentblue2 = ininteger;
      colourchanged = true;
    }
  }
  Serial.println("received");
  Serial.println(incharacter);
  Serial.println(ininteger);
}

// Stick Completion Callback
void StickComplete()
{

  if (Stick.ActivePattern == COLOR_WIPE)
  {

    Stick.ColorWipe(Stick.Wheel(random(255)), 40);
  }
  // Random color change for next scan
  // Stick.Color1 = Stick.Wheel(random(255));
}

The blynk setup is trivial. You need a slider for brightness (I am working on adding one for speed), 8 buttons for the modes and 2 zergba colour pickers set to merge.
Wiring is also relatively straightforward, I would recommend powering the nano and photon / spark / electron on their own supply and allow 60mA per neopixel at least and use multiple input points and ring circuits for long runs. A level shifter is required for the I2C, a couple of 2N7000s and 4 10K resistors do the trick.
One issue is that on white at full brightness the strips go yellow, working on that, any suggestions appreciated. I also failed attempting to reverse the fade pattern.
All credit goes to the author of the non-blocking code here:
multitasking the arduino

I think that it would be nice to see some new patterns (that is a challenge to the more artistic coders out there), I coded snowing (random pixels) and sparkle (same with random colours purely for the Christmas theme but ran out of time and inspiration.
Finally from a coding and performance pov could the photon do all this alone?

Thanks! But Blynk is awesome, not my work.
Another thing that I am in the process of doing is connecting this to my security, dvr and home automation system using Blynk bridge.
Not much use for Christmas lights but planning to fit IP67 strips under the steps at the front of the house and integrate them this way
If I manage to get it working I will update here.

Keep us posted. Ask any questions on our forum if needed. We also monitor this community from time to time

Thanks will do :grin: