I am working on my final project for high school. I need to code arduino nano to receive data from RC receiver tgy ia6c that communicates with tgy i6s transmitter. Receiver has ibus, sbus, pwm and ppm but it goes through 1 pin each so receiver has 2 pins (1 for ibus/sbus, 1 for pwm/ppm). I did some research and I think that I need some kind of inverter because receiver output pins are inverted. Basically I would control direction of motors with 3 position switch, speed of 2 dc motors with gimbal, and 4 leds with 2 switches (2 position switch).
So which one do you want to use?
You should show your own effort what informations you have found
or asking more specific questions.
best regards Stefan
I would like to use either pwm or sbus but I am having trouble writing the code, because receiver sends all that data it received from transmitter through one pin and I need to separate 4 channels from 8/10 (I would use just 4 channels). I managed to get some data into arduino but its just a lot of numbers.
That's all and what data is, or are. Post code that prints the numbers you managed to get from the receiver so far.
Do they track and change with changes to the transmitter controls?
Do you have google over there on your internets?
Try
arduino sbus decoder
and things like that. This has been done many many times.
Oh, welcome to the forum!
a7
You haven't yet understod how a "bus" works.
The main purpose of a bus is to reduce the number of pins.
The channels are "created" through adress-numbers that are sent together with the channel-position-data.
With PWM it is different:
For PWM-signals each channel needs his own Pin.
On the receiver and on the arduino.
you should post the datasheet of your receiver
Do you have a storage oscilloscope?
If not:
can you afford to buy a $10 to $15 24 MHz 8 channel logic analyser like this one?
WIth this logic analyser you can record and display the signal levels
And with the freeware sigrok pulseView you can even decode a lot of bus-protocols
best regards Stefan
Here is the link of my receiver : https://hobbyking.com/en_us/turnigy-ia6c-ppm-sbus-receiver.html?___store=en_us
This is code I used to get numbers, and yes numbers change when I move the knob of transmitter.
unsigned long int a,b,c;
int x[15],ch1[15],ch[7],i;
//specifing arrays and variables to store values
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), read_me, FALLING);
// enabling interrupt at pin 2
}
void loop() {
read_rc();
Serial.print(ch[1]);Serial.print("\ ");
Serial.print(ch[2]);Serial.print("\ ");
Serial.print(ch[3]);Serial.print("\ ");
Serial.print(ch[4]);Serial.print("\ ");
Serial.print(ch[5]);Serial.print("\ ");
Serial.print(ch[6]);Serial.print("\ ");
Serial.println();
delay(100);
}
void read_me() {
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
a=micros(); //store time value a when pin value falling
c=a-b; //calculating time inbetween two peaks
b=a; //
x[i]=c; //storing 15 value in array
i=i+1; if(i==15){for(int j=0;j<15;j++) {ch1[j]=x[j];}
i=0;}}//copy store all values from temporary array another array after 15 reading
void read_rc(){
int i,j,k=0;
for(k=14;k>-1;k--){if(ch1[k]>10000){j=k;}} //detecting separation space 10000us in that another array
for(i=1;i<=6;i++){ch[i]=(ch1[i+j]-1000);}} //assign 6 channel values after separation space
You could say whether those movements relate at all to the numbers they produce as a result…
a7
The code you have posted does ppm-decoding. without a library
I have never used a ppm-library but I guess it will become easier
if you use a library
This example-code looks pretty easy
Well I get these numbers like 0, 500, 1000 which seem to be good. If I dont touch anything on transmitter the numbers dont change but when I move the 3 position switch I get numbers like 8000 and numbers on other channels change but they shouldn't.
I get this error PPM.h no such file or directory but I have downloaded some PPM library from library manager, maybe I have downloaded the wrong one. I also downloaded zip file from that page you posted and opened it through arduino and it says "Error: 13 INTERNAL: Library install failed: moving extracted archive to destination dir: Library PPM is already installed, but with a different version: PPM@1.1.3"
to solve this problem you have again (and like always) to provide precise and detailed information:
what operating system are you using windows 10 , windows 11m linux? which one? apple/iOS, online IDE
what Arduino-IDE-version are you using?
do you use IDE Version 1.8.19 (the one I highly recommend over IDE 2.0.X
IDE 2.0.X is still not really reliable. Though Arduino offers IDE 2.0.4 as the "new standard"
or do you use IDE version 2.0.X??
You have to donwload the zip-file from here
I downloaded the library as ZIP-file installed the ZIP-library and
the democode
#include <PPM.h>
#define CHANNELS 8 // max ppm channels
#define PPM_PIN 2 // receiver ppm pin
void setup() {
ppm.begin(PPM_PIN, CHANNELS);
Serial.begin(115200);
}
void loop() {
for (uint8_t i = 1; i <= CHANNELS; i++) // print all channel values
{
Serial.print(ppm.get(i));
Serial.print('\t');
}
Serial.println();
delay(10);
}
compiled for board Arduino Nano
your description
shows me that you still understand too less how PPM works.
I highly recommend that you learn more details about how ppm works.
Beeing able to say "I get numbers like...." is still too unprecise to be able to tell if the received numbers make sense or not.
You should not go on with your project with a a rough "I get these numbers like 0, 500, 1000 which seem to be good"
If you receive errors
mark, copy the complete log-information from the Arduino-IDE
and post it as a code-section.
best regards Stefan
Im having windows 10. Arduino IDE version 2.1.0. I deleted library from arduino libraries and installed it through arduino ide now code shows no errors but output on serial monitor isn't good it prints out some symbols. I did some research and I found out those numbers 0, 500, 1000 are good and I get those numbers with code I posted above but sometimes numbers I get like 8000 or 7000 and channels mix up. For example my channel 3 becomes channel 5 on serial monitor.
Thanks for help and Im sorry for any language mistakes I made while writing all of this.
Your english is understandable. If you want to write more words but don't know the english words simply use google-translate or deepL
hese translationservices work astonishing good.
How should I know from this unprecise description what is causing this?
If you want more support than just a worded "an encouraging tap on your shoulder" you have to provide the details which are:
- your actual used code even if it is exactly the given example code.
Post the actual used complete sketch - mark copy what output you get in the serial monitor
and post this output as a code-section
best regards Stefan
Hi @StefanL38,
I have formatted the code from post #7:
unsigned long int a, b, c;
int x[15], ch1[15], ch[7], i;
//specifing arrays and variables to store values
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), read_me, FALLING);
// enabling interrupt at pin 2
}
void loop() {
read_rc();
Serial.print(ch[1]); Serial.print("\ ");
Serial.print(ch[2]); Serial.print("\ ");
Serial.print(ch[3]); Serial.print("\ ");
Serial.print(ch[4]); Serial.print("\ ");
Serial.print(ch[5]); Serial.print("\ ");
Serial.print(ch[6]); Serial.print("\ ");
Serial.println();
delay(100);
}
void read_me() {
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
a = micros(); //store time value a when pin value falling
c = a - b; //calculating time inbetween two peaks
b = a; //
x[i] = c; //storing 15 value in array
i = i + 1;
if (i == 15) {
for (int j = 0; j < 15; j++) {
ch1[j] = x[j];
}
i = 0;
}
}
//copy store all values from temporary array another array after 15 reading
void read_rc() {
int i, j, k = 0;
for (k = 14; k > -1; k--) {
if (ch1[k] > 10000) {
j = k; //detecting separation space 10000us in that another array
}
}
for (i = 1; i <= 6; i++) {
ch[i] = (ch1[i + j] - 1000); //assign 6 channel values after separation space
}
}
My findings on a first glance:
void read_rc() {
int i, j, k = 0;
should read:
void read_rc() {
int i, k;
int j = 0;
as i, and k are variables initialized in a for loop while j is not.
loop() consists of three parts:
Copy data from array ch1[] to array ch[]
A number of Serial.prints
A delay of 100 msec
This is done every 100 msecs without checking
- whether new data have been received or not and
- whether the data in ch1[] are complete or not.
There is no synchronisation (which could possibly be done using the separation space of > 10000 µs?!)
for (i = 1; i <= 6; i++) {
ch[i] = (ch1[i + j] - 1000);
}
ch[] index goes from 0 to 6. Used is index 1 to 6
ch1[] index goes from 0 to 14.
If the separation is detected in ch1[] at a position 9 <= j <=14 the index [i + j] will be out of range.
Hi @bicanict,
I rearranged your sketch a little bit (see post #15) except the synchronisation issue (which I think will finally be crucial):
// The arrays that are used globally
unsigned long ch1[15], ch[7];
// A boolean to signalize that a number of 15 data is available
boolean dataAvailable = false;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), read_me, FALLING);
// enabling interrupt at pin 2
}
void loop() {
read_rc();
}
// All the printing in a separate function
void printData(){
Serial.print("\t");
Serial.print(ch[1]); Serial.print("\t");
Serial.print(ch[2]); Serial.print("\t");
Serial.print(ch[3]); Serial.print("\t");
Serial.print(ch[4]); Serial.print("\t");
Serial.print(ch[5]); Serial.print("\t");
Serial.print(ch[6]);
Serial.println();
}
void read_me() {
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
// Variables for data only used in this function but must be static
// so they keep their content for the next call
static unsigned long int lastInterruptTime = 0; // Changed this to zero!!
static unsigned long x[15];
static int counter = 0;
// Variables which are calculated or set new with every call of the function
unsigned long int interruptTime;
unsigned long int timeDifference;
// While the global variable dataAvailable is true
// we "ignore" interrupts ...
// so we do not interfere with data which have been stored
// until they were handled by read_rc();
if (dataAvailable) {
return;
};
interruptTime = micros(); //store time value a when pin value falling
timeDifference = interruptTime - lastInterruptTime; //calculating time inbetween two peaks
lastInterruptTime = interruptTime; //
x[counter] = timeDifference; //storing 15 value in array
counter++;
if (counter == 15) {
for (int j = 0; j < 15; j++) {
ch1[j] = x[j];
}
counter = 0;
// if we got enough data we tell this to "read_rc()" via dataAvailable
dataAvailable = true;
}
}
//copy store all values from temporary array another array after 15 reading
void read_rc() {
int i, k;
int j = 0; // initialize j with zero
if (dataAvailable) {
for (k = 8; k >= 0; k--) { //we start with k = 8 to avoid an overflow in ch1[i + j] later
if (ch1[k] > 10000) {
j = k; //detecting separation space 10000us in that another array
}
}
for (i = 1; i <= 6; i++) {
ch[i] = (ch1[i + j] - 1000); //assign 6 channel values after separation space
}
printData();
dataAvailable = false;
}
}
I introduced unsigned long for all variables that store time and time differences to avoid overflow effects if time differences are stored in int variables.
It will solve some of the issues but is still most likely not solving your intention properly as the synchronisation as well as a "timeout" is missing in case that less than 15 interrupts occur.
A further lack is that data which are received while the previous data are not handled by read_rc() will be discarded.
All of this can be handled but it requires to know more about the timing and structure of the incoming data ...
Good luck!
P.S.: I changed the initialization of lastInterruptTime to zero ...
And this version may give you some possibilities to "play around" with your sketch to check its behaviour:
// The arrays that are used globally
unsigned long ch1[15], ch[7];
int nextPos = 0;
int lastPos = 0;
// A boolean to signalize that a number of 15 data is available
boolean dataAvailable = false;
constexpr byte outPin = 8;
constexpr byte buttonPin = 13;
void setup() {
Serial.begin(9600);
pinMode(outPin, OUTPUT);
digitalWrite(outPin, HIGH);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), read_me, FALLING);
// enabling interrupt at pin 2
}
void loop() {
read_rc();
if (buttonReleased()) {
SendRC();
}
}
// All the printing in a separate function
void printData() {
Serial.print("\t");
Serial.print(ch[1]); Serial.print("\t");
Serial.print(ch[2]); Serial.print("\t");
Serial.print(ch[3]); Serial.print("\t");
Serial.print(ch[4]); Serial.print("\t");
Serial.print(ch[5]); Serial.print("\t");
Serial.print(ch[6]);
Serial.println();
}
void read_me() {
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
// Variables for data only used in this function but must be static
// so they keep their content for the next call
static unsigned long int lastInterruptTime = 0;
static unsigned long x[15];
static int counter = 0;
// Variables which are calculated or set new with every call of the function
unsigned long int interruptTime;
unsigned long int timeDifference;
// While the global variable dataAvailable is true
// we "ignore" interrupts ...
// so we do not interfere with data which have been stored
// until they were handled by read_rc();
if (dataAvailable) {
return;
};
interruptTime = micros(); //store time value a when pin value falling
timeDifference = interruptTime - lastInterruptTime; //calculating time inbetween two peaks
lastInterruptTime = interruptTime; //
x[counter] = timeDifference; //storing 15 value in array
counter++;
if (counter == 15) {
for (int j = 0; j < 15; j++) {
ch1[j] = x[j];
}
counter = 0;
// if we got enough data we tell this to "read_rc()" via dataAvailable
dataAvailable = true;
}
}
//copy store all values from temporary array another array after 15 reading
void read_rc() {
int i, k;
int j = 0; // initialize j with zero
if (dataAvailable) {
showRawData();
for (k = 8; k >= 0; k--) { //we start with k = 8 to avoid an overflow in ch1[i + j] later
if (ch1[k] > 10000) {
j = k; //detecting always the separation space > 10000us in array ch1[] with the lowest(!) index
}
}
for (i = 1; i <= 6; i++) {
ch[i] = (ch1[i + j]-1000); //assign 6 channel values after separation space
}
printData();
dataAvailable = false;
}
}
void SendRC() {
bool toggle = true;
for (int i = 0; i < 15; i++) {
digitalWrite(outPin, LOW);
delayMicroseconds(2);
digitalWrite(outPin, HIGH);
if (toggle) {
delayMicroseconds(2000 + i * 10);
}
else {
delayMicroseconds(1500 + i * 10);
}
if (i==7) delay(10);
toggle = !toggle;
}
}
boolean buttonReleased() {
static unsigned long lastPressed = 0;
static int lastState = HIGH;
static int actState = HIGH;
int state;
state = digitalRead(buttonPin);
if (state != lastState) {
lastPressed = millis();
lastState = state;
}
if (millis() - lastPressed > 30 && state != actState) {
actState = state;
if (actState) {
return true;
}
} else {
return false;
}
}
void showRawData() {
int i;
Serial.println("\n-------------------------------Raw Data -------------------------------------------------");
for (i = 0; i < 15; i++) {
Serial.print("\t");
Serial.print(ch1[i]);
if (i == 7) {
Serial.println();
}
}
Serial.println("\n--------------------------------Prepared Data---------------------------------------------");
}
Instead of using the RC it sends a given sequence if a button is pressed.
Feel free to check it out on Wokwi: https://wokwi.com/projects/363789777128185857
I adjusted the timing so that 1000 can be subtracted without over/underflow.
Hi @ec2021,
This code works the best out of all codes but it still has some issues I will try to explain it.
When I run it first 4 channels show 500 stable, and last 2 show 0. When I move number 2 to the left data on channel 4 changes to around 16 so thats like a 0, but when I move it to the right all channels get some random numbers. When I move number 2 to the bottom channel 3 becomes a 0, but when I move it just slightly to the top all channels get some random numbers. When I move number 1 down channel 2 is 0, when I move same knob to the left channel 1 becomes 0, but if I just slightly move it to the top or to the right same thing happens like it happens with number 1 all channels get random numbers, same thing happens when any of these switches (3,4,5,6) are not in top position.
I would use just number 1, movement from bottom to top so that would be channel 1 let's say, switch 3 on channel 2, and switch 4 on channel 3.
Picture 1. shows my transmitter, picture 2. shows serial monitor when all switches are in top position and knobs 1 and 2 are centered, picture 3 shows when one of those 4 switches is not in top position.
See pictures in attachments.
// The arrays that are used globally
unsigned long ch1[15], ch[7];
// A boolean to signalize that a number of 15 data is available
boolean dataAvailable = false;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), read_me, FALLING);
// enabling interrupt at pin 2
}
void loop() {
read_rc();
}
// All the printing in a separate function
void printData(){
Serial.print("\t");
Serial.print(ch[1]); Serial.print("\t");
Serial.print(ch[2]); Serial.print("\t");
Serial.print(ch[3]); Serial.print("\t");
Serial.print(ch[4]); Serial.print("\t");
Serial.print(ch[5]); Serial.print("\t");
Serial.print(ch[6]);
Serial.println();
}
void read_me() {
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
// Variables for data only used in this function but must be static
// so they keep their content for the next call
static unsigned long int lastInterruptTime = 0; // Changed this to zero!!
static unsigned long x[15];
static int counter = 0;
// Variables which are calculated or set new with every call of the function
unsigned long int interruptTime;
unsigned long int timeDifference;
// While the global variable dataAvailable is true
// we "ignore" interrupts ...
// so we do not interfere with data which have been stored
// until they were handled by read_rc();
if (dataAvailable) {
return;
};
interruptTime = micros(); //store time value a when pin value falling
timeDifference = interruptTime - lastInterruptTime; //calculating time inbetween two peaks
lastInterruptTime = interruptTime; //
x[counter] = timeDifference; //storing 15 value in array
counter++;
if (counter == 15) {
for (int j = 0; j < 15; j++) {
ch1[j] = x[j];
}
counter = 0;
// if we got enough data we tell this to "read_rc()" via dataAvailable
dataAvailable = true;
}
}
//copy store all values from temporary array another array after 15 reading
void read_rc() {
int i, k;
int j = 0; // initialize j with zero
if (dataAvailable) {
for (k = 8; k >= 0; k--) { //we start with k = 8 to avoid an overflow in ch1[i + j] later
if (ch1[k] > 10000) {
j = k; //detecting separation space 10000us in that another array
}
}
for (i = 1; i <= 6; i++) {
ch[i] = (ch1[i + j] - 1000); //assign 6 channel values after separation space
}
printData();
dataAvailable = false;
}
}
You should post serial output as code-section.
Just the same way as a code
A more detailed analysis could be done with a storage-oscilloscope ($200)
or with a 24 MHz 8 Channel logic analyser
or by writing a code that does store just the microseconds between each pulse in an array as big as RAM allows and then too print all theses values for analysing what timely pattern do the pulses really have
So you are on the right way now ...
As I wrote there is too less knowledge about the raw timing. But now you can get this by adding this function
void showRawData() {
int i;
Serial.println("\n-------------------------------Raw Data -------------------------------------------------");
for (i = 0; i < 15; i++) {
Serial.print("\t");
Serial.print(ch1[i]);
if (i == 7) {
Serial.println();
}
}
Serial.println("\n--------------------------------Prepared Data---------------------------------------------");
}
just before the call to printData() in read_rc(). So you read the raw data and compare them with the results from ch[].
It allows you to verify
- if subtracting 1000 from the time difference is always ok.
- which raw content is in the array after the first value larger than 10000 µs (as this content stays always unused!).
You could
- increase the buffer from 15 to e.g. 30 entries (as long as there is enough memory available) to see if there are more data following,
- change the read_rc() si that it does not only print the first six channels but also the second (or more) in case that the 10 ms space is occuring more than once in the array (as long as there are still six data to read).
You have all the tools now.
Good luck!
Hi,
I found new code that works better then all the codes above.
This is the code. I managed to make it when I turn the switch A the 2 LEDs turn on as they are going to be front lights for locomotive, and when I pop switch D on the 2 LEDs turn on (back lights of locomotive). Ofcourse when I turn off those 2 switches LEDs turn off as well.
This code has issue as well because the center point is not 500 but around 1100, top point is 1600(should be 1000) and bottom point is 600 (should be 0). I don't know is this even a issue because it works and values are stable.
I also need to code it when switch B (3 position switch) is in center point 2 DC motors are not rotating, but when I move it to bottom they still shouldn't rotate but it's going to be one direction and top position is going to be opposite direction. DC motors should rotate when I move knob from center to top if switch B (is in top position) so I'm thinking some logic AND to do so. I would control DC motors with pwm so if I'm moving knob to the top slowly the speed rises if the switch is in proper position. I hope you understand me.
const int sig_pin = 2; // ppm input digital pin
const int channel_amount = 8; // replace with your channel amount
unsigned long last_ppm_sig = 0;
int current_channel = 0;
int channel[channel_amount];
void setup() {
Serial.begin(9600);
pinMode(sig_pin, INPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
attachInterrupt(digitalPinToInterrupt(sig_pin), reciever, CHANGE);
}
void reciever()
{
unsigned long diff = micros() - last_ppm_sig;
last_ppm_sig = micros();
if (diff > 3000)
{
current_channel = 1;
return;
}
else if (diff > 450 && current_channel != 0 && current_channel <= channel_amount)
{
channel[current_channel - 1] = diff;
current_channel++;
}
}
void loop() {
for (int i = 0; i < channel_amount; i++)
{
Serial.print(String(channel[i]) + " ");
}
Serial.print("\n");
if (channel[4] == 596 || channel [4] == 600)
{
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
}
else if (channel [4] == 1596 ||channel [4] == 1600)
{
digitalWrite(5, LOW);
digitalWrite(6, LOW);
}
else
{
digitalWrite(5, LOW);
digitalWrite(6, LOW);
}
if (channel[7] == 596 || channel [7] == 600)
{
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
}
else if (channel [7] == 1596 ||channel [7] == 1600)
{
digitalWrite(3, LOW);
digitalWrite(4, LOW);
}
else
{
digitalWrite(3, LOW);
digitalWrite(4, LOW);
}
}