How to Read and Write from the Serial Port

how to write and read to serialPort in same time?

Use an OnRecieveData handler that saves the data to a ConcurrentQueue or something similar.

namespace Test
{ class Program
{
const int bufSize = 2048;
static void Main(string[] args)
{

Byte[] buf = new Byte[bufSize];
SerialPort sp = new SerialPort("COM1", 115200);
sp.DataReceived += port_OnReceiveData; // Add DataReceived Event Handler

sp.Open();

// Wait for data or user input to continue.
Console.ReadLine();

sp.Close();
}

private static void port_OnReceiveData(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = (SerialPort) sender;
switch(e.EventType)
{
case SerialData.Chars:
{
Byte[] buf = new Byte[bufSize];
port.Read(buf, 0, bufSize)
Console.WriteLine("Recieved data! " + buf.ToString());
break;
}
case SerialData.Eof:
{
// means receiving ended
break;
}
}
}
}
}

Reading from a serial port after writing on it

i can't figure out what is the problem

One big problem is that the C program running on the "computer" is incomplete.

The Arduino's program does a serial port setup of at least the baud rate (and whatever else might be performed by default).

But the "computer's" C program never properly configures the serial port. The serial port will use whatever attributes (baud rate, data length, parity setting, canonical versus raw mode) previously configured, which will cause unpredictable reads and writes. (A loopback test would probably produce a false positive result.)

Use the POSIX Serial Port guide or this answer for sample code.

For canonical mode you probably need to add code like (assuming 8N1):

    rc = tcgetattr(fd, &tty);
if (rc < 0) {
/* handle error */
}
savetty = tty; /* preserve original settings for restoration */

spd = B9600;
cfsetospeed(&tty, (speed_t)spd);
cfsetispeed(&tty, (speed_t)spd);

tty.c_cflag &= ~PARENB
tty.c_cflag &= ~CSTOPB
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;

tty.c_cflag &= ~CRTSCTS; /* no HW flow control? */
tty.c_cflag |= CLOCAL | CREAD;

tty.c_iflag |= IGNPAR | IGNCR;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_lflag |= ICANON;
tty.c_oflag &= ~OPOST;

rc = tcsetattr(fd, TCSANOW, &tty);
if (rc < 0) {
/* handle error */
}

You probably should delete the line

fcntl(fd, F_SETFL, FNDELAY);  

as well as the O_NONBLOCK option in the open() call.

How to deal with C# SerialPort read and write data perfectly?

Based on your scenario you want to access the serial port sequentially (one at a time). I extracted this piece of code from my old project which should be of help to you.

using System;
using System.IO.Ports;
using System.Linq;
using System.Threading;

