#include "my_board.h" #include #include #include #include "global.h" #include "JsonConstrain.h" static const char* tag = "board"; PWM_Output *pwmOut[4]; RAMP_Output *RearLightControl; void Init_Board_Pins(void) { pinMode(BoardLED1, OUTPUT); pinMode(BoardLED2, OUTPUT); pinMode(Button1_Pin, INPUT_PULLUP); pinMode(Button2_Pin, INPUT_PULLUP); pinMode(Button3_Pin, INPUT_PULLUP); ESP_LOGV(tag, "Board pins initialized..."); } void Init_PWM_Outputs(void) { File file = LittleFS.open("/cfg/relays.json"); if(!file){ ESP_LOGE(tag, "Error opening relays.json..."); }else{ StaticJsonDocument<1024> doc; DeserializationError error = deserializeJson(doc, file); if(!error){ file.close(); if(error){ ESP_LOGE(tag, "relays.json deserialize error!.."); return;} JsonArray jsArray = doc["relays"]; if(jsArray.isNull() || jsArray.size() < 1 || jsArray.size() > 4 ) { return; } int x = 0; for(JsonObject obj : jsArray){ //Default config if keys are not found if (!obj.containsKey("pin") || !obj.containsKey("freq") || !obj.containsKey("max") || !obj.containsKey("default")) { pwmOut[x] = new PWM_Output(0, x, 12, 500, 100.0, false); ESP_LOGD(tag, "pwmOut[%d] default config", x); x++; continue; } int pin; switch(x){ case 0: pin = jsonConstrainInt(obj, "pin", 0, 48, RELAY1_Pin); break; case 1: pin = jsonConstrainInt(obj, "pin", 0, 48, RELAY2_Pin); break; case 2: pin = jsonConstrainInt(obj, "pin", 0, 48, RELAY3_Pin); break; default: pin = jsonConstrainInt(obj, "pin", 0, 48, RELAY4_Pin); } int freq = jsonConstrainInt(obj, "freq", 100, 2000, 500); float maxVal = jsonConstrainInt(obj, "max", 2.0, 100.0, 95); float defaultVal = jsonConstrainInt(obj, "default", 0.0, 100.0, 40); ESP_LOGD(tag, "pwmOut[%d]: freq:%d, max:%F, default:%F", x, freq, maxVal, defaultVal); pwmOut[x] = new PWM_Output(pin, x, RELAY_RES, freq, maxVal, false); pwmOut[x]->setOutput(defaultVal); x++; } }else{ ESP_LOGE(tag, "Deserialization error on relays.json"); } } } int linearizeLED(float inp){ if(inp > 100.0){ inp = 100.0;}; return (inp*inp*0.4095f); } /******************* PWM_Output Definition ********************/ // max: 0-100% PWM_Output::PWM_Output(uint8_t pin, uint8_t ch, int res, uint32_t freq, float maxDuty, bool visionCorrected) { this->currDuty = 0; this->ch = ch; this->maxDuty = maxDuty; this->visionCorrected = visionCorrected; this->freq = freq; this->res = res; //this->msecRampRate = msecRampRate; pinMode(pin, OUTPUT); if(!ledcSetup(ch, freq, res)) { ESP_LOGE(tag, "pwmOut-> ch:%d ledcSetup failed!", ch); return; } ledcAttachPin(pin, ch); setOutput(this->currDuty); } // Range is 0 to 100% void PWM_Output::setOutput(float duty) { if(duty > maxDuty) { duty = maxDuty;} // calculate correct duty value int outDutyVal; if(this->visionCorrected){ outDutyVal = linearizeLED(duty); } else{ outDutyVal = duty * 40.95f; } // FIXME pwm output ramp cause a freeze here // Smooth Transition to final duty value /* if(msecRampRate){ int d = this->currOutVal; float dutyInc = (outDutyVal - this->currOutVal)/msecRampRate; for(;;){ d += dutyInc; if(d < 0){d = 0;} if(d > 4095){d = 4095;} ledcWrite(this->ch, d); if(d >= outDutyVal || d <= 0){ break; } vTaskDelay(msecRampRate); } ledcWrite(this->ch, outDutyVal); this->currOutVal = outDutyVal; } else{ */ ledcWrite(this->ch, outDutyVal); this->currOutVal = outDutyVal; //} this->currDuty = duty; } void PWM_Output::setFreq(uint32_t fq) { uint32_t newFreq; if(this->freq != fq){ newFreq = ledcChangeFrequency(this->ch, fq, this->res); if(newFreq){ this->freq = fq; } } } /******************* RAMP_Output Definition ********************/ void Initialize_Rear_Control(int relayIndex, int buttonIndex, int rampTime, int steps, float min, float max){ RearLightControl = new RAMP_Output(pwmOut[relayIndex], btn[buttonIndex], rampTime, steps, min, max); } RAMP_Output *RAMP_Output::instance = nullptr; // Initialize the static member variable void longPressStartCallback(void){ xTimerStart(RearLightControl->timerHandle, 0); } void longPressStopCallback(void){ xTimerStop(RearLightControl->timerHandle, 0); RAMP_Output::instance->SwitchDirection(); // Call the non-static member function using the stored instance } void SingleClickCallback(void){ RAMP_Output::instance->ClickOnOff(); } void RAMP_Output::TimerCallback(TimerHandle_t xTimer) { RearLightControl->IncrementTick(); } RAMP_Output::RAMP_Output(PWM_Output *output, OneButton *button, int rampTime, int steps, float min, float max) { this->Output = output; this->Button = button; this->rampTime = rampTime; this->steps = steps; this->min = min; this->max = max; this->stepSize = (max - min) / steps; ESP_LOGD(tag, "Rear Lights: stepSize= %.2f", stepSize); instance = this; // Store the instance in the static member variable if (this->Button) { this->Button->attachLongPressStart(longPressStartCallback); this->Button->attachLongPressStop(longPressStopCallback); this->Button->attachClick(SingleClickCallback); } else { Serial.println("Error: Button is nullptr"); } Output->visionCorrected = true; Output->currDuty = min; Output->setOutput(min); timerHandle = xTimerCreate("RampTimer", pdMS_TO_TICKS(rampTime/8), pdTRUE, this, TimerCallback); } void RAMP_Output::ClickOnOff(void) { if(IsOn){ IsOn = false; Output->setOutput(0); }else{ IsOn = true; Output->setOutput(lastDuty); } } void RAMP_Output::IncrementTick(void) { if (dirFwd) { // Increment output value while ensuring it doesn't exceed max float newValue = std::min(Output->currDuty + stepSize, max); Output->setOutput( newValue); lastDuty = newValue; //ESP_LOGD(tag, "up: %.2f", newValue); } else { // Decrement output value while ensuring it doesn't go below min float newValue = std::max(Output->currDuty - stepSize, min); Output->setOutput( newValue ); lastDuty = newValue; //ESP_LOGD(tag, "down: %.2f", newValue); } } void RAMP_Output::SwitchDirection(void) { dirFwd = !dirFwd; } // Add destructor to clean up resources RAMP_Output::~RAMP_Output() { xTimerDelete(timerHandle, 0); // Add any other necessary cleanup here }