Author Topic: One last try...  (Read 7605 times)

crambo

  • Jr. Member
  • **
  • Posts: 52
Re: One last try...
« Reply #15 on: December 27, 2016, 10:46:11 am »
Hi Pippin88,

Is this the behavior you're after? https://www.youtube.com/watch?v=E6HtUSrpvHk&feature=youtu.be

If so, the examples on the github has the sample code...

https://github.com/Makuna/NeoPixelBus/wiki/Examples

Called:

NeoPixelFunFadeInOut

The overall code structure is quite a bit different from Adafruit library, and that may be you issue, as you can't simply adapt your other method.

HTH

pippin88

  • Newbie
  • *
  • Posts: 7
Re: One last try...
« Reply #16 on: December 29, 2016, 10:09:59 pm »
With some very useful pointers from Makuna (who wrote NeoPixelBus) and a lot of trial and error, I have my word clock working on my Oak. It also uses NTP to set the RTC, and adjusts for daylight saving.

I am having intermittent borked flashes, which mean I have to ground P1 to get to safe mode.

My code:
Code: [Select]
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
#include <NeoPixelBrightnessBus.h>
#include <Wire.h>
#include <TimeLib.h>
#include "RTClib.h"
#include <BH1750.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

BH1750 lightMeter(0x23);
RTC_DS3231 rtc;

//LED pin = GPIO 3 for ESP8266, = pin 3 for Oak
typedef ColumnMajorAlternating270Layout MyPanelLayout;
const uint8_t PanelWidth = 16;
const uint8_t PanelHEIGHT = 6;
const uint16_t PixelCount = PanelWidth * PanelHEIGHT;
NeoTopology<MyPanelLayout> topo(PanelWidth, PanelHEIGHT);
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);

RgbColor red(128, 0, 0);
RgbColor green(0, 128, 0);
RgbColor blue(0, 0, 128);
RgbColor white(128);
RgbColor black(0);
 const uint16_t left = 0;
const uint16_t right = PanelWidth - 1;
const uint16_t top = 0;
const uint16_t bottom = PanelHEIGHT - 1;

const uint8_t AnimationChannels = PixelCount;
NeoPixelAnimator animations(AnimationChannels);
uint16_t effectState = 0;
struct MyAnimationState {
    RgbColor StartingColor;
    RgbColor EndingColor;
};
MyAnimationState animationState[AnimationChannels];

struct wordLocation { //define a data structure. Every wordLocation object will have .row, .firstCol, .lastCol
    const uint8_t row;
    const uint8_t firstCol;
    const uint8_t lastCol;
    byte state;
};

//ENUM acts as index for array. Using NEAR in code, will get replaced by 0
enum Word {
    NEAR,
    PAST1,
    EXACTLY,
    A1,
    QUARTER,
    TWENTY,
    MTEN,
    A2,
    MFIVE,
    I,
    HALF,
    TO,
    PAST2,
    TEN,
    FOUR,
    THREE,
    EIGHT,
    SEVEN,
    NINE,
    ELEVEN,
    TWO,
    FIVE,
    ONE,
    SIX,
    TWELVE,
    Word_COUNT // the last one will represent how many words total
};

Word Word; // <-- the actual instance

wordLocation wordLocations[Word_COUNT] = {
  //{.row, .firstCol, .lastCol, .state},
    {0, 0, 3, 0}, //NEAR
    {0, 4, 7, 0}, //past1
    {0, 8, 14, 0}, //exactly
    {0, 15, 15, 0}, //a1
    {1, 0, 6, 0}, //quarter
    {1, 7, 12, 0}, //twenty
    {1, 13, 15, 0}, //mTEN
    {2, 0, 0, 0}, //a2
    {2, 1, 4, 0}, //mFIVE
    {2, 5, 5, 0}, //i
    {2, 6, 9, 0}, //half
    {2, 10, 11, 0}, //to
    {2, 12, 15, 0}, //past2
    {3, 0, 2, 0},//TEN,
    {3, 3, 6, 0},//FOUR,
    {3, 7, 11, 0},//THREE,
    {3, 11, 15, 0},//EIGHT,
    {4, 0, 4, 0},//SEVEN,
    {4, 4, 7, 0},//NINE,
    {4, 7, 12, 0},//ELEVEN,
    {4, 13, 15, 0},//TWO,
    {5, 0, 3, 0},//FIVE,
    {5, 4, 6, 0},//ONE,
    {5, 7, 9, 0},// SIX,
    {5, 10, 15, 0} // TWELVE, //25 total words
};

