diff --git a/usermods/pwm_outputs/usermod_pwm_outputs.h b/usermods/pwm_outputs/usermod_pwm_outputs.h new file mode 100644 index 00000000..a741135a --- /dev/null +++ b/usermods/pwm_outputs/usermod_pwm_outputs.h @@ -0,0 +1,181 @@ +#pragma once +#include "wled.h" + +#ifndef USERMOD_PWM_OUTPUT_PINS + #define USERMOD_PWM_OUTPUT_PINS 3 +#endif + + +class PwmOutput { + public: + + PwmOutput() { } + + PwmOutput(int8_t pin, uint32_t freq) : pin_(pin), freq_(freq) { + DEBUG_PRINTF("pwm_output[%d]: setup at freq %d\n", pin_, freq_); + open(); + } + + ~PwmOutput() { + close(); + } + + void setDuty(const float duty) { + DEBUG_PRINTF("pwm_output[%d]: set duty %f\n", pin_, duty); + if (!enabled_) { + return; + } + duty_ = min(1.0f, max(0.0f, duty)); + uint32_t value = static_cast((1 << bit_depth_) * duty_); + ledcWrite(channel_, value); + } + + int8_t getPin() const { + return pin_; + } + + uint32_t getFreq() const { + return freq_; + } + + float getDuty() const { + return duty_; + } + + private: + int8_t pin_ {-1}; + uint32_t freq_ {50}; + uint8_t bit_depth_ = 12; + uint8_t channel_ {255}; + bool enabled_ {false}; + float duty_ {-1.0f}; // Unknown duty + + void open() { + DEBUG_PRINTF("pwm_output[%d]: open...\n", pin_); + if (enabled_) + return; + + if (pin_ < 0 || !pinManager.allocatePin(pin_, true, PinOwner::UM_PWM_OUTPUTS)) { + return; + } + + channel_ = pinManager.allocateLedc(1); + if (channel_ == 255) { + DEBUG_PRINTF("pwm_output[%d]: failed to quire ledc\n", pin_); + pinManager.deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS); + return; + } + + ledcSetup(channel_, freq_, bit_depth_); + ledcAttachPin(pin_, channel_); + DEBUG_PRINTF("pwm_output[%d]: open successful\n", pin_); + enabled_ = true; + } + + void close() { + DEBUG_PRINTF("pwm_output[%d]: close\n", pin_); + if (!enabled_) + return; + pinManager.deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS); + if (channel_ != 255) + pinManager.deallocateLedc(channel_, 1); + channel_ = 255; + enabled_ = false; + duty_ = -1.0f; + } + +}; + + +class PwmOutputsUsermod : public Usermod { + public: + + static const char USERMOD_NAME[]; + + void setup() { + // By default all PWM outputs are disabled, no setup do be done + } + + void loop() { + const unsigned long now = millis(); + if (now - lastUpdate_ > 2000) { + Serial.println("I'm alive!"); + DEBUG_PRINTLN("PWM output beat"); + lastUpdate_ = now; + } + } + + void addToJsonState(JsonObject& root) { + DEBUG_PRINTLN("PwmOutputs: addToJsonState"); + for (int i = 0; i < USERMOD_PWM_OUTPUT_PINS; i++) { + const PwmOutput& pwm = pwms[i]; + root["pwm_" + String(i)] = pwm.getDuty(); + } + } + + void readFromJsonState(JsonObject& root) { + DEBUG_PRINTLN("PwmOutputs: readFromJsonState"); + for (int i = 0; i < USERMOD_PWM_OUTPUT_PINS; i++) { + PwmOutput& pwm = pwms[i]; + float duty = 0.0f; + if (getJsonValue(root["pwm_" + String(i)], duty)) { + pwm.setDuty(duty); + } + } + } + + void addToJsonInfo(JsonObject& root) { + DEBUG_PRINTLN("PwmOutputs: addToJsonInfo"); + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + + for (int i = 0; i < USERMOD_PWM_OUTPUT_PINS; i++) { + const PwmOutput& pwm = pwms[i]; + JsonArray data = user.createNestedArray("pwm_" + String(i)); + data.add(1e2f * pwm.getDuty()); + data.add(F("%")); + } + } + + void addToConfig(JsonObject& root) { + DEBUG_PRINTLN("PwmOutputs: addToConfig"); + JsonObject top = root.createNestedObject(USERMOD_NAME); + for (int i = 0; i < USERMOD_PWM_OUTPUT_PINS; i++) { + const PwmOutput& pwm = pwms[i]; + top["pin_" + String(i)] = pwm.getPin(); + top["freq_" + String(i)] = pwm.getFreq(); + } + } + + bool readFromConfig(JsonObject& root) { + DEBUG_PRINTLN("PwmOutputs: readFromConfig"); + JsonObject top = root[USERMOD_NAME]; + bool configComplete = !top.isNull(); + for (int i = 0; i < USERMOD_PWM_OUTPUT_PINS; i++) { + PwmOutput& pwm = pwms[i]; + + int8_t newPin = pwm.getPin(); + uint32_t newFreq = pwm.getFreq(); + configComplete &= getJsonValue(top["pin_" + String(i)], newPin); + configComplete &= getJsonValue(top["freq_" + String(i)], newFreq); + + // Recreate output if config has changed + if (newPin != pwm.getPin() || newFreq != pwm.getFreq()) { + pwm = PwmOutput(newPin, newFreq); + } + } + return configComplete; + } + + uint16_t getId() { + return USERMOD_ID_PWM_OUTPUTS; + } + + private: + unsigned long lastUpdate_ = 0; + PwmOutput pwms[USERMOD_PWM_OUTPUT_PINS]; + +}; + +const char PwmOutputsUsermod::USERMOD_NAME[] PROGMEM = "PwmOutputs"; diff --git a/wled00/const.h b/wled00/const.h index 7d32b200..217ceb5c 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -98,6 +98,7 @@ #define USERMOD_ID_PING_PONG_CLOCK 34 //Usermod "usermod_v2_ping_pong_clock.h" #define USERMOD_ID_ADS1115 35 //Usermod "usermod_ads1115.h" #define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h" +#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index db211961..6b613e61 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -58,7 +58,8 @@ enum struct PinOwner : uint8_t { UM_BME280 = USERMOD_ID_BME280, // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins UM_BH1750 = USERMOD_ID_BH1750, // 0x19 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x1E // Usermod "audio_reactive.h" - UM_SdCard = USERMOD_ID_SD_CARD // 0x24 // Usermod "usermod_sd_card.h" + UM_SdCard = USERMOD_ID_SD_CARD, // 0x24 // Usermod "usermod_sd_card.h" + UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS // 0x21 // Usermod "usermod_pwm_outputs.h" }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index e40ccf75..0f0b3fa9 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -172,6 +172,11 @@ #include "../usermods/sd_card/usermod_sd_card.h" #endif +#ifdef USERMOD_PWM_OUTPUTS +#include "../usermods/pwm_outputs/usermod_pwm_outputs.h" +#endif + + void registerUsermods() { /* @@ -322,5 +327,8 @@ void registerUsermods() #ifdef SD_ADAPTER usermods.add(new UsermodSdCard()); + + #ifdef USERMOD_PWM_OUTPUTS + usermods.add(new PwmOutputsUsermod()); #endif }