public class SPHandler
{
/// <summary>
/// Your serial port
/// </summary>
private SerialPort _serialPort;
private int _timeOut, _timeOutDefault;
private AutoResetEvent _receiveNow;
/// <summary>
/// Possible device end responses such as \r\nOK\r\n, \r\nERROR\r\n, etc.
/// </summary>
private string[] _endResponses;

public SPHandler()
{
}

public void SetPort(string portName, int baudRate, int timeOut, string[] endResponses = null)
{
_timeOut = timeOut;
_timeOutDefault = timeOut;
_serialPort = new SerialPort(portName, baudRate);
_serialPort.Parity = Parity.None;
_serialPort.Handshake = Handshake.None;
_serialPort.DataBits = 8;
_serialPort.StopBits = StopBits.One;
_serialPort.RtsEnable = true;
_serialPort.DtrEnable = true;
_serialPort.WriteTimeout = _timeOut;
_serialPort.ReadTimeout = _timeOut;

if (endResponses == null)
_endResponses = new string[0];
else
_endResponses = endResponses;
}

public bool Open()
{
try
{
if (_serialPort != null && !_serialPort.IsOpen)
{
_receiveNow = new System.Threading.AutoResetEvent(false);
_serialPort.Open();
_serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
if (e.EventType == SerialData.Chars)
{
_receiveNow.Set();
}
}
catch (Exception ex)
{
throw ex;
}
}

public bool Close()
{
try
{
if (_serialPort != null && _serialPort.IsOpen)
{
_serialPort.Close();
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}

public string ExecuteCommand(string cmd)
{
_serialPort.DiscardOutBuffer();
_serialPort.DiscardInBuffer();
_receiveNow.Reset();
_serialPort.Write(cmd); // Sometimes + "\r" is needed. Depends on the device

string input = ReadResponse(); // Returns device response whenever you execute a command

_timeOut = _timeOutDefault;

return input;
}

private string ReadResponse()
{
string buffer = string.Empty;
try
{
do
{
if (_receiveNow.WaitOne(_timeOut, false))
{
string t = _serialPort.ReadExisting();
buffer += t;
}

} while (!_endResponses.Any(r => buffer.EndsWith(r, StringComparison.OrdinalIgnoreCase))); // Read while end responses are not yet received
}
catch
{
buffer = string.Empty;
}
return buffer;
}
}

Usage:

SPHandler spHandler = new SPHandler();

spHandler.SetPort(params);
spHandler.Open();

string response = spHandler.ExecuteCommand("Cmd_Req");

if (response == "Cmd_Res")
{
// Inform user that operation OP_A is allowed
// Enqueue data
}
else
{
// Oh no! Cmd_Res failed?!
}

// ... etc.

spHandler.Close(); // If you need to

Basically you wait for a response as soon as you execute a command. Problem with this SPHandler is that it expects a string command and response. You can just convert it to read/send bytes instead.

You should take note of the AutoResetEvent which is used for sequential access. Even if you use a multi-threading, it will be handled accordingly. The call to _receiveNow.WaitOne does that magic for you. Maybe this is all you need to apply in your code since your current issue is reading both Cmd_Res and Cmd_Req at the same time.

EDIT:

Looking back at your code. You can simply unregisted SerialPort_DataReceived as you only need DoWork to sequentially execute commands and handle responses.

How to read data from a serial port and write it to a file

The standard idiom is:

rdlen = read(fd_serial_port, buf, sizeof(buf));
fwrite(buf, 1, rdlen, log_file);

When you read, you want to tell read that it's allowed to read up to sizeof(buf) (that is, 32) characters into buf. And then, when you call fwrite, you want to tell it to write only as many characters as you just read (i.e., rdlen).

In this way, you use buf over and over again, to read and write one chunk (up to 32 bytes) of data at a time. You don't need to keep track of a pointer into buf or anything; you just need to track how many characters you got each time through the loop -- which is your rdlen variable. But you don't need those i and ptr variables.

Also, you might want to test for end of file / error and break out of the loop if that happens:

if(rdlen <= 0) break;

Also, you don't necessarily need that fflush call.

Python: Writing to and Reading from serial port

ser.read(64) should be ser.read(size=64); ser.read uses keyword arguments, not positional.

Also, you're reading from the port twice; what you probably want to do is this:

i=0
for modem in PortList:
for port in modem:
try:
ser = serial.Serial(port, 9600, timeout=1)
ser.close()
ser.open()
ser.write("ati")
time.sleep(3)
read_val = ser.read(size=64)
print read_val
if read_val is not '':
print port
except serial.SerialException:
continue
i+=1

Is it safe to read and write to a serial port at the same time via different threads?

From the documentation of SerialPort:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

Since Read and Write are not static, they would not be threadsafe. It's a very bad idea, in any case, since the SerialPort class maintains internal buffers for you.

You'll need to synchronize your I/O to your serial port.

Read & Write simultaneously on same JAVA RXTX Serial Port within same thread

I had same problem but finally I got the Solution !

There is another JAVA comm library "com.fazecast.jSerialComm" which is the ultimate solution for real-time read & write operations on serial port simultaneously.
I'm posting my findings, if someone needs help regarding this issue...

SerialPort Class using jSerialComm

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;

public class MySerialPort {
public static SerialPort arduinoPort = null;
public static InputStream arduinoStream = null;
static String logText="";
private static boolean isRunning=false;
private byte[] sendingPack;
private byte[] receivingPack;
public static boolean isRunning() {
return isRunning;
}

public static void setRunning(boolean running) {
sendingPack = new byte[6];
receivingPack= new byte[36];
isRunning = running;

}
public static void connectPort(String port) {
devicePortName = port;

int len = SerialPort.getCommPorts().length;
SerialPort serialPorts[] = new SerialPort[len];
serialPorts = SerialPort.getCommPorts();

for (int i = 0; i < len; i++) {

String portName = serialPorts[i].getDescriptivePortName();
System.out.println(serialPorts[i].getSystemPortName() + ": " + portName + ": "
+ i);

if (portName.contains(devicePortName)) {
try {
arduinoPort = serialPorts[i];
arduinoPort.setBaudRate(115200);
arduinoPort.openPort();
setRunning(true);
System.out.println("connected to: " + portName + "[" + i + "]");
logText="Connected to: " + portName ;

break;
} catch (Exception e) {
e.printStackTrace();
loogger.stop();
arduinoPort.closePort();
}
} }

(new Thread(new SerialReader(receivingPack))).start();
(new Thread(new SerialWriter(sendingPack))).start();
}
public static class SerialReader implements Runnable
{
byte[] buffer;

public SerialReader ( byte[] buffer )
{
this.buffer = buffer;
System.out.println("Reader");

}

public void run () {

readData(buffer,isRunning());
}
}
public static class SerialWriter implements Runnable
{
byte[] buffer;

public SerialWriter ( byte[] buffer )
{
this.buffer = buffer;

}

public void run () {

sendData(buffer);

}
}
public static void sendData(byte[] buffer){

while (isRunning){

arduinoPort.writeBytes(sendingPack,6,0);

System.out.println("Sending"+bytesToHexString(sendingPack));
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
}
}

public static void readData(byte[] buffer,boolean loopStatus){
while (isRunning()){

arduinoPort.readBytes(receivingPack,36,0);

if(receivingPack()[0] & 0xff)==144 ){

String bufferD=bytesToHexString(receivingPack);
System.out.println(bufferD);

}
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
} }


public static String bytesToHexString(byte[] bytes){
StringBuilder sb = new StringBuilder();
for (byte b : bytes){
sb.append(String.format("%02x", b&0xff));
}
return sb.toString();
}
}

Main Class

   public static void main(String[] args) {
MySerialPort.setRunning(true);
MySerialPort.connectPort("COM3");
}

Best Practise to Read Desired Amount of Data from Serial?

But AFAIK system calls are expensive, ...

True, a system call can consume many more CPU cycles than a local procedure/function call. The system call requires CPU-mode transitions between user-mode to (protected) kernel mode, and then back to user mode.

... thus, isn't that approach somehow crude, especially when the desired length is large?

The first question you have to ask yourself when reading from a serial terminal (e.g. a /dev/ttyXn device)(rather than a serial port) is "what kind of data is going to be received, that is, is the data lines of (ASCII) text terminated by some type of EOL (end of line) character, or does the data need to be simply treated as binary (or raw) data?"

Lines of text should be read from a serial terminal using canonical (aka cooked) mode. The OS will perform a lexical scan of the received data for your program, and delimit each line of text based on the EOL characters you specify.
The read() can then return a line assuming that blocking I/O is used, and the line of text is not longer than the buffer that is provided.

Binary data should be read from a serial terminal using noncanonical (aka raw) mode. The OS will ignore the values of the data, and (when blocking I/O is used) each read() will return an amount of data based on constraints of time and number of bytes.

See this answer for more details.

Note that the post your question refers to actually is about reading text, yet the OP is (mis)using non-canonical mode. If the OP had used the proper mode to match the input, then he might have never had a partial-read problem.



I am just curious that whether there is a better practise to avoid intensive system calls in this scenario?

Proper termios configuration is essential for efficient I/O with serial terminals.

Blocking I/O mode should be considered the preferred mode.

The OS can perform its scheduling for multitasking better when processes relinquish control more often.

The OS is more efficient in determining when data is available for return to a user.

Note also that the termios configuration is most effective when blocking mode is used, e.g. the VMIN and VTIME specifications in noncanonical mode.

For example using a select() or poll() and then a read() is one additional syscall more than when compared to just the (blocking) read(). And yet you can find many such code examples because there seems to be some popular misconception that the program can get the data faster from the "UART" that way.

But non-blocking and async modes are not necessarily faster (in a multitasking OS), and the read() merely fetches data from the termios buffer which is several layers removed from the actual hardware.

If your program uses non-blocking mode but does not perform useful work while waiting for data, and instead uses select() or poll() (or even worse calls sleep()), then your program is unnecessarily complex and ineffecient. See this answer.

A blocking-mode read() can do all that waiting for your program, make your program simpler and easier to write and maintain, and be more runtime efficient.

However for blocking non-canonical reads, you will have to accept some degree of inefficiency. The best you can do is trade-off latency versus the number of syscalls. One possible example is this answer which tries to fetch as much data per syscall, yet allow for an easy byte-by-byte lexical scan of the received binary data.


Note that a possible source of latency when reading a serial terminal is a poorly configured kernel, rather than the termios API and read() overhead.

For instance, setting the ASYNC_LOW_LATENCY flag via ioctl() (e.g. see High delay in RS232 communication on a PXA270) is one way to improve read() latency.



Related Topics



Leave a reply



Submit