# PWM input (RC receiver) to control WLED

**URL:** <https://wled.discourse.group/t/pwm-input-rc-receiver-to-control-wled/10639>\
**Category:** Usermods\
**Created:** [January 3, 2024, 9:32pm UTC](https://wled.discourse.group/t/pwm-input-rc-receiver-to-control-wled/10639 "2024-01-03T21:32:35Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![barrymossel](https://avatars.discourse-cdn.com/v4/letter/b/f19dbf/32.png) [@barrymossel](https://wled.discourse.group/u/barrymossel)\
**Post date:** [January 3, 2024, 9:32pm UTC](https://wled.discourse.group/t/pwm-input-rc-receiver-to-control-wled/10639/1 "2024-01-03T21:32:35Z")

</div>

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:

```auto
#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)?

---

<div class="post-metadata">

**Author:** ![barrymossel](https://avatars.discourse-cdn.com/v4/letter/b/f19dbf/32.png) [@barrymossel](https://wled.discourse.group/u/barrymossel)\
**Post date:** [January 4, 2024, 8:47pm UTC](https://wled.discourse.group/t/pwm-input-rc-receiver-to-control-wled/10639/2 "2024-01-04T20:47:04Z")

</div>

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

```auto
#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?

---

<div class="post-metadata">

**Author:** ![barrymossel](https://avatars.discourse-cdn.com/v4/letter/b/f19dbf/32.png) [@barrymossel](https://wled.discourse.group/u/barrymossel)\
**Post date:** [January 4, 2024, 8:51pm UTC](https://wled.discourse.group/t/pwm-input-rc-receiver-to-control-wled/10639/3 "2024-01-04T20:51:33Z")

</div>

```auto
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:

```auto
#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;

```