const int timeZone = 10;               // AEST
char timeServer[] = "0.au.pool.ntp.org";

WiFiUDP Udp;
const unsigned int localPort = 8888;  // local port to lisTEN for UDP packets
const int NTP_PACKET_SIZE = 48;       // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets

//variables
long lastMillis = 0;
long serialMillis = 0;

int mytimemonth;
int mytimeday;
int mytimehr;
int mytimemin;
int mytimesec;

void setup() {
  Serial.begin(115200);  //Begin serial communcation
  // init RTC
  Wire.begin();
  rtc.begin();

  Serial.println("Power on");

  Serial.println("Set RTC to 01/01/2010 at 01:01:01 for debugging");
  rtc.adjust(DateTime(2010, 1, 1, 01, 01, 01)); //set RTC to 01/01/2010 at 01:01:01
  Serial.print("RTC epoch time is: ");
  DateTime now = rtc.now();

  lightMeter.begin(BH1750_CONTINUOUS_HIGH_RES_MODE);
  Serial.println(F("BH1750 enable CONTINUOUS_HIGH_RES_MODE"));
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }

  Serial.print("Local IP: ");
  Serial.println(WiFi.localIP());

  Serial.print("Starting UDP... ");
  Udp.begin(localPort);
  Serial.print("local port: ");
  Serial.println(Udp.localPort());

  Serial.println("Getting NTP time");
  getNTPtime(); //get NTP time and set RTC to UTC

  setSyncInterval(1);  //Arduino/RTC Sync interval to 1 second for intial sync
  setSyncProvider(localTime);
 
  setSyncInterval(300);  //Arduino/RTC Sync interval to 300 seconds

  strip.Begin();
  strip.Show();   
}

unsigned long getNTPtime()
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE); //Set all bytes in the buffer to 0
  packetBuffer[0] = 0b11100011;  //LI, Version, Mode
  packetBuffer[1] = 0;  //Stratum, or type of clock
  packetBuffer[2] = 6;  //Polling Interval
  packetBuffer[3] = 0xEC;  //Peer Clock Precision
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(timeServer, 123);  //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
  //Wait to see if a reply is available
  delay(500);  //Adjust this delay for time server (effects accuracy, use shortest delay possible)
  if (Udp.parsePacket())
  {
    Udp.read(packetBuffer, NTP_PACKET_SIZE);
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    const unsigned long SEVENTY_YEARS = 2208988800UL; //Unix time starts on Jan 1 1970. In seconds, that's 2208988800:   
    unsigned long epoch = secsSince1900 - SEVENTY_YEARS;  //Subtract SEVENty years
    Serial.print("NTP: ");
    Serial.println(epoch);
    rtc.adjust(DateTime(epoch));
    }
return 0;
}

int dstOffset (unsigned long unixTime)
{
  //Receives unix epoch time and returns seconds of offset for local DST
  //Code idea from doughboy @ "http://forum.arduino.cc/index.php?PHPSESSID=uoj11hu5j72556mk3gh0ba5ok3&topic=197637.0"
  //Get epoch times @ "http://www.epochconverter.com/" for testing
  //DST update wont be reflected until the next time sync
  time_t t = unixTime; //take unixTime variable passed and put it in time_t type variable
  tmElements_t te;
  te.Year = year(t)-1970;
  te.Month = 10;
  te.Day = 1;
  te.Hour = 0;
  te.Minute = 0;
  te.Second = 0;
  time_t dstStart,dstEnd, current;
//  Serial.println(time_t);
  dstStart = makeTime(te);
  Serial.print("dstStart ");
  Serial.println(dstStart);
  dstStart = nextSunday(dstStart);  //Once, first Sunday in Oct
  dstStart += 2*SECS_PER_HOUR;  //2AM
  te.Year = year(t)-1970+1;
  te.Month=4;
  dstEnd = makeTime(te);
  dstEnd = nextSunday(dstEnd);  //First Sunday in April
  dstEnd += SECS_PER_HOUR;  //1AM
  Serial.print("dstEnd ");
  Serial.println(dstEnd);
  if (t>=dstStart && t<dstEnd) return (3600);  //Add one hours worth of seconds - DST in effect
  if (t>=dstStart && t<dstEnd) Serial.println("DST on");
  else return (0);  //NonDST
}

