Questions about Arduino signal and controller multi-control methods for RC cars

Hello
I'm already asking you the third question.

Through the blog post below,
It seems to be quite close to controlling RC cars simultaneously using Arduino signals (such as Write 100) and controllers.

http://rcarduino.blogspot.com/2012/04/how-to-read-multiple-rc-channels-draft.html?m=1

Currently, using the code at the bottom of the blog, we even checked that the signals sent to the receiver via the controller were delivered through Arduino to the servo and ESC.

Therefore, I think we should add control via Arduino signal to this code now. I don't know where to insert the code, so I leave a question.

Thank you for always responding sincerely.
Please understand my poor English.

Doyoon.

Here is the Whole Sketch

RC_ARD.ino (8.1 KB)

Hello
I canยดt see any sketch.

Thanks to the Reply!

I upload the Whole Sketch.

You can find it under the post.

not clear what the code doesn't do that you want to add

It sounds like you want to make the RC car do something autonomously, without being controlled by a human with a radio transmitter.

What sort of thing do you want it to do?

How do you want to switch between manual and autonomous control?

Actually, I just want to put in This code now,

Servo.write(100)
delay(2000)
Servo.write(120)
delay(2000)

Like this.

After the Validation, I will set the more complex code.

When do you want that action to happen?

I think I didn't explain the situation enough, so I'm writing a long comment.

If you use Arduino to generate repetitive movements of a simple servo motor, such as Servo.write (100) > delay (2000) > delay (120) > delay (2000), you want to turn on or operate the controller to stop motion (a signal I randomly inputted) and to control it.

I was informed that using the blog post above can implement the above situation, so I tried using the code above.

However, I have no idea which part of the code I should enter the signal that I randomly input.

I don't know if the situation was explained well.
I'm not good at English, so please understand.

If you leave a comment, I'll reply right away. Thank you.

Do you want servo to do this:

Whenever the radio transmitter is turned off?

All three servo outputs? If not all three, which ones?

Here is a version that does that to the steering servo when the transmitter is turned off:

//modified my Doyoon Kong


// MultiChannels
//
// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// include the pinchangeint library - see the links in the related topics section above for details
//#include <PinChangeInt.h>
// ๋“€์—๋กœ ์ „ํ™˜ํ•จ์œผ๋กœ์จ PinChangeInt์˜ ์‚ฌ์šฉ๋„ ์—†์•ฐ.

//Servo.h๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฒฐ์ •ํ•จ.
//๋˜ํ•œ AUX๋Š” ํ›„์— IMU์˜ ๋ณ€ํ™”๋ฅผ ์ธก์ •ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ์ƒ์ •.
#include <Servo.h>

// Assign your channel in pins
#define THROTTLE_IN_PIN 35
#define STEERING_IN_PIN 44
#define AUX_IN_PIN 24

// ์ธํ„ฐ๋ŸฝํŠธ ์„ค์ •_์ถ”๊ฐ€ํ•จ.
//#define THROTTLE_IN_INTERRUPT 0 // Pin 2 = Interrupt 0
//#define STEERING_IN_INTERRUPT 1 // pin 3 = Interrupt 1
//#define AUX_IN_INTERRUPT 2 // pin 21 = Interrupt 2
//DigitalPinToInterrupt๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ธํ„ฐ๋ŸฝํŠธ ์„ค์ •์„ ํ•ด๊ฒฐํ•จ(๋ฒ„์ „ ์—…)

// Assign your channel out pins
#define THROTTLE_OUT_PIN 37
#define STEERING_OUT_PIN 46
#define AUX_OUT_PIN 26

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoThrottle;
Servo servoSteering;
Servo servoAux;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

volatile unsigned long ulLastInterruptTime = 0;

// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
uint32_t ulAuxStart;

