Minimum delay to communicate through serial using Python - python

I have a simple Python script to communicate with a micro-controller (STM32F103C8T6) using the serial port. I'm using pySerial to write a couple of 44-bytes messages at a time.
[...]
serial = serial.Serial(serial.tools.list_ports.comports()[0].device, 115200)
packet0 = bytearray(INSERT_RELEVANT_44-BYTES)
packet1 = bytearray(INSERT_RELEVANT_44-BYTES)
serial.write(packet0)
time.sleep(0.1) # Delay between communications
serial.write(packet1)
[...]
I had to insert a delay between the communications, otherwise it wouldn't work. My reasoning is that for a baud rate of 115200 bps the messages should take 44*8/115200 = ~0,003 seconds to be sent, thus this should be the minimum ideal interval between sending the packets. The code, however, doesn't work for anything smaller than 0,1.
Why? Am I missing something? I suppose there is some delay due to the operating system and the USB, but it shouldn't account for ~0,7 seconds. How can I optimize this to use the minimum possible delay?

Rather then calculating a nominal delay based on the UART link, you could simply poll the serial driver to determine whether the Tx buffer is empty:
serial.write(packet0)
while serial.outWaiting() > 0 :
pass
serial.write(packet1)
This has the advantage of accounting automatically for any latency, software overhead and buffer limitations anywhere in the chain of application code, library, driver, USB-Serial bridge. It will not however solve any problem with the STM32 serial I/O implementation, you should probably address the root problem of why the data cannot be streamed, which is most likely down to poor implementation of the at the STM32 device end.

Related

Pymodbus with Raspberry 4 PL011 uart4 and TTL/RS485 transreceiver

I'm trying to act as a master with my Pi 4 B in an rs485 Modbus communication.
I'll ask for register values.
All ok with pyModbus and a USB-RS485 (like ftdi) using its virtual com (/dev/ttyUSB0)
Now...
I summoned the uart4 with the standard procedure.
dtoverlay=uart4,ctsrts
So I'll be working with /dev/ttyAMA1, TX=GPIO8, RX=GPIO9, RTS=GPIO11, CTS=GPIO10
I bought one of those cheap TTL/RS-485.
DE and RE are connected to uart RTS (CTS enabled but not used).
TX to DI, RX to RO
I ask for 5 registers one at a time (with 3s timeout for the response each) and a final pause of 3 sec.
I connected some LEDs and slowed the baud rate to 1200 to see how LEDs act.
From the other side, I'm monitoring Modbus activity with ftdi USB-rs485 and dock light programmed to respond with a certain message (acts as a 'fake' slave)
No activity of the ftdi at all.
If I de-attach DE and leave only RE with RTS, I see the TTL TX message on the RX pin (like a looping echo).
What's wrong??
I bought one of those cheap TTL/RS-485. DE and RE together and connected to uart RTS (CTS enabled but not used).
That is not right. With Modbus only one device is allowed to use the bus at a time, so you need one of those GPOx (it can also be RTS or CTS) to go low right before the device is ready to write something on the bus and stay low until the whole frame is transmitted. After a determined amount of time elapses, the signal has to go high to free the bus and allow other devices to talk.
For the practical part, since this is a very common problem, you will find all you need to fix your problem in this Q&As. A very short summary: you need to tweak whatever library you use to add the signaling to feed the drive enable/read enable pin on your TTL converter.
If you are wondering why the USB converter works and the TTL won't, the answer is very simple: your USB converter has this signal implemented in its hardware (you can read more details on the link).
The problem is not difficult to fix, but if you are short on patience or don't have time to waste, just take the fast track and buy a decent transceiver. There are many options, this is just one (I'm not affiliated with the manufacturer nor I make any profit off it). You can also stick to those connected through USB; most of them will work (not all though).
If you decide to implement the software solution be aware that you will be asking for real-time performance on a non-real-time platform, meaning that even though the solution will probably work almost 100% of the time, it is not guaranteed to always work. If your RPi is loaded with too much work, there might be instances where the signaling will not be properly timed and some frames might not get to the other side. You are warned: if your application is mission-critical in any way, just forget about it and buy a proper transceiver.

CanOpen communication (Python) 1 Slave and CAN-USB adapter

