C# - Grabbing data from serialport becomes too slow above 200kb/s - c#

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);
}

Related

Windows BLE GattValueChangedEventArgs.Characteristic.Value missing data

I use windows BLE to connect and read from BLE barcode scanner. Everything works fine while the value I receive from the BLE device is only short string. If I scan a barcode which is more than 16 bytes, the GattCharacteristic.ValueChanged event fires multiple times, and each time I receive a chunk from the data. It would not be problem but I don't receive everything and parts are missing. I use identical method on Xamarin.IOS and the event raised once and I receive 1 long string containing the whole data. So it works in Xamarin.IOS but not in winform.net.
It seems windows uses about 16bytes IBuffer for GattCharacteristic.Value and while it raises the event, that buffer gets cleared and replaced with new data.
Is there any way to increase Windows BLE IBuffer size or tell windows to get the whole data together before raise the event?
What I should receive:
Collection: 1000
From: West Midlands
To: Distribution
RaisedBy: holloway
Raised: 29/09/2021
Item: Camargue 572 4m - hometx44cama
SKU: 26479
Identifier: 174435
SubIdentifier: 21642727-6
Qty: 4.27
What I receive:
Collection: 1000
From: West Mid
land To: Distr
edBy: holloway
ised: 29/09/20
21 Item: Camarg
ue 572 4m
Identifier
: 174435
SubIde
ntifier: 21642
4.27
The code:
CurrentConnectedDevice = await BluetoothLEDevice.FromIdAsync(deviceid);
GattDeviceServicesResult result = await CurrentConnectedDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
var services = result.Services;
GattDeviceService ScannerService = services.Single(d => d.Uuid == ScannerServiceUUID);
var Characteristics = await ScannerService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
ScanCharacteristic = Characteristics.Characteristics.Single(c => c.Uuid == ScannerNotifyUUID);
var status = await ScanCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
ScanCharacteristic.ValueChanged += BarcodeScanned;
private void BarcodeScanned(GattCharacteristic sender, GattValueChangedEventArgs args)
{
byte[] data;
CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
Console.WriteLine(Encoding.UTF8.GetString(data));
}
In the Buffer constructor (link) you can pass the size of the buffer. In your implementation, you are using args.CharacteristicValue as the buffer. If you can control the value of args.CharacteristicValue then you can increase the buffer size, or you can pass in a new Buffer to the CopyToByteArray method.

Missing bytes while reading the bytes from C# serial port class

I wrote down some app in C# to read the bytes from (USB-SerialPort) payment terminal.
But I can see the bytes are missing/overridden in my application while I read from the read-Buffer of windows serial port.
I have following piece of code for opening the port and reading the bytes( either from ReadExisting or ReadByteArray) from serial port when the DataReceivedEvent gets triggered.
//Open the port
m_port.Open();
//attach the event handler
m_port.ErrorReceived += OnSerialErrorReceived;
m_port.DataReceived += OnSerialDataReceived;
public void OnSerialDataReceived(object sender ,SerialDataReceivedEventArgs serialDataArgs)
{
//Thread.Sleep(20);
//int numberOfBytesToRead = m_port.BytesToRead;
//byte[] readByteArray = new byte[m_port.BytesToRead];
//m_port.Read(readByteArray, 0, readByteArray.Length);
string readData = m_port.ReadExisting();
ParseWLinkProtocolMsgToHexStringArray(Encoding.UTF8.GetBytes(readData));
}
But when I use the Thread.Sleep(20ms) before read the data in OnSerialDataReceived method , I have no missing bytes in my data.
Also I have not set any other properties on the serial port instance except the following.
BaudRate,StopBits,Parity and DataBits.
Can some one please suggest me any other alternative way with out applying the time delay in my application.

Reading data from RadioLink R12DS receiver by S-BUS protocol

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.

read data from serialport

I am new to C# and programming in general.
I am trying to communicate with a ohmmeter which is conneted via usb to my computer.
I am able to configurate the device and even recieve data.
But i can not acces this data. I can just print it on to the console.
(Was inspired by the code on the microsoft site)
Here is the constructer of my "communication"-class where i configurate the port:
public SCPI_Commands()
{
_SerialPort.PortName = SetPortName(_SerialPort.PortName);
_SerialPort.BaudRate = 115200;
_SerialPort.Parity = Parity.None;
_SerialPort.DataBits = 8;
_SerialPort.StopBits = StopBits.One;
_SerialPort.Handshake = Handshake.None;
_SerialPort.ReadTimeout = 500;
_SerialPort.WriteTimeout = 500;
_SerialPort.Open();
_SerialPort.DataReceived += _serialPort_DataReceived;
}
Here is my function which sends a query to the device (The constant Measurment_Value represents a scpi command which is understood by my ohmmeter) :
public void get_measurement()
{
_SerialPort.WriteLine(Measurment_Value);
}
And here is the private function which checks if the device is sending data and printing it on the console (not sure how this function works) :
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine(_SerialPort.ReadLine());
}
Unfortunately i am not able to return the data as a string. My goal is to do some calculations with the received data.
Does someone has any ideas ?
Greetings from Germany.
Luke
You can read from buffer to temp byte array and then get it as string, see the below example. Put this in _serialPort_DataReceived
// this the read buffer
byte[] buff = new byte[9600];
int readByteCount = _serialPort.BaseStream.Read(buff, 0, _serialPort.BytesToRead);
// you can specify other encodings, or use default
string response = System.Text.Encoding.UTF8.GetString(buff);
Side Note
If you want to keep your sanity while working with SerialPort, always send and receive as byte array. Then, get the equivalent string using Encoding.