void setup()
{
  //๋ฐ”๊ฟ”์คŒ.
  Serial.begin(9600);

  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers
  servoThrottle.attach(THROTTLE_OUT_PIN);
  servoSteering.attach(STEERING_OUT_PIN);
  servoAux.attach(AUX_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  // ์ธํ„ฐ๋ŸฝํŠธ ๋ฒˆํ˜ธ๋ฅผ ์ฒดํ‚นํ•ด์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •.
  attachInterrupt(digitalPinToInterrupt(THROTTLE_IN_PIN), calcThrottle, CHANGE);
  attachInterrupt(digitalPinToInterrupt(STEERING_IN_PIN), calcSteering, CHANGE);
  attachInterrupt(digitalPinToInterrupt(AUX_IN_PIN), calcAux, CHANGE);
}

void loop()
{
  static unsigned long ulLastUpdateTime = 0;

  // If there have been any control pulses...
  if (bUpdateFlagsShared)
  {
    ulLastUpdateTime = millis();  // ...reset the timer
  }

  // If any control pulses have arrived in the last 100 milliseconds
  if (millis() - ulLastUpdateTime <= 100)
  {
    // We have control inputs.  Do manual control.
    ManualControl();
  }
  else
  {
    // Transmitter is off.  Do automatic control.
    AutomaticControl();
  }
}

void ManualControl()
{
  // create local variables to hold local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  // check shared update flags to see if any channels have a new signal
  if (bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;

    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.

    if (bUpdateFlags & THROTTLE_FLAG)
    {
      //unThrottleInShared = 1500;
      unThrottleIn = unThrottleInShared;
    }

    if (bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }

    if (bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }

    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;

    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }

  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop

  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.

  // we are checking to see if the channel value has changed, this is indicated
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if (bUpdateFlags & THROTTLE_FLAG)
  {
    if (servoThrottle.readMicroseconds() != unThrottleIn)
    {
      servoThrottle.writeMicroseconds(unThrottleIn);
    }
  }

  if (bUpdateFlags & STEERING_FLAG)
  {
    if (servoSteering.readMicroseconds() != unSteeringIn)
    {
      servoSteering.writeMicroseconds(unSteeringIn);
    }
  }

  if (bUpdateFlags & AUX_FLAG)
  {
    if (servoAux.readMicroseconds() != unAuxIn)
    {
      servoAux.writeMicroseconds(unAuxIn);
    }
  }

  bUpdateFlags = 0;
}

void AutomaticControl()
{
  /*
    Servo.write (100);
    delay (2000);
    Servo.write(120);
    delay (2000);
  */

  // Do that but without delay()
  static unsigned long previousMillis = 0;
  static boolean position = false;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= 2000)
  {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (position)
    {
      servoSteering.write (100);
    }
    else
    {
      servoSteering.write (120);
    }
    // Toggle position for next time
    position = !position;
  }
}

  // simple interrupt service routine

  //๊ณ„์‚ฐ ๋งค์ปค๋‹ˆ์ฆ˜์„ ์กฐ๊ธˆ ๋ฐ”๊ฟˆ.
  //1. PCintPort๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , Pin์„ Readํ•ด์„œ HIGH/LOW๋ฅผ ์ง์ ‘ํŒ๋‹จ.
  //2. TCNT1๊ณผ ๊ฐ™์€ AVRํƒ€์ž…๋งŒ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋Š” ๋งค์„œ๋“œ๋ฅผ ์ œ๊ฑฐ.
  //3. uint16_t์™€ ๊ฐ™์€ ๋ณ€ํ™˜๋ฐฉ์‹์„ ์ด์šฉํ•ด micros๋ฅผ ์ด์šฉํ•จ.

  void calcThrottle()
  {
    // if the pin is high, its a rising edge of the signal pulse, so lets record its value
    if (digitalRead(THROTTLE_IN_PIN) == HIGH)
    {
      ulThrottleStart = micros();
    }
    else
    {
      // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
      // this gives use the time between the rising and falling edges i.e. the pulse duration.
      unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
      // use set the throttle flag to indicate that a new throttle signal has been received
      bUpdateFlagsShared |= THROTTLE_FLAG;
    }
  }

  void calcSteering()
  {
    if (digitalRead(STEERING_IN_PIN) == HIGH)
    {
      ulSteeringStart = micros();
    }
    else
    {
      unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
      bUpdateFlagsShared |= STEERING_FLAG;
    }
  }

  void calcAux()
  {
    if (digitalRead(AUX_IN_PIN) == HIGH)
    {
      ulAuxStart = micros();
    }
    else
    {
      unAuxInShared = (uint16_t)(micros() - ulAuxStart);
      bUpdateFlagsShared |= AUX_FLAG;
    }
  }
1 Like

Yes, the situation you told me is correct.
First of all, the mechanism's tester was first.


Thank you very much for your kind reply.
I think it's an amazing solution.
I got the impression why I didn't think of this even when I saw it.

Tomorrow, I think I can do a proper test. I'll visit again to let you know the results in the comments.

Thank you again for putting in so much effort.
I wish you all the best.

Doyoon.

First of all, thank you for your hard work.
Thanks to you, there have been so many improvements.

In conclusion, half of the above code was successful and half of it failed.

I'll attach the video at the bottom, so I think it'll be quick to explain the situation if you watch it.

As you can see, the moment the controller is powered on, ManualControl(); is clearly operational.
However, AutomaticControl(); does not seem to work when the controller is not powered on.
I've thought about it deeply, but I can't find a solution, so I'm leaving you a comment again.

Below are the things I've tried.

  1. First, to ensure that the AutomaticControl function continues to run correctly, we tried to verify it using a serial monitor, but this was abandoned due to a collision of motor signals that we do not know why.

  2. Secondly, you uploaded the code in a way that does not use delay(), so I modified it to a way that uses delay that is annotated, but there was no change.

  3. So, thirdly, we've written the "else{AutomaticControl();}" section running AutomaticControl directly, but this time there's no change. From what I thought, I don't think the code goes into "else".

  4. After posting this comment, I inserted Serial.print into the bottom of where the Manual and Automatic functions run to check the monitor, and I noticed that whether the controller is powered off or on, only the Manual functions run.
    So now I'm testing by changing the "if" condition.

  5. Also, as I read the code slowly,
    The defined ulLastInterruptTime variable, such as "volatile unsigned long ulLastInterruptTime = 0;", was declared only, but was not used. In case this can help you solve the problem, I write it down here.

  6. Let me continue to write down my attempts.
    After much consideration, I thought that the entire state remained in the Manual state because the variable "bUpdateFlagShared" would still return same values.
    Therefore, I checked this value through the serial monitor and found that it was still returning "0" regardless of external changes.

