RPI UART receiving weird data - python

I have a Raspberry Pi 3B+ with an IC card reader that uses UART. The program is written in Python.
The data it receives is often wrong, but sometimes accurate.
There are 3 variations of the same card:
b'\\x02\\t\\x01\\xf4\\xec7\\xf6\\xd1\\x03'
HEX: f4ec37f6
DEC: 4109121526
b'\\x01\\x02\\x9d\\x85\\\\xfe'
HEX: 855cfe
DEC: 0008740094
b'\\x01\\x86\\x9d\\xc5\\\\xfe'
HEX: c55cfe
DEC: 0012934398
The first variation is the right one, which the card reader of my computer always reads the same result 4109121526, and it seldom appears on the RPI. For the other 2 variations, they always show up without a specific pattern.
The baud rate is 9600, I tried other rates, but it got even worse. The timeout is 0.5s, doesn't seem to matter.
Tried switching to a better power supply, nothing changed.
The store that sold me the reader told me to check the protocol and do data validation, but I have no idea what he's talking about. Is that related to the issue?
Got any idea? Thanks in advance!

Related

Arduino serial tx to Raspberry Pi serial rx

I haven't found a useful answer to my problem from the many forums I've found. If you know one please comment a link below.
I have a Raspberry Pi Zero and an Arduino Fio w/ an Xbee module. I'm currently trying to do a simple test with the Zero and an Arduino Uno just to practice serial communication. I only need the Zero to receive data and not transmit, which is what several forums only direct towards. I want to take an integer from a compass heading (received by the Xbee from a beacon unit at some distance away).
So for my practice I send an integer on the Arduino using Serial.write(). On the Zero I simply use python and the library pyserial with the function serial.readline(). Also, yes, I've stepped the Arduino tx pin's voltage to 3.3 volts and I know the Zero is receiving a transmission. When I set the Arduino integer to say 39, the Zero will (with a print function) show the ASCII character corresponding to the hex value of 39 which is 9 (but it is a string not an integer).
In essence, my dilemma is I can't figure out how to convert the transmission to an integer that I can use trig with to make calculations. Firstly, I'm not sure if it's possible to make a fix on the Arduino side. Secondly, if it can be on the Zero, I'm not familiar enough with the pyserial library to know how to fix it. In addition to my second point, I've tried type casting it with int(string) to convert it to a string. I get an error code saying that the operation is invalid with an operator of base 10 (or something close to that).
I'm currently at my home and do not have access to my code on either the Arduino or Zero so please talk to me first instead of complaining 'show code'. I'm an experienced programmer and adept enough to talk through complications. Thank you.

Impulse length - Arduino -> Raspberry

