Adalight-FastLED_rgbwMod/src/main.cpp

353 lines
9.4 KiB
C++

/*
* Project Adalight FastLED
* @author David Madison
* @link github.com/dmadison/Adalight-FastLED
* @license LGPL - Copyright (c) 2017 David Madison
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//Upload for teensy4.1 with: pio run --environment teensy41 -t upload
#define SK6812RGBW //power consumption is around 51mA per LED at RGBW 100%
/*
R=1234mA (with 112 LEDS 100%)
G=1234mA (with 112 LEDS 100%)
B=1090mA (with 112 LEDS 100%)
W=2100mA (with 112 LEDS 100%)
*/
#include <Arduino.h>
// --- General Settings
const uint16_t
Num_Leds = 268; // strip length
const uint8_t
Brightness = 255; // maximum brightness
// --- FastLED Setings
#if defined(SK6812RGBW)
#define LED_TYPE SK6812 // led strip type for FastLED
#define COLOR_ORDER RGB // color order for bitbang
#else
#define LED_TYPE WS2812B // led strip type for FastLED
#define COLOR_ORDER GRB // color order for bitbang
#endif
//#define PIN_DATA D5 // led data output pin2
// #define PIN_CLOCK 7 // led data clock pin (uncomment if you're using a 4-wire LED type)
// --- Serial Settings
const unsigned long
//SerialSpeed = 115200; // serial port speed
SerialSpeed = 256000; // serial port speed
const uint16_t
SerialTimeout = 60*10; // time before LEDs are shut off if no data (in seconds), 0 to disable
// --- Optional Settings (uncomment to add)
//#define SERIAL_FLUSH // Serial buffer cleared on LED latch
#define CLEAR_ON_START // LEDs are cleared on reset
// --- Debug Settings (uncomment to add)
#define DEBUG_LED LED_BUILTIN // toggles the Arduino's built-in LED on header match
// #define DEBUG_FPS 8 // enables a pulse on LED latch
// --------------------------------------------------------------------
#include <FastLED.h>
#if defined(SK6812RGBW)
#include "FastLED_RGBW.h" //rgbw
#endif
//
// FastLED with RGBW
#if defined(SK6812RGBW)
CRGBW send_leds[Num_Leds];
CRGB *send_ledsRaw = (CRGB *) &send_leds[0];
CRGB leds[Num_Leds];
uint8_t * ledsRaw = (uint8_t *)leds;
#else
CRGB leds[Num_Leds];
uint8_t * ledsRaw = (uint8_t *)leds;
#endif
// A 'magic word' (along with LED count & checksum) precedes each block
// of LED data; this assists the microcontroller in syncing up with the
// host-side software and properly issuing the latch (host I/O is
// likely buffered, making usleep() unreliable for latch). You may see
// an initial glitchy frame or two until the two come into alignment.
// The magic word can be whatever sequence you like, but each character
// should be unique, and frequent pixel values like 0 and 255 are
// avoided -- fewer false positives. The host software will need to
// generate a compatible header: immediately following the magic word
// are three bytes: a 16-bit count of the number of LEDs (high byte
// first) followed by a simple checksum value (high byte XOR low byte
// XOR 0x55). LED data follows, 3 bytes per LED, in order R, G, B,
// where 0 = off and 255 = max brightness.
const uint8_t magic[] = {
'A','d','a'};
#define MAGICSIZE sizeof(magic)
// Check values are header byte # - 1, as they are indexed from 0
#define HICHECK (MAGICSIZE)
#define LOCHECK (MAGICSIZE + 1)
#define CHECKSUM (MAGICSIZE + 2)
enum processModes_t {Header, Data} mode = Header;
int16_t c; // current byte, must support -1 if no data available
uint16_t outPos; // current byte index in the LED array
uint32_t bytesRemaining; // count of bytes yet received, set by checksum
unsigned long t, lastByteTime, lastAckTime; // millisecond timestamps
void headerMode();
void dataMode();
void timeouts();
// Macros initialized
#ifdef SERIAL_FLUSH
#undef SERIAL_FLUSH
#define SERIAL_FLUSH while(Serial.available() > 0) { Serial.read(); }
#else
#define SERIAL_FLUSH
#endif
#ifdef DEBUG_LED
#define ON 1
#define OFF 0
#define D_LED(x) do {digitalWrite(DEBUG_LED, x);} while(0)
#else
#define D_LED(x)
#endif
#ifdef DEBUG_FPS
#define D_FPS do {digitalWrite(DEBUG_FPS, HIGH); digitalWrite(DEBUG_FPS, LOW);} while (0)
#else
#define D_FPS
#endif
void setup(){
#ifdef DEBUG_LED
pinMode(DEBUG_LED, OUTPUT);
digitalWrite(DEBUG_LED, LOW);
#endif
#ifdef DEBUG_FPS
pinMode(DEBUG_FPS, OUTPUT);
#endif
#if defined(PIN_CLOCK) && defined(PIN_DATA)
FastLED.addLeds<LED_TYPE, PIN_DATA, PIN_CLOCK, COLOR_ORDER>(leds, Num_Leds);
#elif defined(PIN_DATA)
#if defined(SK6812RGBW)
//FastLED with RGBW
FastLED.addLeds<LED_TYPE, PIN_DATA, COLOR_ORDER>(send_ledsRaw, getRGBWsize(Num_Leds));
#else
FastLED.addLeds<LED_TYPE, PIN_DATA, COLOR_ORDER>(leds, Num_Leds);
#endif
#else
#error "No LED output pins defined. Check your settings at the top."
#endif
FastLED.setBrightness(Brightness);
#ifdef CLEAR_ON_START
FastLED.show();
#endif
Serial.begin(SerialSpeed);
Serial.print("Ada\n"); // Send ACK string to host
lastByteTime = lastAckTime = millis(); // Set initial counters
}
void loop(){
t = millis(); // Save current time
// If there is new serial data
if((c = Serial.read()) >= 0){
lastByteTime = lastAckTime = t; // Reset timeout counters
switch(mode) {
case Header:
headerMode();
break;
case Data:
dataMode();
break;
}
}
else {
// No new data
timeouts();
}
}
void headerMode(){
static uint8_t
headPos,
hi, lo, chk;
if(headPos < MAGICSIZE){
// Check if magic word matches
if(c == magic[headPos]) {headPos++;}
else {headPos = 0;}
}
else{
// Magic word matches! Now verify checksum
switch(headPos){
case HICHECK:
hi = c;
headPos++;
break;
case LOCHECK:
lo = c;
headPos++;
break;
case CHECKSUM:
chk = c;
if(chk == (hi ^ lo ^ 0x55)) {
// Checksum looks valid. Get 16-bit LED count, add 1
// (# LEDs is always > 0) and multiply by 3 for R,G,B.
#if defined(SK6812RGBW)
D_LED(ON);
bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L);
outPos = 0;
memset(leds, 0, Num_Leds * sizeof(struct CRGB));
mode = Data; // Proceed to latch wait mode
#else
D_LED(ON);
bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L);
outPos = 0;
memset(leds, 0, Num_Leds * sizeof(struct CRGB));
mode = Data; // Proceed to latch wait mode
#endif
}
headPos = 0; // Reset header position regardless of checksum result
break;
}
}
}
void dataMode(){
// If LED data is not full
if (outPos < sizeof(leds)){
#if defined(SK6812RGBW)
ledsRaw[outPos++] = c; // Issue next byte
#else
ledsRaw[outPos++] = c; // Issue next byte
#endif
}
bytesRemaining--;
if(bytesRemaining == 0) {
// End of data -- issue latch:
mode = Header; // Begin next header search
#if defined(SK6812RGBW)
//Copy data over
for(int i = 0; i < Num_Leds; i++){
uint8_t r=leds[i].r;
uint8_t g=leds[i].g;
uint8_t b=leds[i].b;
/* Simple 255,255,255 = White approach
uint8_t w=min(r,min(g,b)); //get white content and use for white
r-=w; //subtract white content
g-=w;
b-=w;
*/
// Calibration 20201207. These RGB values match the Neutral White color with calibW brightness.
uint8_t calibR=255;
uint8_t calibG=175;
uint8_t calibB=90;
uint8_t calibW=135;
//one of calibR,calibG or calibB should be 255.
//int minval=min(r-calibR,min(g-calibG,b-calibB)); //0 if rgb contains full white. <0 if not full white. -1*(max(calibR,calibG,calibB)) if no white content
//float whitecontent=1.0 + (minval/255.0); //scale to: 0=no white, 1=full white content
float whitecontent=min(float(r)/calibR, min(float(g)/calibG, float(b)/calibB));
//subtract white contents from rgb
r-=calibR*whitecontent;
g-=calibG*whitecontent;
b-=calibB*whitecontent;
uint8_t w=calibW*whitecontent;
int inew = i;
#ifdef LED_OFFSET
inew = (i+LED_OFFSET)%Num_Leds; //if offset defined, move led 1 around and wrap around at the end
#endif
send_leds[inew] = CRGBW(r, g, b, w); //transfer to rgbw struct
}
#endif
FastLED.show();
D_FPS;
D_LED(OFF);
SERIAL_FLUSH;
}
}
void timeouts(){
// No data received. If this persists, send an ACK packet
// to host once every second to alert it to our presence.
if((t - lastAckTime) >= 1000) {
Serial.print("Ada\n"); // Send ACK string to host
lastAckTime = t; // Reset counter
// If no data received for an extended time, turn off all LEDs.
if(SerialTimeout != 0 && (t - lastByteTime) >= (uint32_t) SerialTimeout * 1000) {
#if defined(SK6812RGBW)
memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes
#else
memset(leds, 0, Num_Leds * sizeof(struct CRGB)); //filling Led array by zeroes
#endif
#if defined(SK6812RGBW)
//All Black
for(int i = 0; i < Num_Leds; i++){
send_leds[i] = CRGBW(0, 0, 0, 0);
}
#endif
FastLED.show();
mode = Header;
lastByteTime = t; // Reset counter
}
}
}