/*-----------------------------------------------------------------------------------------
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
*
* LEDstripe.ino
*
* Functions to controll RGB-LED with an WS2801 stripe.
* LED stripes are used to support differnt movement and rotation modes of the ship with
* colourscheme and running light.
*
* supported States:
* alarm modes LED_Amode[flagnumber]:
* - [ 0] Failsafe: all LED -> shine red
* - [ 1] Wateralert: LED_stripe_1 -> blinking violet
*
* special modes LED_Smode[flagnumber]:
* - [ 0] Turntable rtr: LED_stripe_1 -> blinking blue for 3 times
* - [ 1] Turntable to slow: LED_stripe_1 -> blinking orange
* - [ 2] Switchprop rtr: LED_stripe_2 -> blinking olive for 3 times
* - [ 3] SP not calibrated: LED_stripe_2 -> shine olive
*
* movement modes as described in VScontrol (int LED_Mmode):
* - [0] MoLess: LED_stripe_1 -> negativ running light (one LED off) with turntable in yellow
* LED_stripe_2 -> off
* - [1] RotOnly: LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
* LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)
* - [2] LinOnly: LED_Tmode is used
*
* - [2] LinRot: LED_Tmode is used
*
* movement modes as described in turntable (int LED_Tmode):
* - [1] PropMode: LED_stripe_1 -> running light with turntable in green
* LED_stripe_2 -> running light to direction of travel
* - [2] ServoMode:LED_stripe_1 -> running light with turntable in blue
* LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
* - [3] RotMode: LED_stripe_1 -> running light with turntable in colourchange
* LED_stripe_2 -> lighting with colourchange
* - [4] SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
* LED_stripe_2 -> running light to direction of travel with colourchange
*
* Author: Volker Frauenstein
* Date: 01/07/2023 Version: 2.7 remapping of LED position in stripes added to reflect
* different instalation position at ship
*
* Date: 04/12/2022 Version: 2.6 interface to Flash LED functions added
* Date: 22/04/2021 Version: 2.5 functional implementation of LED modes and LED strip definitions
* Date: 04/03/2021 Version: 2.4 changed to WS2801 LED-chip to use SPI interface for LED to resolve
* RTM conflicts
* Date: 25/02/2021 Version: 2.3 define of FASTLED_RMT_BUILTIN_DRIVER added to share RTM resources
* Date: 13/01/2021 Version: 2.2 LED blinking functionality added
* Date: 08/01/2021 Version: 2.1 RGB_LED function for wateralert added
* Date: 05/01/2021 Version: 2.0 definitions adapted for ESP32 migration
* without WS2811 interface du to interrupt conficts
* Date: 30/12/2020 Version: 1.0 implementation of WS2811 RGB-LED control
*/
// Definitions to handle output to LED driver
//#define FASTLED_RMT_BUILTIN_DRIVER 1// prepare FastLED to share RMT resources with RcPwmRead
//#define FASTLED_FORCE_SOFTWARE_SPI
#include "FastLED.h" // include Lib to acces the WS2811 RGB-LED
// definition for pinout to WS2801 LED driver
#define DATA_PIN 13 // definition of digital data pin for SPI
#define CLOCK_PIN 12 // definition of digital clock pin for SPI
// global definition of LED stripe behavior
#define BRIGHTNESS 180 // brightness of LED (0 to 255)
// definitions for adjustable values
const int LEDnum = 24; // number of LED/WS2801 to be controlled
const int stripe1A = 0; // definition start LED of stripe 1
const int stripe1E = (LEDnum/2) -1; // definition end LED of stripe 1
const int stripe2A = stripe1E +1; // definition start LED of stripe 2
const int stripe2E = LEDnum -1; // definition end LED of stripe 2
const int stripeL = LEDnum/2; // ammount of LED per stripe
const int Blinks = 3; // amount of LED blink state changes for LED_Smode blinking time
const uint16_t blinkmilles = 750; // blinkinterval in ms
// remapping of LED positiom im stripe due to not matching instalation position in the cycle
// mapping starts with first LED (0) at rear of ship (same as at 180 degree)
const int LEDPos[LEDnum] = {6,7,8,9,10,11,0,1,2,3,4,5,18,19,20,21,22,23,12,13,14,15,16,17};
// definition of LED objects
CRGB LEDrgb[LEDnum];
// generig definitions
CRGB rainbow; // colour definition of colourchange scheme
int r = 255; // inittial definition of rgb color
int g = 0; // for colourchange scheme
int b = 0; // rainbow
int c_phase = 0; // counter to run the colour blend phases
static unsigned long blinkTimer = 0; // timer for blink interval
int counter = 0; // counter for MPU startup claibration
bool sblink = true; // indicator if state blinking is aktiv
bool bchange = false; // blinking state change flag
int blinkcountT = 0; // counter for Smode turntable blinking
int blinkcountSP = 0; // counter for Smode SwitchProp blinking
/* definitions transfered to main.h to handle LED state globaly
volatile bool LED_Amode[2] = {false, false}; // alarm LED Mode definitions
volatile bool LED_Smode[4] = {false, false, false, false}; // special LED Mode definitions
volatile int LED_Mmode = 0; // movement LED Mode definitions
volatile int LED_Tmode = 0; // turntable LED Mode definitions */
// generig definitions for debug
// #define DEBUG_LED 1
// #define DEBUG_BLINK
void setup_LEDstripes() { // set up function to enable WS2801
FastLED.addLeds(LEDrgb, LEDnum).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
for (int i = stripe1A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Yellow;// Startup: all LED -> light yellow
}
FastLED.show();
}
void runLEDatStartUp() { // function to set RGB LED at MPU calibration phase
// one by one with blinkTimer interval
if (millis() - blinkTimer > blinkmilles) {
blinkTimer = millis(); // time over, reset timer
if (counter == 0) {
for (int i = stripe1A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all at start or loop
}
}
LEDrgb[LEDPos[counter]] = CRGB::Yellow; // switch on one by one with light yellow
FastLED.show();
counter++; // prepare for next LED
if (counter > stripe2E) { // if all LED on
counter = 0;} // reset counter
#ifdef DEBUG_S
Serial.print("counter: ");Serial.println(counter);
#endif
}
}
void runLEDstripes() { // function to set or change colour of RGB LED
checkBlinkState(); // check state for blinking LED scheme
runcolour(); // update colourchange scheme for this cycle
// evaluation for movement and turntable LED modes
switch (LED_Mmode) { // special movement modes overuling the other movement modes
case 0: // MoLess: LED_stripe_1 -> negativ running light (one LED off) with turntable in yellow
// LED_stripe_2 -> off
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Yellow; // set all LED to yellow of stripe1
}
LEDrgb[runLEDTT(true)] = CRGB::Black;
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // LEDs off at stillstand for stripe2
}
break;
case 1: // RotOnly: LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
runLED_Rot2(); // LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)
break;
default: // no special movement mode check other modes
switch (LED_Tmode) { // modes as defined with Switchprop settings
case 1: // PropMode: LED_stripe_1 -> running light with turntable in green
runLED_Prop(); // LED_stripe_2 -> running light to direction of travel
break;
case 2: // DirMode: LED_stripe_1 -> running light with turntable in blue
runLED_Servo(); // LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
break;
case 3: // RotMode: LED_stripe_1 -> running light with turntable in colourchange
runLED_Rot(); // LED_stripe_2 -> lighting with colourchange
break;
case 4: // SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
runLED_Sync(); // LED_stripe_2 -> running light to direction of travel with colourchange
break;
}
break;
}
// evaluation for special LED modes, overwriting of Mmode setting if needed
if (LED_Smode[0]) { // Turntable rtr: LED_stripe_1 -> blinking blue for 3 times
if (sblink) { // check for blinkinterval
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Blue; // set all LED of stripe1 to blue
}
if (bchange) { // count amount of blink times
blinkcountT++;
}
}
else {
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all LED of stripe1
}
}
if (blinkcountT > Blinks) { // check if blinking interval is over
LED_Smode[0] = false; // reset LED mode
blinkcountT = 0; // reset counter
}
}
if (LED_Smode[1]) { // Turntable to slow: LED_stripe_1 -> blinking orange
if (sblink) { // check for blinkinterval
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Orange; // set all LED of stripe1 to orange
}
}
else {
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all LED of stripe1
}
}
}
if (LED_Smode[2]) { // Switchprop rtr: LED_stripe_2 -> blinking olive for 3 times
if (sblink) { // check for blinkinterval
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Olive; // set all LED of stripe2 to olive
}
if (bchange) { // count amount of blink times
blinkcountSP++;
}
}
else {
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all LED of stripe2
}
}
if (blinkcountSP > Blinks) { // check if blinking interval is over
LED_Smode[2] = false; // reset LED mode
blinkcountSP = 0; // reset counter
}
}
if (LED_Smode[3]) { // SP not calibrated: LED_stripe_2 -> light olive
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Olive; // set all LED of stripe2 to olive
}
}
// evaluation for alarm LED modes, overwriting of Mmode and Smode settings if needed
if (LED_Amode[0]) { // Failsafe: all LED -> light red
for (int i = stripe1A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Red; // set all LED of stripe1 and stripe2 to red
}
}
if (LED_Amode[1]) { // Wateralert: LED_stripe_1 -> blinking violet
if (sblink) { // check for blinkinterval
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::MediumPurple;// set all LED of stripe1 to violet
}
}
else {
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all LED of stripe1
}
}
}
FastLED.show(); // send evaluation for this cycle to LEDs
#ifdef DEBUG_LED
Serial.print(" LED_Mmode: ");Serial.print(LED_Mmode);
Serial.print(" LED_Smode: [");
for (int i=0; i < 3; i++){
Serial.print(LED_Smode[i]);Serial.print(",");
}
Serial.print(LED_Smode[3]);Serial.print("] LED_Amode: [");
Serial.print(LED_Amode[0]);Serial.print(",");Serial.print(LED_Amode[1]);
Serial.print("] sblink: ");Serial.print(sblink);
Serial.print(", bchange: ");Serial.print(bchange);
Serial.print(", bcount: [T");Serial.print(blinkcountT);Serial.print(",SP");Serial.print(blinkcountSP);Serial.print("]");
Serial.print(", cPhase: ");Serial.print(c_phase);
//Serial.print(", rainbow: [r");Serial.print(r);Serial.print(",g");Serial.print(g);
//Serial.print(",b");Serial.print(b);Serial.println("]");
Serial.print(", stepOffset: ");Serial.println(stepOffset);
#endif
} // function set_LEDstate closed
void runLED_Prop() { // PropMode: LED_stripe_1 -> running light with turntable in green
// LED_stripe_2 -> running light to direction of travel
LEDrgb[runLEDTT(true)] = CRGB::Green; // stripe1 -> set one LED to colour
LEDrgb[runLEDir(true)] = CRGB::Green; // stripe2 -> set one LED to colour
}
void runLED_Servo() { // DirMode: LED_stripe_1 -> running light with turntable in blue
// LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
LEDrgb[runLEDTT(true)] = CRGB::Blue; // stripe1 -> set one LED to colour
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Blue; // set all LED to blue of stripe2
}
LEDrgb[runLEDir(false)] = CRGB::Black; // stripe2 -> set the one LED off
}
void runLED_Rot() { // RotMode: LED_stripe_1 -> running light with turntable in colourchange
// LED_stripe_2 -> lighting with colourchange
// runLEDTT (rainbow); // not working with new verion
LEDrgb[runLEDTT(true)] = rainbow; // stripe1 -> set one LED to colour
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = rainbow; // set all LED to colour of stripe2
}
}
void runLED_Sync() { // SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
// LED_stripe_2 -> running light to direction of travel with colourchange
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Green; // set all LED to green
}
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Blue; // set all LED to blue
}
}
void runLED_Rot2() { // RotOnly: LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
// LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)
if (abs(yawrate) > 720) { // fast rotation: LED_stripe_1 -> blinking orange
if (sblink) { // check for blinkinterval
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::White; // set all LED of stripe1 to orange
}
}
else {
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[LEDPos[i]] = CRGB::Black; // switch off all LED of stripe1
}
}
}
else {
for (int i = stripe1A; i <= stripe2E; i++) {
LEDrgb[LEDPos[i]] = CRGB::White; // set all LED to white
}
}
}
void checkBlinkState() { // function to switch blinking state
if (millis() - blinkTimer > blinkmilles) {
blinkTimer = millis(); // time over, reset timer
bchange = true; // update blinking state change flag
sblink = !sblink; // set semaphore to other state for LED
#ifdef DEBUG_BLINK
Serial.print(" Blink reset,");
#endif
}
else {bchange = false;} // reset blinking state change flag
}
int runLEDTT(bool kill) { // function to run LED light with turntable
// for Stripe 1 only
// find turntable position as LED number in stripe
int flash = LEDPos[tablePos(stripeL)];
if (kill) { // check for switch off LEDs
for (int i = stripe1A; i <= stripe1E; i++) {
LEDrgb[i] = CRGB::Black; // switch off all LED of stripe1
}
}
return flash; // return result
}
int runLEDir(bool kill) { // function to run LED light with direction of travel
// for Stripe 2 only
float tseg = calcYawPos()/stripeL; // find yaw position as segment LED number in stripe
int flash = (int)(tseg + 0.5); // transfer segment LED number to int and round it
flash = LEDPos[flash + stripeL]; // map it to LED position in Stripe 2
if (kill) {
for (int i = stripe2A; i <= stripe2E; i++) {
LEDrgb[i] = CRGB::Black; // switch off all LED of stripe2
}
}
return flash; // return result
}
void runcolour() { // function to cycle thrue the colour scheme
switch (c_phase) {
case 0: // colour phase from red
b++; // to magenta
if (b > 254) {c_phase++;} // r255, g000, b255
break;
case 1: // colour phase from magenta
r--; // to blue
if (r < 1) {c_phase++;} // r000, g000, b255
break;
case 2: // colour phase from blue
g++; // to cyan
if (g > 254) {c_phase++;} // r000, g255, b255
break;
case 3: // colour phase from cyan
b--; // to green
if (b < 1) {c_phase++;} // r000, g255, b000
break;
case 4: // colour phase from green
r++; // to yellow
if (r > 254) {c_phase++;} // r255, g255, b000
break;
case 5: // colour phase from cyan
g--; // to green
if (g < 1) {c_phase = 0;} // r255, g000, b000 -> restart cycle
break;
}
rainbow = CRGB(r,g,b);
} // end function runcolour