I am currently trying to implement simple communication between an I/O module as a CanOpen slave and my computer(Python script). The I/O module is connected to my computer with a PEAK USB-CAN adapter.
My goal would be to read or write the inputs/outputs. Is this even possible with the hardware, since I don't have a real "master" from that point of view?
Unfortunately I don't know what else I have to do to be able to communicate correctly with my I/O module.
import canopen
import time
network = canopen.Network()
network.connect(bustype='pcan', channel='PCAN_USBBUS1', bitrate=500000)
#add node and DCF File
IO_module = network.add_node(1, 'path to my DIO.DCF')
network.add_node(IO_module)
IO_module.nmt.state = 'RESET COMMUNICATION' # 000h 82 01
print(IO_module.nmt.state)
time.sleep(5)
IO_module.nmt.state = 'OPERATIONAL'
print(IO_module.nmt.state)
for node_id in network:
print(network[node_id])
IO_module.load_configuration()
i see some kind of communication in my console with timeout errors
INITIALISING
OPERATIONAL
<canopen.node.remote.RemoteNode object at 0x000002A023493A30>
Transfer aborted by client with code 0x05040000
No SDO response received
Transfer aborted by client with code 0x05040000
No SDO response received
Any advices ?
I can't get any further with the documentation alone
https://canopen.readthedocs.io/en/latest/
thank you
The good news is, you probably have all the required hardware. You are doing the "master" part from Python, that's fine. (The CAN bus isn't really master/slave, just broadcast. CANopen can be master/slave sometimes, but it's still all broadcast messages among equals on the same bus.)
You haven't provided information about your device, but I would start checking at a lower level.
Do you even have the CAN-Bus wired up correctly, and if so, what did you do to verify it? (Most common mistake: CAN-Bus not terminated with two 120ohm resistors. Though you usually can get away with just one instead of two.) And have you verified that you are using the correct baud rate?
The library docu example suggests to wait for the heartbeat with node.nmt.wait_for_heartbeat(). Why are you using a sleep instead? If there is no heartbeat, you don't need to continue. (Unless the device docu says that it doesn't implement NMT heartbeat - would be unusual.)
I certainly wouldn't try to go ahead with SDOs if you cannot confirm a NMT heartbeat. Also, some devices don't implement SDOs but only PDOs.
Try sniffing the CAN bus at a lower level (e.g. not PDOs/SDOs but just print the raw messages received - from Python, or with a separate application - e.g. candump on Linux.) Try getting statistics of the CAN "network" interface (on Linux, e.g. ifconfig). If everything is okay, the adapter should be in state "ERROR-ACTIVE", and you should see the frame counter increase for frames you've sent via Python.

changing update rate with gpsd and python

I'm using the adafruit ultimate GPS breakout with my Raspberry Pi 2 using python2.7.9, GPSD, and the python-gps package. I'm successfully getting gps updates at 1Hz over the serial port.
This device is supposedly capable of 10Hz updates, which I would like to enable. According to the datasheet (see snippet below), this can be set by serial command.
My problem is that I can't find sufficient documentation for the python-gps module or GPSD that would tell me how I'm supposed to send commands to the GPS over serial with my python script. Can someone point me in the right direction?
I just picked up a gps from the same family as yours. (MTK) They use $PMTK control strings. Any search for PMTK protocol gives endless resources. The limit is the gps itself.
For a one-off conversion, the standard method is from a root terminal execute echo -e "\$PMTK220,200*2C\r\n" > /dev/ttyAMA0, or wherever the device is attached, for a 5Hz response. The
-e flag allows parsing the backslash(s)
$ The start of the NMEA sentence
P Proprietary message
MTK NMEA Data type
220 Packet type
200 Packet data
* End of data
2C Checksum
\r\n The end of the NMEA sentence
There are scripts and other projects available as well, but all require the gpsd to not be in control of the gps. The gpsd will prevent sending a control string to the gps.
Additionally, if you have the -b flag in /etc/default/gpsd the gpsd will not write to the device when it is attached. It will choose a generic NMEA driver, and will not write any control strings to the gps.
You can slip in behind, and manipulate the rate with control strings from the shell. The gps will spew the data, but the gpsd will not acknowledge the rate.
It appears, gpsd prefers the 1 second timing. Even if the gps is capable of faster cycles, even if you have already use another methods to re/set the rate, and it doing so, the gpsd needs to be told the rate has changed.
This is where gpsctl -c 0.2 (no sudo) comes into play. It is bundled in the gpsd package.
If there is only one device connected to the gpsd, in the example, gpsctl will change that device to 0.2 second timing cycles and convey that to the gpsd. The time is in seconds. Yes, it can be set pretty slow/fast, but there's no faking ability. If it can't do it, it won't do it, nor tell you that it hasn't/can't, unless there is a fatal error.
Port speed is not an issue, as long as there is capacity. Someone who counted once said 4800 baud is sufficient for one reading of data in the one second it takes to send the data. It depends on payload, but it works for a rule of thumb. 10Hz might make the 38400 baud default on many systems. I tried it and nothing broke.
You just want to make sure gpsd hasn't negotiated to 9600 baud before you can ramp up the speed, just in case. gpsctl -s XXXX (to set the baud rate at which the GPS emits packets) returns an error for me.
Even without the -bflag in the gpsd default setting, this new gps of mine doesn't stay latched into the higher frequency updates between powerdowns. I must re-issue the command. It might be a bad battery, operator error, or I don't understand this defect is a feature to not latch the gps out of reach for other systems. (A reason for the -b)
This is how you change the response from a gps that uses the gpsd, or how I did.
In answering your question, however, to change the rate of a gps response through the gpsd with Python
import subprocess
subprocess.call(['gpsctl', '-c', '0.2']) # Digits are the time in seconds.
Try it and see. Using gpsctl -c 0.25 returns quarter second gps readings, etc..
To help are two Python gpsd client demonstration scripts to which I have just added a gpsd device report refresh keystroke, (Hit d to refresh and see the numbers from the new setting.)
They are for a Python 2.7-3.5 gpsd python client, when the function finds a home in the demo scripts it will look something like this:
def hertz(hz):
"""Change or enumerate a Faster/Slower gps refresh rate if device is able"""
from subprocess import call
inverse = str(1 / hz)
call((['gpsctl', '-c', inverse]))

