2020-12-01 21:51:24 +00:00
/*
* Project Adalight FastLED
* @ author David Madison
* @ link github . com / dmadison / Adalight - FastLED
* @ license LGPL - Copyright ( c ) 2017 David Madison
2017-06-08 19:58:47 +00:00
*
* 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/>.
2020-12-01 21:51:24 +00:00
*
2016-12-21 17:02:29 +00:00
*/
2016-12-21 16:01:46 +00:00
2020-12-05 17:20:57 +00:00
//Upload for teensy4.1 with: pio run --environment teensy41 -t upload
2020-12-01 22:27:56 +00:00
# define SK6812RGBW //power consumption is around 51mA per LED at RGBW 100%
/*
R = 1234 mA ( with 112 LEDS 100 % )
G = 1234 mA ( with 112 LEDS 100 % )
B = 1090 mA ( with 112 LEDS 100 % )
W = 2100 mA ( with 112 LEDS 100 % )
*/
2020-12-01 21:51:24 +00:00
# include <Arduino.h>
2017-03-19 22:48:14 +00:00
// --- General Settings
2020-12-01 21:51:24 +00:00
const uint16_t
2020-12-14 21:50:46 +00:00
Num_Leds = 268 ; // strip length
2020-12-01 21:51:24 +00:00
const uint8_t
2020-12-14 21:50:46 +00:00
Brightness = 255 ; // maximum brightness
2016-12-21 16:19:03 +00:00
2016-12-21 16:29:46 +00:00
// --- FastLED Setings
2020-12-01 21:51:24 +00:00
# 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
2020-12-05 17:20:57 +00:00
//#define PIN_DATA D5 // led data output pin2
2020-12-01 21:51:24 +00:00
// #define PIN_CLOCK 7 // led data clock pin (uncomment if you're using a 4-wire LED type)
2016-12-21 16:29:46 +00:00
2016-12-21 16:19:03 +00:00
// --- Serial Settings
2020-12-01 21:51:24 +00:00
const unsigned long
2020-12-03 09:04:27 +00:00
//SerialSpeed = 115200; // serial port speed
2020-12-05 17:20:57 +00:00
SerialSpeed = 256000 ; // serial port speed
2020-12-01 21:51:24 +00:00
const uint16_t
2020-12-03 09:04:27 +00:00
SerialTimeout = 60 * 10 ; // time before LEDs are shut off if no data (in seconds), 0 to disable
2017-03-27 21:35:44 +00:00
2017-03-19 22:48:14 +00:00
// --- Optional Settings (uncomment to add)
2020-12-03 09:04:27 +00:00
//#define SERIAL_FLUSH // Serial buffer cleared on LED latch
2020-12-01 21:51:24 +00:00
# define CLEAR_ON_START // LEDs are cleared on reset
2017-05-03 16:48:48 +00:00
// --- Debug Settings (uncomment to add)
2020-12-01 21:51:24 +00:00
# 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
2016-12-20 09:30:27 +00:00
2016-12-21 16:19:03 +00:00
// --------------------------------------------------------------------
2016-12-20 09:47:22 +00:00
2016-12-21 16:29:46 +00:00
# include <FastLED.h>
2020-12-01 21:51:24 +00:00
# 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
2016-12-20 09:30:27 +00:00
// 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
2017-03-27 21:35:44 +00:00
// likely buffered, making usleep() unreliable for latch). You may see
2016-12-20 09:30:27 +00:00
// 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
2017-03-27 21:35:44 +00:00
// avoided -- fewer false positives. The host software will need to
2016-12-20 09:30:27 +00:00
// 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
2017-03-27 21:35:44 +00:00
// XOR 0x55). LED data follows, 3 bytes per LED, in order R, G, B,
2016-12-20 09:30:27 +00:00
// where 0 = off and 255 = max brightness.
2020-12-01 21:51:24 +00:00
const uint8_t magic [ ] = {
2017-03-27 21:35:44 +00:00
' A ' , ' d ' , ' a ' } ;
2016-12-20 09:30:27 +00:00
# define MAGICSIZE sizeof(magic)
2017-03-27 23:20:55 +00:00
// Check values are header byte # - 1, as they are indexed from 0
# define HICHECK (MAGICSIZE)
# define LOCHECK (MAGICSIZE + 1)
# define CHECKSUM (MAGICSIZE + 2)
2016-12-20 09:30:27 +00:00
2017-06-08 21:04:22 +00:00
enum processModes_t { Header , Data } mode = Header ;
2016-12-20 09:30:27 +00:00
2020-12-01 21:51:24 +00:00
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
2017-05-03 16:48:48 +00:00
# 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
2017-05-05 09:40:40 +00:00
# ifdef DEBUG_FPS
# define D_FPS do {digitalWrite(DEBUG_FPS, HIGH); digitalWrite(DEBUG_FPS, LOW);} while (0)
# else
# define D_FPS
# endif
2016-12-21 17:21:39 +00:00
void setup ( ) {
2017-04-19 02:52:37 +00:00
# ifdef DEBUG_LED
pinMode ( DEBUG_LED , OUTPUT ) ;
digitalWrite ( DEBUG_LED , LOW ) ;
# endif
2017-05-05 09:40:40 +00:00
# ifdef DEBUG_FPS
pinMode ( DEBUG_FPS , OUTPUT ) ;
# endif
2020-12-01 21:51:24 +00:00
# if defined(PIN_CLOCK) && defined(PIN_DATA)
2018-10-18 21:42:33 +00:00
FastLED . addLeds < LED_TYPE , PIN_DATA , PIN_CLOCK , COLOR_ORDER > ( leds , Num_Leds ) ;
2020-12-01 21:51:24 +00:00
# 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
2018-10-18 21:42:33 +00:00
# else
2020-12-01 21:51:24 +00:00
# error "No LED output pins defined. Check your settings at the top."
2018-10-18 21:42:33 +00:00
# endif
2017-03-27 21:35:44 +00:00
FastLED . setBrightness ( Brightness ) ;
2016-12-20 09:30:27 +00:00
2017-03-27 21:35:44 +00:00
# ifdef CLEAR_ON_START
FastLED . show ( ) ;
# endif
2017-03-27 19:14:02 +00:00
2021-08-13 08:30:22 +00:00
2017-03-27 21:35:44 +00:00
Serial . begin ( SerialSpeed ) ;
2017-05-05 13:05:47 +00:00
Serial . print ( " Ada \n " ) ; // Send ACK string to host
lastByteTime = lastAckTime = millis ( ) ; // Set initial counters
2021-08-13 08:30:22 +00:00
2017-05-05 13:05:47 +00:00
}
2016-12-21 17:21:39 +00:00
2020-12-01 21:51:24 +00:00
void loop ( ) {
2017-05-05 13:16:01 +00:00
t = millis ( ) ; // Save current time
2017-05-05 11:28:29 +00:00
2021-08-13 08:30:22 +00:00
2017-05-05 13:16:01 +00:00
// If there is new serial data
2017-05-05 13:05:47 +00:00
if ( ( c = Serial . read ( ) ) > = 0 ) {
lastByteTime = lastAckTime = t ; // Reset timeout counters
2017-05-05 11:28:29 +00:00
2017-05-05 13:05:47 +00:00
switch ( mode ) {
2017-06-08 21:04:22 +00:00
case Header :
2017-05-05 13:05:47 +00:00
headerMode ( ) ;
break ;
2017-06-08 21:04:22 +00:00
case Data :
2017-05-05 13:05:47 +00:00
dataMode ( ) ;
break ;
2017-05-05 11:28:29 +00:00
}
}
2017-05-05 13:05:47 +00:00
else {
2017-05-05 13:16:01 +00:00
// No new data
2017-05-05 13:05:47 +00:00
timeouts ( ) ;
}
2017-05-05 11:28:29 +00:00
}
2017-03-27 23:20:55 +00:00
2017-05-05 11:28:29 +00:00
void headerMode ( ) {
static uint8_t
headPos ,
hi , lo , chk ;
if ( headPos < MAGICSIZE ) {
2017-05-05 13:16:01 +00:00
// Check if magic word matches
2017-05-05 11:33:00 +00:00
if ( c = = magic [ headPos ] ) { headPos + + ; }
else { headPos = 0 ; }
2017-05-05 11:28:29 +00:00
}
else {
2017-05-05 13:16:01 +00:00
// Magic word matches! Now verify checksum
2017-05-05 11:28:29 +00:00
switch ( headPos ) {
case HICHECK :
hi = c ;
headPos + + ;
2017-03-27 23:20:55 +00:00
break ;
2017-05-05 11:28:29 +00:00
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.
2020-12-01 21:51:24 +00:00
# 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
2017-03-27 23:20:55 +00:00
}
2017-05-05 11:28:29 +00:00
headPos = 0 ; // Reset header position regardless of checksum result
2017-03-27 23:20:55 +00:00
break ;
2017-05-05 11:28:29 +00:00
}
}
}
void dataMode ( ) {
2017-05-05 13:16:01 +00:00
// If LED data is not full
2017-05-05 11:54:41 +00:00
if ( outPos < sizeof ( leds ) ) {
2020-12-01 21:51:24 +00:00
# if defined(SK6812RGBW)
ledsRaw [ outPos + + ] = c ; // Issue next byte
# else
ledsRaw [ outPos + + ] = c ; // Issue next byte
# endif
2017-05-05 11:28:29 +00:00
}
2017-05-05 11:54:41 +00:00
bytesRemaining - - ;
2017-05-05 11:28:29 +00:00
if ( bytesRemaining = = 0 ) {
// End of data -- issue latch:
2017-06-08 21:04:22 +00:00
mode = Header ; // Begin next header search
2020-12-01 21:51:24 +00:00
# if defined(SK6812RGBW)
//Copy data over
2020-12-01 22:27:56 +00:00
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 ;
2020-12-07 19:51:15 +00:00
2021-08-13 08:30:22 +00:00
2020-12-07 19:51:15 +00:00
/* Simple 255,255,255 = White approach
2020-12-01 22:27:56 +00:00
uint8_t w = min ( r , min ( g , b ) ) ; //get white content and use for white
r - = w ; //subtract white content
g - = w ;
b - = w ;
2020-12-07 19:51:15 +00:00
*/
2021-08-13 08:30:22 +00:00
2020-12-07 19:51:15 +00:00
// 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.
2020-12-17 23:11:28 +00:00
//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 ) ) ;
2020-12-07 19:51:15 +00:00
//subtract white contents from rgb
r - = calibR * whitecontent ;
g - = calibG * whitecontent ;
b - = calibB * whitecontent ;
uint8_t w = calibW * whitecontent ;
2021-08-13 08:30:22 +00:00
2020-12-07 19:51:15 +00:00
2020-12-14 21:50:46 +00:00
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
2020-12-01 22:27:56 +00:00
2020-12-14 21:50:46 +00:00
send_leds [ inew ] = CRGBW ( r , g , b , w ) ; //transfer to rgbw struct
2020-12-01 21:51:24 +00:00
}
# endif
2017-05-05 11:28:29 +00:00
FastLED . show ( ) ;
D_FPS ;
D_LED ( OFF ) ;
2020-12-01 21:51:24 +00:00
SERIAL_FLUSH ;
2017-05-05 11:28:29 +00:00
}
}
void timeouts ( ) {
// No data received. If this persists, send an ACK packet
// to host once every second to alert it to our presence.
2017-12-05 00:11:30 +00:00
if ( ( t - lastAckTime ) > = 1000 ) {
2017-05-05 11:28:29 +00:00
Serial . print ( " Ada \n " ) ; // Send ACK string to host
lastAckTime = t ; // Reset counter
2017-05-05 12:07:35 +00:00
// If no data received for an extended time, turn off all LEDs.
2017-12-05 00:43:20 +00:00
if ( SerialTimeout ! = 0 & & ( t - lastByteTime ) > = ( uint32_t ) SerialTimeout * 1000 ) {
2020-12-01 21:51:24 +00:00
# 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
2017-05-05 12:07:35 +00:00
FastLED . show ( ) ;
2017-06-08 21:47:19 +00:00
mode = Header ;
2017-05-05 12:07:35 +00:00
lastByteTime = t ; // Reset counter
}
2017-05-05 11:28:29 +00:00
}
2016-12-20 09:30:27 +00:00
}