I'm talking serially to a Smart Motor and I'm trying to look for the specific string "# Positon" coming back from the motor. When I see that I want to set the Play button to be enabled (btnPlay.Enabled=true;).
I've tried every way but can't seem to fit it to the following code. What can I place in my code below where I can test the incoming data and then trigger the enable?
The following code works - I just don't know where and how to read for specific information.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// If the com port has been closed, do nothing
if (!comport.IsOpen) return;
// Determain which mode (string or binary) the user is in
if (CurrentDataMode == DataMode.Text)
{
// Read all the data waiting in the buffer
string data = comport.ReadExisting();
// Display the text to the user in the terminal
Log(LogMsgType.Incoming, data);
}
else
{
// Obtain the number of bytes waiting in the port's buffer
int bytes = comport.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[bytes];
// Read the data from the port and store it in our buffer
comport.Read(buffer, 0, bytes);
// Show the user the incoming data in hex format
Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
}
}
Related
I'm developing two separate applications for data transfer over Bluetooth RFCOMM using the Obex File Transfer protocol. On one side, a Windows C# Console Application running on a PC listens for incoming bluetooth connections and sends an image whenever a client makes a request. On the other side, an Android application running on a mobile device scans the nearby bluetooth devices, looks for the server and receives the image.
In most cases, everything works fine and the image is transmitted without problems. Sometimes - not very often, I still can't figure out how to reproduce the error - the image is corrupted during the transmission, as some of the received bytes from the Android app do not match the original buffer (I compute the CRC of the received buffer and compare it with the original one to check if the image has been sent successfully).
Here's an example:
Original
,
Received
This kind of "glitchy" image is just an example, every time something goes wrong the received image has a different 'glitch effect'.
Few things I tried to solve the problem:
Changing UUID, but neither the OOP UUID nor a custom UUID seems to work, as the exact same problem arises.
My smartphone (Xiaomi Redmi Note 8T) from which I am running the client app had almost zero free space of internal storage, so I got desperate and tried to free some memory to see if that was causing the error for some reason (yeah it doesn't make much sense but it's worth mentioning). At first it worked and I thought that solved the problem somehow, but then the error re-appeared just like before.
Using an ACK system to control each sub array of data sent from the server to the client, something like: the PC sends the first sub array of data, then it waits until the smartphone sends an ACK to acknowledge the reception of the sub array, and ONLY after that it proceeds to send the next sub array of data, and so on until the end of the buffer. Needless to say that neither this option worked (again, same error and corrupted data).
I also tried to see if other devices trying to connect to my smartphone could cause the problem, but it wasn't the case.
CODE
Server side
Here's my implementation of the listener in the C# Console app running on Windows 10. I took this Server sample as a reference.
// Initialize the provider for the hosted RFCOMM service
_provider = await RfcommServiceProvider.CreateAsync(
RfcommServiceId.ObexFileTransfer); // Use Obex FTP protocol
// UUID is 00001106-0000-1000-8000-00805F9B34FB
// Create a listener for this service and start listening
StreamSocketListener listener = new StreamSocketListener();
listener.ConnectionReceived += OnConnectionReceivedAsync;
await listener.BindServiceNameAsync(
_provider.ServiceId.AsString(),
SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication);
// Set the SDP attributes and start advertising
InitializeServiceSdpAttributes(_provider);
_provider.StartAdvertising(listener);
InitializeServiceSdpAttributes function:
const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0A; // UINT32
const uint SERVICE_VERSION = 200;
void InitializeServiceSdpAttributes(RfcommServiceProvider provider)
{
Windows.Storage.Streams.DataWriter writer = new Windows.Storage.Streams.DataWriter();
// First write the attribute type
writer.WriteByte(SERVICE_VERSION_ATTRIBUTE_TYPE);
// Then write the data
writer.WriteUInt32(MINIMUM_SERVICE_VERSION);
IBuffer data = writer.DetachBuffer();
provider.SdpRawAttributes.Add(SERVICE_VERSION_ATTRIBUTE_ID, data);
}
Whenever a new connection attempt is detected, the OnConnectionReceivedAsync function stops the advertisement, disposes the listener and creates a new StreamSocket object. At this point I set the input and output streams, convert the image to an array of bytes and send the buffer length to the remote device through the socket. Once the Android app has received the length of the buffer, it sends an ACK which means that it is ready to recevie the actual data.
// Create input and output stream
DataWriter writer = new DataWriter(_socket.OutputStream);
// Convert image to array of bytes
byte[] imageByteArray;
using (var inputStream = await file.OpenSequentialReadAsync())
{
var readStream = inputStream.AsStreamForRead();
imageByteArray = new byte[readStream.Length];
await readStream.ReadAsync(imageByteArray, 0, imageByteArray.Length);
}
// Write length of data
writer.WriteBytes(intToByteArray(imageByteArray.Length));
await writer.StoreAsync();
// Wait for ACK ...
Finally, I send the image:
// Write bytes and send
writer.WriteBytes(imageByteArray);
await writer.StoreAsync();
// Wait for ACK ...
As soon as the image is sent, the app receives an ACK from the remote device when all the data has been received and then closes the connection.
Client Side
First of all, the Android app creates a BluetoothSocket object using the same UUID specified from the Server app:
// Scan devices and find the remote Server by specifying the target MAC address
// ...
// targetDevice is the Server device
BluetoothSocket socket = targetDevice.createInsecureRfcommSocketToServiceRecord(
UUID.fromString("00001106-0000-1000-8000-00805F9B34FB") // FTP
);
// Connect the server
socket.connect();
Finally, it reads the incoming data from the socket InputStream. First it reads the length of the incoming buffer and sends an ACK to confirm that it's ready to receive the image. Then waits for each sub array until all the buffer is complete. At this point, it sends a final ACK and closes the connection.
// Get input stream
InputStream inputStream = socket.getInputStream();
// Buffer that contains the incoming data
byte[] buffer = null;
// The numOfBytes is the expected length of the buffer
int numOfBytes = 0;
// Index of the sub array within the complete buffer
int index = 0;
// flag is true if the receiver is computing the number of bytes that it has to receive
// flag is false if the receiver is actually reading the image sub arrays from the stream
int flag = true;
while(true){
// Estimate number of incoming bytes
if(flag){
try{
// inputStream.available() estimates the number of bytes that can be read
byte[] temp = new byte[inputStream.available()];
// Read the incoming data and store it in byte array temp (returns > 0 if successful)
if(inputStream.read(temp) > 0){
// Get length of expected data as array and parse it to an Integer
String lengthString = new String(temp, StandardCharsets.UTF_8);
numOfBytes = Integer.parseInt(lengthString);
// Create buffer
buffer = new byte[numOfBytes];
// Set the flag to false (turn on read image mode)
flag = false;
// Send ACK
}
}
catch (IOException e){
// ...
}
}
// Read image sub arrays
else {
try{
byte[] data = new byte[inputStream.available()];
// Read sub array and store it
int numbers = inputStream.read(data);
if(numbers <= 0 && index < numOfBytes)
continue;
// Copy sub array into the full image byte array
System.arraycopy(data, 0, buffer, index, numbers);
// Update index
index = index + numbers;
// Reached the end of the buffer (received all the data)
if(index == numOfBytes){
// Send ACK (Transfer success)
// ...
// Decode buffer and create image from byte array
Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, numOfBytes);
// Store output image
outputImage = bmp;
// Dismiss the bluetooth manager (close socket, exit waiting loop...)
dismiss();
// Return the image
return bmp;
}
}
catch (IOException e){
// ...
}
}
}
First I'll apologize if any of my code doesn't make sense as i am fairly new to coding and currently in University.
Any way my problem is as described.
I have this code in my python server to send the data which is gathered from my local oracle server:
query = "SELECT * FROM DATABASE"
cursor.execute(query)
for row in cursor:
for cell in row:
cellF = str.format("{0}$",cell)
self.send(cellF.encode())
And this code to receive the data on my C# client:
public void Read()
{
var buffer = new byte[1024];
var stream = client.socket.GetStream();
stream.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
public void EndRead(IAsyncResult result)
{
var stream = client.socket.GetStream();
int endBytes = stream.EndRead(result);
if (endBytes == 0)
{
return;
}
var buffer = (byte[])result.AsyncState;
string data = Encoding.ASCII.GetString(buffer, 0, endBytes);
SetRecieved(data);
stream.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
Revision:
I realized i had some misc code so i removed it and now it consistently works but only when the method is ran for the second time.
public void Read()
{
while (client.stream.DataAvailable)
{
client.stream.BeginRead(client.buffer, 0, client.buffer.Length, EndRead, client.buffer);
}
}
public void EndRead(IAsyncResult result)
{
var stream = client.socket.GetStream();
int endBytes = stream.EndRead(result);
var buffer = (byte[])result.AsyncState;
string data = Encoding.ASCII.GetString(buffer, 0, endBytes);
SetRecieved(data);
}
The first time i send the request to the server to get this data it works as expected but the second time i request the data i loose a random string of data along the way, the first time always works perfectly however and i receive all strings so I'm not sure what is happening.
Lets do some analysis...
You send some data from the server to the client that is less then size of the buffer. You read it and that's all. You call return in the asynccallback function. If you don't use while loop then you will not be able to receive new data from server.
You send two messages from the server to the client. This two messages are in the same buffer, because they summed size is less then buffer size. You read them as one with your approach and your data is corrupted, because you don't know the data size.
If you want to treat your data as separate messages you need to know the message size and handle each case differently. Data is less then buffer size - you need to continue waiting for incoming data. Data is greater then buffer size - you need to read whole message. And so on.
I have an application that requires I send a string of 4-10 ASCII character to an RS422 Uart serial receiver. The problem is that the Uart buffer can only receive 2bytes max ever 10ms or so. How do I parse out the data and send it in chunks without timing out on the other side.
Normal serial.write() method overflows the buffer and I get an error response from the device every time I send anything. The device specified a baudrate of 19200 but also say the data i write needs to be spaced out 2 bytes at a time. There is no parity, handshake or flow control support for the device.
Essentially I want to do something like this:
private void sendData(string text)
{
string textnew = text +(char)13;
byte[] r_bytes = Encoding.ASCII.GetBytes(textnew);
if (SeriialComms.IsOpen)
{
for (int i = 0; i > (textnew.Length/2); i = i + 2)
{
byte[] bytesToSend = { r_bytes[i], r_bytes[i+1] };
SerialComms.Write(bytesToSend, 0, 2);
System.Threading.Thread.Sleep(10);
}
}
}
is this possible and is there an easier way to do this?
im trying to send an image via network stream, i have a sendData and Getdata functions
and i always get an invalid parameter when using the Image.FromStream function
this is my code :
I am Getting the pic from the screen, then converting it to a byte[]
Inserting it to a Memory stream that i send via a networkStream.
private void SendData()
{
StreamWriter swWriter = new StreamWriter(this._nsClient);
// BinaryFormatter bfFormater = new BinaryFormatter();
// this method
lock (this._secLocker)
{
while (this._bShareScreen)
{
// Check if you need to send the screen
if (this._bShareScreen)
{
MemoryStream msStream = new MemoryStream();
this._imgScreenSend = new Bitmap(this._imgScreenSend.Width, this._imgScreenSend.Height);
// Send an image code
swWriter.WriteLine(General.IMAGE);
swWriter.Flush();
// Copy image from screen
this._grGraphics.CopyFromScreen(0, 0, 0, 0, this._sizScreenSize);
this._imgScreenSend.Save(msStream, System.Drawing.Imaging.ImageFormat.Jpeg);
msStream.Seek(0, SeekOrigin.Begin);
// Create the pakage
byte[] btPackage = msStream.ToArray();
// Send its langth
swWriter.WriteLine(btPackage.Length.ToString());
swWriter.Flush();
// Send the package
_nsClient.Write(btPackage, 0, btPackage.Length);
_nsClient.Flush();
}
}
}
}
private void ReciveData()
{
StreamReader srReader = new StreamReader(this._nsClient);
string strMsgCode = String.Empty;
bool bContinue = true;
//BinaryFormatter bfFormater = new BinaryFormatter();
DataContractSerializer x = new DataContractSerializer(typeof(Image));
// Lock this method
lock (this._objLocker)
{
while (bContinue)
{
// Get the next msg
strMsgCode = srReader.ReadLine();
// Check code
switch (strMsgCode)
{
case (General.IMAGE):
{
// Read bytearray
int nSize = int.Parse(srReader.ReadLine().ToString());
byte[] btImageStream = new byte[nSize];
this._nsClient.Read(btImageStream, 0, nSize);
// Get the Stream
MemoryStream msImageStream = new MemoryStream(btImageStream, 0, btImageStream.Length);
// Set seek, so we read the image from the begining of the stream
msImageStream.Position = 0;
// Build the image from the stream
this._imgScreenImg = Image.FromStream(msImageStream); // Error Here
Part of the problem is that you're using WriteLine() which adds Environment.NewLine at the end of the write. When you just call Read() on the other end, you're not dealing with that newline properly.
What you want to do is just Write() to the stream and then read it back on the other end.
The conversion to a string is strange.
What you're doing, when transferring an image, is sending an array of bytes. All you need to do is send the length of the expected stream and then the image itself, and then read the length and the byte array on the other side.
The most basic and naive way of transferring a byte array over the wire is to first send an integer that represents the length of the array, and read that length on the receiving end.
Once you now know how much data to send/receive, you then send the array as a raw array of bytes on the wire and read the length that you previously determined on the other side.
Now that you have the raw bytes and a size, you can reconstruct the array from your buffer into a valid image object (or whatever other binary format you've just sent).
Also, I'm not sure why that DataContractSerializer is there. It's raw binary data, and you're already manually serializing it to bytes anyway, so that thing isn't useful.
One of the fundamental problems of network programming using sockets and streams is defining your protocol, because the receiving end can't otherwise know what to expect or when the stream will end. That's why every common protocol out there either has a very strictly defined packet size and layout or else does something like sending length/data pairs, so that the receiving end knows what to do.
If you implement a very simple protocol such as sending an integer which represents array length and reading an integer on the receiving end, you've accomplished half the goal. Then, both sender and receiver are in agreement as to what happens next. Then, the sender sends exactly that number of bytes on the wire and the receiver reads exactly that number of bytes on the wire and considers the read to be finished. What you now have is an exact copy of the original byte array on the receiving side and you can then do with it as you please, since you know what that data was in the first place.
If you need a code example, I can provide a simple one or else there are numerous examples available on the net.
Trying to keep it short:
the Stream.Read function (which you use) returns an int that states how many bytes were read, this is return to you so you could verify that all the bytes you need are received.
something like:
int byteCount=0;
while(byteCount < nSize)
{
int read = this._nsClient.Read(btImageStream, byteCount, nSize-byteCount);
byteCount += read;
}
this is not the best code for the job
I am an embedded system software developer for safety critical systems, so I am fairly new with C# but proficient in C-based languages.
To provide a little background, I have developed a Windows Form that interprets serial data packets sent from my embedded software through the serial port into meaningful debugging information.
What I want to do is display each byte of each packet in a TextBox Control. The textbox control that displays the packet information is actually a second form opened by the first form. Here's the code for the event handler that opens the second form from the first:
private void ShowRawSerialData(object sender, EventArgs e)
{
SendSerialDataToForm = true;
if (SerialStreamDataForm == null)
SerialStreamDataForm = new RawSerialDataForm();
SerialStreamDataForm.Instance.Show();
}
In the above code, the .Instance.Show() directive is a means by which I may open a new form if the form is closed, but not show a new form if the form is already open.
Then, in my serial data received event handler, I do this:
// Get bytes from the serial stream
bytesToRead = IFDSerialPort.BytesToRead;
MsgByteArray = new Byte[bytesToRead];
bytesRead = IFDSerialPort.Read(MsgByteArray, 0, bytesToRead);
// Now MsgByteArray has the bytes read from the serial stream,
// send to raw serial form
if (SendSerialDataToForm == true && SerialStreamDataForm != null)
{
SerialStreamDataForm.UpdateSerialDataStream(MsgByteArray);
}
Where MsgByteArray is the byte array of the serial packet received. And here is the code for UpdateSerialDataStream:
public void UpdateSerialDataStream(Byte[] byteArray)
{
String currentByteString = null;
currentByteString = BitConverter.ToString(byteArray);
currentByteString = "0x" + currentByteString.Replace("-", " 0x") + " ";
if (RawSerialStreamTextBox.InvokeRequired)
{
RawSerialStreamTextBox.Invoke(new SerialTextBoxDelegate(this.UpdateSerialDataStream), new object[] { byteArray });
}
else
{
RawSerialStreamTextBox.Text += currentByteString;
}
RawSerialStreamTextBox.Update();
}
The end result is that the value of RawSerialStreamTextBox.Text is correctly updated with the string I intend on adding to the text box! For example, if I pass the byte array {0x01, 0x7F, 0x7E}, then, through the debugger, I can see that the value of RawSerialStreamTextBox.Text = "0x01 0x7F 0x7E".
The problem is that the text box control itself does not show the newly added text. So even though I can confirm through the debugger that RawSerialStreamTextBox.Text = "0x01 0x7F 0x7E" the text box in Windows does not show "0x01 0x7F 0x7E" but rather, remains blank.
Any ideas for what might be happening here?
I would guess that you are setting the Text property on an instance other than the one that is actually being displayed. A sanity check would be something like
RawSerialStreamTextBox.Visible = false;
Does it disappear?
To simplify a little, I would have UpdateSerialDataStream return a string (or pass a string to an out parameter) so that your Event Handler would, instead, look like this:
// Get bytes from the serial stream
bytesToRead = IFDSerialPort.BytesToRead;
MsgByteArray = new Byte[bytesToRead];
bytesRead = IFDSerialPort.Read(MsgByteArray, 0, bytesToRead);
// Now MsgByteArray has the bytes read from the serial stream,
// send to raw serial form
if (SendSerialDataToForm == true && SerialStreamDataForm != null)
{
RawSerialStreamTextBox.Text = UpdateSerialDataStream(MsgByteArray);
}
And UpdateSerialDataStream would look something like this:
public string UpdateSerialDataStream(Byte[] byteArray)
{
String currentByteString = null;
currentByteString = BitConverter.ToString(byteArray);
currentByteString = "0x" + currentByteString.Replace("-", " 0x") + " ";
return currentByteString;
}
You'd have to move your code handling the display of the form around a little, but this would allow the form already containing the TextBox to handle the update on its own.