RS232 with raspberry pi

My issue is to make a serial communication between raspberry pi and another hardware. The recommended connection for this hardware is as shown on the manual, I have to connect, RX, TX, GND, RS, and CS.
But on raspberry pi we have only RX, TX so I connected RX and TX and The GNG of Pi to this hardware.
I modified Pi's parameters as shown on the link : here
Then I maked a simple python program that initialize the communication, and send data.
Here is the code :
import serial,os
port=serial.Serial("/dev/ttyAMA0",baudrate=9600)
print ('port is ok')
port.write('Command')
rcv=port.read(10)
print rcv
after running this code on pi, I got ('port is ok'), But the problem is that this hardware don't respond correctly to the command, and as respoce it gave me normally OK, but I got some extra caracter( non readable).
Is that a problem of encoding? Can some one help about this?
You need to check the baud rate on the other hardware
or make sure that the length of the received message = to the printed message.
In a serial communication, there are two important things to be careful :
The two devices have to work with the same baudrate IF the link is bidirectional.
When writing data on serial, you have to flush data just after the write().
refer to here for it.
In a lot of case, flush isn't needed, but when two different devices have to communicate, it could unlock the comm'.
If it's not efficient, try to set up your other device with the same conf (no flow control, etc)

How does PySerial work?

Say I have the following python script to read in serial data from my Arduino:
import serial
ser = serial.Serial("dev/ttyACM1", 9600)
ser.timeout = 2
ser.readlines()
On the other end I've flashed my Arduino with a program that sends 20 voltage readings every 0.5 seconds. The Arduino starts sending those readings from the moment it's hooked up, then after 20 seconds it stops and sends nothing.
Now what I've noticed is that I can read those 20 voltage values using the first script whenever I want. That is, I can hook up the Arduino, wait a couple of minutes then read in the values. This makes me think that the data is getting stored somewhere. I'm inclined to think that it's not being stored on the Arduino but on my laptop somewhere.
I've come up with a few questions that I hope the community could help me with:
Where is PySerial getting the data from (the Arduino or some buffer on my laptop)?
For how long is the data stored in this place?
Is there a way to clear this space before reading in values?
How much storage space is there?
When you set the baud rate in PySerial (second line in script), is this the rate that
PySerial reads data from the storage area (not the Arduino)?
I've noticed that if I set the baud rate in PySerial too high the first few lines of data are fragmented and sometimes completely wrong, why?
Not exactly related but when you set serial.Serial.timeout are the
units in seconds?
I appreciate your time.
Have you tried using a terminal program like TerraTerm (windows) or GTKTerm (linux) to open the same port to the arduino? I think this would be helpful to answer some of your questions.
Some quick answers to your questions that I can dump off the top of my head.
From the port specified, I'm guessing you are asking something deeper than this?
If you do a
x = ser.readlines()
then the data will be in x as long as you'd like.
There is a flush function defined in PySerial
Not sure. You can specify how many characters you would like to read though
example:
x = ser.read(number)
The pyserial documentation states the following
Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.
http://pyserial.sourceforge.net/pyserial_api.html
This is the clock rate of the port you are opening, ie /dev/ttyACM1, most serial comms are at 9600, if you happen to be using a USB to serial you'll need 115200
Clock rate mismatch. You're computer is sampling data at a rate higher than the arduino is providing it, causing it to be incorrectly displayed.
Seconds, quote from Pyserial documentation : "timeout = x: set timeout to x seconds (float allowed)" Same link as number 4
Hope that helps some!

Categories