Sending serial commands to a Kangaroo motion controller with pyserial - python

Hi everybody, I need help with following problem please:
I try to drive a Dimension Engineering Kangaroo/Sabertooth motor controller from a Python script using pyserial.
Everything works so far except that the motor output stops after some time and the USB/serial connection becomes 'unresponsive'. The faster the rate at which the serial commands are sent, the quicker this happens.
This is how I initialize the serial connection:
# open serial port
Saber = serial.Serial("COM1", 19200, timeout=1, write_timeout=0)
# starting the motor
Saber.write("1,start\r".encode("gbk"))
Then the position commands are sent using a loop:
while True:
pos = 2000 # just an example, the script calculates a new required position every iteration
command = "1,p" + str(pos) + "s500\r"
Saber.write(command.encode("gbk"))
time.sleep(.1) # the lower this break, the faster the device becomes unresponsive...?
After the device stops, I have to disconnect it / power it down, to be able to open a serial connection again.
Could it be that some sort of buffer gets filled and 'locks' the port?
I have tried to free the read/write buffers using:
Saber.reset_input_buffer()
Saber.reset_output_buffer()
But that does not solve the problem :/ Any help is greatly apprechiated! Thanks!

Related

Arduino pyfirmata analog reading problems

I am having some problems reading analog values from my Arduino Mega using pyfirmata.
I use Arduino Mega with a Mega Sensor Shield.
I would like to read analog values from a HW-201 IR sensor (pin A5).
I have uploaded the Standard firmata sketch on Arduino IDE, and I am running the following code using Anaconda Spyder:
import serial
import serial.tools.list_ports
from pyfirmata import ArduinoMega, util
from time import sleep
def readArduinoPort():
COM = []
ports = list(serial.tools.list_ports.comports())
for p in ports:
if "Arduino" in p.description:
COM.append(str(p.device))
return COM
COM = readArduinoPort()
board = ArduinoMega(COM[0])
pin = board.get_pin('a:5:i')
it = util.Iterator(board)
it.start()
try:
while True:
print(pin.read())
sleep(0.1)
except KeyboardInterrupt:
pass
Now, this code runs just fine the first time I use it, generating numbers close to 1 when the sensor is detecting proximity and numbers close to 0 when not detecting anything.
However, whenever I try to run the Iterator a second time (without restarting the kernel), the code generates seemingly random numbers (almost like the pin was floating, like nothing was connected to it).
Any idea why this is happening? Is this a normal behavior?
Thank you!
I already checked these questions that do not entirely address my issue:
arduino pyfirmata analog reading
Analog readings on Arduino returns wrong values
I have the same result by not having anything pinned to pin A5 of the Arduino Mega.
I wasn't sure, but I tried adding:
board.analog[5].enable_reporting()
before the iterator and then:
board.analog[5].disable_reporting()
but nothing changed.
Board Differences
I ran into the same problem. I own both a Mega and an Uno and needed to select the appropriate Board and Processor in the tools menu:
Tools > Board > _board_
Tools > Processor > _processor_

Raspberry Pi, Python: How to send large data over usb serial?

I am trying to send a large amount of data from my Raspberry Pi 4 to my computer. I configured the Raspberry Pi as USB OTG Serial Gadget and the data is sent through the usb-c port to my computer.
Please take a look at the following code running on the Raspberry Pi. One MB of data is sent.
import serial
ser = serial.Serial( port='/dev/ttyGS0', baudrate=115200)
packet = bytearray()
for i in range(0, 1000000):
packet.append(0x2f)
ser.write(packet)
This is the code I am running first on my computer.
import serial
import time
ser = serial.Serial(port='COM30', baudrate=115200)
sum = 0
while 1:
bytesToRead = ser.inWaiting()
if bytesToRead > 0:
serial_line = ser.read(bytesToRead)
sum += bytesToRead
print(sum)
time.sleep(0.01)
I would expect that the received data has always the same length as the sent data. But in this example the computer receives a data length of around 990.000 Bytes in most cases. Even if I run the code without the sleep function on my computer, there are sometimes missing bytes.
How can I make sure that the data is sent and received without data loss?
First, if you have enabled logins on the serial port, you need to disable them first otherwise the port will be inaccessible.
Second, stop getty#ttyGS0.service and disable it.
And then everything will work fine.

Bind Bluetooth device programmatically to rfcomm via python in

