PWM input (RC receiver) to control WLED

Just started building a RC car. I want to add led strips in it. And of course WLED is the best way to drive leds. But unfortunately WLED doesn’t have PWM input, so I guess a usermod needs to be created.

Currently I can read the PWM output from my RC receiver and run code when one of my two channels goes above 1750 (pulse width) or under 1250.

So, this is what I have now:

#pragma once

#include "wled.h"

//This is an empty v2 usermod template. Please see the file usermod_v2_example.h in the EXAMPLE_v2 usermod folder for documentation on the functions you can use!

class PWMInput : public Usermod {
  private:

    #define RCPinChan1 2
    #define RCPinChan2 4

    volatile long StartTimeChan1 = 0;
    volatile long CurrentTimeChan1 = 0;
    volatile long PulsesChan1 = 0;
    int PulseWidthChan1 = 0;
    int PulseOptionChan1 = 0;

    volatile long StartTimeChan2 = 0;
    volatile long CurrentTimeChan2 = 0;
    volatile long PulsesChan2 = 0;
    int PulseWidthChan2 = 0;
    int PulseOptionChan2 = 0;

  public:
    void setup() {
      Serial.begin(9600);
      pinMode(RCPinChan1, INPUT_PULLUP);
      pinMode(RCPinChan2, INPUT_PULLUP);

      attachInterrupt(digitalPinToInterrupt(RCPinChan1),PulseTimerChan1,CHANGE);
      attachInterrupt(digitalPinToInterrupt(RCPinChan2),PulseTimerChan2,CHANGE);
    }

    void PulseTimerChan1(){
      CurrentTimeChan1 = micros();
      if (CurrentTimeChan1 > StartTimeChan1){
        PulsesChan1 = CurrentTimeChan1 - StartTimeChan1;
        StartTimeChan1 = CurrentTimeChan1;
      }
    }
    void PulseTimerChan2(){
      CurrentTimeChan2 = micros();
      if (CurrentTimeChan2 > StartTimeChan2){
        PulsesChan2 = CurrentTimeChan2 - StartTimeChan2;
        StartTimeChan2 = CurrentTimeChan2;
      }
    }

