Weird (py)serial linux corruption - python

I have a Linux SBC based on the Atmel SAMA5D36. I have another device hooked up to it via /dev/ttyS2 via TTL lines (115200 8N1). Using pyserial, I have a pretty high bandwidth query/response conversation with that device.
Periodically (at least once a minute), I see a very repeatable corruption of the date coming back from the other device. If it were to respond with some text like
"123456" (ascii character values)
It will drop one character AND add character-0 after the following character:
"13\x00456"
Hopefully that's clear. It will drop the 2, the next character is as expected, a character-0 follows, and then back to normal.
I am using kernel 4.1.10. Via some debug statements, I'm pretty sure this is not happening in my python loop, because the 0's show up in random spots of the read() buffer. I have also hooked up a scope on the incoming lines and have verified that the wire is not carrying this corruption.
I am looking for an answer that can get me in the right direction of figuring out why this is happening. CPU load does seem to increase the frequency (for example, when I'm doing a bunch of DBUS traffic for a BLE adaptor attached).

This could be a result of overflow errors. If you look at atmel_serial you can see if there are any errors.
cat /proc/tty/driver/atmel_serial
For example on ttyS2 you might see something like this (oe: shows the overflow errors):
2: uart:ATMEL_SERIAL mmio:0xF0020000 irq:31 tx:266758 rx:361385 oe:51 RTS|DTR|DSR|CD|RI
Since you are high rate serial you might try implementing DMA on the USART lines. Tweak the appropriate dts file in your kernel by adding the following to your usart settings:
atmel,use-dma-rx;
atmel,use-dma-tx;
For my kernel, I had to disable SPI and I2C so that there would be enough DMA channels available for the USART.

Related

Failing to communicate with digital dial indicator via USB serial python library

I have this digital dial indicator : Helios-Preisser Digimet 1722-502". It comes with a capacity to output its reading over a USB serial cable. The USB cable is a special 4 pin connector on the end that plugs into the calipers and a normal USB on the other end.
Although the device comes with special software, I am trying to write a basic python library to communicate with it. Below is the snippet of the manuel which explains the data communication protocol
I am using the python Serial library and have managed to get some communication going with it. Here's what I have so far
import serial
ser = serial.Serial(port ='/dev/tty.usbserial-MA4LOCLF', baudrate=4800,parity=serial.PARITY_EVEN, bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_TWO, dsrdtr=True, xonxoff=True)
# press the small red button on the cable. This generates a data entry
In [77]: ser.inWaiting()
Out[77]: 8
In [78]: ser.read(8)
Out[78]: '+000.00\r'
So this works great when using the mode the data is being requested by the pressing of the small red button on the cable plugged into the dial indicator
However, there is another mode where one can request a data entry. This is the mode described in the manual as "data transmission by request of peripheral" where one has to pulse the DataRequest pin low for T1 (100ms<T1<1000ms).
I have tried mostly randomly all possible combinations I could think to get this data request working but to no avail. All attempts at using the write function from the serial library have not worked
In [79]: ser.write('0\r')
Out[79]: 2
In [80]: ser.inWaiting()
Out[80]: 0
I am a bit out of ideas. I know this mode works because when you download (in Windows only) the software which comes with the device, you have the ability to send that request. SO there must be a way of emulating this request with the python serial library but I'm stuck and I'm not even sure how to proceed.
Any help would be appreciated.
Thanks
pyserial supports (or it should, but at the end of the day that would depend on the driver and not on pyserial itself) a function to change the state of the port's control lines.
You need to do something like this:
ser.setDTR(False)
time.sleep(0.5)
ser.setDTR(True)
If you read from the port right after you should get the value you are looking for.
To get the timing more or less right you should run your code as a script instead of line by line.
Ok, well after much "messing around", I was able to debug this problem and find the solution but it was certainly not a linear process. I should point out that I was placed on the right path thanks to the suggestions by Marcos G. who pointed out that you can indeed control the dtr lines directly with pyserial.
Here's the answer.
After some more searching the web, I found this link which provided a suggestion for how to troubleshoot your serial connection on a mac. This Coolterm software was immensely helpful.
I downloaded it and it allows you to monitor the state of the various RTS and DTS (and other) lines while you are communicating in a real time with you device. Super useful!
This showed me that:
When "sending" a reading from the device by pressing the red button, the DTR line was True and the RTS line was irrelevant. This is indeed what is shown in the documentation.
In order to "request" a reading from the device the DTR line needed to be False and the RTS line needed to be pulsed from False to True back to False with the precise timing. This worked and indeed produced a reading waiting on the USB line.
Here's the code below
import time
import serial
ser = serial.Serial(port ='/dev/tty.usbserial-MA4LOCLF', baudrate=4800,parity=serial.PARITY_EVEN, bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_TWO, dsrdtr=True)
ser.dtr=False
ser.rts=False
# request one reading every second
while(1):
ser.rts=True
time.sleep(0.1)
ser.rts=False
time.sleep(0.1)
print ser.read(ser.inWaiting())
time.sleep(1)

No response using PyVISA from instrument on GPIB

I'm trying to control an instrument (very old hall measurement device) on a GPIB with PyVISA. I know it works with labview, where I've found which addresses do what and some basic commands with a tracer but to no joy. I've been asked to write a DAQ and analytical code in python 3.
So far I've been able to identify the addresses:
import visa
rm = visa.ResourceManager()
print(rm.list_resources())
>>>('ASRL1::INSTR', 'GPIB0::3::0::INSTR', 'GPIB0::3::1::INSTR', 'GPIB0::3::3::INSTR', 'GPIB0::3::4::INSTR', 'GPIB0::3::5::INSTR', 'GPIB0::3::6::INSTR', 'GPIB0::3::7::INSTR', 'GPIB0::3::8::INSTR', 'GPIB0::3::9::INSTR', 'GPIB0::3::10::INSTR')
however when I try to query with any "wave" or indeed measurement command string (found tracing labview I/O) I am always met with a timeout error.
instr3_8 = rm.open_resource('GPIB0::3::8::INSTR')
instr3_8.query("*IDN?")
>>>VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
This is the result for everything I try to read from the instrument.
(PyVISA and the GPIB work with a Keithley source meter IDN query so I know the backend is working. The instrument is a bio-rad HL 5200 on the off chance that's of use to anyone, I've found no manual and next to no reference online.)
Here's an image of the labview block diagram that reads the magnet position. I've since been able to change the position by writing with pyvisa but reading still gives a timeout, similar issues on github lead me to believe the termination character is wrong but I've no fix yet.
The issue came down to both PyVISA and NI MAX not recognising the termination characters when reading from the instrument. After trying all the possible termination characters with .read_termination I found one that worked can finally read from my hall probe.
Very old GPIB instruments often have incomplete or non-standard implementation of GPIB. For example they can lack support for *IDN?, or have only one hard-coded command termination character.
Some of these old instrument also use address +1 as a 'printer' address. So the instrument reserves the address it is set to, but also the next one. This can be a cause of major confusion specially if there are more (old) instruments on the same network, so it is good idea never to use consequent addresses but go 1, 3, 5, 7 instead.
Also, even just polling the status byte too frequently can overload the processing capabilities of really old instruments, let alone reading the answer buffer.

PySerial does not receive data correctly

I have a little problem receiving data correctly via pySerial: it often does not read the full data or too much of it. Sometimes and sometimes more often, there are additional characters or some characters/parts of the sended data are missing. It seems, PC and the emitter of the data are not synchronised correctly.
In the current example I use a arduino, sending 'Hello World' to the serial port of my PC (os is Ubuntu 14.04), with the following simple code:
void setup(){
Serial.begin(9600);
Serial.print("Programme initiated\n");
}
I use the following python3 code to receive the data:
import serial
import time
arduino = serial.Serial(port, baudrate=9600, timeout=2)
print(arduino.isOpen)
print(arduino)
time.sleep(1)
while True:
print(arduino.readline())
This is pretty much a simple tutorial example, and here is what I receive (apart from the correct stuff):
b'PrProgramme initiated\n'
or
b'PProgramme initiated\n'
or
b'ProgProgramme initiated\n'
or
b'ogramme initiated\n'
I moved on with more complex problems in my code, but still I didn't solved that problem. When sending a message in a loop from the arduino (the standard hello world code), it often needs time to stabilise (while that, it again does only show the middle fragment of the data) and after that running quite stable, but even then it sometimes breaks down single lines.
I faced the same difficulties when communicating with a multimeter device. There, it often does not read the first characters or mixes up with previous data.
Did anyone faced that problem before? I think it is a question of synchronisation, but I don't know how to solve it. And what puzzles me, is that I really only used tutorial stuff and it doesn't seem to work properly. Or is it a configuration problem of my PC?
What you are looking at is happening because some different things are going on.
First of all every time you open the serial port, you are causing what is called and "autoreset" and the Arduino reboot. That can be avoided in hardware or even in software by explicitly disabling RST signal on open. How to do that may vary and is out of scope of the question.
Then we have to understand that serial does NOT wait for the other part to be listening to send data; so if you disable the autoreset and connecting to the Arduino you should see random part of output of the program, depending of its current state.
Finally we have some buffer capability on pc (and sometimes even on the UART to USB side), so its not true that if you are not listening that data get lost, it may be still in the buffer.
We could say the first 3 artifact may be given by buffered data + reboot (this happen a lot when you send a lot of data, and that break the autoupload of code and you have to do a manual procedure), while the last one may be something that prevented the buffer to fill, maybe it was disabled by you, maybe some weird timing opening the serial, maybe you disabled the autoreset, maybe time that the arduino got enumerated part of the message was gone.

Python Serial Communication Using HC-05 Connected to OS X Version 10.11.1 (15B42)

this is my first post on stack overflow yet I use it all the time to solve my many programming issues. So first off, thank you for any help that is given.
I have been given a task by my advisor to get HC-05 sending and receiving serial comms with python and I am at my wits end on the final part of getting it up and running. I have searched everything I possibly can and have not yet found the answer, or even a similar issue. I have the comms working somewhat well. My baud rate is set at 115200 and have that matched in python serial. I am using an Arduino to gather values from sensors on a robot, send the values to python over serial to run calculations then have the result send back. I am sending two values and only receiving one. I first got it up and running by using serial comms over usb cable and it works perfect. I then made a few alterations to get it sending over bluetooth. The data that is being sent from the Arduino through bluetooth is perfect. When I do calculations and send the result back, however, the values are off. I have a timer Interrupt Service Routine (ISR) running on the Arduino that sends the values at 100Hz. If I slow that down to 10Hz, the response from computer to Arduino gets better and if I slow it down to 1HZ it is perfect. Unfortunately, I really need it to be working well at 100Hz for accurate control of my robot. I have included a link below of a plot of the response at 10Hz. Alpha and Theta are values sent from Arduino, V is a calculation done by python and V_echo is V being sent to Arduino, multiplied by 2 and sent back to python. As you can see, V_echo is broken and jagged when it should be a smooth line like the others. All of this tells me that while the send rate of the HC-05 is fine, the receive rate is too slow. I have changed baud rate possibilities, played with making the HC-05 a master vs. slave and slave-loop... slave works the best. The last thing I can think of is using stop bits. I have pyserial sending two stop bits and have the HC=05 stop bit set to two by using AT+UART=115200,1,0. If stop bits are my solution, I clearly have it set up wrong. I don't know exactly what python does when I include stop bits and furthermore I don't know if I need to do any program that reads the bit or if the HC-05 just understands it... If stop bits are not my answer, I look forward to finding out what is. Thanks again.

Pyserial buffer fills faster than I can read

I am reading data from a microcontroller via serial, at a baudrate of 921600. I'm reading a large amount of ASCII csv data, and since it comes in so fast, the buffer get's filled and all the rest of the data gets lost before I can read it. I know I could manually edit the pyserial source code for serialwin32 to increase the buffer size, but I was wondering if there is another way around it?
I can only estimate the amount of data I will receive, but it is somewhere around 200kB of data.
Have you considered reading from the serial interface in a separate thread that is running prior to sending the command to uC to send the data?
This would remove some of the delay after the write command and starting the read. There are other SO users who have had success with this method, granted they weren't having buffer overruns.
If this isn't clear let me know and I can throw something together to show this.
EDIT
Thinking about it a bit more, if you're trying to read from the buffer and write it out to the file system even the standalone thread might not save you. To minimize the processing time you might consider reading say 100 bytes at a time serial.Read(size=100) and pushing that data into a Queue to process it all after the transfer has completed
Pseudo Code Example
def thread_main_loop(myserialobj, data_queue):
data_queue.put_no_wait(myserialobj.Read(size=100))
def process_queue_when_done(data_queue):
while(1):
if len(data_queue) > 0:
poped_data = data_queue.get_no_wait()
# Process the data as needed
else:
break;
There's a "Receive Buffer" slider that's accessible from the com port's Properties Page in Device Manager. It is found by following the Advanced button on the "Port Settings" tab.
More info:
http://support.microsoft.com/kb/131016 under heading Receive Buffer
http://tldp.org/HOWTO/Serial-HOWTO-4.html under heading Interrupts
Try knocking it down a notch or two.
You do not need to manually change pyserial code.
If you run your code on Windows platform, you simply need to add a line in your code
ser.set_buffer_size(rx_size = 12800, tx_size = 12800)
Where 12800 is an arbitrary number I chose. You can make receiving(rx) and transmitting(tx) buffer as big as 2147483647a
See also:
https://docs.python.org/3/library/ctypes.html
https://msdn.microsoft.com/en-us/library/system.io.ports.serialport.readbuffersize(v=vs.110).aspx
You might be able to setup the serial port from the DLL
// Setup serial
mySerialPort.BaudRate = 9600;
mySerialPort.PortName = comPort;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
mySerialPort.RtsEnable = true;
mySerialPort.ReadBufferSize = 32768;
Property Value
Type: System.Int32
The buffer size, in bytes. The default value is 4096; the maximum value is that of a positive int, or 2147483647
And then open and use it in Python
I am somewhat surprised that nobody has yet mentioned the correct solution to such problems (when available), which is effective flow control through either software (XON/XOFF) or hardware flow control between the microcontroller and its sink. The issue is well described by this web article.
It may be that the source device doesn't honour such protocols, in which case you are stuck with a series of solutions that delegate the problem upwards to where more resources are available (move it from the UART buffer to the driver and upwards towards your application code). If you are losing data, it would certainly seem sensible to try and implement a lower data rate if that's a possibility.
For me the problem was it was overloading the buffer when receiving data from the Arduino.
All I had to do was mySerialPort.flushInput() and it worked.
I don't know why mySerialPort.flush() didn't work. flush() must only flush the outgoing data?
All I know is mySerialPort.flushInput() solved my problems.

Categories

Resources