My goal is pretty simple: I wanna read information from my RadioLink R12DS receiver by S-BUS protocol using desktop console application, written on C#.
I use AT9S transmitter blinded together with receiver in 12 channel mode. I tested it on Pixhawk flight controller. Everything was fine there, no any problem with retrieving data.
I designed a console application based on investigated articles. Here is a couple most valuable of them:
http://forum.fpv.kz/topic/303-frsky-x8r-sbus-v-cppm-konverter-na-arduino/ https://github.com/bolderflight/SBUS
My application receiving byte stream from a COM Port, one by one, and tries catch message header "0x0F", but it doesn't appear.
The SBUS protocol uses inverted serial logic with a baud rate of 100000, 8 data bits, even parity bit, and 2 stop bits. The SBUS packet is 25 bytes long consisting of:
Byte[0]: SBUS Header, 0x0F
Byte[1-22]: 16 servo channels, 11 bits per servo channel
Byte[23]:
Bit 7: digital channel 17 (0x80)
Bit 6: digital channel 18 (0x40)
Bit 5: frame lost (0x20)
Bit 4: failsafe activated (0x10)
Bit 0 - 3: n/a
Byte[24]: SBUS End Byte, 0x00
A table mapping bytes[1-22] to servo channels is included.
Here is listing of my code:
static void Main(String[] args)
{
var availablePorts = SerialPort.GetPortNames();
using(var port = new SerialPort(availablePorts[0], 100000, Parity.Even, 8, StopBits.Two))
{
port.DataReceived += PortOnDataReceived;
while(true)
{
if(!port.IsOpen)
TryReconnect(port);
Thread.Sleep(1000);
}
}
}
// HANDLERS ///////////////////////////////////////////////////////////////////////////////
private static void PortOnDataReceived(Object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
{
var serialPort = (SerialPort)sender;
if(SbusConverter.TryReadMessage(serialPort, out var messageBuffer))
{
var message = SbusConverter.Convert(messageBuffer);
Console.WriteLine(message.ServoChannels[0]);
}
}
public static Boolean TryReadMessage(SerialPort serialPort, out Byte[] messageBuffer)
{
const Int32 messageLength = 25;
const Int32 endOfStream = -1;
const Byte sBusMessageHeader = 0x0f;
const Byte sBusMessageEndByte = 0x00;
messageBuffer = new Byte[messageLength];
if(serialPort.BytesToRead < messageLength)
return false;
do
{
var value = serialPort.ReadByte();
if(value == endOfStream)
return false;
if(value == sBusMessageHeader)
{
messageBuffer[0] = (Byte)value;
for(var i = 1; i < messageLength; i++)
{
messageBuffer[i] = (Byte)serialPort.ReadByte();
}
if(messageBuffer[0] == sBusMessageHeader &&
messageBuffer[24] == sBusMessageEndByte)
return true;
}
} while(serialPort.BytesToRead > 0);
return false;
}
I have thoughts in my head and I want ask one question here.
It's possible, that RadioLink use different, modified or their own S-BUS implementation, than Futaba and I found no proper documentation yet.
Anybody, who experienced in that field, any suggestions please. It seems, I am stuck.
Thank you!
I made some investigations of received data stream and uncovered that RadioLink devices uses "0x1F" as a frame start byte insted of "0x0F". Another connection and message properties are the same.
var availablePorts = SerialPort.GetPortNames();
using(var port = new SerialPort(availablePorts[0], 100000, Parity.None, 8, StopBits.One)
{
Handshake = Handshake.None
})
{
port.DataReceived += PortOnDataReceived;
while(true)
{
if(!port.IsOpen)
OpenPort(port);
Thread.Sleep(1000);
}
}
Two years too late, but I just noticed you have 1 stop bit set, instead of 2, in the code of your answer. That could probably explain 0x1F instead 0x0F and otherwise shifted data.
I am trying to do the same kind of operation, intercepting Sbus signal from a herelink radio controller, so far I discovered a shift in the data and my start byte is recognised with the value of 0x1E. Moreover the data seems super noisy when looked at the oscilloscope, some 1 might be missing because of the poor quality of the signal and the ramp from 0 to 1.
Related
I seem to have issue to receive correct bytes from a PC to a PIC18F27J53.
The PIC UART is set standard, asynchronous, 8bits, 9600, No parity.
The PC is a win 10, I have made a simple UART program, and sending a few ints, all separated by commas, like the following.
"123,24,65,98,12,45,564,987,321,0,5.9,87,65,789,123,6554,213,8754\n"
I have tried different ways,
Tried to send each char one by one, however the PIC seems to get stuck midway or early in the transfer and the RX flag doesn't go high anymore.
I have tried to send each int followed by "\n" and my PIC to parse each chars and cut the read after a "\n" is found. This seems better, I can get more data in, but the final received data is corrupted: some ints are wrong etc.
It clearly show this is a sync issue, it looks like the PC is too fast for the PIC?
If so, I am looking at having a synchronous uart, however according to the web, this seems to be far from the chosen method, which makes me thing I must have another issue to resolve, in asynchronous mode?
My question, what is the most popular robust way to do that PIC to PC UART full duplex communication?
Here are my PIC receive APIs, fairly standard and simple (I think).
void int_receive_data(void)
{
char input_element[10] = { 0 };
char full_rx[128] = { 0 };
for (int i = 0; i < 22; i++) {
p18f47j53_uart2_read_text(input_element, sizeof(input_element));
strncat(full_rx, input_element, strlen(input_element));
strncat(full_rx, ",", 1);
}
}
void p18f47j53_uart2_read_text(char *output, uint8_t max_length)
{
uint8_t c;
char buffer[64] = { 0 };
for (uint8_t i = 0; i < max_length; i++) {
c = p18f47j53_uart2_receive_u8();
buffer[i] = c;
if ((c == 10) || (c == '\n')) {
buffer[i] = 0;
memcpy(output, buffer, i);
i = max_length;
}
}
}
uint8_t p18f47j53_uart2_receive_u8(void)
{
// wait for the flag
while (!PIR3bits.RC2IF);
// reset receiver if over run error
if (RCSTA2bits.OERR) {
RCSTA2bits.CREN = 0;
RCSTA2bits.CREN = 1;
return PIC_RC_FAIL;
}
// reset if frame error
if (RCSTA2bits.FERR) {
RCSTA2bits.SPEN = 0;
RCSTA2bits.SPEN = 1;
return PIC_RC_FAIL;
}
return RCREG2;
}
On the PC C# side, my sending looks like this
string[] full_separated = full_tx.Split(',');
foreach (string s in full_separated)
my_port.WriteLine(s);
The PIC is running from its internal clock 8MHz.
I never tried the synchronous way as it seems more complicated and 99 percent of the web result will show asynchronous way, which makes me think I better debug what I am doing.
Any idea? advice? Thanks
Well not really a solution, but an alternative. You should break the frame in small chunks. And if possible the receiver to ack with a char to notify the transmitter to go ahead with another chunk.
Reason I am saying that, I have a mikroE dev board with a similar PIC, and while running an "out of the box" example, and sending
"111,222,333,444,555,666,777,888,999"
It looks like the "999" is creating issues, too much byte, maybe buffer issue, maybe the not perfect baud rate mismatch builds up after a few bytes?
Repeat the sending every 50ms, 500ms or 1000ms doesn't make it better.
Changing the baud rate neither.
Only removing ",999" and it seems to work all right.
Without the ",999" I am guessing it is still on the "edge of working", so maybe just remove "666,777,888,999" and the communication should feel more comfortable.
More code, more traffic, but at least it works..
I created a wpf application to control and monitor a sensor invented at my university. I connect to the device with 2 comports of which one is exclusively for sending data from the device. The device encodes each dataset to 5 bytes and starts sending them as soon as I give it the order via the other comport. Internally the sensor has a buffer of ~40 kilobytes.
Problem:
It works very will until a sampling rate above 25kHz (25000 times 5 bytes each second) is chosen. Then data seems to be lost and the device sends an error, that the internal buffer ran full (which explains the lost data)
I tried several approaches without success yet, the latest was trying to apply this solution: https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
I fail to understand what determines how often I receive an event that grabs the data from the serialport. The problem doesn't change whether my program is fast or slow .. always ~25kHz = ~200kBytes/s
Code:
private SerialPort _comPortData;
_comPortData.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);
The Connect() function, stripped-down:
void connect()
{
comPort.BaudRate = 115200;
comPort.DataBits = 8;
comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
comPort.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
comPort.PortName = port;
comPort.Open();
Console.WriteLine(comPort.PortName + " opened | Baud Rate: " + comPort.BaudRate);
}
The (edited) PortDataReceived function:
private void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!_comPortData.IsOpen) return;
// Obtain the number of bytes waiting in the port's buffer
int numberOfBytes = _comPortData.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[numberOfBytes];
// Read the data from the port asynchronously and store it in the buffer
await _comPortData.BaseStream.ReadAsync(buffer, 0, numberOfBytes);
}
I'm trying to write a program in C# to communicate with my Arduino UNO from my computer via a serial connection. At the moment I'm just writing a simple application that establishes contact with the Arduino, and then with several controls to control each pin on the arduino; either read a value from it or write a value to it.
I've managed to establish contact with the Arduino and to set pin-values, but it doesn't always want to obey my commands. I set up a few check boxes, and when I check a box, an LED should turn on, and off when I un-check it. The problem is that sometimes the LED's just stay on or off and I have to click the box a few times before it responds again, or reset my circuit...
I was trying to do some fault-finding, but couldn't get to the root of the problem: is it my app or is it the Arduino code?
Here are the relevant pieces of my code:
private void sendValue(int RW, int _Pin, int Val) //send from my app to serial port
{
if (CommPort.IsOpen)
{
CommPort.WriteLine(RW.ToString() + "," + _Pin.ToString() + "," + Val.ToString());
}
}
private void chP9_CheckedChanged(object sender, EventArgs e) //call the sendValue routine
{
if (chP9.Checked)
{
sendValue(1, 9, 255); //('Write', to pin 9, 'On')
}
else
{
sendValue(1, 9, 0); //('Write', to pin 9, 'Off')
}
}
This is my C# code, it compiles a comma-delimited string to send over the serial port to be read by the Arduino.
Here's the Arduino code:
int RW; //0 to read pin, 1 to write to pin
int PN; //Pin number to read or write
int Val; //Value to write to pin
void setup() {
Serial.begin(38400);
}
void loop() {
ReadIncoming();
ProcessIncoming();
}
void ReadIncoming()
{
if(Serial.available() > 0)
{
RW = Serial.parseInt();
PN = Serial.parseInt();
Val = Serial.parseInt();
}
while(Serial.available() > 0) //Clear the buffer if any data remains after reading
{
Serial.read();
}
}
void ProcessIncoming()
{
if(RW == 0)
{
pinMode(PN, INPUT);
}
else
{
pinMode(PN, OUTPUT);
analogWrite(PN, Val);
}
}
parseInt just takes out the first integer value it finds, stores it and throws away the comma, and does it again and again, but it seems a bit counter-intuitive.
I think my problem lies here:
while(Serial.available() > 0) //Clear the buffer if any data remains after reading
{
Serial.read();
}
I think the App is sending data faster than the Arduino code could handle, especially with this loop, but what do I do with excess data?
I don't like to use the parseInt, but it's the only way I could find to read my instructions correctly. How do I send a byte array from C# and read that array into an array in Arduino?
I've pointed out my hypotheses, and explored alternatives but couldn't get any solutions. What suggestions do you guys have for me?
It is not that clear to me why it works at all. You ought to consider a smarter way to encode the command. You need only three bytes:
private void sendValue(int RW, int _Pin, int Val) {
var cmd = new byte[] { (byte)RW, (byte)_Pin, (byte)Val };
ComPort.Write(cmd, 0, cmd.Length);
}
Then you just need to read those 3 bytes on the Arduino end:
void ReadIncoming() {
if (Serial.available() >= 3) {
RW = Serial.read();
PN = Serial.read();
Val = Serial.read();
ProcessIncoming();
}
}
I am currently working on a robotics project with Arduino. I want to access the serial port from different methods at different times.
For instance, I want to read the ADC at time t1 and get the motor currents at time t2. So I create readADC() and motorCurrents() methods which should both return int arrays of different sizes. The serial port data received is given below.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
int n = serialPort1.BytesToRead;
serialPort1.Read(data, 0, data.Length);
}
I have implemented all the relevant coding on the Arduino side. I also have set the serial port. I need to implement the following commands in C#
private int[] ReadADC()
{
string input = "AN\n"; // This is the command for reading the ADC on the Wildthumper board.
serialPort1.Write(input);
// The Wildthumper now sends 11 bytes, the last of which is '*' and the first
// 10 bytes are the data that I want. I need to combine two bytes together
// from data received and make five values and return it.
for (int i = 0; i < 5; i++)
{
adcValues[i] = data[0 + i * 2] <<8+ data[1 + i * 2];
}
return adcValues;
// Where data is the bytes received on serial port;
}
Similarly:
private int[] getMotorCurrents()
{
string input = "MC\n"; // Wildthumper board command
serialPort1.Write(input);
// The Wildthumper now sends 5 bytes with the last one being '*'.
// And the first four bytes are the data that I want.
for (int i = 0; i < 2; i++)
{
MotorCurrents[i] = data[0 + i * 2] <<8 +data[1 + i * 2];
}
return MotorCurrents;
}
First of all, the number of bytes sent to me change. So how can I use a global variable? For data (the variable used to store serial data received as given above)?
You need to create a global variable and save the data to it when data received fires. This isn't hard.
Here is a code example:
public class myclass{
public string arduinoData = "";
private void serialPort1_DataReceived(
object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
this.arduinoData = serialPort1.ReadLine(data, 0, data.Length);
}
//....The rest of your code, such as main methods, etc...
}
hello i have problem with receiving data from serial port in c# in am inserting a new line operator at the end of data buffer. then i send this data buffer on serial port, after this my c# GUI receiver will take this data via Readline() function but it always give me raw data not the actual one how to resolve this problem.
//configuring the serial port this code in c# with problem
serialPort.PortName = "COM1";
serialPort.BaudRate = 9600;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
//opening the serial port
if(!serialPort.IsOpen)
serialPort.Open();
//read 2byte data for msG code from serial port
string strReadData=serialPort.ReadLine();
char[] temp=new char[350];
//strReadData.CopyTo(1, temp, 0, strReadData.Length - 2);
//strReadData = temp.ToString();
//string strReadData = serialPort.ReadExisting();
//strReadData.Replace(' ', '\0');
//strReadData.Replace(' ', '');
byte[] RecievedData = Encoding.ASCII.GetBytes(strReadData);
RecievedDataDecoder(RecievedData);
//close the port
if(serialPort.IsOpen)
serialPort.Close();
but my c++ receiver is working perfectly i dont know what is the problem here is working c++ code
// variables used with the com port
BOOL m_bPortReady;
HANDLE m_hCom;
DCB m_dcb;
COMMTIMEOUTS m_CommTimeouts;
BOOL bWriteRC;
BOOL bReadRC;
DWORD iBytesWritten;
DWORD iBytesRead;
DWORD dwCommEvent;
DWORD dwRead;
char sBuffer[128];
m_hCom = CreateFile("Com1",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security
OPEN_EXISTING,
0, // no overlapped I/O
NULL); // null template
m_bPortReady = SetupComm(m_hCom, 128, 128); // set buffer sizes
m_bPortReady = GetCommState(m_hCom, &m_dcb);
m_dcb.BaudRate = 9600;
m_dcb.ByteSize = 8;
m_dcb.Parity = NOPARITY;
m_dcb.StopBits = ONESTOPBIT;
m_dcb.fAbortOnError = TRUE;
m_bPortReady = SetCommState(m_hCom, &m_dcb);
m_bPortReady = GetCommTimeouts (m_hCom, &m_CommTimeouts);
m_CommTimeouts.ReadIntervalTimeout = 50;
m_CommTimeouts.ReadTotalTimeoutConstant = 50;
m_CommTimeouts.ReadTotalTimeoutMultiplier = 10;
m_CommTimeouts.WriteTotalTimeoutConstant = 50;
m_CommTimeouts.WriteTotalTimeoutMultiplier = 10;
m_bPortReady = SetCommTimeouts (m_hCom, &m_CommTimeouts);
if (!SetCommMask(m_hCom, EV_RXCHAR))
{
printf("Error in set comm mask");
}
while(1)
{
if (WaitCommEvent(m_hCom, &dwCommEvent, NULL))
{
if (ReadFile(m_hCom, &sBuffer, 128, &iBytesRead, NULL))
printf("");
else
{
printf("Error in reading");
break;
}
}
else
{
printf("Error in Waiting");
break;
}
printf("%s",sBuffer);
strcpy(sBuffer,"");
}
CloseHandle(m_hCom);
getch();
exit(0);
Your question is a bit vague, but the ReadLine() method will return when the port receives a carriage return and line feed byte combination by default on Windows. Or \r\n, or 0x0d 0x0a if you prefer.
If the 'buffer' you are sending in the fist place contains several messages delimited by \r\n, then ReadLine() will only return the first one, then the port will close in the C# code you have posted.
Maybe your code is in a loop, but it is not shown.
Beyond that, after any data that you have received, your are converting it back to an array of bytes, determined by ASCII encoding. Are you sending ASCII in the first place? If not, it is likely that you could be missing information.
Also, if all you do is convert the received string into bytes, you could just receive the data as bytes in the first place.
I think you need to explain in a bit more detail exactly what is in the buffer you are sending, and what exactly do you receive.
Most of my serial port errors are caused by bad baude rate settings. This might be your problem too. (You can set the baude rate in some constructors of the SerialPort class, or with the BaudRate property)