    void loop() {
      if (PulsesChan1 < 2100){
        PulseWidthChan1 = PulsesChan1;
      }
        if ((PulseWidthChan1 < 2100) && (PulseWidthChan1 > 1750) && (PulseOptionChan1 != 3)) {
          Serial.println("Hoog 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 3;
        } 
        if ((PulseWidthChan1 < 1250) && (PulseWidthChan1 > 900) && (PulseOptionChan1 != 2)) {
          Serial.println("Laag 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 2;
        }
      if (PulsesChan2 < 2100){
        PulseWidthChan2 = PulsesChan2;
      }
        if ((PulseWidthChan2 < 2100) && (PulseWidthChan2 > 1750) && (PulseOptionChan2 != 3)) {
          Serial.println("Hoog 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 3;
        } 
        if ((PulseWidthChan2 < 1250) && (PulseWidthChan2 > 900) && (PulseOptionChan2 != 2)) {
          Serial.println("Laag 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 2;
        }
    }
};

So, if I for instance want to cycle through presets, will that be possible by adding a command (instead of the prinln’s)?

Ok, I managed to get to this code and compile it succesfully:

#pragma once

#include "wled.h"

class PWM_input : public Usermod {
  private:
    static volatile long StartTimeChan1;
    static volatile long CurrentTimeChan1;
    static volatile long PulsesChan1;
    static int PulseWidthChan1;
    static int PulseOptionChan1;

    static volatile long StartTimeChan2;
    static volatile long CurrentTimeChan2;
    static volatile long PulsesChan2;
    static int PulseWidthChan2;
    static int PulseOptionChan2;

  public:
    #define RCPinChan1 5
    #define RCPinChan2 4



    void setup() override {
      Serial.begin(9600);
      pinMode(RCPinChan1, INPUT_PULLUP);
      pinMode(RCPinChan2, INPUT_PULLUP);

      attachInterrupt(digitalPinToInterrupt(RCPinChan1), PulseTimerChan1, CHANGE);
      attachInterrupt(digitalPinToInterrupt(RCPinChan2), PulseTimerChan2, CHANGE);
    }

    void loop() override {
      if (PulsesChan1 < 2100){
        PulseWidthChan1 = PulsesChan1;
      }
        if ((PulseWidthChan1 < 2100) && (PulseWidthChan1 > 1750) && (PulseOptionChan1 != 3)) {
          Serial.println("Hoog 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 3;
        } 
        if ((PulseWidthChan1 < 1250) && (PulseWidthChan1 > 900) && (PulseOptionChan1 != 2)) {
          Serial.println("Laag 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 2;
        }
      if (PulsesChan2 < 2100){
        PulseWidthChan2 = PulsesChan2;
      }
        if ((PulseWidthChan2 < 2100) && (PulseWidthChan2 > 1750) && (PulseOptionChan2 != 3)) {
          Serial.println("Hoog 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 3;
        } 
        if ((PulseWidthChan2 < 1250) && (PulseWidthChan2 > 900) && (PulseOptionChan2 != 2)) {
          Serial.println("Laag 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 2;
        }
    }

    static void PulseTimerChan1() {
      CurrentTimeChan1 = micros();
      if (CurrentTimeChan1 > StartTimeChan1){
        PulsesChan1 = CurrentTimeChan1 - StartTimeChan1;
        StartTimeChan1 = CurrentTimeChan1;
      }
    }

    static void PulseTimerChan2() {
      CurrentTimeChan2 = micros();
      if (CurrentTimeChan2 > StartTimeChan2){
        PulsesChan2 = CurrentTimeChan2 - StartTimeChan2;
        StartTimeChan2 = CurrentTimeChan2;
      }
    }
};

// Initialize static variables
volatile long PWM_input::StartTimeChan1 = 0;
volatile long PWM_input::StartTimeChan2 = 0;
volatile long PWM_input::CurrentTimeChan1 = 0;
volatile long PWM_input::CurrentTimeChan2 = 0;
volatile long PWM_input::PulsesChan1 = 0;
volatile long PWM_input::PulsesChan2 = 0;
int PWM_input::PulseWidthChan1 = 0;
int PWM_input::PulseWidthChan2 = 0;
int PWM_input::PulseOptionChan1 = 0;
int PWM_input::PulseOptionChan2 = 0;

This seems to work. WLED works and in the Arduino Serial Monitor I see the serial output as expected when I give certain PWM input through the RC receiver. So far so good.

But… when I add
applyPreset(1, true);
to the code, the ESP32 gets a panic attack on PWM input. So apparently something is wrong with the applyPreset?

Guru Meditation Error: Core  1 panic'ed (Cache disabled but cached memory region accessed)
Core 1 register dump:
PC      : 0x4010f064  PS      : 0x00060034  A0      : 0x40085140  A1      : 0x3ffbffe0  
A2      : 0x00000004  A3      : 0x3ffc93b8  A4      : 0x00000000  A5      : 0x00000010  
A6      : 0x000420bc  A7      : 0x3ffb8798  A8      : 0x800819a4  A9      : 0x00000001  
A10     : 0x00000000  A11     : 0x00000000  A12     : 0x8009048d  A13     : 0x3ffb18e0  
A14     : 0x00000000  A15     : 0x3ffb191c  SAR     : 0x0000001a  EXCCAUSE: 0x00000007  
EXCVADDR: 0x00000000  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0xffffffff  
Core 1 was running in ISR context:
EPC1    : 0x4009055a  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x4010f064

ELF file SHA256: 0000000000000000

Backtrace: 0x4010f061:0x3ffbffe0 0x4008513d:0x3ffc0000 0x40090557:0x3ffb1940 0x40090ce9:0x3ffb1960 0x40088cfc:0x3ffb1980 0x4019fb76:0x3ffb19f

Immediately at
applyPreset(1, true);

For clarity I added those below PulseOptionChan, so:

#pragma once

#include "wled.h"

class PWM_input : public Usermod {
  private:
    static volatile long StartTimeChan1;
    static volatile long CurrentTimeChan1;
    static volatile long PulsesChan1;
    static int PulseWidthChan1;
    static int PulseOptionChan1;

    static volatile long StartTimeChan2;
    static volatile long CurrentTimeChan2;
    static volatile long PulsesChan2;
    static int PulseWidthChan2;
    static int PulseOptionChan2;

  public:
    #define RCPinChan1 5
    #define RCPinChan2 4



    void setup() override {
      Serial.begin(9600);
      pinMode(RCPinChan1, INPUT_PULLUP);
      pinMode(RCPinChan2, INPUT_PULLUP);

      attachInterrupt(digitalPinToInterrupt(RCPinChan1), PulseTimerChan1, CHANGE);
      attachInterrupt(digitalPinToInterrupt(RCPinChan2), PulseTimerChan2, CHANGE);
    }

    void loop() override {
      if (PulsesChan1 < 2100){
        PulseWidthChan1 = PulsesChan1;
      }
        if ((PulseWidthChan1 < 2100) && (PulseWidthChan1 > 1750) && (PulseOptionChan1 != 3)) {
          Serial.println("Hoog 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 3;
          applyPreset(1, true);
        } 
        if ((PulseWidthChan1 < 1250) && (PulseWidthChan1 > 900) && (PulseOptionChan1 != 2)) {
          Serial.println("Laag 1");
          Serial.println(PulseWidthChan1);
          PulseOptionChan1 = 2;
          applyPreset(2, true);
        }
      if (PulsesChan2 < 2100){
        PulseWidthChan2 = PulsesChan2;
      }
        if ((PulseWidthChan2 < 2100) && (PulseWidthChan2 > 1750) && (PulseOptionChan2 != 3)) {
          Serial.println("Hoog 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 3;
          applyPreset(3, true);
        } 
        if ((PulseWidthChan2 < 1250) && (PulseWidthChan2 > 900) && (PulseOptionChan2 != 2)) {
          Serial.println("Laag 2");
          Serial.println(PulseWidthChan2);
          PulseOptionChan2 = 2;
          applyPreset(4, true);
        }
    }

    static void PulseTimerChan1() {
      CurrentTimeChan1 = micros();
      if (CurrentTimeChan1 > StartTimeChan1){
        PulsesChan1 = CurrentTimeChan1 - StartTimeChan1;
        StartTimeChan1 = CurrentTimeChan1;
      }
    }

    static void PulseTimerChan2() {
      CurrentTimeChan2 = micros();
      if (CurrentTimeChan2 > StartTimeChan2){
        PulsesChan2 = CurrentTimeChan2 - StartTimeChan2;
        StartTimeChan2 = CurrentTimeChan2;
      }
    }
};

// Initialize static variables
volatile long PWM_input::StartTimeChan1 = 0;
volatile long PWM_input::StartTimeChan2 = 0;
volatile long PWM_input::CurrentTimeChan1 = 0;
volatile long PWM_input::CurrentTimeChan2 = 0;
volatile long PWM_input::PulsesChan1 = 0;
volatile long PWM_input::PulsesChan2 = 0;
int PWM_input::PulseWidthChan1 = 0;
int PWM_input::PulseWidthChan2 = 0;
int PWM_input::PulseOptionChan1 = 0;
int PWM_input::PulseOptionChan2 = 0;