boothifier/temporary/Temp/led_strip.cpp

565 lines
20 KiB
C++

#include "led_strip.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <Arduino.h>
#include <FS.h>
#include <LittleFS.h>
#include "common/led_animation.h"
#include "common/LEDStrip.h"
#include "global.h"
#include "common/neo_colors.h"
#include "common/HSVTable.h"
#include "JsonConstrain.h"
#include "my_board.h"
#include "luma_master.h"
#include "common/luma-stiks.h"
#define STRIP1_PIN RGBLED1_Pin
#define STRIP2_PIN RGBLED2_Pin
#define POWERUP_TIME 3000
static const char* tag = "led_strip";
ANIMATION_PROPS animProps;
LEDSTRIP *strip1;
LEDSTRIP *strip2;
TaskHandle_t Strip1_Task_Handle;
TaskHandle_t Strip2_Task_Handle;
TaskHandle_t FrontLight_Task_Handle;
QueueHandle_t eventQueue = xQueueCreate( 4, sizeof( ANIMATION_EVENT ) );
QueueHandle_t whiteQueue = xQueueCreate( 4, sizeof( ANIMATION_EVENT ) );
ANIMATION_EVENT lastNormalEventMsg;
ANIMATION_EVENT eventMsg;
LUMA_PACKET TempLumaPacket;
int CurrentRunningEventIndex = -1;
void Init_LED_Devices(BOARD_PINS boardPins)
{
ESP_LOGD(tag, "Strips Initialization entered...");
File file = LittleFS.open("/cfg/led-devices.json");
if(!file){
ESP_LOGE(tag,"Error opening led-devices.json!");
file.close();
}else{
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if(error){ ESP_LOGE(tag, "led-devices.json deserialize error!.."); return;}
// ********** STRIP1 ********************************
JsonObject stripJson = doc["strip1"];
if(jsonConstrainBool(tag, stripJson, "en", false)){
int port = jsonConstrain<int>(tag, stripJson, "i2s-ch", 0, 1, 0);
int size = jsonConstrain<int>(tag, stripJson, "size", 1, 200, 10);
//int pin = jsonConstrainInt(stripJson, "pin", 0, 48, RGBLED1_Pin);
LED_ORDER order= getRGBOrder( jsonConstrainString(tag, stripJson, "rgb-order", "grb").c_str() );
int shift = jsonConstrain<int>(tag, stripJson, "shift", -size, size, 0);
int offset = jsonConstrain<int>(tag, stripJson, "offset", -size, size, 0);
int core = jsonConstrain<int>(tag, stripJson, "core", 0, 1, 0);
int powerDiv = jsonConstrain<int>(tag, stripJson, "power-div", 0, 3, 0);
strip1 = new LEDSTRIP(port, size, boardPins.rgb1, order, shift, offset);
strip1->setPowerDiv(powerDiv);
ESP_LOGI(tag,"Strip1 initialized.");
ESP_LOGD(tag," Size: %d, port: %d, shift: %d, offset: %d", size, port, shift, offset);
ESP_LOGD(tag," Strip1 Task Creating...Core: %d", core);
xTaskCreatePinnedToCore(Strip1_Control_Task, "LED_Strip1_Task", 1024*10, NULL, 1, &Strip1_Task_Handle, core);
}
// ********** STRIP2 ********************************
stripJson.clear();
stripJson = doc["strip2"];
if(jsonConstrainBool(tag, stripJson, "en", false)){
int port = jsonConstrain<int>(tag, stripJson, "i2s-ch", 0, 1, 0);
int size = jsonConstrain<int>(tag, stripJson, "size", 1, 200, 10);
//int pin = jsonConstrainInt(stripJson, "pin", 0, 48, RGBLED2_Pin);
LED_ORDER order= getRGBOrder( jsonConstrainString(tag, stripJson, "rgb-order", "grb").c_str() );
int shift = jsonConstrain<int>(tag, stripJson, "shift", -size, size, 0);
int offset = jsonConstrain<int>(tag, stripJson, "offset", -size, size, 0);
int core2 = jsonConstrain<int>(tag, stripJson, "core", 0, 1, 0);
int powerDiv = jsonConstrain<int>(tag, stripJson, "power-div", 0, 3, 0);
strip2 = new LEDSTRIP(port, size, boardPins.rgb2, order, shift, offset);
strip2->setPowerDiv(powerDiv);
ESP_LOGI(tag,"Strip2 initialized.");
ESP_LOGD(tag," Size: %d, port: %d, shift: %d, offset: %d", size, port, shift, offset);
ESP_LOGD(tag,"Strip2 Task Creating...Core: %d", core2);
xTaskCreatePinnedToCore(Strip2_Control_Task, "LED_Strip2_Task", 1024*8, NULL, 1, &Strip2_Task_Handle, core2);
}else{
ESP_LOGE(tag,"Strip2 disabled");
}
JsonObject flightJson = doc["front-light"];
animProps.frontLight.enabled = jsonConstrainBool(tag, flightJson, "en", false);
if(animProps.frontLight.enabled){
animProps.frontLight.relayIndex = jsonConstrain<int>(tag, flightJson, "relay", 0, 3, 0);
animProps.frontLight.core = jsonConstrain<int>(tag, flightJson, "core", 0, 1, 1);
animProps.frontLight.style = (FRONT_LIGHT_STYLE)(flightJson, "style", 0, 1, 0);
ESP_LOGI(tag, "Front Light initialized..");
Init_FrontLight();
}
JsonObject rlightJson = doc["rear-light"];
animProps.rearLight.enabled = jsonConstrainBool(tag, rlightJson, "en", false);
if(animProps.rearLight.enabled){
animProps.rearLight.relayIndex = jsonConstrain<int>(tag, rlightJson, "relay", 0, 3, 1);
animProps.rearLight.buttonIndex = jsonConstrain<int>(tag, rlightJson, "button", 0, 2, 2);
animProps.rearLight.rampTime = jsonConstrain<int>(tag, rlightJson, "ramp", 1000, 20000, 3000);
animProps.rearLight.rampSteps = jsonConstrain<int>(tag, rlightJson, "steps", 2, 20, 8);
animProps.rearLight.min = jsonConstrain<float>(tag, rlightJson, "min", 0.0f, 50.0f, 0.0f);
animProps.rearLight.max = jsonConstrain<float>(tag, rlightJson, "max", animProps.rearLight.min, 100.0f, 100.0f);
ESP_LOGD(tag, "relay: %d, btn: %d, ramp: %d, steps: %d, min: %.2f, max: %.2f", animProps.rearLight.relayIndex, animProps.rearLight.buttonIndex, animProps.rearLight.rampTime, animProps.rearLight.rampSteps, animProps.rearLight.min, animProps.rearLight.max);
Initialize_Rear_Control(animProps.rearLight.relayIndex, animProps.rearLight.buttonIndex, animProps.rearLight.rampTime, animProps.rearLight.rampSteps , animProps.rearLight.min, animProps.rearLight.max);
ESP_LOGI(tag, "Rear Light initialized..");
}
}
}
void Init_FrontLight(void)
{
xTaskCreatePinnedToCore(FrontLight_Task, "FrontLight_Task", 5000, NULL, 1, &FrontLight_Task_Handle, animProps.frontLight.core);
}
void Init_RearLight(void)
{
//if(jsonOb.isNull()){ Serial.println(" rear_light json is Null.."); return; }
}
void Test_Animations(void){
ANIMATION_EVENT event;
strip1->fill({0,0,0}, 0, strip1->effSize);
strip1->show(true);
vTaskDelay(100);
event.hue = RGBtoHUE(col_blue);
event.hueRange = 180;
event.param1 = 11;
event.speed = 70;
LEDStrip_FireInit(*strip1);
while(1){
event.hue = 0;
event.hueRange = 359;
event.param1 = 22;
event.speed = 70;
//Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
event.param1 = 30;
//Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
event.param1 = 40;
//Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
event.param1 = 50;
Animation_Sectors(*strip1, event, DT_BOTH, 2);
event.param1 = 50;
Animation_Dashes(*strip1, event, DT_BOTH, 3);
event.param1 = 50;
Animation_Dashes(*strip1, event, DT_BOTH, 3);
/*
Animation_Fire(*strip1, event, RED_FIRE, 5000);
Animation_Fire(*strip1, event, GREEN_FIRE, 5000);
Animation_Fire(*strip1, event, BLUE_FIRE, 5000);
event.col1 = col_blue;
event.col2 = {255,136,0};
event.density = 11;
event.speed = 100;
Animation_Snakes(*strip1, event, false, DT_FWD, 2, 1);
event.col1 = HUEtoRGB(359);
event.col2 = HUEtoRGB(0);
event.density = 22;
event.speed = 70;
Animation_Snakes(*strip1, event, true, DT_BOTH, 12, 4);
event.col1 = HUEtoRGB(359);
event.col2 = HUEtoRGB(0);
event.density = 56;
event.speed = 50;
Animation_Snakes(*strip1, event, true, DT_BOTH, 12, 4);
*/
}
}
void Strip1_Control_Task(void *parameters)
{
vTaskDelay(1000); // small start delay
ESP_LOGD(tag, "Strip1 Task Entered....");
//Test_Animations();
LEDStrip_FireInit(*strip1); // must be initialized before calling fire animation
// Set default event 1 animation base on profile values
ANIMATION_EVENT ev; // empty event
ev.animIndex = 0;
ev.countDown = 6000;
ev.param2 = 75;
ev.type = EV_NON_INJECT;
PostNewEvent(ev); // Start Animation (White Fill)
animProps.event[1].type = EV_NORMAL;
PostNewEvent(animProps.event[1]); // default attraction animation
while(true){
xQueueReceive( eventQueue, &eventMsg, portMAX_DELAY );
ESP_LOGD(tag, "Queue waiting: %d", uxQueueMessagesWaiting(eventQueue));
if(eventMsg.type == EV_NORMAL ) { // EV_NORMAL
ESP_LOGD(tag, "Running event: type: EV_NORMAL, event: %d, anim: %d", eventMsg.selfIndex, eventMsg.animIndex);
lastNormalEventMsg = eventMsg; //save last Normal EventMsg
if( eventMsg.selfIndex >= 0 ) {
CurrentRunningEventIndex = eventMsg.selfIndex;
}
if(Luma_PeerCount() > 0){
TempLumaPacket.type = LPT_ANIM;
memcpy(&TempLumaPacket.data, &eventMsg, sizeof(ANIMATION_EVENT));
Luma_Master_Broadcast(TempLumaPacket);
}
if( eventMsg.selfIndex == 0 ){
RunWhitefillAnimation( eventMsg );
}else{
RunAnimation( eventMsg );
}
}
else if( eventMsg.type == EV_INJECT ){ // insert and return to previous animation
ESP_LOGD(tag, "EV_INJECT");
xQueueSend( eventQueue, &lastNormalEventMsg, 100 ); // requeue previous normal event
RunPopUpAnimations( eventMsg );
}
else if( eventMsg.type == EV_NON_INJECT ){
ESP_LOGD(tag, "EV_NON_INJECT");
RunPopUpAnimations( eventMsg );
}
else if( eventMsg.type == EV_TEST ){
ESP_LOGD(tag, "EV_TEST");
animStatus.EventTestCountdown = EVENT_TESTMODE_TIMEOUT; // start timeout countdown
// Should not re-post last event from here in case repeated tests are started.
// that would cause a backlog of the same previous event.
if( eventMsg.selfIndex == 0 ){
RunWhitefillAnimation( eventMsg);
}else{
RunAnimation( eventMsg );
}
}
}
}
void PostLastNormalEvent(){
xQueueSend( eventQueue, &lastNormalEventMsg, 100 ); // requeue previous normal event
ESP_LOGD(tag, "RePosted Last Normal Event Index: %d", lastNormalEventMsg.selfIndex);
}
// Used to cycle to the next event primarity when usinging buttons
int IncrementEventIndex(){
int x = (CurrentRunningEventIndex + 1) % animStatus.EventsCount;
ESP_LOGD(tag, "Increment Event Index from %d to %d", CurrentRunningEventIndex, x);
animProps.event[x].type = EV_NORMAL;
PostNewEvent(animProps.event[x]);
return animStatus.EventsIndex;
}
//EVENT_MSG newEvent;
void PostNewEvent( ANIMATION_EVENT &animEvent) {
//newEvent.type = type;
//newEvent.event = animEvent;
xQueueSend(eventQueue, &animEvent, 100); // queue new event
// if(type == EV_NORMAL){
// If whitefill index ( could be restarted )
// if(animEvent.animIndex == ANIM_WHITEFILL_INDEX){
//if( countDown == 0 ) { animStatus.countDown = 1000; } // TODO Countdown calc maybe wrong...double check
//if( animStatus.busy ) { animStatus.repeat = true; }
// if( FrontLight_Task_Handle ) { xTaskNotifyGive( FrontLight_Task_Handle ); }
// }
// }
if( Strip1_Task_Handle ){ xTaskNotifyGive( Strip1_Task_Handle ); }
}
void Strip2_Control_Task(void *parameters)
{
vTaskDelay(100);
ESP_LOGD(tag, "Strip2 Task Entered....\n");
for(;;){
vTaskDelay(10000);
}
}
void FrontLight_Task(void *parameters){
animStatus.busy = false;
ANIMATION_EVENT whiteMsg;
for(;;){
xQueueReceive( whiteQueue, &whiteMsg, portMAX_DELAY );
int msFillTime = 0;
if(whiteMsg.countDown > 0){ // use countdown if available
msFillTime = whiteMsg.countDown;
}else{
msFillTime = map(whiteMsg.param1, 0, 100, 0, 10000);
if(msFillTime < 1000){ msFillTime = 1000; }
}
float delayFactor = (float)map(whiteMsg.param2, 0, 100, 0, 100) / 100.0;
Const_White_Fill(whiteStatus, animProps.frontLight, delayFactor, msFillTime, 50);
}
}
void load_animation_profile(void){
File file = LittleFS.open("/cfg/anim-profile-common.json");
if(!file){
ESP_LOGE(tag, "Error opening anim-profile-common.json...");
file.close();
}else{
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if(error){ ESP_LOGE(tag, "anim-profile-common.json deserialize error!.."); return;}
JsonObject frontJson = doc["countdown"];
animProps.frontLight.minDuty = jsonConstrain<float>(tag, frontJson, "min", 0.0f, 99.0f, 20.0f);
animProps.frontLight.maxDuty = jsonConstrain<float>(tag, frontJson, "max", 1.0f, 100.0f, 99.0f);
animProps.frontLight.holdTime = jsonConstrain<int>(tag, frontJson, "hold", 100, 5000, 1000);
animProps.frontLight.rampTime = jsonConstrain<int>(tag, frontJson, "ramp", 100, 5000, 500);
animProps.profileIndex = jsonConstrain<int>(tag, doc.as<JsonObject>(), "profile-index", 0, 7, 0);
ESP_LOGI(tag, "anim-profile-common.json settings loaded.");
}
}
void load_animation_profileByIndex(int index){
//for(int index = 1; index <= 8; index++){
String strFileName = "/cfg/anim-profile" + String(index + 1) + ".json";
File file = LittleFS.open(strFileName);
if(!file){
ESP_LOGE(tag, "%s", "Error opening " + strFileName);
file.close();
}else{
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if(error){ ESP_LOGE(tag, "%s deserialize error!..", strFileName.c_str()); return;}
JsonObject profJson = doc.as<JsonObject>();
animProps.profileName = jsonConstrainString(tag, profJson, "name", "").c_str();
ESP_LOGD(tag, "Profile Index: %d, Name: %s", index, animProps.profileName);
//
JsonArray eventsJson = profJson["events"];
for(int i = 0; i < animStatus.EventsCount; i++){
animProps.event[i].selfIndex = i; // Self Index
if(i == 0){
animProps.event[i].animIndex = jsonConstrain<int>(tag, eventsJson[i], "anim", 0, animProps.whitefillsCount-1, 0);
}else{
animProps.event[i].animIndex = jsonConstrain<int>(tag, eventsJson[i], "anim", 0, animProps.animationsCount-1, 0);
}
animProps.event[i].hue = jsonConstrain<int>(tag, eventsJson[i], "hue", -2, 360, 0);
animProps.event[i].hueRange = jsonConstrain<int>(tag, eventsJson[i], "hue-range", 0, 360, 1);
animProps.event[i].speed = jsonConstrain<int>(tag, eventsJson[i], "speed", 0, 100, 50);
animProps.event[i].param1 = jsonConstrain<int>(tag, eventsJson[i], "param1", 0, 100, 50);
animProps.event[i].param2 = jsonConstrain<int>(tag, eventsJson[i], "param2", 0, 100, 50);
animProps.event[i].check1 = jsonConstrainBool(tag, eventsJson[i], "check1", false);
animProps.event[i].check2 = jsonConstrainBool(tag, eventsJson[i], "check2", false);
animProps.event[i].check3 = jsonConstrainBool(tag, eventsJson[i], "check3", false);
animProps.event[i].check4 = jsonConstrainBool(tag, eventsJson[i], "check4", false);
ESP_LOGD(tag, " self: %d, anim: %d, param1: %d, speed: %d", animProps.event[i].selfIndex, animProps.event[i].animIndex, animProps.event[i].param1, animProps.event[i].speed);
}
ESP_LOGI(tag, "%s settings loaded.", strFileName.c_str());
}
//}
}
void RunWhitefillAnimation(ANIMATION_EVENT &event){
//Start Constant Light Task if available
if( FrontLight_Task_Handle ){
xQueueSend(whiteQueue, &event, 100); // queue new event
xTaskNotifyGive( FrontLight_Task_Handle );
}
int ret;
switch(event.animIndex){
case 0: // Bottom/Up Fill
ESP_LOGD(tag, " Running WhiteFill: %d , bottom/up", event.animIndex);
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
case 1: // Snake Fill
ESP_LOGD(tag, " Running WhiteFill: %d , Snake fill", event.animIndex);
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
case 2: // Tick Fill
ESP_LOGD(tag, " Running WhiteFill: %d , Tick fill", event.animIndex);
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
case 3: // Smooth Brighten
ESP_LOGD(tag, " Running WhiteFill: %d , Smooth Brighten", event.animIndex);
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
case 4: // Cycle All
ESP_LOGD(tag, " Running WhiteFill: %d , Cycle All", event.animIndex);
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
default: // Random Select
ESP_LOGD(tag, " Running WhiteFill: default , bottom/up");
ret = Animation_White_Fill_Mirrored(*strip1, event);
break;
}
// if exited loop naturally... not from a notification so keep waiting
if(ret == EXIT_FINISHED || ret == EXIT_TIMEOUT){
ESP_LOGD(tag, "Whitefill normal exit... waiting");
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
}
}
void RunAnimation(ANIMATION_EVENT &event){
if(event.animIndex < 80){
switch(event.animIndex){ // AnimationEventIndex (0-X)
case 0: // Rainbow
ESP_LOGD(tag, " Running Anim: %d , Rainbow", event.animIndex);
Animation_Rainbow(*strip1, event, INFINITE_LOOP);
break;
case 1: // Hue Spectrum Mirrored
ESP_LOGD(tag, " Running Anim: %d , Hue Spectrum Mirrored", event.animIndex);
Animation_Hue_Spectrum_Mirrored(*strip1, event, INFINITE_LOOP);
break;
case 2: // Color Pulse Cycle
ESP_LOGD(tag, " Running Anim: %d , Color Pulse Cycle", event.animIndex);
Animation_Pulse_Color_Cycling(*strip1, event);
break;
case 3: // Comets Mixed Colors
ESP_LOGD(tag, " Running Anim: %d , Comets", event.animIndex);
Animation_Comets(*strip1, event, DT_BOTH, INFINITE_LOOP);
break;
case 4: // Dashes Single and Mixed Colors
ESP_LOGD(tag, " Running Anim: %d , Dashes", event.animIndex);
Animation_Dashes(*strip1, event, DT_BOTH, INFINITE_LOOP);
break;
case 5: // Snakes Single and Mixed Colors
ESP_LOGD(tag, " Running Anim: %d , Snakes", event.animIndex);
Animation_Snakes(*strip1, event, DT_BOTH, INFINITE_LOOP);
break;
case 6: // Sectors Mixed Colors (No Sigle Colors because pointless)
ESP_LOGD(tag, " Running Anim: %d , Sectors", event.animIndex);
Animation_Sectors(*strip1, event, DT_BOTH, INFINITE_LOOP);
break;
case 7: // Fire (Red)
ESP_LOGD(tag, " Running Anim: %d , Fire", event.animIndex);
Animation_Fire(*strip1, event);
break;
case 8: //
break;
case 9: // Color Strobing
break;
case 10: // Random Color Twinkle
break;
case 11: // Stacking
break;
case 12: // Rain
break;
case 13: // Stacking
break;
case 14: // RED/WHITE/BLU Flag (density selects color pallet) (sector/comets,dashes, snakes)
break;
case 15:
break;
default:
break;
}
}else{ // Non Looping Functions
switch(event.animIndex){ // AnimationEventIndex (0-X)
case 80: // clear strip
strip1->fill(col_black, 0, strip1->effSize);
strip1->show(true);
ESP_LOGD(tag, "Animation Strip1 Clear");
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
break;
case 81: // set pixel
strip1->setPixel(event.param1, GetColorFromHue(event.hue));
strip1->show(true);
ESP_LOGD(tag, "Animation Set Pixel: %d", event.param1);
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
break;
case 82: //
break;
default:
break;
}
}
}
// These animations should be quick and not infinite
void RunPopUpAnimations(ANIMATION_EVENT &event){
switch(event.animIndex){ // AnimationEventIndex (0-X)
case 0: // Start/Boot/Power Up Sequence
ANIMATION_EVENT ev;
ev.animIndex = 0;
ev.hue = -1; //{200, 200, 200}; //col_white;
ev.hueRange = 1; //col_black;
ev.countDown = 6000; // 6 seconds
ev.param2 = 75;
if( FrontLight_Task_Handle ){
xQueueSend(whiteQueue, &ev, 100); // queue new event
xTaskNotifyGive( FrontLight_Task_Handle );
}
Animation_White_Fill_Mirrored(*strip1, ev);
break;
case 1: // BLE Connected
break;
case 2: // BLE Disconnected
break;
case 3: // WiFi Connected
break;
case 4: // WiFi Disconnected
break;
case 5: // Luma Stick Connected
break;
default:
break;
}
}
// json color to rgbPixel conversion
// Unused now
rgbpixel_t hexToRGB(const char* hexColor) {
long hexValue = strtol(hexColor + 1, nullptr, 16); // Skip the '#' character
rgbpixel_t pix;
pix.red = (hexValue >> 16) & 0xFF;
pix.grn = (hexValue >> 8) & 0xFF;
pix.blu = hexValue & 0xFF;
return pix;
}