i wrote a script in python for serial communication between my M5Stack Stick C (like raduino) and the raspberry pi.
all work fine. i can send "X","Y" or "Z" from raspberry py to the stick and he will reply the value (G-Force) back to the raspi! so far so good
Codes:
Python on raspy:
import serial
import time
import threading
ser = serial.Serial('/dev/rfcomm5') #init serial port
input_line = []#init input char array
def process_data(_data):
#called every time a sream is terminated by \n
#and the command string is ready to use
command = convert(_data)
print(command)
def convert(s): #convert the char list in a string
new = "" #init string to append all chars from char array
for x in s: # traverse in the string
new += str(x)
return new # return string
def processIncomingByte(inByte):#adding incoming chars to input_line
global input_line# globalize the input_line
if(inByte == '\n'):#if \n is incoming, end the chararray and release process data method
process_data(input_line)
input_line = []#reset input_line for next incoming string
elif(inByte == '\r'):
pass
else:#put all incoming chars in input_line
input_line.append(inByte)
while True:
while(ser.in_waiting > 0):#while some data is waiting to read....
processIncomingByte(ser.read())#.... process bytes whit method
ser.write(b'X\n')
time.sleep(0.5)
before the script work, i have to manually bind the m5Stak Stick-C over Blueman
to /dev/Rfcomm5. it work just fine over GUI or Console....
but now i would like to connect the stick via python to rfcomm5 (just by know the MAC adress, will be found in a config file later on...)
i startet to investigate a bit, but the more i research the more confused i am!!
i read some stuff over sockets and server-client aproaches. over a seperated script and so on....
i tested this code:
from bluetooth import *
target_name = "M5-Stick-C"
target_address = None
nearby_devices = discover_devices()
for address in nearby_devices:
if (target_name == lookup_name( address )):
target_address = address
break
if (target_address is not None):
print ("found target bluetooth device with address ", target_address)
else:
print ("could not find target bluetooth device nearby")
and indeed it found the device (just testing)!
but do i realy need to make a second script/process to connect to from my script?
is the the M5stack Stick-C the server? (i think so)
im so confused about all that stuff. i coded a lot, but never whit sockets, server-client stuff.
basically the communication (server/client?) works.
i just need to connect the device i found in the second script via macadress to rfcomm5 (or whatever rfcomm).
do i need a bluetooth socket? like in this example
https://gist.github.com/kevindoran/5428612
isnt the rfcomm the socket or am i wrong?
There are a number of layers that are used in the communication process and depending where you tap into that stack will depend what coding you need to do. The other complication is that BlueZ (the Bluetooth stack on linux) changed how it works over recent times leaving a lot of out of date information on the internet and easy for people to get confused.
With two Bluetooth devices, they need to establish a pairng. This is typically a one off provisioning step. This can be done with tools like Blueman or on the command line with bluetoothctl. Once you have a pairing established between your RPi and the M5Stack Stick, you shouldn't need to discover nearby devices again. Your script should just be able to connect if you tell it which device to connect to.
The M5Stack stick is advertising as having a Serial Port Profile (SPP). This is a layer on top of rfcomm.
There is a blog post about how this type of connection can be done with the standard Python3 installation: http://blog.kevindoran.co/bluetooth-programming-with-python-3/
My expectation is that you will only have to do the client.py on your RPi as the M5Stack Stick is the server. You will need to know its address and which port to connect on. Might be some trial and error on the port number (1 and 3 seem to be common).
Another library that I find helpful for SPP, is bluedot as it abstracts away some of the boilerplate code: https://bluedot.readthedocs.io/en/latest/btcommapi.html#bluetoothclient
So in summary, my recommendation is to use the standard Python Socket library or Bluedot. This will allow you to specify the address of the device you wish to connect to in your code and the underlying libraries will take care of making the connection and setting up the serial port (as long as you have already paired the two devices).
Example of what the above might look like with Bluedot
from bluedot.btcomm import BluetoothClient
from signal import pause
from time import sleep
# Callback to handle data
def data_received(data):
print(data)
sleep(0.5)
c.send("X\n")
# Make connection and establish serial connection
c = BluetoothClient("M5-Stick-C", data_received)
# Send initial requests
c.send("X\n")
# Cause the process to sleep until data received
pause()
Example using the Python socket library:
import socket
from time import sleep
# Device specific information
m5stick_addr = 'xx:xx:xx:xx:xx:xx'
port = 5 # This needs to match M5Stick setting
# Establish connection and setup serial communication
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((m5stick_addr, port))
# Send and receive data
while True:
s.sendall(b'X\n')
data = s.recv(1024)
print(data)
sleep(0.5)
s.close()

