Arduino based S-Bus decoder with CPPM and Servo
Arduino based S-Bus decoder with CPPM and Servo
Hy,
my S-Bus Decoder with Arduino pro micro is running.
input is S-Bus form X8R at RX-pin
output is PPM at Pin 9 or pin 10 with free selectible Timing
PPM-Timing 100 to 500us, 10000us to 22500 to 40000us ch1 to ch 16
positiv or negativ
and has 8 Kanal Servo output Ch9 to Ch16 Pin 2,3,4,5,6,7,8,16 or others
and Serial to PC via USB for test and control S-Bus data and Servo Data
here you will find the software, the libraries and the shematic
http://fpv-community.de/showthread.php? ... ods/page12
and Page #80, #113, #115, #116, #119. #132
Software is adapted from some big Librarys with lots of options but only some funktions I need
Most librarys are for Mega 128 or 2560, so I had to change some details for AT32u4
from Futaba S-bus decoder Library
from PPM encoder but with Timer 3 so Timer 1 is free!
from RC Library the ServoOut Function
SerialOut via USB to PC from Serial Streaming library
Some Funktions from Arduino
Its quick and dirty programmed at one evening, I know, but its running
and works fine on the table and pinboard
all signals are testetd with Oszi
Helle
my S-Bus Decoder with Arduino pro micro is running.
input is S-Bus form X8R at RX-pin
output is PPM at Pin 9 or pin 10 with free selectible Timing
PPM-Timing 100 to 500us, 10000us to 22500 to 40000us ch1 to ch 16
positiv or negativ
and has 8 Kanal Servo output Ch9 to Ch16 Pin 2,3,4,5,6,7,8,16 or others
and Serial to PC via USB for test and control S-Bus data and Servo Data
here you will find the software, the libraries and the shematic
http://fpv-community.de/showthread.php? ... ods/page12
and Page #80, #113, #115, #116, #119. #132
Software is adapted from some big Librarys with lots of options but only some funktions I need
Most librarys are for Mega 128 or 2560, so I had to change some details for AT32u4
from Futaba S-bus decoder Library
from PPM encoder but with Timer 3 so Timer 1 is free!
from RC Library the ServoOut Function
SerialOut via USB to PC from Serial Streaming library
Some Funktions from Arduino
Its quick and dirty programmed at one evening, I know, but its running
and works fine on the table and pinboard
all signals are testetd with Oszi
Helle
Re: Arduino based S-Bus decoder with CPPM and Servo
Well done! There are a lot of Taranis owners looking for something like this. I will build one this weekend.
Vielen Dank
Alan
Vielen Dank
Alan
Anything can be made to fly - wings are just an option
Re: Arduino based S-Bus decoder with CPPM and Servo
Hi Is this also possible to have channels 1 to 8?Helle wrote: and has 8 Kanal Servo output Ch9 to Ch16 Pin 2,3,4,5,6,7,8,16 or others
and Serial to PC via USB for test and control S-Bus data and Servo Data
Could this be adapted to work with an arduino Pro Mini, Nano, Uno, etc?here you will find the software, the libraries and the shematic
http://fpv-community.de/showthread.php? ... ods/page12
and Page #80, #113, #115, #116, #119. #132
Software is adapted from some big Librarys with lots of options but only some funktions I need
Most librarys are for Mega 128 or 2560, so I had to change some details for AT32u4
Thanks,
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
I just developped a similar poject this week.jhsa wrote:Hi Is this also possible to have channels 1 to 8?Helle wrote: and has 8 Kanal Servo output Ch9 to Ch16 Pin 2,3,4,5,6,7,8,16 or others
and Serial to PC via USB for test and control S-Bus data and Servo Data
Could this be adapted to work with an arduino Pro Mini, Nano, Uno, etc?here you will find the software, the libraries and the shematic
http://fpv-community.de/showthread.php? ... ods/page12
and Page #80, #113, #115, #116, #119. #132
Software is adapted from some big Librarys with lots of options but only some funktions I need
Most librarys are for Mega 128 or 2560, so I had to change some details for AT32u4
Thanks,
João
My target was to expand an X4R receiver.
It uses an arduino pro mini at 16 mhz.
It uses no big library (except for debugging).
I think that it does not require an inverter on the SBSUS even if such an inverter is better (at least to protect the arduino).
It decodes currently 6 channels but it could adapted in order to decode more channels (probably up to 12 on top of the 4 already provided by X4R)
It is possible to set up in the firmware the channels that have to be decoded.
Debugging uses the SPI interface and another arduino pro mini.
If you want I can send you the alpha code.
I also plan to design a small board having the same size as the arduino pro mini in order to support all servo connectors (otherwise it is possible to connect servo cables directly to the arduino.
Re: Arduino based S-Bus decoder with CPPM and Servo
That is what I'm looking for. Connecting the servos directly to the arduino with frsky s.bus as input.
Thanks
João
Thanks
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
I propose to make some more tests and then to publish the code and some explanations how to use it.jhsa wrote:That is what I'm looking for. Connecting the servos directly to the arduino with frsky s.bus as input.
Thanks
João
I expect it should be OK in less than 1 week
Re: Arduino based S-Bus decoder with CPPM and Servo
If you don't want to bother, there's a neat little Chinese device with an ARM processor that already has 16 servo connectors, converts Sbus, PPM and servo signals to and from each other in all directions, and costs a mere $13. Hardware generation on all signals too, so no jitter and other issues.
-
- Posts: 750
- Joined: Tue Dec 27, 2011 11:22 pm
- Country: United States
- Location: Carson City, Nv
Re: Arduino based S-Bus decoder with CPPM and Servo
Got a link?Kilrah wrote:If you don't want to bother, there's a neat little Chinese device with an ARM processor that already has 16 servo connectors, converts Sbus, PPM and servo signals to and from each other in all directions, and costs a mere $13. Hardware generation on all signals too, so no jitter and other issues.
TIA
Dean
OldDmbThms: 1. Takeoff, 2. Crash, 3. Repair, GOTO 1
OldDmbThms: 1. Takeoff, 2. Crash, 3. Repair, GOTO 1
Re: Arduino based S-Bus decoder with CPPM and Servo
Well, I would like to botherKilrah wrote:If you don't want to bother, there's a neat little Chinese device with an ARM processor that already has 16 servo connectors, converts Sbus, PPM and servo signals to and from each other in all directions, and costs a mere $13. Hardware generation on all signals too, so no jitter and other issues.

I like to build my own stuff. Radios, sensors, planes, etc


João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
-
- Posts: 750
- Joined: Tue Dec 27, 2011 11:22 pm
- Country: United States
- Location: Carson City, Nv
Re: Arduino based S-Bus decoder with CPPM and Servo
So do I except for the mechanics of the connectors.
This shield would do to end that.
http://www.ebay.com/itm/F08019-16-Chann ... 41878ec866
But it is also $13
This shield would do to end that.
http://www.ebay.com/itm/F08019-16-Chann ... 41878ec866
But it is also $13
Dean
OldDmbThms: 1. Takeoff, 2. Crash, 3. Repair, GOTO 1
OldDmbThms: 1. Takeoff, 2. Crash, 3. Repair, GOTO 1
Re: Arduino based S-Bus decoder with CPPM and Servo
The reason I want to build the S.Bus to PWM decoder is to work with this project here..
viewtopic.php?f=91&t=5476
Eepskye can now output 16 channels in the S.bus format. we Already have the possibility of decoding to PPM for the 9x users, thanks to Mike, but having the arduino decoding the SBus signal to PWM would also be nice as it would give more possibilities..
João
viewtopic.php?f=91&t=5476
Eepskye can now output 16 channels in the S.bus format. we Already have the possibility of decoding to PPM for the 9x users, thanks to Mike, but having the arduino decoding the SBus signal to PWM would also be nice as it would give more possibilities..
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
If you use the Sbus and servo librarys with the Arduino it becomes quite simple.
I have used this sketch just to see if it worked.
You just feed the sbus signal from the receiver through an inverter to rx on the Arduino
You can have 12 servos connected with a Arduino mini.
I used a transistor as inverter.
#include <FUTABA_SBUS.h>
#include <Servo.h>
FUTABA_SBUS sBus;
Servo myservo;
void setup(){
sBus.begin();
myservo.attach(14);
}
void loop(){
sBus.FeedLine();
if (sBus.toChannels == 1){
sBus.UpdateServos();
sBus.UpdateChannels();
sBus.toChannels = 0;
myservo.writeMicroseconds(sBus.channels[6]+800);
}
}
I have used this sketch just to see if it worked.
You just feed the sbus signal from the receiver through an inverter to rx on the Arduino
You can have 12 servos connected with a Arduino mini.
I used a transistor as inverter.
#include <FUTABA_SBUS.h>
#include <Servo.h>
FUTABA_SBUS sBus;
Servo myservo;
void setup(){
sBus.begin();
myservo.attach(14);
}
void loop(){
sBus.FeedLine();
if (sBus.toChannels == 1){
sBus.UpdateServos();
sBus.UpdateChannels();
sBus.toChannels = 0;
myservo.writeMicroseconds(sBus.channels[6]+800);
}
}
Re: Arduino based S-Bus decoder with CPPM and Servo
Please find here the code I made in order to extend the X4R receiver (or other X serie receiver) with 6 additional PPM channels.
The PPM signals are generated every 18 ms so it should work with all kind of servos.
All explanations are at the beginning of the code.
It does not use complex libraries (except if you want to debug).
Even the standard arduino interrupts are disabled.
I tested it and it seems me OK (e.g. no jitter at all)
Comments are welcome.
The PPM signals are generated every 18 ms so it should work with all kind of servos.
All explanations are at the beginning of the code.
It does not use complex libraries (except if you want to debug).
Even the standard arduino interrupts are disabled.
I tested it and it seems me OK (e.g. no jitter at all)
Comments are welcome.
Code: Select all
// This poject uses Arduino pro mini 5 volt 16 mhz in order to decode Futaba SBUS.
// This version decodes the 16 channels available on SBUS but generates only 6 PPM signals.
// The 6 PPM signals are available on arduino pins 2 up to 7.
// The 6 PPM signals are based on 6 consecutive channels.
// By default, pin 2 deliver the first channel on traditional receiver and pin 7 deliver the channel 6.
// Still, you can select the fist channel changing the value "1" in the line "#define FIRSTCHANNEL 1" here under.
// E.g. if you want that PPM channel on arduino pin 2 is channel 4 from a traditional the line would be would be "#define FIRSTCHANNEL 4"
// In this case, pin 3 will generate channel 5, pin 4 will generate channel 6 ...
// The PPM signals are generates once per 18 msec. This refresh rate is supported by all servos.
//
// The SBUS signal has to be inverted before connecting it to arduino pin Rx. There are 2 ways to invert the SBUS signal
// 1 : Best is to invert the SBUS signal from the Receiver with a transistor and 2 resistors (see the web for a schema or look at a commecial product at e.g. Hobbyking).
// 2: It is also possible to let Arduino invert the SBUS signal but this takes some delay and some SBUS frame are lost. Still servos seems to react fast.
// In this case, the original (non inverted) SBUS signal has to be applied on arduino digital pin 8.
// The inverted signal is then available on arduino digital pin 9. Pin 9 has then to be connected to arduino pin Rx.
//
// As long as Arduino did not get a valid Sbus frame after power on, it wil not generate PPM signals.
// As soon as a valid Sbus frame is received, the 6 PPM signal wil be generated.
// Unvalid sbus frames are discarded and arduino generates PPM signals based on last correct frame received.
// Please note that if no new (valid) frames are received, the latest valid frame will continue to be used to generate the PPM signals (so servos will keep their last position).
//
// Arduino has a green led. This led will work as follow:
// If arduino never got a valid Sbus (e.g. Tx not powered on, no bind with Rx), led is off.
// Once a valid Sbus frame is received, the led will start blinking.
// When led blink fast (about 1 per sec) , it means that there are no Sbus frame error (normal case).
// If Arduino get at one invalid frame, the led wil be on for about 3 sec.
// If during the 3 sec, there are no other Sbus frame error, it will continue with fast binking rate.
// If there is at least one new frame error within the 3 sec, the led will be on for the next 3 seconds
// So, if there are many frame errors, it could be that the led is always on.
//
// Debugging :
// It is not possible to debug this project with serial.print command because the hardware serial is already used to read the Sbus (and a sofware serial would be to slow).
// Still, it is possible to debug this project using a second arduino connected to this one via the SPI interface.
// The second arduino has then to be loaded with a "slave" program.
// More informations at this link : http://www.utopiamechanicus.com/article/spi-debug/
//
// Note about loading the Arduino pro mini with this firmware.
// Programming the Arduino pro mini is normally done using a FTDI cable (or board).
// This FTDI connects on Pc side with the USB. On arduino side, it uses the RX and TX pins.
// In this project Arduino Rx pin is normally connected to the (inverted Sbus signal).
// Please take care not to connect the arduino to the Rx (Sbus port) while programming it (otherwise programming will fail).
// this line selects the first channels being generated; this version will then generate this channel and the next 5 channels;
#define FIRSTCHANNEL 1
#define PPMCHANNEL (FIRSTCHANNEL - 1)
#include <avr/io.h>
#include <avr/interrupt.h> // Needed to use interrupts
#include <SPI.h>
#include "pins_arduino.h"
// conditional debugging
// uncomment to debug using SPI interface
//#define DEBUG
#ifdef DEBUG
#define BEGIN_DEBUG do { SPI.begin (); SPI.setClockDivider(SPI_CLOCK_DIV8); } while (0)
#define TRACE(x) SPIdebug.print (x)
#define TRACE2(x,y) SPIdebug.print (x,y)
#define TRACELN(x) SPIdebug.println (x)
#define TRACELN2(x,y) SPIdebug.println (x,y)
class tSPIdebug : public Print
{
public:
virtual size_t write (const byte c) {
digitalWrite(SS, LOW);
SPI.transfer (c);
digitalWrite(SS, HIGH);
} // end of tSPIdebug::write
}; // end of tSPIdebug
// an instance of the SPIdebug object
tSPIdebug SPIdebug;
#else
#define BEGIN_DEBUG ((void) 0)
#define TRACE(x) ((void) 0)
#define TRACE2(x,y) ((void) 0)
#define TRACELN(x) ((void) 0)
#define TRACELN2(x,y) ((void) 0)
#endif // DEBUG
#define PIN_LED 13 // The Signal LED (default=13=onboard LED)
#define FRAME_DURATION 36000 // 36000 = 18000 usec * 2 because frequency is 16Mz and prescaler is 8
#define ZEROCYCLE 3000 // 1500 usec * 2
#define SBUS_MAXCHAR 25
#define MAX_CHANNELS 16
// Measured values with Futaba FX-30/R6108SB:
// -+100% on TX: PCM 1.100/1.520/1.950ms -> SBus raw values: 350/1024/1700 (100% ATV)
// -+140% on TX: PCM 0.930/1.520/2.112ms -> SBus raw values: 78/1024/1964 (140% ATV)
// -+152% on TX: PCM 0.884/1.520/2.160ms -> SBus raw values: 1/1024/2047 (140% ATV plus dirty tricks)
// define range mapping here, -+100% -> 1000..2000
#define SBUS_RANGE_MIN 200.0f
#define SBUS_RANGE_MAX 1800.0f
#define SBUS_TARGET_MIN 2000.0f // 2000 cycles = 1000 usec (because prescaler = 1/8 and Mhz = 16)
#define SBUS_TARGET_MAX 4000.0f // 4000 cycles = 2000 usec (because prescaler = 1/8 and Mhz = 16)
// pre-calculate the floating point stuff as far as possible at compile time
#define SBUS_SCALE_FACTOR ((SBUS_TARGET_MAX - SBUS_TARGET_MIN) / (SBUS_RANGE_MAX - SBUS_RANGE_MIN))
#define SBUS_SCALE_OFFSET (int)(SBUS_TARGET_MIN - (SBUS_SCALE_FACTOR * SBUS_RANGE_MIN + 0.5f))
uint32_t counter = 1;
uint32_t debugCounter ;
static boolean sbusStarted = false;
static boolean channelToLoad = false;
static uint8_t servoIndex ;
static uint8_t servoPin[] = { 0 , 0 , 0x04 , 0x04 , 0x08 , 0x08 , 0x10 , 0x10 , 0x20 , 0x20 , 0x40 , 0x40 , 0x80 , 0x80} ; // each value will toggle a pin
volatile static uint16_t servoCycle[] = { 0 , 0 , 3000 , 5000 , 8000 , 10000, 13000, 15000 , 18000 , 20000 , 23000 , 25000 , 28000 , 40000 } ; // !!!! the last value must be higher than FRAME_DURATION in order to let CompB generates an interrupt
static uint8_t UART_error ;
static uint8_t timer0 ;
static uint8_t bufferIndex ;
static uint8_t buffer[SBUS_MAXCHAR] ;
//static uint16_t channels[ MAX_CHANNELS ] ;
static uint16_t channelsMicrosec[ MAX_CHANNELS ] ;
unsigned char readOneChar ;
unsigned char c ;
uint16_t countUART_Error ;
uint16_t countSBUS_TimeToBigError ;
uint16_t countSBUS_TimeToSmallError ;
uint16_t countSBUS_StartError ;
uint16_t countSBUS_EndError ;
uint16_t countSBUS_Frame ;
uint16_t countSBUS_Error ;
uint8_t led ;
//************************************ Init*************************************
void init() // this function is replace the standard Arduino function
{
// Timer1
TIMSK1 &= ~( 1<< OCIE1A ) ; // Disable interupt on timer 1 for compA
TIMSK1 &= ~( 1<< OCIE1B ) ; // Disable interupt on timer 1 for compB
TCCR1A = 0x00 ; //Init.
TCCR1B = 0xC1 ; // I/p noise cancel, rising edge, Clock/1
// the bootloader connects pins 0 and 1 to the USART; disconnect them
// here so they can be used as normal digital i/o; they will be
// reconnected in Serial.begin()
UCSR0B = 0;
sei(); //allow interrupt in general
}
void initPinChange()
{
DDRB &= ~(1 << DDB0); // Clear the PB0 pin (= arduino digital pin 8)
// PB0 (used by PCINT0 pin) is now an input
DDRB |= (1 << DDB1); // Set the PB1 pin (= arduino digital pin 9)
// PB1 is now an output
PORTB |= (1 << PORTB0) ; // turn On the Pull-up
// // PB0 is now an input with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
}
#define MYUBRR 9 // FOSC/16/BAUD-1 = 16000000/16/100000 -1
#define SBUS_STARTBYTE 0x0F
#define SBUS_ENDBYTE 0x00
void USART_Init( unsigned int ubrr){
//Set baud rate
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
// Enable receiver only
UCSR0B = 0x10 ; //no interrupt, Rx enabled, TX disabled, only 8 bits
/* Set frame format: 8data, 2stop bit */
UCSR0C = 0x2E ; // mode normal, even parity , 2 stop bit, 8 bits, no polarity for asynchrone
}
void initTimer0(){ // timer0 is used to check if there are at least 3 msec without received char before starting a frame.
TCCR0A = 0x00; // no use of O0A and B and normal mode of operation (increment of TCNT0)
TCCR0B = 0x05 ; // initialise prescaler to 1024 ; so timer is active and 1 count = 1024/16 usec = 64 usec)
OCR0A = 46 ; // 3000 usec / 64 usec = 46
TCNT0 = 0; // set the timer count to 0
TIFR0 |= _BV(OCF0A) ; // clear any pending interrupts on CompA
TIMSK0 = 0 ; // do not enable interrupt
}
void initTimer1(){
TCCR1A = 0; // no output based on compA/compB
TCCR1B = 0x0 ; // initialise prescaler to 0 ; so timer is not active
// TCCR1B = 0x02 ; // set prescaler of 8 (binary = 0000 0010 (only CS11 = 1)
OCR1A = servoCycle[2] ;
OCR1B = FRAME_DURATION ;
TCNT1 = 0; // clear the timer count
TIFR1 |= _BV(OCF1A) | _BV(OCF1B); // clear any pending interrupts on CompA and compB
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt on CompA
TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt on CompB
}
void startTimer1(){
TCCR1B = 0x02 ; // set prescaler of 8 (binary = 0000 0010 (only CS11 = 1)
OCR1A = servoCycle[2] ;
PORTD = 0x04 ; // set PD2 high to start the first frame
TCNT1 = 0; // clear the timer count
TIFR1 |= _BV(OCF1A) | _BV(OCF1B); // clear any pending interrupts on CompA and CompB;
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt on CompB
}
// ************************* ISR vector ****************************
ISR (PCINT0_vect){ // invert digital pin 9 compare to digital pin 8
if( (PINB & (1 << PINB0)) )
{
// HIGH to LOW pin change
PORTB &= ~(1 << PORTB1) ;
}
else
{
// Low to HIGH pin1 change
PORTB |= (1 << PORTB1) ;
}
}
// this function is called when timer1 = compA
ISR(TIMER1_COMPA_vect) {
// PORTD &= ~(1 << servoIndex) ;
servoIndex++ ;
OCR1A = servoCycle[servoIndex] ;
PIND = servoPin[servoIndex] ;
// PORTD |= (1 << (servoIndex )) ;// toggle pin of servoIndex and servoIndex-1
}
// this function is called when timer1 = compB (at the end of refresh
ISR(TIMER1_COMPB_vect){
TCNT1 = 0;
servoIndex = 2 ;
OCR1A = servoCycle[2] ;
PORTD = 0x04 ;// toggle pin of first servo (on PD2)
}
//************************** Other functions
inline void ledOn() {
PORTB |= (1 << PORTB5) ;
}
inline void ledOff() {
PORTB &= (~(1 << PORTB5)) ;
}
inline void togleLed() {
PORTB ^= (1 << PORTB5) ;
}
void convertBufferToChannelMicrosec() { // put unused channel as comment in order to reduce execution time for main loop.
#if (PPMCHANNEL == 0)
channelsMicrosec[0] = (uint16_t)(((buffer[1] |buffer[2]<<8) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 0) || (PPMCHANNEL == 1)
channelsMicrosec[1] = (uint16_t)(((buffer[2]>>3 |buffer[3]<<5) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 0) || (PPMCHANNEL == 1) || (PPMCHANNEL == 2)
channelsMicrosec[2] = (uint16_t)(((buffer[3]>>6 |buffer[4]<<2 |buffer[5]<<10) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 0) || (PPMCHANNEL == 1) || (PPMCHANNEL == 2) || (PPMCHANNEL == 3)
channelsMicrosec[3] = (uint16_t)(((buffer[5]>>1 |buffer[6]<<7) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 0) || (PPMCHANNEL == 1) || (PPMCHANNEL == 2) || (PPMCHANNEL == 3) || (PPMCHANNEL == 4)
channelsMicrosec[4] = (uint16_t)(((buffer[6]>>4 |buffer[7]<<4) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 0) || (PPMCHANNEL == 1) || (PPMCHANNEL == 2) || (PPMCHANNEL == 3) || (PPMCHANNEL == 4) || (PPMCHANNEL == 5)
channelsMicrosec[5] = (uint16_t)(((buffer[7]>>7 |buffer[8]<<1 |buffer[9]<<9) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 1) || (PPMCHANNEL == 2) || (PPMCHANNEL == 3) || (PPMCHANNEL == 4) || (PPMCHANNEL == 5) || (PPMCHANNEL == 6)
channelsMicrosec[6] = (uint16_t)(((buffer[9]>>2 |buffer[10]<<6) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 2) || (PPMCHANNEL == 3) || (PPMCHANNEL == 4) || (PPMCHANNEL == 5) || (PPMCHANNEL == 6) || (PPMCHANNEL == 7)
channelsMicrosec[7] = (uint16_t)(((buffer[10]>>5|buffer[11]<<3) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 3) || (PPMCHANNEL == 4) || (PPMCHANNEL == 5) || (PPMCHANNEL == 6) || (PPMCHANNEL == 7) || (PPMCHANNEL == 8)
channelsMicrosec[8] = (uint16_t)(((buffer[12] |buffer[13]<<8) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 4) || (PPMCHANNEL == 5) || (PPMCHANNEL == 6) || (PPMCHANNEL == 7) || (PPMCHANNEL == 8) || (PPMCHANNEL == 9)
channelsMicrosec[9] = (uint16_t)(((buffer[13]>>3|buffer[14]<<5) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 5) || (PPMCHANNEL == 6) || (PPMCHANNEL == 7) || (PPMCHANNEL == 8) || (PPMCHANNEL == 9) || (PPMCHANNEL == 10)
channelsMicrosec[10] = (uint16_t)(((buffer[14]>>6|buffer[15]<<2|buffer[16]<<10) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 6) || (PPMCHANNEL == 7) || (PPMCHANNEL == 8) || (PPMCHANNEL == 9) || (PPMCHANNEL == 10) || (PPMCHANNEL == 11)
channelsMicrosec[11] = (uint16_t)(((buffer[16]>>1|buffer[17]<<7) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 7) || (PPMCHANNEL == 8) || (PPMCHANNEL == 9) || (PPMCHANNEL == 10) || (PPMCHANNEL == 11) || (PPMCHANNEL == 12)
channelsMicrosec[12] = (uint16_t)(((buffer[17]>>4|buffer[18]<<4) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 8) || (PPMCHANNEL == 9) || (PPMCHANNEL == 10) || (PPMCHANNEL == 11) || (PPMCHANNEL == 12) || (PPMCHANNEL == 13)
channelsMicrosec[13] = (uint16_t)(((buffer[18]>>7|buffer[19]<<1|buffer[20]<<9) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 9) || (PPMCHANNEL == 10) || (PPMCHANNEL == 11) || (PPMCHANNEL == 12) || (PPMCHANNEL == 13) || (PPMCHANNEL == 14)
channelsMicrosec[14] = (uint16_t)(((buffer[20]>>2|buffer[21]<<6) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
#if (PPMCHANNEL == 10) || (PPMCHANNEL == 11) || (PPMCHANNEL == 12) || (PPMCHANNEL == 13) || (PPMCHANNEL == 14) || (PPMCHANNEL == 15)
channelsMicrosec[15] = (uint16_t)(((buffer[21]>>5|buffer[22]<<3) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
#endif
}
void setup() {
// start = micros ();
BEGIN_DEBUG;
TRACELN ("Commenced device-under-test debugging!");
initPinChange() ;
servoIndex = 2 ;
initTimer1() ;
pinMode(PIN_LED, OUTPUT); // The signal LED is used for testing the pin change (in interrupt)
PORTD = 0x0 ;
DDRD = 0xFC ; // 1111 1100 set the pin 2 to 7 as output
PORTD = 0x0 ;
// startTimer1() ;
USART_Init(MYUBRR) ;
initTimer0() ;
} // end of setup
void loop()
{
if ( (UCSR0A & (1<<RXC0)) ) { // check if a byte is ready to be read
UART_error = UCSR0A & 0x1C ; // save error
c = UDR0; // save char (it clear automatically the RXC0 flag)
timer0 = TIFR0 & (1 << OCF0A ) ; // save the flag that says if there is more than 3 ms since previous char
TCNT0 = 0 ; //reset the timer
TIFR0 |= (1 << OCF0A ) ; // reset the flag in timer 0 saying that it reach OCR0A value
if ( UART_error ) { // if error in UART
bufferIndex = 0 ;
countUART_Error++ ;
countSBUS_Error++ ;
TRACE ("UART: ");
TRACELN2 (UART_error, HEX );
} else {
if ( bufferIndex == 0 ) {
if ( timer0 == 0 ) {
// bufferIndex = 0 ;
countSBUS_TimeToSmallError++ ;
countSBUS_Error++ ;
} else if ( c != SBUS_STARTBYTE ) {
// bufferIndex = 0 ;
countSBUS_StartError++ ;
countSBUS_Error++ ;
} else {
buffer[bufferIndex] = c ;
bufferIndex++ ;
}
} else {
if ( timer0 ) {
bufferIndex = 0 ;
countSBUS_TimeToBigError++ ;
countSBUS_Error++ ;
} else {
buffer[bufferIndex] = c ;
bufferIndex++ ;
if ( bufferIndex == SBUS_MAXCHAR ) {
if ( c == SBUS_ENDBYTE ) {
convertBufferToChannelMicrosec() ;
channelToLoad = true ;
countSBUS_Frame++ ;
} else {
countSBUS_EndError++ ;
countSBUS_Error++ ;
}
bufferIndex = 0 ;
}
}
}
} // end else UART error
} // end if ( (UCSR0A & (1<<RXC0)) )
if( channelToLoad) {
if ( (OCR1A - TCNT1) > 200 ) { // avoid to load new SBUS channel when an interrupt will occurs in order not to delay the interrupt
cli() ;
servoCycle[2] = channelsMicrosec[PPMCHANNEL] ;
sei() ;
cli() ;
servoCycle[4] = channelsMicrosec[PPMCHANNEL + 1] + 5000;
sei() ;
cli() ;
servoCycle[6] = channelsMicrosec[PPMCHANNEL + 2] + 10000;
sei() ;
cli() ;
servoCycle[8] = channelsMicrosec[PPMCHANNEL + 3] + 15000;
sei() ;
cli() ;
servoCycle[10] = channelsMicrosec[PPMCHANNEL + 4] + 20000;
sei() ;
cli() ;
servoCycle[12] = channelsMicrosec[PPMCHANNEL + 5] + 25000;
sei() ;
channelToLoad = false ;
if ( sbusStarted == false ) {
startTimer1() ;
sbusStarted = true ;
counter = 1 ;
}
}
}
if (sbusStarted) { // led is blinking as soon as a Sbus has been received, Led is on for a long time (at least 3 X normal time each time there is an Sbus error)
if (countSBUS_Error > 0) {
counter = 500000 ;
ledOn();
countSBUS_Error = 0 ;
}
counter--;
if (counter == 0) {
counter = 100000 ;
// ledOn();
// digitalWrite(13,HIGH) ;
togleLed() ;
}
}
#ifdef DEBUG
debugCounter++;
if (debugCounter == 1000000)
{
// ledOff() ; // Led pin is used by SPI too. So here we put it to 0 before
TRACE ("SBUS_Error: ");
TRACE (countSBUS_Error);
TRACE (" ch0: ");
TRACE (channelsMicrosec[0]);
TRACE ("Servo1: ");
TRACELN (servoCycle[2]);
// TRACE ("UART: ");
// TRACE (countUART_Error);
// TRACE (" Time_to_small:") ;
// TRACE (countSBUS_TimeToSmallError) ;
// TRACE (" Time_to_big:") ;
// TRACE (countSBUS_TimeToBigError) ;
// TRACE (" Start:") ;
// TRACE (countSBUS_StartError) ;
// TRACE (" End:") ;
// TRACE (countSBUS_EndError) ;
// TRACE (" Frames:") ;
// TRACELN (countSBUS_Frame) ;
debugCounter = 0;
} // end of if
#endif
} // end of loop
unsigned long millis()
{
}
unsigned long micros() {
}
void delay(unsigned long ms)
{
}
/* Delay for the given number of microseconds. Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
}
- MikeB
- 9x Developer
- Posts: 17927
- Joined: Tue Dec 27, 2011 1:24 pm
- Country: -
- Location: Poole, Dorset, UK
Re: Arduino based S-Bus decoder with CPPM and Servo
Couple of things here.
1. Do the two commented lines in:
ISR(TIMER1_COMPA_vect)
like:
// PORTD &= ~(1 << servoIndex) ;
need to be uncommented?
2. You have the sequence:
sei();
cli();
in loop().
This does not enable interrupts as one instruction after sei() is executed before any interrupt is taken and you disable interrupts in that instruction.
Mike.
1. Do the two commented lines in:
ISR(TIMER1_COMPA_vect)
like:
// PORTD &= ~(1 << servoIndex) ;
need to be uncommented?
2. You have the sequence:
sei();
cli();
in loop().
This does not enable interrupts as one instruction after sei() is executed before any interrupt is taken and you disable interrupts in that instruction.
Mike.
erskyTx/er9x developer
The difficult we do immediately,
The impossible takes a little longer!
The difficult we do immediately,
The impossible takes a little longer!
Re: Arduino based S-Bus decoder with CPPM and Servo
Mike,MikeB wrote:Couple of things here.
1. Do the two commented lines in:
ISR(TIMER1_COMPA_vect)
like:
// PORTD &= ~(1 << servoIndex) ;
need to be uncommented?
2. You have the sequence:
sei();
cli();
in loop().
This does not enable interrupts as one instruction after sei() is executed before any interrupt is taken and you disable interrupts in that instruction.
Mike.
Thanks for comments.
About point 1, the 2 lines commented can in fact be totally removed.
In a previous version, I used them but finally I replaced them by the line with PIND. This instruction makes an XOR on one of the pin. So depending on the index it sets or it clears a bit.
I made it in this way in order to run faster (I hoped inverting the SBUS signal with a pin change interrupt but it is not 100% ok) and in order to avoid setting the next pin at the same time as the previous is cleard. Now the pin are set always at a fixed interval. This allows having always 18ms between the rising edge on each pin.
About point 2.
Initially I had only one cli() at the beginning of the block and one sei() at the end of the block.
I added those cli() sei() sequence in between hoping getting less Sbus frame error when the Sbus signal is inverted by the arduino.
Still It did not work. Problably you just found the reason.
I will try adding a dummy instruction (1 cycle) between the cli() and the sei() and see if it solves the sbus frame errors.
I presume I can use a second cli() just after the first one as dummy instruction.
Re: Arduino based S-Bus decoder with CPPM and Servo
Is it not possible to have channels 1 to 8 or 9 to 16? 

My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
Not really with this code.jhsa wrote:Is it not possible to have channels 1 to 8 or 9 to 16?
Currently, it provides only 6 consecutive channels starting from the one you want (e.g. you can get channels 4 to 9 or 9 to 14).
I made it in this way because I planned to use a pcb having the same size as the arduino pro mini.
It would be very easy to add one channel (so having a total of 7 channels) without impacting the performance and having the possibility of using the arduino to invert the Sbus signal.
With more rework, it would be possible to add even more channels. E.g. it would be possible to use the analog pins A0 up to A5 (so 6 channels) and, if required, even 3 others pins in order to get a total of 16 channels but in this case, it will for sure require implementing an hardware inverter on the Sbus signal.
Re: Arduino based S-Bus decoder with CPPM and Servo
Ok, Thank you anyway. But It's not really what I'm looking for at the moment 
João

João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
If you want getting more than 6 channels, you can also connect several modules (arduino) in parallel on the same bus. Each module can provide 6 different channels (or 7 with a minor software change)jhsa wrote:Ok, Thank you anyway.But It's not really what I'm looking for at the moment
João
- MikeB
- 9x Developer
- Posts: 17927
- Joined: Tue Dec 27, 2011 1:24 pm
- Country: -
- Location: Poole, Dorset, UK
Re: Arduino based S-Bus decoder with CPPM and Servo
I've got some code that generates PPM outputs, 4 at a time using just timer1. It sorts the 4 pulses into length order, shortest first. Then it starts them 10 uS apart, using timer1 COMPA interrupt, and ends them in the same way. The end times MUST differ by at least 10uS as well as they are ordered on length. This avoids the interrupts being too close together.
With this, it should be possible to receive the SBUS data using a software serial input, then generate 4 pulses followed by 4 more. Then wait for the SBUS frame again, then if required, generate two more sets of 4 pulses for the other 8 channels. The software serial input should therefore be able to handle the inverted data.
Mike.
With this, it should be possible to receive the SBUS data using a software serial input, then generate 4 pulses followed by 4 more. Then wait for the SBUS frame again, then if required, generate two more sets of 4 pulses for the other 8 channels. The software serial input should therefore be able to handle the inverted data.
Mike.
erskyTx/er9x developer
The difficult we do immediately,
The impossible takes a little longer!
The difficult we do immediately,
The impossible takes a little longer!
Re: Arduino based S-Bus decoder with CPPM and Servo
Thanks Mike.MikeB wrote:I've got some code that generates PPM outputs, 4 at a time using just timer1. It sorts the 4 pulses into length order, shortest first. Then it starts them 10 uS apart, using timer1 COMPA interrupt, and ends them in the same way. The end times MUST differ by at least 10uS as well as they are ordered on length. This avoids the interrupts being too close together.
With this, it should be possible to receive the SBUS data using a software serial input, then generate 4 pulses followed by 4 more. Then wait for the SBUS frame again, then if required, generate two more sets of 4 pulses for the other 8 channels. The software serial input should therefore be able to handle the inverted data.
Mike.
In fact I have no need for more than 6 channels (on top of the 3 or 4 already deliverd by the X4R).
I plan to make a board having the same size as the arduino pro mini. This allows just to put 6 (or max 7) servo connector pin headers.
This board is also useful in order to add one resistor for each channel in order to better protect the arduino pin (to the servo).
I think it will be easy to put the inverter (one transistor + 2 resistors) on this PCB too. This will also be safier in order to protect the arduino pin Rx.
Re: Arduino based S-Bus decoder with CPPM and Servo
FYI,
I am currently working on a version that should deliver the 16 channels.
This version will require that the Sbus signal is inverted (e.g. using 1 transistor and 2 resistors).
I hope it will be OK this PM
I am currently working on a version that should deliver the 16 channels.
This version will require that the Sbus signal is inverted (e.g. using 1 transistor and 2 resistors).
I hope it will be OK this PM
Re: Arduino based S-Bus decoder with CPPM and Servo
Here a version that read the SBUS and generates 16 PPM pulse on 16 Arduino pins from an arduino pro mini.
Read more explanations at the beginning of the code
Read more explanations at the beginning of the code
Code: Select all
// This poject uses Arduino pro mini 5 volt 16 mhz in order to decode Futaba SBUS.
// This version decodes the 16 channels available on SBUS and generates only 16 PPM signals.
// The 16 PPM signals are available on arduino pins 2 up to 9, A0 up to A5 , Tx and 12 (in this sequence)
// The PPM signals are generates once per 20 msec. This refresh rate is supported by all servos.
//
// The SBUS signal has to be inverted before connecting it to arduino pin Rx.
// So it requires to invert the SBUS signal from the Receiver with a transistor and 2 resistors (see the web for a schema or look at a commecial product at e.g. Hobbyking).
//
// As long as Arduino did not get a valid Sbus frame after power on, it wil not generate PPM signals.
// As soon as a valid Sbus frame is received, the 16 PPM signals wil be generated.
// Unvalid sbus frames are discarded and arduino generates PPM signals based on last correct frame received.
// Please note that if no new (valid) frames are received, the latest valid frame will continue to be used to generate the PPM signals (so servos will keep their last position).
//
// Arduino has a green led. This led will work as follow:
// If arduino never got a valid Sbus (e.g. Tx not powered on, no bind with Rx), led is off.
// Once a valid Sbus frame is received, the led will start blinking.
// When led blink fast (about 1 per sec) , it means that there are no Sbus frame error (normal case).
// If Arduino get at one invalid frame, the led wil be on for about 3 sec.
// If during the 3 sec, there are no other Sbus frame error, it will continue with fast binking rate.
// If there is at least one new frame error within the 3 sec, the led will be on for the next 3 seconds
// So, if there are many frame errors, it could be that the led is always on.
//
// Debugging :
// It is not possible to debug this project with serial.print command because the hardware serial is already used to read the Sbus (and a sofware serial would be to slow).
// Still, it is possible to debug this project using a second arduino connected to this one via the SPI interface.
// The second arduino has then to be loaded with a "slave" program.
// More informations at this link : http://www.utopiamechanicus.com/article/spi-debug/
//
// Note about loading the Arduino pro mini with this firmware.
// Programming the Arduino pro mini is normally done using a FTDI cable (or board).
// This FTDI connects on Pc side with the USB. On arduino side, it uses the RX and TX pins.
// In this project Arduino Rx pin is normally connected to the (inverted Sbus signal).
// Please take care not to connect the arduino to the Rx (Sbus port) while programming it (otherwise programming will fail).
// this line selects the first channels being generated; this version will then generate this channel and the next 5 channels;
#include <avr/io.h>
#include <avr/interrupt.h> // Needed to use interrupts
#include <SPI.h>
#include "pins_arduino.h"
// conditional debugging
// uncomment to debug using SPI interface
//#define DEBUG
#ifdef DEBUG
#define BEGIN_DEBUG do { SPI.begin (); SPI.setClockDivider(SPI_CLOCK_DIV8); } while (0)
#define TRACE(x) SPIdebug.print (x)
#define TRACE2(x,y) SPIdebug.print (x,y)
#define TRACELN(x) SPIdebug.println (x)
#define TRACELN2(x,y) SPIdebug.println (x,y)
class tSPIdebug : public Print
{
public:
virtual size_t write (const byte c) {
digitalWrite(SS, LOW);
SPI.transfer (c);
digitalWrite(SS, HIGH);
} // end of tSPIdebug::write
}; // end of tSPIdebug
// an instance of the SPIdebug object
tSPIdebug SPIdebug;
#endif // DEBUG
#define PIN_LED 13 // The Signal LED (default=13=onboard LED)
//#define FRAME_DURATION 36000 // 36000 = 18000 usec * 2 because frequency is 16Mz and prescaler is 8
//#define ZEROCYCLE 3000 // 1500 usec * 2
#define TIMER1_ZERO 25536 // in order to get an overflow every 20 ms (delay between each frame), timer 1 start at 25536 = 65536 - 40000
#define SBUS_MAXCHAR 25
#define MAX_SBUSCHANNELS 16
// Measured values with Futaba FX-30/R6108SB:
// -+100% on TX: PCM 1.100/1.520/1.950ms -> SBus raw values: 350/1024/1700 (100% ATV)
// -+140% on TX: PCM 0.930/1.520/2.112ms -> SBus raw values: 78/1024/1964 (140% ATV)
// -+152% on TX: PCM 0.884/1.520/2.160ms -> SBus raw values: 1/1024/2047 (140% ATV plus dirty tricks)
// define range mapping here, -+100% -> 1000..2000
#define SBUS_RANGE_MIN 200.0f
#define SBUS_RANGE_MAX 1800.0f
#define SBUS_TARGET_MIN 2000.0f // 2000 cycles = 1000 usec (because prescaler = 1/8 and Mhz = 16)
#define SBUS_TARGET_MAX 4000.0f // 4000 cycles = 2000 usec (because prescaler = 1/8 and Mhz = 16)
// pre-calculate the floating point stuff as far as possible at compile time
#define SBUS_SCALE_FACTOR ((SBUS_TARGET_MAX - SBUS_TARGET_MIN) / (SBUS_RANGE_MAX - SBUS_RANGE_MIN))
#define SBUS_SCALE_OFFSET (int)(SBUS_TARGET_MIN - (SBUS_SCALE_FACTOR * SBUS_RANGE_MIN + 0.5f))
uint32_t counter = 1;
uint32_t debugCounter ;
static boolean sbusStarted = false;
static boolean channelToLoad_1 = false;
static boolean channelToLoad_2 = false;
static uint8_t servoIndex1 ;
static uint8_t servoIndex2 ;
// sequence for frame 1 (arduino pin=PORT) 2=D2 3=D3 4=D4 5=D5 6=D6 7=D7 8=B0 9=B1
static uint8_t servoPin1B[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x01, 0x02 , 0x02 } ; // each value will toggle a pin
static uint8_t servoPin1C[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ; // each value will toggle a pin
static uint8_t servoPin1D[] = { 0x04 , 0x04 , 0x08 , 0x08 , 0x10 , 0x10 , 0x20 , 0x20 , 0x40 , 0x40 , 0x80 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 } ; // each value will toggle a pin
volatile static uint16_t servoCycle1[] = { 25700 , 28700 , 30200 , 33200 , 34700 , 37700 , 39200 , 42200 , 43700 , 46700 , 48200 , 51200 , 52700 , 55700 , 57200 , 60200 , 25700 } ;
// sequence for frame 2 (arduino pin=PORT) A0=C0 A1=C1 A2=C2 A3=C3 A4=C4 A5=C5 Tx=D1 12=B4
static uint8_t servoPin2B[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x10 , 0x10 } ; // each value will toggle a pin
static uint8_t servoPin2C[] = { 0x01 , 0x01 , 0x02 , 0x02 , 0x04 , 0x04 , 0x08 , 0x08 , 0x10 , 0x10 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0x00 } ; // each value will toggle a pin
static uint8_t servoPin2D[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 , 0x02 , 0x00 , 0x00 } ; // each value will toggle a pin
volatile static uint16_t servoCycle2[] = { 25750 , 28750 , 30250 , 33250 , 34750 , 37750 , 39250 , 42250 , 43750 , 46750 , 48250 , 51250 , 52750 , 55750 , 57250 , 60250 , 25750 } ; // !!!! the last value must be higher than FRAME_DURATION in order to let CompB generates an interrupt
static uint8_t UART_error ;
static uint8_t timer0 ;
static uint8_t bufferIndex ;
static uint8_t buffer[ SBUS_MAXCHAR ] ;
// static uint16_t channels[ MAX_SBUSCHANNELS ] ;
static uint16_t channelsMicrosec[ MAX_SBUSCHANNELS ] ;
unsigned char readOneChar ;
unsigned char c ;
uint16_t countUART_Error ;
uint16_t countSBUS_TimeToBigError ;
uint16_t countSBUS_TimeToSmallError ;
uint16_t countSBUS_StartError ;
uint16_t countSBUS_EndError ;
uint16_t countSBUS_Frame ;
uint16_t countSBUS_Error ;
uint8_t led ;
//************************************ Init*************************************
void init() // this function replaces the standard init Arduino function
{
// Timer1
TIMSK1 &= ~( 1<< OCIE1A ) ; // Disable interupt on timer 1 for compA
TIMSK1 &= ~( 1<< OCIE1B ) ; // Disable interupt on timer 1 for compB
TCCR1A = 0x00 ; //Init.
TCCR1B = 0xC1 ; // I/p noise cancel, rising edge, Clock/1
// the bootloader connects pins 0 and 1 to the USART; disconnect them
// here so they can be used as normal digital i/o; they will be
// reconnected in Serial.begin()
UCSR0B = 0;
sei(); //allow interrupt in general
}
#define MYUBRR 9 // FOSC/16/BAUD-1 = 16000000/16/100000 -1
#define SBUS_STARTBYTE 0x0F
#define SBUS_ENDBYTE 0x00
void USART_Init( unsigned int ubrr){
//Set baud rate
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
// Enable receiver only
UCSR0B = 0x10 ; //no interrupt, Rx enabled, TX disabled, only 8 bits
/* Set frame format: 8data, 2stop bit */
UCSR0C = 0x2E ; // mode normal, even parity , 2 stop bit, 8 bits, no polarity for asynchrone
}
void initTimer0(){ // timer0 is used to check if there are at least 3 msec without received char before starting a frame.
TCCR0A = 0x00; // no use of O0A and B and normal mode of operation (increment of TCNT0)
TCCR0B = 0x05 ; // initialise prescaler to 1024 ; so timer is active and 1 count = 1024/16 usec = 64 usec)
OCR0A = 46 ; // 3000 usec / 64 usec = 46
TCNT0 = 0; // set the timer count to 0
TIFR0 |= _BV(OCF0A) ; // clear any pending interrupts on CompA
TIMSK0 = 0 ; // do not enable interrupt
}
void initTimer1(){
TCCR1A = 0; // no output based on compA/compB
TCCR1B = 0x0 ; // initialise prescaler to 0 ; so timer is not active
// TCCR1B = 0x02 ; // set prescaler of 8 (binary = 0000 0010 (only CS11 = 1)
OCR1A = servoCycle1[0] ;
OCR1B = servoCycle2[0] ;
TCNT1 = TIMER1_ZERO; // clear the timer count
TIFR1 |= _BV(OCF1A) | _BV(OCF1B) | _BV( TOV1); // clear any pending interrupts on CompA and compB and overflow
// TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt on CompA
// TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt on CompB
// TIMSK1 |= _BV( TOIE1); // enable the overflow interrupt on timer1
}
void startTimer1(){
TCCR1B = 0x02 ; // set prescaler of 8 (binary = 0000 0010 (only CS11 = 1)
servoIndex1 = 0 ;
servoIndex2 = 0 ;
OCR1A = servoCycle1[0] ; // initialize compA with first value (to set the first pin)
OCR1B = servoCycle2[0] ; // initialize compB with first value (to set the first pin)
PORTB &= 0b11101100 ;// set PB0, PB1, PB4 to 0
PORTC &= 0b11100000 ;// set PC0 to PC5 to 0
PORTD &= 0b00000001 ;// set PD1 to PD7 to 0
TCNT1 = TIMER1_ZERO; // set the timer count in order to get 20ms before overflow
TIFR1 |= _BV(OCF1A) | _BV(OCF1B) | _BV( TOV1) ; // clear any pending interrupts on CompA and CompB;
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt on CompB
TIMSK1 |= _BV( TOIE1); // enable the overflow interrupt on timer1
}
// ************************* ISR vector ****************************
// this function is called when timer1 = compA and is used to generate the frame 1
ISR(TIMER1_COMPA_vect) {
PINB = servoPin1B[servoIndex1] ;
PINC = servoPin1C[servoIndex1] ;
PIND = servoPin1D[servoIndex1] ;
servoIndex1++ ;
OCR1A = servoCycle1[servoIndex1] ;
}
// this function is called when timer1 = compB and is used to generate the frame 2
ISR(TIMER1_COMPB_vect) {
PINB = servoPin2B[servoIndex2] ;
PINC = servoPin2C[servoIndex2] ;
PIND = servoPin2D[servoIndex2] ;
servoIndex2++ ;
OCR1B = servoCycle2[servoIndex2] ;
}
// this function is called when timer1 overflow (at the end of the 20 msec)
ISR(TIMER1_OVF_vect){
TCNT1 = TIMER1_ZERO; // set the timer count in order to get 20ms before overflow
servoIndex1 = 0 ; // reset the index
servoIndex2 = 0 ;
PORTB &= 0b11101100 ;// set PB0, PB1, PB4 to 0
PORTC &= 0b11100000 ;// set PC0 to PC5 to 0
PORTD &= 0b00000001 ;// set PD1 to PD7 to 0
}
//************************** Other functions
inline void ledOn() {
PORTB |= (1 << PORTB5) ;
}
inline void ledOff() {
PORTB &= (~(1 << PORTB5)) ;
}
inline void togleLed() {
PORTB ^= (1 << PORTB5) ;
}
void convertBufferToChannelMicrosec() { // put unused channel as comment in order to reduce execution time for main loop.
channelsMicrosec[0] = (uint16_t)(((buffer[1] |buffer[2]<<8) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[1] = (uint16_t)(((buffer[2]>>3 |buffer[3]<<5) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[2] = (uint16_t)(((buffer[3]>>6 |buffer[4]<<2 |buffer[5]<<10) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[3] = (uint16_t)(((buffer[5]>>1 |buffer[6]<<7) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[4] = (uint16_t)(((buffer[6]>>4 |buffer[7]<<4) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[5] = (uint16_t)(((buffer[7]>>7 |buffer[8]<<1 |buffer[9]<<9) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[6] = (uint16_t)(((buffer[9]>>2 |buffer[10]<<6) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[7] = (uint16_t)(((buffer[10]>>5|buffer[11]<<3) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[8] = (uint16_t)(((buffer[12] |buffer[13]<<8) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[9] = (uint16_t)(((buffer[13]>>3|buffer[14]<<5) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[10] = (uint16_t)(((buffer[14]>>6|buffer[15]<<2|buffer[16]<<10) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[11] = (uint16_t)(((buffer[16]>>1|buffer[17]<<7) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[12] = (uint16_t)(((buffer[17]>>4|buffer[18]<<4) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[13] = (uint16_t)(((buffer[18]>>7|buffer[19]<<1|buffer[20]<<9) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[14] = (uint16_t)(((buffer[20]>>2|buffer[21]<<6) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
channelsMicrosec[15] = (uint16_t)(((buffer[21]>>5|buffer[22]<<3) & 0x07FF) * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
}
void setup() {
// start = micros ();
#ifdef DEBUG
BEGIN_DEBUG;
TRACELN ("Commenced device-under-test debugging!");
#endif
servoIndex1 = 0 ;
servoIndex2 = 0 ;
initTimer1() ;
pinMode(PIN_LED, OUTPUT); // The signal LED is used for testing the pin change (in interrupt)
// startTimer1() ;
USART_Init(MYUBRR) ;
DDRB |= 0b00010011 ;// set PB0, PB1, PB4 as output
DDRC |= 0b00011111 ;// set PC0 to PC5 as output
DDRD |= 0b11111110 ;// set PD1 to PD7 to 0
PORTB &= 0b11101100 ;// set PB0, PB1, PB4 to 0
PORTC &= 0b11100000 ;// set PC0 to PC5 to 0
PORTD &= 0b00000001 ;// set PD1 to PD7 to 0
initTimer0() ;
} // end of setup
void loop()
{
if ( (UCSR0A & (1<<RXC0)) ) { // check if a byte is ready to be read
UART_error = UCSR0A & 0x1C ; // save error
c = UDR0; // save char (it clear automatically the RXC0 flag)
timer0 = TIFR0 & (1 << OCF0A ) ; // save the flag that says if there is more than 3 ms since previous char
TCNT0 = 0 ; //reset the timer
TIFR0 |= (1 << OCF0A ) ; // reset the flag in timer 0 saying that it reach OCR0A value
if ( UART_error ) { // if error in UART
bufferIndex = 0 ;
countUART_Error++ ;
countSBUS_Error++ ;
#ifdef DEBUG
TRACE ("UART: ");
TRACELN2 (UART_error, HEX );
#endif
} else {
if ( bufferIndex == 0 ) {
if ( timer0 == 0 ) {
// bufferIndex = 0 ;
countSBUS_TimeToSmallError++ ;
countSBUS_Error++ ;
} else if ( c != SBUS_STARTBYTE ) {
// bufferIndex = 0 ;
countSBUS_StartError++ ;
countSBUS_Error++ ;
} else {
buffer[bufferIndex] = c ;
bufferIndex++ ;
}
} else {
if ( timer0 ) {
bufferIndex = 0 ;
countSBUS_TimeToBigError++ ;
countSBUS_Error++ ;
} else {
buffer[bufferIndex] = c ;
bufferIndex++ ;
if ( bufferIndex == SBUS_MAXCHAR ) {
if ( c == SBUS_ENDBYTE ) {
convertBufferToChannelMicrosec() ;
channelToLoad_1 = true ;
channelToLoad_2 = true ;
countSBUS_Frame++ ;
} else {
countSBUS_EndError++ ;
countSBUS_Error++ ;
}
bufferIndex = 0 ;
}
}
}
} // end else UART error
} // end if ( (UCSR0A & (1<<RXC0)) )
if( channelToLoad_1) {
if ( (OCR1A > TCNT1) && ((OCR1A - TCNT1) > 200 )) { // avoid to load new SBUS channel when an interrupt of compA will occurs in order not to delay the interrupt
cli() ;
servoCycle1[1] = channelsMicrosec[0] + servoCycle1[0];
servoCycle1[3] = channelsMicrosec[1] + servoCycle1[2];
servoCycle1[5] = channelsMicrosec[2] + servoCycle1[4];
servoCycle1[7] = channelsMicrosec[3] + servoCycle1[6];
servoCycle1[9] = channelsMicrosec[4] + servoCycle1[8];
servoCycle1[11] = channelsMicrosec[5] + servoCycle1[10];
servoCycle1[13] = channelsMicrosec[6] + servoCycle1[12];
servoCycle1[15] = channelsMicrosec[7] + servoCycle1[14];
sei() ;
channelToLoad_1 = false ;
if ( sbusStarted == false ) {
startTimer1() ;
sbusStarted = true ;
counter = 1 ;
}
}
}
if( channelToLoad_2) {
if ( (OCR1B > TCNT1) && ((OCR1B - TCNT1) > 200 )) { // avoid to load new SBUS channel when an interrupt of compB will occurs in order not to delay the interrupt
cli() ;
servoCycle2[1] = channelsMicrosec[8] + servoCycle2[0];
servoCycle2[3] = channelsMicrosec[9] + servoCycle2[2];
servoCycle2[5] = channelsMicrosec[10] + servoCycle2[4];
servoCycle2[7] = channelsMicrosec[11] + servoCycle2[6];
servoCycle2[9] = channelsMicrosec[12] + servoCycle2[8];
servoCycle2[11] = channelsMicrosec[13] + servoCycle2[10];
servoCycle2[13] = channelsMicrosec[14] + servoCycle2[12];
servoCycle2[15] = channelsMicrosec[15] + servoCycle2[14];
sei() ;
channelToLoad_2 = false ;
if ( sbusStarted == false ) {
startTimer1() ;
sbusStarted = true ;
counter = 1 ;
}
}
}
if (sbusStarted) { // led is blinking as soon as a Sbus has been received, Led is on for a long time (at least 3 X normal time each time there is an Sbus error)
if (countSBUS_Error > 0) {
counter = 500000 ;
ledOn();
countSBUS_Error = 0 ;
}
counter--;
if (counter == 0) {
counter = 100000 ;
togleLed() ;
}
}
#ifdef DEBUG
debugCounter++;
if (debugCounter == 1000000)
{
// ledOff() ; // Led pin is used by SPI too. So here we put it to 0 before
TRACE ("SBUS_Error: ");
TRACE (countSBUS_Error);
TRACE (" ch0: ");
TRACE (channelsMicrosec[0]);
TRACE ("Servo1: ");
TRACELN (servoCycle1[2]);
// TRACE ("UART: ");
// TRACE (countUART_Error);
// TRACE (" Time_to_small:") ;
// TRACE (countSBUS_TimeToSmallError) ;
// TRACE (" Time_to_big:") ;
// TRACE (countSBUS_TimeToBigError) ;
// TRACE (" Start:") ;
// TRACE (countSBUS_StartError) ;
// TRACE (" End:") ;
// TRACE (countSBUS_EndError) ;
// TRACE (" Frames:") ;
// TRACELN (countSBUS_Frame) ;
debugCounter = 0;
} // end of if
#endif
} // end of loop
unsigned long millis()
{
}
unsigned long micros() {
}
void delay(unsigned long ms)
{
}
/* Delay for the given number of microseconds. Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
}
Re: Arduino based S-Bus decoder with CPPM and Servo
Thanks..will see if it works with eepskye.. 
João

João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
Allow me to share my opinion on a subject that might lead to some confusion.mstrens wrote: // This poject uses Arduino pro mini 5 volt 16 mhz in order to decode Futaba SBUS.
// This version decodes the 16 channels available on SBUS and generates only 16 PPM signals.
// The 16 PPM signals are available on arduino pins 2 up to 9, A0 up to A5 , Tx and 12 (in this sequence)
// The PPM signals are generates once per 20 msec. This refresh rate is supported by all servos.
//
Your code is generating 16 PWM channels, not PPM?

João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
Re: Arduino based S-Bus decoder with CPPM and Servo
Hmm it isn't working for me.. But maybe because eepskye is sending a signal which might be a bit different. well, not eepskye directly but the other arduino code..
Thanks anyway
João
Thanks anyway
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
- MikeB
- 9x Developer
- Posts: 17927
- Joined: Tue Dec 27, 2011 1:24 pm
- Country: -
- Location: Poole, Dorset, UK
Re: Arduino based S-Bus decoder with CPPM and Servo
Ok João, try this one:
The plan is to auto detect the incoming baudrate, and possibly whether it is inverted or not.
I will also extend it to more outputs, probably 16.
Mike.
I'm using a software serial, non-inverted input at 57600 baud, compatible with eepskye output. The serial input is on the Rx pin. Just got it working, so not much testing yet.The plan is to auto detect the incoming baudrate, and possibly whether it is inverted or not.
I will also extend it to more outputs, probably 16.
Mike.
erskyTx/er9x developer
The difficult we do immediately,
The impossible takes a little longer!
The difficult we do immediately,
The impossible takes a little longer!
Re: Arduino based S-Bus decoder with CPPM and Servo
I tested it using the Sbus output from a X8R.jhsa wrote:Hmm it isn't working for me.. But maybe because eepskye is sending a signal which might be a bit different. well, not eepskye directly but the other arduino code..
Thanks anyway
João
In my case it worked.
Still as explained, when using a "normal" sbus it requires to use an harware inverter (using e.g. 1 NPN transistor and 2 resistors. I presume that you know how to build such an inverter.
Re: Arduino based S-Bus decoder with CPPM and Servo
Yes Mstrens, thank you.. I did build one.. But as Mike said above the signal from eepskye is a bit different.
it's good to give the people with the X receivers the option to have 16 channels though. thank you.
Mike, on your other arduino code, aren't you sending a normal sbus inverted signal at 100000 baud on pin 11? I had the idea that you did, therefore I thought it would work with this code
will try your code now, thanks
João
it's good to give the people with the X receivers the option to have 16 channels though. thank you.
Mike, on your other arduino code, aren't you sending a normal sbus inverted signal at 100000 baud on pin 11? I had the idea that you did, therefore I thought it would work with this code

will try your code now, thanks
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9
Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW