Infinite Loop Receive from Serial Port

infinite loop receive from serial port

Look at your code

while (1) 
{
read(serialfd, buf, sizeof(buf));
printf("length: %d\n", strlen(buf));
}

You have written a packet prior to this loop, then on the first iteration you read the available data which gets read into your buffer. You need to either memset your buffer to zeros each time OR zero terminate your buffer using the read byte count given in the return value of your call to read. You then loop infinitely, each time reading again - but subsequent reads will not copy any more data as there is none to read. However, your buffer remains unchanged by the call to read therefore and your printed output remains the same every iteration as the buffer remains the same every iteration.

As for the blocking aspect, you should read the following guide (which has been recommended on SO before and is very good as an introduction to serial port programming)

http://www.easysw.com/~mike/serial/serial.html

This section describes the behaviour you get when setting VMIN and VTIME to various values. In particular the last paragraph explains the blocking behaviour you see.

VMIN specifies the minimum number of characters to read. If it is set
to 0, then the VTIME value specifies the time to wait for every
character read. Note that this does not mean that a read call for N
bytes will wait for N characters to come in. Rather, the timeout will
apply to the first character and the read call will return the number
of characters immediately available (up to the number you request).

If VMIN is non-zero, VTIME specifies the time to wait for the first
character read. If a character is read within the time given, any read
will block (wait) until all VMIN characters are read. That is, once
the first character is read, the serial interface driver expects to
receive an entire packet of characters (VMIN bytes total). If no
character is read within the time allowed, then the call to read
returns 0. This method allows you to tell the serial driver you need
exactly N bytes and any read call will return 0 or N bytes. However,
the timeout only applies to the first character read, so if for some
reason the driver misses one character inside the N byte packet then
the read call could block forever waiting for additional input
characters.

VTIME specifies the amount of time to wait for incoming characters in
tenths of seconds. If VTIME is set to 0 (the default), reads will
block (wait) indefinitely unless the NDELAY option is set on the port
with open or fcntl.

Serial port reading loop in background

Here is a version that would use the DataReceived event:

With the button click you would open the port and register the event

public void button1_Click(object sender, EventArgs e)
{
try
{
serialPort1.PortName = cBoxComPort.Text;
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.ReadTimeout = 5000;
serialPort1.WriteTimeout = 500;
serialPort1.Open();
lblStatusCom.Text = "Connected";
lblMessage.Text = "I am on!";

serialPort1.DataReceived += portDataReceived;

}
catch(Exception err)
{
MessageBox.Show(err.Message,"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//lblStatusCom.Text = "Disconnected";
}
}

private void portDataReceived(object sender, EventArgs args)
{
SerialPort port = sender as SerialPort;

if (port == null)
{
return;
}

string mgs1 = port.ReadLine();
byte[] key = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F };
byte[] enc = StringToByteArray(mgs1);
byte[] dec = Decrypt(enc, key);
if (lblMessage.InvokeRequired)
{
lblMessage.Invoke(new Action(()=> lblMessage.Text = getString(dec)));
}
else
{
lblMessage.Text = getString(dec);
}
}

Incoming serial data loop is causing my program to freeze

Instead of looping to read data, use a DataReceived event to get at the incoming bytes asynchronously.

See the documentation and example.
Also see this question for troubleshooting.

P.S. Here is a code sample to avoid locking up the UI.

private void ReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
var incoming_data = portMotor.ReadExisting();

// this is executing on a separate thread - so throw it on the UI thread
Invoke(new Action(() => {
textBox3.Text += incoming_data;
}));
}

Interrupting a for loop while receiving data from a serial port

You can't, since it is not already running.

Your program won't read another char from the input until the for x in range (1, 10) loop has finished executing.

Imagine the input is 'SK'. Your program will read the 'S' character and execute the loop. In each iteration, sleep stops the whole thread, not just the for loop. So your program will stop 9 times for at least one second each time before processing the next character, 'K', when it will break from the for line in ser.read(1) loop (Also, note that this loop will always run for one iteration only, because ser.read(1) only reads one byte).

If I understand correctly, you want to run a possibly long task after receiving a certain character from the serial input, and stop the running task if you receive another character.
In order to do this, you should run your task in a separate thread.

You'd probably want to encapsulate your task in a class that manages the thread, but the simplest way to do this that I can think of is writing a function for your task and executing it in a Thread. You can use a threading.Event to tell the task to stop when you receive the stop character in your main thread.

import serial
import threading
from time import sleep

ser = serial.Serial(port='/dev/ttyUSB0',baudrate=9600,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=0)

thread = None
stop_task = threading.Event()

def do_task():
for i in xrange(10):
if stop_task.is_set():
break

print(i)
sleep(1)

while True:
byte = ser.read(1): # No need for a loop here, read(1) returns a length 1 string
character = chr(byte) # I'm not familiar with the serial module, but I think this isn't needed

if character == 'S':
# We check that we are not executing the task already, and if so we handle it accordingly
if thread:
print('Error: a task is already running!')
continue

# Start the task in a thread
stop_task.clear()
thread = threading.Thread(target=do_task)
thread.start()
elif character == 'K':
print('K received -> BREAK FROM TASK')

if thread:
stop_task.set()
thread = None

Keep in mind that this will only work for one task at a time only and that in general you should make sure that any thread you start finishes (in this example it is pretty clear they will finish eventually after setting the event, but more complex executions may get the thread stuck in an infinite loop).

It's a small example, but I hope it helps you get in the right direction.

How to create a SerialPort.ReadLine() loop that breaks upon receiving data

Consider using the SerialPort.DataReceived event instead of blocking.



Related Topics



Leave a reply



Submit