time_t localTime(){
    time_t local = rtc.now().unixtime();
    Serial.print("Epoch: ");
    Serial.println(local);
    unsigned long offset;
    offset = timeZone * 3600;
    Serial.print("AEST Offset: ");
    Serial.println(offset);
    local = local + offset; //add timezone offset to RTC time
    Serial.print("Epoch + offset : ");
    Serial.println(local);
    local = local + dstOffset(local);  //Adjust for DST
    Serial.print("DST: ");
    Serial.println(local);
    return local; //return value adjusted for timezone and then DST
}

void displayWord(int WORD, int wState) { //function to display word, effect if just turned on, no effect (stay on) otherwise
  if (wState == ON) {
      if (wordLocations[WORD].state == 0) { //do the following when word first displayed
        fadeIn(WORD); //fade word in
        wordLocations[WORD].state = 1; //set state to show word has been displayed
      } else { //do the following if word already on
        //LEDs will stay at prior state if no command sent, leave this else empty if just want word to stay on as is
        fadeIn(WORD); //calling this here will fade the word from existing colour to a new colour
        // Just show word without animation
        //animationState[WORD].StartingColor = strip.GetPixelColor(topo.Map(wordLocations[WORD].firstCol,wordLocations[WORD].row));
        //animationState[WORD].StartingColor = white;
        //animations.StartAnimation(WORD, 0, showWordUpdate);
      }
  } else { //if word to be turned off, do:
    animations.StartAnimation(WORD, 0, hideWordUpdate);
    wordLocations[WORD].state = 0; //word hidden -> reset state
  }
}

void hideWordUpdate(const AnimationParam& param) { //simple hide word
   
    for (uint16_t pixel = wordLocations[param.index].firstCol; pixel <= wordLocations[param.index].lastCol; pixel++)
    {
        strip.SetPixelColor(topo.Map(pixel,wordLocations[param.index].row), black);
    }
}

void showWordUpdate(const AnimationParam& param) { //simple show word, no effect

    for (uint16_t pixel = wordLocations[param.index].firstCol; pixel <= wordLocations[param.index].lastCol; pixel++)
    {
        strip.SetPixelColor(topo.Map(pixel,wordLocations[param.index].row), animationState[param.index].StartingColor);
    }
}

void fadeIn(int WORD) {
    RgbColor target = HslColor(random(360) / 360.0f, 1.0f, 0.25f);
    uint16_t time = random(800, 2000);

    animationState[WORD].StartingColor = strip.GetPixelColor(topo.Map(wordLocations[WORD].firstCol,wordLocations[WORD].row));
    animationState[WORD].EndingColor = target;

    animations.StartAnimation(WORD, 5000, BlendAnimUpdate);
   
}

void BlendAnimUpdate(const AnimationParam& param) {
    RgbColor updatedColor = RgbColor::LinearBlend(
        animationState[param.index].StartingColor,
        animationState[param.index].EndingColor,
        param.progress);

    for (uint16_t pixel = wordLocations[param.index].firstCol; pixel <= wordLocations[param.index].lastCol; pixel++)
    {
        strip.SetPixelColor(topo.Map(pixel,wordLocations[param.index].row), updatedColor);
    }
}