I dont know why is this happening but am quite pissed off :-), because am struggling with this for two days now..
HW:
Arduino board --> Raspberry Model 2 B
Am sending one second impulse from arduino mega digital pin 8 to raspberry.
I added voltage divider so i have clean 3,25V impulse.
RSP reads most of impulses correctly but sometimes i get false readings.
E.g. one impulse is triggered and few are not or it starts to trigger impulses but I didn't send any from arduino side.
I try with pullup, pulldown setup but the problem is the same.
Maybe i should define that the "impulse" is value that last 1 second and everything else is "garbage" ?. If so, how to make this ?
CODE:
GPIO.setmode(GPIO.BCM)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
...
GPIO.Cleanup()
The funny part is that if i test the impulse from RSP 3,3V pin, it works every time ( of course with pull_down setup ). So, what could be a problem ?
3,3V and 3,25 is no difference at all and RSP defines HIGH from 2.8 - 3.3V...
Any ideas ?
For me, it's more a matter of electronic.
In fact, for that kind of library, we can assume that the functions work correctly (I suppose that you wrote correct code that was built like that: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/buttons_and_switches/).
I believe that the electronic problem can come from several sources:
Either your ground is badly connected (or it's not the same for the arduino and the rasperry pi). So, the reference tension isn't the same and a 0V for the arduino can be 1V for the raspberry pi.
It can also be a result of a bad interpretation of when your circuit is on and off. Don't forget that to put a plain 0V in the input of the raspberry pi (when the circuit is off). You need to explicitly write 0V at the output of the arduino (I mean: digitalWrite(ledPin, LOW);) otherwise your pin would not be "grounded" but "floating" and the tension can fluctuate from any value (close to 0) to another depending on environmental factors.
(to achieve it, you can also use pull down resistances, see: http://playground.arduino.cc/CommonTopics/PullUpDownResistor).
It can also be a transient phenomenon due to unwanted capacitive reactions. So, when your current changes quickly, the circuit cannot follow it instantaneously and it will produce some voltage glitches.
I suggest checking the two first points. If it cannot solve your problem, it should be related to the second point and you could handle it with capacitors and wait times.
I hope it will help :)
Thank you for replay. This are the test's that I made today.
- I have connected the zener diode to get the 3,3v and this part is good
- tested with different impulse lengths
None of them worked.
It's interesting, if i start the RSP script and if i just wait for impulse ( without triggering it ) i will get some 30 reading in few minutes. So it looks like that there is something going on.
I disabled this part of the code in arduino, but i still get this readings...
Btw. should i connect arduino and rsp GND together ?
E.

how do I monitor data from serial port and have certain data act as a flag in Python

I am working on a project for work and I am stuck on a part where I need to monitor a serial line and listen for certain words using python
so the setup is that we have a automated RAM testing machine that tests RAM one module at a time and interacts with software that came with the machine via serial. The software that came with the RAM tester is for monitoring/configuring the testing process, it also displays all of the information from the SPD chip from each module. while the RAM tester was running I ran a serial port monitoring program and I was able to see the same information that it displays in the software. The data I'm interested in is the speed of the RAM and the pass/fail result, both of which I was able to find in the data I monitored coming over the serial line. There are only 5 different speeds of RAM that we test, so I was hoping to have python monitor the serial line and wait for the speed of the RAM and the pass/fail results to come across. once python detects the speed of the RAM, and if it passes, I will have python write to an Arduino, and the Arduino will control a conveyor belt that will sort the ram by speed.
my idea is to have a variable for each of the RAM speeds and set the variables to 0. when python detects the RAM speed from the serial line it will set the corresponding variable to 1. then when the test is over the results, either pass or fail, will come over the serial line. this is where I am going to try to use a if statement. I imagine it would look something like this:
if PC-6400 == 1 and ser.read() == pass
ser.write(PC-6400) #serial write to the arduino
I know the use of the ser.read() == pass is incorrect and that's where I'm stuck. I do not know how to use a ser.read() function to look for certain words. I need it to look for the ram speed (in this case its PC-6400) and the word pass but I have not been successful in getting it to find either. I am currently suck in is getting python to detect the RAM speed so it can change the value of the variable. would it be something close to this?
if ser.read() == PC-6400
PC-6400 = 1
This whole thing is a bit difficult for me to explain and I hope it all makes sense, I thank you in advance if anyone can give me some advice on how to get this going. I am pretty new to python and this is the most adventurous project I have worked on using python so far.
I'm still a bit confused, but here's a very basic example to hopefully get you started.
This will read from a serial port. It will attempt to read 512 bytes (which just means 512 characters from a string). If 512 bytes aren't available then it will wait forever, so make sure you set a timeout when you made the serial connection.
return_str = ser.read(size = 512)
You can also see how many bytes are waiting to be read:
print "num bytes available = ", ser.inWaiting()
Once you have a string, you can check words within the string:
if "PASS" in return_str:
print "the module passed!"
if "PC-6400" in return_str:
print "module type is PC-6400"
To do something similar, but with variables:
# the name pass is reserved
pass_flag = "PASS"
PC6400 = 0
if pass_flag in return_str and "PC-6400" in return_str:
PC6400 = 1
Keep in mind that you it is possible to read part of a line if you are too quick. You can add delays by using timeouts or the time.sleep() function. You might also find you need to wait for a couple of seconds after initiating the connection as the arduino resets when you connect. This will give it time to reset before you try and read/write.

pyserial 2.7, python 3.3, sending carriage returns

I have been attempting to have a Raspberry Pi interface with an embedded circuit using the UART interface. The UART interface on the Pi is in working order and I can receive messages from the circuit, though I am having trouble sending messages to the circuit.
I am using Python 3.3 with Pyserial 2.7. Sample code is available, though it uses Pyserial 2.6. When used with older versions of Python (<2.6), ser.write() accepts strings, but now it only accepts bytearrays.
The problem I am having is in sending carriage returns... The old code supposedly functioned with just:
ser.write("L1\r")
but now I am using the following:
ser.write(bytearray("L1\r", "ascii"))
The circuit does not respond to the command. I think the resultant message is sending \r as two individual characters rather than a carriage return. How would I make sure my code is outputting commands appended with carriage returns?
Notes: I can reasonably expect that the circuit is working well and that the Pi's UART interface is functional. The circuit is an Atlas Scientific Dissolved Oxygen Circuit. The circuit's documentation demands that commands be written in the form l1<cr> or L1<CR>.
Relevant links:
Old sample code (https://www.atlas-scientific.com/_files/code/pi_sample_code.pdf)
Documentation describing write method (http://pyserial.sourceforge.net/pyserial_api.html#classes)
Thanks in advance!
EDIT: Netch makes a strong point: ser.write(b'L1\r') works and is much cleaner. Both methods, however, ARE sending a correct '\r' sequence.. The problem is that the circuit still does not regard L1\r as a valid command. At this point, I think my issue may be some property of my serial port.
My port is declared as such:
ser = serial.Serial(
port = '/dev/ttyAMA0',
baudrate = 38400,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
timeout = 1
)
This port declaration is done with accordance to the circuit's datasheet (I can only post two links unfortunately :( Google brings it up easily).
[EDIT] For future viewers, I just want to point out that for the newer EZO version of the circuit, the command is "L,1" or really "L,1\r"
[/EDIT]
This is a known issue with the circuit. The first read after power up will fail. instead of prepending a \r with every read, just send a bogus command with \r included and then reset input buffer
ser.write('bogus\r'.encode()) # EDIT: had to add .encode() to get it to work. see https://stackoverflow.com/questions/22275079/pyserial-write-wont-take-my-string
ser.flushInput() # or for pyserial 3+ ser.reset_input_buffer()
#now do your thing
EDIT: had to add .encode() to get it to work. see pySerial write() won't take my string
After powering up the EZO™ class circuit when it is in UART mode the
first command sent to it will comeback as an error. This is because
the UART buffer will show that it has received a character during
power up. Simply send a blank character to the pH circuit after it is
powered up, this will clear the buffer.
I have found a solution!! Unfortunately, I cannot explain how it works. Perhaps anyone reading this could elaborate on it and give a proper explanation!
The circuit's documentation demands commands be in the form CMD<CR>. Indeed, sample code provided by the manufacturer sends the L1 command through pyserial as ser.write("L1\r").
Now that ser.write() demands bytes however, I have found that ser.write(b'L1\r') does not work.. The command is received though it is somehow unknown to the circuit.
After toying around for a while, I have discovered that ser.write(b'\rL1\r') works! The debugging led flashes red once before processing the command. It seems like I just need to send a 'dummy command' to get the circuit's attention!
I am not sure if this is the fault of pyserial, the circuit, or my own ignorance. If anyone can shed some light on this, it would be much appreciated! :D
I have linked here the circuits documentation in case anyone is interested. https://www.atlas-scientific.com/_files/_datasheets/_circuit/DO_Circuit_5.0.pdf

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