If you find a solution, please share it with me.
You're a big help to a beginner who doesn't know much about Arduino yet.
Thank you always.

-Doyoon-

+Here is the Test video link.(I uploaded in youtube)

Maybe the receiver has a failsafe mode where it sends servo control pulses to the Arduino for some default position when it loses contact with the transmitter. If that is the case and the feature can't be turned off, there may be no way to detect when the signal is lost by looking for a loss of servo control pulses. Is there an LED on the receiver that turns on or off when the transmitter is turned on or off?

Yes. My mistake. You can delete that line.

1 Like

Aha, there could be a reason for that.
Unfortunately, the receiver has only one led, which seems to have the ability to check the power of the receiver.
Turning the controller on and off doesn't change the LED.

Currently, my RC car is a transxas car, and the receiver and controller are also using the TQi model of that company.
Since I am not good at English, I have not read the manual deeply, so as soon as I wake up tomorrow, I think I should find a way to turn off the fail-safe mode.

If you think of another reason suddenly, please always reply.
Your comments always give me a lot of strength because I'm tired of the dark road ahead.
Once again, thank you from the bottom of my heart.

-doyoon-

What model receiver? 6519, 6518, or 6533?
What model transmitter? 6507R, 6509R, 6528, or 6530?

One possible solution: Use a 4-channel transmitter and use one channel just to switch between Manual and Automatic.

Another possible solution: Recognize the "Failsafe" position (if that is what it is doing) and switch to automatic control if the inputs stay in the failsafe position for more than a few seconds.

Thank you for your reply.
I can't remember the exact model name because I'm at home now, but from the picture on the Internet, the controller seems to be model 6509 and receiver is model 6518, depending on the lack of a shift switch.
I don't think we can use solution number one because it probably wasn't four channels.
If there's a way to stop the fail-safe function, would it work?
Or if it's inevitable to modify the code, can you give me your opinion on which part to change?

Run this sketch with Serial Monitor open. Do the numbers always go to the same set of values when the transmitter is off?

//modified my Doyoon Kong


// MultiChannels
//
// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
// Assign your channel in pins
#define THROTTLE_IN_PIN 35
#define STEERING_IN_PIN 44
#define AUX_IN_PIN 24

void setup()
{
  Serial.begin(115200);
  delay(500);
}

void loop()
{
  Serial.print(pulseIn(THROTTLE_IN_PIN, HIGH, 30000));
  Serial.print('\t'); // Tab
  Serial.print(pulseIn(STEERING_IN_PIN, HIGH, 30000));
  Serial.print('\t'); // Tab
  Serial.println(pulseIn(AUX_IN_PIN, HIGH, 30000));

  delay(1000);
}

My reply was late. I'm sorry.

As soon as I woke up today, I conducted a test.

First of all, I uploaded the code you taught me and checked the serial monitor continuously, but there was no change in the value of the monitor, whether it was turned off or off. Even though I tried to operate the controller, the value continued to point to zero.

You can find the results of this experiment in the Code Test - 1 video attached at the bottom.

However, I think you taught me the above code to check the difference between the pulse delivered by Transmitter and the pulse emitted by Arduino itself, or if there is a significant difference when the signal is sent from Transmitter.

We've checked the pulse with the actual circuit running by inserting the code right above into the existing drive code.

You can check the results of the experiment in the Test-2 video below.

As shown in the video, there was no difference in the actual pulse value whether the controller was turned on or off (AUX was not used during the experiment).)

However, turning the steering knob on the controller strongly showed a drop in pulse value or a very large jump. How's it going in Arduino?

Perhaps if we create a condition with one 'inverse value', we can signal that it is an urgent situation and switch from Auto mode to Manual mode.

Additionally, I did one more test. It was difficult to check the value with a serial monitor, so it was to use a serial plotter to check the pattern of pulse changes due to the presence or absence of a signal.

The results were pretty positive.
As you can see in the following Test - 3 images, without the controller turned on, the pulse's maximum point was specified (no matter how the car was operating.

There was a pulse maximum point that was still visible with the controller turned on, but this clearly showed a different value than when it was not turned on.
If I code it so that it can be used as a trigger, I think I can expect something good.

Anyway, thank you so much for helping us reach so many developments.
If you think of a good algorithm as a trigger to enter manual and automatic mode with the experimental results above, please leave a comment at all times.

Thank you always from the bottom of my heart, and I wish you all the luck and happiness today.

-Doyoon-


Test1

Test2

Test3