void displayClock() {
 
  Serial.println("displayClock");

if ((mytimemin== 0)|(mytimemin== 5)|(mytimemin== 10)|(mytimemin== 15)|(mytimemin== 20)
      | (mytimemin== 25)|(mytimemin == 30)|(mytimemin == 35)|(mytimemin == 40)
      | (mytimemin == 45)|(mytimemin == 50)|(mytimemin == 55)) {
    displayWord(EXACTLY, ON);
  } else {
    displayWord(EXACTLY, OFF);
  }
  if ((mytimemin == 1)|(mytimemin == 2)|(mytimemin == 6)|(mytimemin == 7)|(mytimemin == 11)
      | (mytimemin == 12)|(mytimemin == 16)|(mytimemin == 17)|(mytimemin == 21)|(mytimemin == 22)
      | (mytimemin == 26)|(mytimemin == 27)|(mytimemin == 31)|(mytimemin == 32)|(mytimemin == 36)
      | (mytimemin == 37)|(mytimemin == 41)|(mytimemin == 42)|(mytimemin == 46)|(mytimemin == 47)
      | (mytimemin == 51)|(mytimemin == 52)|(mytimemin == 56)|(mytimemin == 57)) {
    displayWord(PAST1, ON);
  } else {
    displayWord(PAST1, OFF);
  }
  if ((mytimemin == 3)|(mytimemin == 4)|(mytimemin == 8)|(mytimemin == 9)|(mytimemin == 13)
      | (mytimemin == 14)|(mytimemin == 18)|(mytimemin == 19)|(mytimemin == 23)|(mytimemin == 24)
      | (mytimemin == 28)|(mytimemin == 29)|(mytimemin == 33)|(mytimemin == 34)|(mytimemin == 38)
      | (mytimemin == 39)|(mytimemin == 43)|(mytimemin == 44)|(mytimemin == 48)|(mytimemin == 49)
      | (mytimemin == 53)|(mytimemin == 54)|(mytimemin == 58)|(mytimemin == 59)) {
      displayWord(NEAR, ON);
      } else {
      displayWord(NEAR, OFF);
  }
  //minutes
    if(mytimemin<3){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, OFF);
    }
    if(mytimemin>2 && mytimemin<8){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, ON);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
      }
    if(mytimemin>7 && mytimemin<13){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, ON);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
    }
    if(mytimemin>12 && mytimemin<18){
      displayWord(QUARTER, ON);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
    }
    if(mytimemin>17 && mytimemin<23){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, ON);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
    }
    if(mytimemin>22 && mytimemin<28){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, ON);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, ON);
      displayWord(HALF, OFF);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
    }
    if(mytimemin>27 && mytimemin<33){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, ON);
      displayWord(TO, OFF);
      displayWord(PAST2, ON);
    }
    if(mytimemin>32 && mytimemin<38){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, ON);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, ON);
      displayWord(HALF, OFF);
      displayWord(TO, ON);
      displayWord(PAST2, OFF);
    }
    if(mytimemin>37 && mytimemin<43){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, ON);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, ON);
      displayWord(PAST2, OFF);
    }
    if(mytimemin>42 && mytimemin<48){
      displayWord(QUARTER, ON);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, ON);
      displayWord(PAST2, OFF);
    }
    if(mytimemin>47 && mytimemin<53){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, ON);
      displayWord(MFIVE, OFF);
      displayWord(HALF, OFF);
      displayWord(TO, ON);
      displayWord(PAST2, OFF);
    }
    if(mytimemin>52 && mytimemin<58){
      displayWord(QUARTER, OFF);
      displayWord(TWENTY, OFF);
      displayWord(MTEN, OFF);
      displayWord(MFIVE, ON);
      displayWord(HALF, OFF);
      displayWord(TO, ON);
      displayWord(PAST2, OFF);
    }
  //hours
  if(mytimehr==1||mytimehr==13){
    if(mytimemin>32){
      displayWord(ONE, OFF);
      displayWord(TWO, ON);
      displayWord(THREE, OFF);
    }
    else
    {
      displayWord(ONE, ON);
      displayWord(TWO, OFF);
      displayWord(TWELVE, OFF);
    }
  }
  if(mytimehr==2||mytimehr==14){
    if(mytimemin>32){
     displayWord(TWO,OFF);
     displayWord(THREE,ON);
     displayWord(FOUR,OFF);
    }
    else
    {
     displayWord(ONE,OFF);
     displayWord(TWO,ON);
     displayWord(THREE,OFF);
    }
  }
    if(mytimehr==3||mytimehr==15){
    if(mytimemin>32){
     displayWord(THREE,OFF);
     displayWord(FOUR,ON);
     displayWord(FIVE,OFF);
    }
    else
    {
     displayWord(TWO,OFF);
    displayWord(THREE,ON);
    displayWord(FOUR,OFF);
    }
  }
  if(mytimehr==4||mytimehr==16){
    if(mytimemin>32){
     displayWord(FOUR,OFF);
     displayWord(FIVE,ON);
     displayWord(SIX,OFF);
    }
    else
    {
    displayWord(THREE,OFF);
    displayWord(FOUR,ON);
    displayWord(FIVE,OFF);
    }
  }
  if(mytimehr==5||mytimehr==17){
    if(mytimemin>32){
    displayWord(FIVE,OFF);
    displayWord(SIX,ON);
    displayWord(SEVEN,OFF);
    }
    else
    {
    displayWord(FOUR,OFF);
    displayWord(FIVE,ON);
    displayWord(SIX,OFF);
    }
  }
  if(mytimehr==6||mytimehr==18){
    if(mytimemin>32){
    displayWord(SIX,OFF);
    displayWord(SEVEN,ON);
    displayWord(EIGHT,OFF);
    }
    else
    {
    displayWord(FIVE,OFF);
    displayWord(SIX,ON);
    displayWord(SEVEN,OFF);
    }
  }
  if(mytimehr==7||mytimehr==19){
    if(mytimemin>32){
    displayWord(SEVEN,OFF);
    displayWord(EIGHT,ON);
    displayWord(NINE,OFF);
    }
    else
    {
    displayWord(SIX,OFF);
    displayWord(SEVEN,ON);
    displayWord(EIGHT,OFF);
    }
  }
  if(mytimehr==8||mytimehr==20){
    if(mytimemin>32){
    displayWord(EIGHT,OFF);
    displayWord(NINE,ON);
    displayWord(TEN,OFF);
    }
    else
    {
    displayWord(SEVEN,OFF);
    displayWord(EIGHT,ON);
    displayWord(NINE,OFF);
    }
  }
  if(mytimehr==9||mytimehr==21){
    if(mytimemin>32){
    displayWord(NINE, OFF);
   displayWord(TEN,ON);
   displayWord(ELEVEN,OFF);
    }
    else
    {
    displayWord(EIGHT, OFF);
    displayWord(NINE, ON);
    displayWord(TEN, OFF);
    }
  }
  if(mytimehr==10||mytimehr==22){
    if(mytimemin>32){
    displayWord(TEN, OFF);
    displayWord(ELEVEN, ON);
    displayWord(TWELVE, OFF);
    }
    else
    {
    displayWord(NINE, OFF);
    displayWord(TEN, ON);
    displayWord(ELEVEN, OFF);
    }
  }
  if(mytimehr==11||mytimehr==23){
    if(mytimemin>32){
    displayWord(ONE, OFF);
    displayWord(ELEVEN, OFF);
    displayWord(TWELVE, ON);
    }
    else
    {
    displayWord(TEN, OFF);
    displayWord(ELEVEN, ON);
    displayWord(TWELVE, OFF);
    }
  }
  if(mytimehr==12||mytimehr==0){
    if(mytimemin>32){
      displayWord(ONE, ON);
      displayWord(TWO, OFF);
      displayWord(TWELVE, OFF);
    }
    else
    {
      displayWord(ONE, OFF);
      displayWord(TWO, OFF);
      displayWord(TWELVE, ON);
    }
  }
}

unsigned long  minuteCheck = 0;

void loop (){
  now();
  mytimemonth=month();
  Serial.print(mytimemonth);
  Serial.print("/");
  mytimeday=day();
  Serial.print(mytimeday);
  Serial.print(", ");
  mytimehr=hour();
  Serial.print(mytimehr);
  Serial.print(":");
  mytimemin=minute();
  Serial.print(mytimemin);
  Serial.print(":");
  mytimesec=second();
  Serial.println(mytimesec);

  minuteCheck = millis() / 1000;
  if (minuteCheck % 60 == 0) { // i.e if it's fully divisible by 60
    displayClock();
  }
 
  animations.UpdateAnimations();
  strip.Show();   
}
}

pippin88

  • Newbie
  • *
  • Posts: 7
Re: One last try...
« Reply #17 on: January 01, 2017, 12:43:55 am »
Actually, my daylight saving code fell over when the year changed...

I've switched to using a library: https://github.com/JChristensen/Timezone