How to check simultaneously for serial input and input from keyboard (simultaneous use of readchar and serial library)

I am trying to code the following using python3 in a raspberry pi:
1) wait for a 14 digits bar code (barcode scanner connected through usb port and input received as keyboard data)
2) after a barcode is read, wait for serial communication (device connected to usb port and sends serial commands. could be one, or more....) the idea is that all commands received are going to be associated with the scanned barcode
3) the process of waiting for serial commands has to stop when a new barcode is read. THIS IS THE PART I HAVE NOT FIGURED OUT HOW TO DO IT
After some research, I decided to use the "readchar" library for the barcode scanner and the "serial" library for the serial communication received. Both of them work by themselves but the problem is when I try to detect both things at the same time.
In the following code, I managed to read a barcode and then wait for 5 lines of serial communication to finally repeat the process and read a barcode again. The program works as it is right now BUT the problem is that I don't know how many lines of serial communication I will receive so I need to somehow detect a new barcode while also waiting to receive the serial communication.
import readchar
import time
import serial
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate = 115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
print("Waiting for barcode...")
while 1:
inputStr = ""
while len(inputStr) != 14: #detect only 14 digit barcodes
inputStr += str(readchar.readchar())
inputStr = ''.join(e for e in inputStr if e.isalnum()) #had to add this to strip non alphanumeric characters
currentCode = inputStr
inputStr = ""
print(currentCode)
ser.flushInput()
time.sleep(.1)
# Wait for 5 lines of serial communication
# BUT it should break the while loop when a new barcode is read!
count = 0
while count < 5:
dataRead=ser.readline()
if len(dataRead) > 0:
print(dataRead)
count+=1
print("Waiting for barcode...")
If I add a condition to the while loop that reading the serial communication using (ser.readline()) so that if a character is read from the scanner (readchar.readchar()) then it messes thing up. It is like if readline and reacher can not be in the same while loop.
Doing some research I think I need to use Asynchronous IO, or threads or something like that, but I have no clue. Also I don't know if I could keep using the same libraries (serial and readchar). Please help
I cannot be sure (I don't have your barcode reader and serial port device) but based on what you say I don't think you need threads, you just have to rely on the buffers to keep your data stored until you have time to read them.
Simply change the condition on your second while loop to:
while serial.inWaiting() != 0:
This way you will make sure the RX buffer on your serial port will empty. This approach might or might not work depending on the speed and timing of your devices.
You could also try to add a short delay after the buffer is emptied:
import serial
import time
ser=serial.Serial(port="/dev/ttyUSB0",baudrate=115200, timeout=1.0)
time.sleep(1)
data=b""
timeout = time.time() + 1.0
while ser.inWaiting() or time.time()-timeout < 0.0: #keep reading until the RX buffer is empty and wait for 1 seconds to make sure no more data is coming
if ser.inWaiting() > 0:
data+=ser.read(ser.inWaiting())
timeout = time.time() + 1.0
else:
print("waiting...")
This keeps trying to read from the port for 1 second after the last byte is received, to make sure nothing else is coming. You might want to play with the duration of the delay depending, again, on the speed and timing of your devices.
Again, I don't have your devices, so I'm in no position to judge, but the way you read characters from the barcode/keyboard looks far from optimum. I doubt readchar is the best approach. At the end of the day, your barcode reader is probably a serial port. You might want to dig into that and/or find a more efficient way to read several keyboard strokes in one go.
I found this answer in another question:
How to read keyboard-input?
I have tried it and it works! I´ll also give a try to the method proposed by Marcos G.

Raspberry Pi Xbee serial.Serial timeout not working Python

I am trying to connect Rasberry Pi with Xbee on UART header ttyAMA0
Both are connected and I am getting response of +++ -> OK
Xbee is on mode 2 API
s = serial.Serial("/dev/ttyAMA0", 9600, timeout=5)
xb = ZigBee(s)
xb.send('at', command="SH")
print "working till now"
ansh = xb.wait_read_frame()
print "don't wait here there is a time out"
I am getting output
working till now
And code goes for infinite wait
Please help
I also had this problem with the "SH" at command. After a LOT of debugging and pretty much taking apart the Python-Xbee library I eventually found the solution!
Add "escaped=True" to the constructor:
xb = Zigbee(s, escaped=True)
It would seem that the SH (High Address) response contains bytes which need escaping. Also check your API mode matches.

Categories

Resources