C# and serial communication how to flush the device to read same data again?

Okay so I am stuck for almost 20 days now in the same problem of a serial communication device. I have a hardware sensor which read tags and returns the tag code number on every read through serial com port 1 or 3.Any of these I use doesn't matter. I am using a program I wrote in c# to play with the incoming data.
Now problem is that if forexample:
my sensor reads tag with code "e2 0 10 1 83 10 1 23 7 0 d0 c0 1 be"
It will not read this tag again unless I switch of the sensor and turn it on again (Power reset) . So I can't figure out how to make my sensor forget all the data it read till I closed the port. ANY ONE CAN HELP PLEASE I AM DESPERATE NOW
Some one told me that we need to write to device with some commands but he didn't know more than that.
Here is the current code:
void IntializeSensor()
{
try
{
if (mySerialPort==null)
{
mySerialPort = new SerialPort("COM3");
mySerialPort.BaudRate = 9600;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.ReadTimeout = 2000;
mySerialPort.Handshake = Handshake.None;
LoglistBox.Items.Add("--Port Intilalized at COM1--");
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void OpenPort()
{
try
{
str = "";
if (mySerialPort.IsOpen)
{
ClosePort();
Thread.Sleep(6000);
}
mySerialPort.Open();
LoglistBox.Items.Add("--Port Opened at COM1 Success--");
}
catch (Exception ex)
{
LoglistBox.Items.Add("--Port Opened Failed--Error: "+ex.Message);
}
}
void ClosePort()
{
try
{
mySerialPort.Write("ABCABCABCABCABC");
mySerialPort.DiscardInBuffer();
mySerialPort.DiscardOutBuffer();
mySerialPort.Dispose();
mySerialPort.Close();
LoglistBox.Items.Add("--Port Closed at COM1 Success--");
}
catch (Exception ex)
{
LoglistBox.Items.Add("--Port Closed Failed--Error: " + ex.Message);
MessageBox.Show(ex.Message);
}
}
private void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)
{
try
{
if (e.EventType != SerialData.Chars) return;
SerialPort COMPort = (SerialPort)sender;
int bytes = COMPort.BytesToRead;
//create a byte array to hold the awaiting data
byte[] comBuffer = new byte[bytes];
//read the data and store it
COMPort.Read(comBuffer, 0, bytes);
// System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
str = ByteArrayToHexString(comBuffer);
holdfirstvalue += str;
//str = str +" "+ str;
//MessageBox.Show("after concat "+str);
if (str.Contains("FF") || str.Contains("F F") || str.Contains("F"))
{
SetText(holdfirstvalue.ToString());// ONE TAG CODE SENT TO BE SET IN LIST
str = "";
holdfirstvalue = "";
}
}
catch (Exception ex)
{
// LoglistBox.Items.Add("--Port Opened Failed--Error: " + ex.InnerException);
MessageBox.Show(ex.Message+" "+ex.InnerException);
}
}
As I understood from your comments, even the program that is shipped with it does not read the same bar-code twice in a row. Am I right?
To me it seems that "the tag reader manufacturer" may have put that mechanism intentionally to prevent user mistakenly scan an item twice at check-out. because it happens a lot that a same stays on the scanner or be crossed against the scanner couple of times when moving things around.
Unless you have access to the scanner Firmware and are able to make changes yourself, I'd say contact the manufacturer. I would contact the manufacturer and ask about this directly. There should be a command that tells the scanner to "Get out of lock mode and restart scanning again" for the special case of scanning a same item several times (e.g. buying multiple similar things.)
They should provide you with a manual with the list of all the commands you can send to your device and you use this commands to build up your system.
One more thing to try! can you scope out your serial port using "Real Term" or any other terminal monitoring application to see if the scanner sends the code to the PC after you scan the same item again or not? This helps you to isolate the problem to make sure if it is the Scanner Firmware or the desktop software. (it seems to me that it is the scanner Firmware ignoring the item because you say it works fine when you reset it)
edit: also I see you are reading your serial port based on DataREadyEvent, again, if it was me, I would add another thread with a very short delay say 20ms or 50ms to keep reading the serial port constantly. there are plenty of examples on how to implement this on the net, one simple and quick way of this is described here:
Read com ports using threading in c#
Hope it helps.
Ok so I finally found the code in hex which actually resets the sensor.
So to help anyone out there who bought Middle Range UHF RFID Reader or any type of this model.
What we did is we hacked into the wires of serial port by soldering copper wire on data pins. Then we attached the other end of those copper wires to my laptop and used the terminal reader "Putty" to see what is actually being sent by the PC to the Reader on RESET HEAD button click.
This way we found the hex code , converted it to byte array and here is the C# code to write it to reader device by serial port and resets the device:
{
mySerialPort.Open();
byte[] bytesToSend = StringToByteArray("A00265F9");// correct command to reset READER
mySerialPort.Write(bytesToSend, 0, 4);
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

Categories

Resources