boothifier/src/main.cpp
2025-08-25 23:38:53 -07:00

629 lines
20 KiB
C++

#include <Arduino.h>
#include <FS.h>
#include <LittleFS.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
// #include "freertos/FreeRTOSConfig.h"
#include "sdkconfig.h"
#include <HTTPClient.h>
#include <esp_system.h>
#include <esp_sleep.h>
#include "soc/ledc_reg.h"
#include "soc/ledc_struct.h"
#include "esp_adc_cal.h"
#include "esp_log.h"
#include <Wire.h>
#include <ArduinoJson.h>
#include <anyrtttl.h>
#include "system.h"
#include "JsonConstrain.h"
#include "common/fileSystem.h"
#include "my_board.h"
#include "global.h"
#include "my_wifi.h"
#include "my_buzzer.h"
#include "my_tsensor.h"
#include "my_buttons.h"
#include "PWM_Output.h"
#include "Ramp_Lights.h"
#include "BleServer.h"
#include "ATALights.h"
#include "OnEveryN.h"
#include "BLE_SP110E.h"
#include "BleSettings.h"
#define FREERTOs_DIAGNOSTICS 0
#define OLED_ENABLED 0
#define WIFI_ENABLED 0
#define STRIPS_ENABLED 1
#define LUMASTIK_ENABLED 0
#define PRINT_SYSTEM_STATUS 0
#if OLED_ENABLED
#include "my_oled.h"
#endif
#if WIFI_ENABLED
#include "my_wifi.h"
#endif
#if STRIPS_ENABLED
#include "led_strip.h"
#endif
#if LUMASTIK_ENABLED
#include "luma_master.h"
#endif
#if LUMASTIK_ENABLED
#define LumaCountReset (20000 / 25)
int LumaCountdown = LumaCountReset;
LUMA_PACKET lumaPacket;
#endif
static const char *tag = "main";
SYS_SETTINGS sys_settings;
PWM_Output *pwmOutputs[4];
RAMP_LIGHT *rampLight1;
RAMP_LIGHT *rampLight2;
bool UpgradeMode = false;
void Init_ADC(void);
float readBoardInputVoltage(void);
void setupLogLevels(esp_log_level_t logLevel);
void Get_Board_and_Booth_File_Paths(const char *, String &, String &);
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath);
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4]);
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4]);
void checkLEDCChannels()
{
for (int i = 0; i < 8; i++)
{
if (LEDC.channel_group[0].channel[i].conf0.val != 0 || LEDC.channel_group[1].channel[i].conf0.val != 0)
{
ESP_LOGD(tag, "Channel %d is configured\n", i);
}
}
}
#define Button1Pin 8
void setup()
{
// Serial Port
Serial.begin(115200);
while (!Serial);
// Initialize I2C Port for TSensor, ...
Wire.begin(I2C_SDA1_Pin, I2C_SCL1_Pin);
sys_settings.ledStripSettings[0] = &ledSettings[0];
sys_settings.ledStripSettings[1] = &ledSettings[1];
// Set Logging Levels
// esp_log_level_t logLevel = ESP_LOG_INFO;
// pinMode(Button1Pin, INPUT); // Button1
// if(!digitalRead(Button1Pin)){
// logLevel = ESP_LOG_VERBOSE;
//}
// setupLogLevels(logLevel);
setupLogLevels(ESP_LOG_INFO);
// chip id, mac,
print_chip_info();
// Init LittleFS
Init_File_System();
// Print all system files
if (digitalRead(sys_settings.boardPins.btn[0]) == LOW){
printAllSystemFiles();
}
String board_file_path, booth_file_path;
Get_Board_and_Booth_File_Paths("/system/system.json", board_file_path, booth_file_path);
// Load Board Pins
Load_Board_Pins(sys_settings.boardPins, board_file_path);
// Set status pins off
setStatusPin1(false);
setStatusPin2(false);
// Load Booth Settings
Load_Booth_Settings(sys_settings, booth_file_path);
// Load Wifi Settings
Wifi_Load_Settings("/system/wifi.json");
// config stat, relays, buttons
Init_Board_Basic(sys_settings.boardPins);
// Load tunes.json and initialize
Init_Buzzer(sys_settings.boardPins.buzzer, "/system/tunes.json");
// Initialize PWM Outputs
Init_PWM_Outputs(sys_settings.boardPins.relay, sys_settings.pwmOutSettings);
// activate button clicks
Init_ButtonEvents(sys_settings.boardPins.btn);
// Initialize Ramp Lights
Init_Ramp_Lights(sys_settings.rampLightSettings, boardButtons, pwmOutputs);
// Initialize ADC
Init_ADC();
// Initialize Temperature Sensor
Init_TSensor(72);
float val = readBoardInputVoltage();
ESP_LOGI(tag, "Input Volage = %f", val);
// Initialize BLE
Load_BLE_Settings("/system/ble.json");
if (digitalRead(sys_settings.boardPins.btn[0]) == LOW)
{
setStatusPin1(true);
UpgradeMode = true;
ESP_LOGW(tag, "Enabling BLE and Update Service");
Init_BleServer(true, true);
ESP_LOGW(tag, "Enabling Wifi AP and Client");
Wifi_Init();
}
else
{
ESP_LOGI(tag, "Enabling BLE, No Update Service");
Init_BleServer(true, false); // Dont start the Upgrade service
}
// If lightstick mode is enabled, start ble lightstick client task
if (sys_settings.mode == BOOTH_MODE_STIK)
{
Init_BLE_LightStick_Client();
}
#if OLED_ENABLED
// Init OLED
Init_OLED(sys_settings.oledSettings.width, sys_settings.oledSettings.height, sys_settings.boardPins.oled_mosi, sys_settings.boardPins.oled_sck, sys_settings.boardPins.oled_dc, sys_settings.boardPins.oled_rst, sys_settings.boardPins.oled_cs);
#endif
#if STRIPS_ENABLED
Init_Lights_Task();
#endif
Buzzer_Play_Tune(TUNE_BOOT, true, true);
// TODO... Test if this is still necessary need to configure pin 0 for some reason
// pinMode(0, INPUT); // button0/boot pin
#if LUMASTIK_ENABLED
Init_Luma_Master();
#endif
vTaskDelay(100);
Lights_Control_Task_Resume();
}
void loop()
{
// Button Scanning
ON_EVERY_N_MILLISECONDS(10)
{
for (int i = 0; i < 3; i++)
{
if (boardButtons[i] != NULL)
{
boardButtons[i]->tick();
}
}
}
// Temperature Monitor
ON_EVERY_N_MILLISECONDS(5000)
{
static float boardTemperature;
// Read temperature if the sensor is enabled
if (sys_settings.tSensorSettings.enabled)
{
boardTemperature = tSensor->readTemperatureF();
// ESP_LOGI(tag, "Board T: %F", boardTemperature);
}
// Fan Control
if (sys_settings.tSensorSettings.enabled)
{
UpdateFanControl(boardTemperature, pwmOutputs[sys_settings.tSensorSettings.pwmIndex]);
}
}
// Update Tune Playing
//if (anyrtttl::nonblocking::isPlaying())
//{
// anyrtttl::nonblocking::play();
//}
// Animation TestMode Timeout
#if LEDS_ENABLED
if (animStatus.EventTestCountdown)
{
if (--animStatus.EventTestCountdown == 0)
{
ESP_LOGD(tag, "Test Timeout trigger");
PostLastNormalEvent();
if (Strip1_Task_Handle)
{
xTaskNotifyGive(Strip1_Task_Handle);
} // trigger exit of animation loop
}
};
#endif
// Reboot requested
#if WIFI_ENABLED
if (RebootSystem)
{
if (--RebootSystem == 0)
{
for (int i = 0; i < 3; i++)
{
#if BUZZER_ENABLED
Buzzer_Play_Tune(TUNE_BEEP, false); // blocking
#endif
vTaskDelay(200);
}
ESP_LOGW(tag, "Restarting...");
vTaskDelay(200);
ESP.restart();
}
}
#endif
// Toggle Status LED L2
ON_EVERY_N_MILLISECONDS(500)
{
if (sys_settings.boardPins.stat[1] >= 0)
{
static bool ledState = false;
// digitalWrite(sys_settings.boardPins.stat[1], ledState = !ledState);
setStatusPin2(ledState = !ledState);
}
}
// Upgrade Mode Tune
if(UpgradeMode){
ON_EVERY_N_MILLISECONDS(5000)
{
Buzzer_Play_Tune(TUNE_ACK, true, true);
}
}
#if FREERTOs_DIAGNOSTICS
ON_EVERY_N_MILLISECONDS(60000)
{
print_task_watermarks();
}
#endif
// Turn off white light after timeout
ON_EVERY_N_MILLISECONDS(100)
{
// Only decrement if timeout is active
if (whiteTimeout > 0) {
whiteTimeout--;
if (whiteTimeout == 0) {
Lights_Set_White(0);
ESP_LOGD(tag, "White light timeout triggered");
}
}
}
}
void setupLogLevels(esp_log_level_t logLevel)
{
// Options: ESP_LOG_ERROR,ESP_LOG_WARN,ESP_LOG_INFO,ESP_LOG_DEBUG,ESP_LOG_VERBOSE
esp_log_level_set("*", logLevel);
esp_log_level_set("fs", logLevel);
esp_log_level_set("main", logLevel);
esp_log_level_set("board", logLevel);
esp_log_level_set("ramp", logLevel);
esp_log_level_set("button", logLevel);
esp_log_level_set("buzzer", logLevel);
esp_log_level_set("global", logLevel);
esp_log_level_set("jsCon", logLevel);
esp_log_level_set("ble", logLevel);
esp_log_level_set("pwmout", logLevel);
esp_log_level_set("wifi", logLevel);
esp_log_level_set("stiks", logLevel);
esp_log_level_set("oled", logLevel);
esp_log_level_set("trx433", logLevel);
esp_log_level_set("tsensor", logLevel);
}
// TODO Restore original setOutput code..
#define RELAY_RES 10
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4])
{
for (int i = 0; i < 4; i++)
{
int chIndex = findUnusedLedcChannel();
if (chIndex < 0)
{
ESP_LOGE(tag, "No available LEDC channel for PWM Output%d", i);
continue;
}
pwmOutputs[i] = new PWM_Output(pin[i], chIndex, RELAY_RES, pwmSettings[i].freq, pwmSettings[i].max, false);
pwmOutputs[i]->setOutput(pwmSettings[i].def);
// pwmOutputs[i]->setOutput(5.0);
ESP_LOGI(tag, "PWM Output%d: Pin=%d, Freq=%d, ch=%d", i, pin[i], pwmSettings[i].freq, chIndex);
}
}
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4])
{
if (settings[0].enabled)
{
rampLight1 = new RAMP_LIGHT(btn[settings[0].btnIndex], pwm[settings[0].pwmOutIndex], 5.0, 100.0, 1.5);
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 1, settings[0].btnIndex, settings[0].pwmOutIndex);
}
if (settings[1].enabled)
{
rampLight2 = new RAMP_LIGHT(btn[settings[1].btnIndex], pwm[settings[1].pwmOutIndex], 5.0, 100.0, 1.5);
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 2, settings[1].btnIndex, settings[1].pwmOutIndex);
}
}
// Get the files that should be used to setup the system
void Get_Board_and_Booth_File_Paths(const char *sysPath, String &boardPath, String &boothPath)
{
File file = LittleFS.open(sysPath);
if (!file)
{
ESP_LOGE(tag, "Error opening %s...", sysPath);
return;
}
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if (error)
{
ESP_LOGE(tag, "%s deserialize error!..", sysPath);
return;
}
// get hardware version string
boardPath = jsonConstrainString(tag, doc.as<JsonObject>(), "boardfile", "/cfg/boards/board15.json");
boothPath = jsonConstrainString(tag, doc.as<JsonObject>(), "configfile", "/cfg/booths/custom.json");
}
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath)
{
File file = LittleFS.open(boothPath);
if (!file)
{
ESP_LOGE(tag, "Error opening %s...", boothPath.c_str());
return;
}
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if (error)
{
ESP_LOGE(tag, "%s deserialize error!..", boothPath.c_str());
return;
}
// ********** Mode ***********
String modeStr = jsonConstrainString(tag, doc.as<JsonObject>(), "mode", "booth");
if (modeStr == "roamer")
{
sys_settings.mode = BOOTH_MODE_ROAMER;
}
else if (modeStr == "stik")
{
sys_settings.mode = BOOTH_MODE_STIK;
}
else
{
sys_settings.mode = BOOTH_MODE_NONE;
}
// ********** PWM Out ***********
JsonArray pwmJsonArray = doc["pwmout"];
if (!pwmJsonArray.isNull())
{
int pwmIndex = 0;
for (JsonObject obj : pwmJsonArray)
{
if (pwmIndex >= 4)
break;
sys_settings.pwmOutSettings[pwmIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
sys_settings.pwmOutSettings[pwmIndex].freq = jsonConstrain<int>(tag, obj, "freq", 100, 5000, 250);
sys_settings.pwmOutSettings[pwmIndex].min = jsonConstrain<float>(tag, obj, "min", 0.0, 95.0, 0.0);
sys_settings.pwmOutSettings[pwmIndex].max = jsonConstrain<float>(tag, obj, "max", 5.0, 100.0, 100.0);
sys_settings.pwmOutSettings[pwmIndex].def = jsonConstrain<float>(tag, obj, "default", 0.0, 100.0, 0.0);
sys_settings.pwmOutSettings[pwmIndex].deltaRate = 1.0;
// sys_settings.pwmOutSettings[pwmIndex]. = jsonConstrainBool(tag, obj, "vision", true);
pwmIndex++;
}
ESP_LOGI(tag, "Loaded PWmOutput settings...");
}
else
{
ESP_LOGE(tag, "Error!, %s key: pwmout not found..", boothPath);
}
// ********** Ramp Lights ***********
JsonArray rampJsonArray = doc["ramp-lights"];
if (!rampJsonArray.isNull())
{
int rampIndex = 0;
for (JsonObject obj : rampJsonArray)
{
if (rampIndex >= 2)
break;
sys_settings.rampLightSettings[rampIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
sys_settings.rampLightSettings[rampIndex].vision = jsonConstrainBool(tag, obj, "vision", true);
sys_settings.rampLightSettings[rampIndex].pwmOutIndex = jsonConstrain<int>(tag, obj, "relay-index", 0, 1, 0);
sys_settings.rampLightSettings[rampIndex].btnIndex = jsonConstrain<int>(tag, obj, "button-index", 0, 1, 0);
sys_settings.rampLightSettings[rampIndex].min = jsonConstrain<float>(tag, obj, "min", 0.0, 100.0, 0.0);
sys_settings.rampLightSettings[rampIndex].max = jsonConstrain<float>(tag, obj, "max", 5.0, 100.0, 100.0);
sys_settings.rampLightSettings[rampIndex].step = jsonConstrain<float>(tag, obj, "step", 0.01, 100.0, 1.5);
rampIndex++;
}
ESP_LOGI(tag, "Loaded Ramp Lights settings...");
}
else
{
ESP_LOGE(tag, "Error!, %s key: ramp-lights not found..", boothPath);
}
// ********** Fan ***********
JsonObject sensorJson = doc["t-sensor"];
if (!sensorJson.isNull())
{
sys_settings.tSensorSettings.enabled = jsonConstrainBool(tag, sensorJson, "en", true);
sys_settings.tSensorSettings.pwmIndex = jsonConstrain<int>(tag, sensorJson, "relay", 0, 3, 3);
sys_settings.tSensorSettings.setpoint1 = jsonConstrain<float>(tag, sensorJson, "sp1", 0.0, 100.0, 80.0);
sys_settings.tSensorSettings.setpoint2 = jsonConstrain<float>(tag, sensorJson, "sp2", 0.0, 100.0, 85.0);
sys_settings.tSensorSettings.fanPower1 = jsonConstrain<float>(tag, sensorJson, "fan-pwr1", 0.0, 100.0, 50.0);
sys_settings.tSensorSettings.fanPower2 = jsonConstrain<float>(tag, sensorJson, "fan-pwr2", 0.0, 100.0, 50.0);
sys_settings.tSensorSettings.hyst = jsonConstrain<float>(tag, sensorJson, "hyst", 1.0, 10.0, 1.0);
sys_settings.tSensorSettings.intervalMs = jsonConstrain<int>(tag, sensorJson, "interval", 1000, 30000, 5000);
ESP_LOGI(tag, "Loaded TSensor settings...");
ESP_LOGD(tag, " SP1: %F, SP2 %F, Hyst: %F", sys_settings.tSensorSettings.setpoint1, sys_settings.tSensorSettings.setpoint2, sys_settings.tSensorSettings.hyst);
}
else
{
ESP_LOGE(tag, "Error!, %s key: t-sensor not found..", boothPath);
}
// ********** RGB Strips ***********
JsonArray stripsJsonArray = doc["strips"];
if (!stripsJsonArray.isNull())
{
int stripIndex = 0;
for (JsonObject obj : stripsJsonArray)
{
if (stripIndex >= 2)
break;
sys_settings.ledStripSettings[stripIndex]->enabled = jsonConstrainBool(tag, obj, "en", true);
sys_settings.ledStripSettings[stripIndex]->size = jsonConstrain<int>(tag, obj, "size", 1, 250, 25);
sys_settings.ledStripSettings[stripIndex]->chip = jsonConstrainString(tag, obj, "chip", "WS2812B");
sys_settings.ledStripSettings[stripIndex]->rgbOrder = jsonConstrainString(tag, obj, "rgb-order", "WS2812B");
sys_settings.ledStripSettings[stripIndex]->shift = jsonConstrain<int>(tag, obj, "shift", -250, 250, 0);
sys_settings.ledStripSettings[stripIndex]->offset = jsonConstrain<int>(tag, obj, "offset", -250, 250, 0);
sys_settings.ledStripSettings[stripIndex]->bright = jsonConstrain<int>(tag, obj, "bright", 5, 255, 200);
sys_settings.ledStripSettings[stripIndex]->powerDiv = 0;
sys_settings.ledStripSettings[stripIndex]->i2sCh = 0;
sys_settings.ledStripSettings[stripIndex]->core = jsonConstrain<int>(tag, obj, "core", 0, 1, 0);
stripIndex++;
}
sys_settings.ledStripSettings[0]->pin = sys_settings.boardPins.rgb1;
sys_settings.ledStripSettings[1]->pin = sys_settings.boardPins.rgb2;
ESP_LOGI(tag, "Loaded LED Strip settings...");
}
else
{
ESP_LOGE(tag, "Error!, %s key: strips not found..");
}
// ********** BLE ***********
JsonObject bleJson = doc["ble"];
if (!bleJson.isNull())
{
sys_settings.bleSettings.enabled = jsonConstrainBool(tag, bleJson, "en", true);
sys_settings.bleSettings.name = jsonConstrainString(tag, bleJson, "name", "ATA_LIGHTS");
ESP_LOGI(tag, "Loaded BLE settings...");
}
else
{
ESP_LOGE(tag, "Error!, %s key: ble not found..", boothPath);
}
// ********** RF Remote***********
/*
JsonObject rfJson = doc["wifi-ap"];
if(!rfJson.isNull()){
//sys_settings.rampLights[0].enabled = jsonConstrainBool(tag, wifiApJson, "en", true);
ESP_LOGI(tag, "Loaded RF Remote settings...");
}else{
ESP_LOGE(tag, "Error!, %s key: wifi-ap not found..", boothPath);
}
*/
}
void Init_ADC(void)
{
// Configure ADC
analogReadResolution(12); // 12-bit ADC
analogSetAttenuation(ADC_11db);
if (sys_settings.boardPins.adc1 >= 0)
{
analogSetPinAttenuation(sys_settings.boardPins.adc1, ADC_11db);
}
// Enable ADC calibration
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 0, &adc_chars);
// Check calibration success
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK)
{
ESP_LOGI(tag, "ADC calibration: Using Two Point values from eFuse");
}
else if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK)
{
ESP_LOGI(tag, "ADC calibration: Using reference voltage from eFuse");
}
else
{
ESP_LOGW(tag, "ADC calibration: Using default reference voltage");
}
}
float readBoardInputVoltage(void)
{
const int SAMPLES = 64;
uint32_t reading = 0;
if (sys_settings.boardPins.adc1 < 0)
{
ESP_LOGE(tag, "ADC Pin not valid");
return 0.0;
}
// Multiple readings for averaging
for (int i = 0; i < SAMPLES; i++)
{
reading += analogRead(sys_settings.boardPins.adc1);
delayMicroseconds(50); // Small delay between samples
}
reading /= SAMPLES;
// Convert raw ADC to voltage using calibration
uint32_t voltage_mv;
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
voltage_mv = esp_adc_cal_raw_to_voltage(reading, &adc_chars);
// Scale to 12V range
float voltage = (voltage_mv / 1000.0f) * (10470.0f / 470.0f);
return voltage;
}