Controlling an rc car with a sim rig

I want to control an rc car with several usb inputs to my pc while maintaining the original hard ware. It was a Spektrum based rc car but I converted it to elrs for range, more channels, and to use a better transmitter. The plan was to send the channel values over serial to an Arduino nano, and have it output a single ppm signal. The trainer port on the radio (Radiomaster Pocket elrs) can take a ppm signal and then output it to the rc car so it should've worked. I had no problem with the python code for finding the sim rig inputs and outputting them over serial, but getting the Arduino to send a proper ppm signal is hard. I don't know C++ so I've been using gpt to get the code and I don't know edge tx (os for the radio). I don't know if I'm setting it up right to receive the ppm signal but I know that it needs to be a 3.5mm mono audio jack and 22.5hz. It should be 3.3v or 5v and I have the hardware to convert the signal to either but its too much for me right now to debug all of it. I'm coming here to see if someone can help me out with this problem because no matter what I do the radio doesn't react to anything.

Post a wiring diagram.

Have you been practicing programming so you can write, maintain or improve your code or will you be expecring someone to do the work for you?


This is what I have now and the voltage regulator can be bypassed if the radio needs 5v instead of 3.3v. Please excuse the crude drawing. I learned python in school and ive taken lots of comp sci classes but im doing this project for personal use and for the robotics club.

What code do you use to get the PPM signal?

// Arduino Nano: PPM output with Timer1 interrupts
// 8 channels coming from Serial

const byte ppmPin = 3;
const byte channelCount = 8;
const int frameLength = 22500;                                                         // microseconds
const int pulseLength = 300;                                                           // microseconds
int channelValues[channelCount] = { 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500 };  // default mid values

volatile byte currentChannel = 0;
volatile bool pulseState = true;
volatile unsigned long nextPulseTime = 0;

String inputString = "";

void setup() {
  pinMode(ppmPin, OUTPUT);
  digitalWrite(ppmPin, LOW);

  Serial.begin(115200);
  inputString.reserve(100);

  setupTimer();
}

void loop() {
  readSerial();
}

// Setup Timer1 to trigger an interrupt
void setupTimer() {
  noInterrupts();

  TCCR1A = 0;  // normal operation
  TCCR1B = 0;
  TCNT1 = 0;

  OCR1A = 1000;                         // First compare match in 1000 ticks (~62us at 16MHz / 256)
  TCCR1B |= (1 << WGM12);               // CTC mode
  TCCR1B |= (1 << CS11) | (1 << CS10);  // Prescaler 64

  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt

  interrupts();
}

ISR(TIMER1_COMPA_vect) {
  static unsigned long lastMicros = 0;
  unsigned long now = micros();

  if (pulseState) {
    digitalWrite(ppmPin, LOW);  // End pulse
    pulseState = false;

    if (currentChannel >= channelCount) {
      // Sync pause
      nextPulseTime = frameLength - ((channelCount) * (pulseLength)) - totalChannelsTime();
      currentChannel = 0;
    } else {
      // Space between pulses
      nextPulseTime = channelValues[currentChannel] - pulseLength;
      currentChannel++;
    }
  } else {
    digitalWrite(ppmPin, HIGH);  // Start pulse
    pulseState = true;
    nextPulseTime = pulseLength;
  }

  OCR1A = (nextPulseTime * 2);  // Convert microseconds to timer ticks (16MHz / 64 prescaler ≈ 250kHz, so 1 tick = 4us, so ×2 for better precision)
}

unsigned long totalChannelsTime() {
  unsigned long sum = 0;
  for (byte i = 0; i < channelCount; i++) {
    sum += channelValues[i];
  }
  return sum;
}

void readSerial() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n') {
      parseInput(inputString);
      inputString = "";
    } else {
      inputString += c;
    }
  }
}

void parseInput(String data) {
  int index = 0;
  char *token = strtok((char *)data.c_str(), " ");

  while (token != NULL && index < channelCount) {
    channelValues[index] = constrain(atoi(token), 1000, 2000);
    index++;
    token = strtok(NULL, " ");
  }
}

And does your oscilloscope show the code is producing the correct range of pulse positions?

what oscilloscope haha

Then how are you determining what your program is doing?

I know what my python code is doing, but like I said in the post I have no clue how to use C++. gpt just kind of made it and I made sure it lined up right with my code but it still dosent show up on the radio. I was hoping one of you could help me figure out where its going wrong. I don't know if its even the code or my radio, or both.

Tell chat to NOT use those timers. You need to guide it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.