Send serial message to device until it is online - linux

I am trying to update the firmware of a controller through a serial interface. To do this, I must send a reboot message to the controller (no problem there) and then send another message (the character 'w') THE MOMENT it starts up so that it may start up in write mode. This is easily done with the minicom utility by pressing w continuously while the device restarts.
I want to achieve this functionality using python code instead, but I can't figure out how to send a message until the device is up without throwing exceptions (since the device is not connected).
This is what I have tried, but it does not work (with pyserial):
def send_w(serial_port, baud_rate):
msgw = "w_"
ans = ""
ser = serial.Serial(port=serial_port, baudrate=baud_rate,timeout = 10)
ser.write(msgw)
ans = ser.read(24)
ser.close()
print(ans)
return ans
def set_firmware_version(serial_port, baud_rate):
s = ""
try:
with serial.Serial(serial_port,baud_rate,timeout=1) as ser:
msgr = "%reset "+sk+"_"
ser.write(msgr)
ser.close()
print("reset")
except (IOError) as e:
print("Error in: set_firmware_version")
print(e)
return s
time.sleep(1)
send_w(serial_port, baud_rate)
set_firmware_version(sp,br)
This gives the following error:
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
I also tried sending the messages in a loop with a short timeout, but had the same problem. Is there any way to send a message continuously and disregard exceptions if the device is not found?
Any help will be greatly appreciated.
Full Traceback:
Traceback (most recent call last):
File "mc_config.py", line 69, in <module>
set_firmware_version(sp,br)
File "mc_config.py", line 64, in set_firmware_version
send_w(serial_port, baud_rate)
File "mc_config.py", line 46, in send_w
ans = ser.read(24)
File "/home/avidbots/.local/lib/python2.7/site-packages/serial/serialposix.py", line 501, in read
'device reports readiness to read but returned no data '
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
(I am using ubuntu 16.04 and python 3)

What if you put the excepting code into a try and then catch the exception with an except serial.serialutil.SerialException {...} block?
Clearly there's a significant window of time to submit the w ( otherwise the "press w" method wouldn't often work.) Your requirement, then, would be to retry only the part of the code that's absolutely necessary to send the w, so that you send it quickly enough to "catch" the system in its bootup state. Since the backtrace shows that the exceptions occurs in send_w, then you can add try/except blocks and a while loop around what is now one line at the end of set_firmware_version.
Instead of just this:
send_w(serial_port, baud_rate)
Something like this might solve the problem:
while True:
try:
send_w(serial_port, baud_rate)
break
except serial.serialutil.SerialException:
pass # retry
You may need to change your imports to expose that exception, fyi. And you may need to consider whether you're catching too many possible exceptions - it's possible that exception might also represent other errors that shouldn't be retried. You might also need to add a small sleep time there - this is essentially a busy wait loop (https://en.wikipedia.org/wiki/Busy_waiting).

Related

Program Stops Without Reason (RasPi, Linux, Python3)

First, thank for fixing my post. I'm still not sure how to include a sketch. I've been reading posts here for many months, but never posted one before.
My headless RasPi is running two sketches of mine, one reads data from a pm2.5 sensor (PMS7003) and the other is the program listed above that sends information to another Pi, the client, that turns on a pm2.5 capable air filter. (I live in California) The program that reads the PMS7003 sorts the data, called max_index, into one of six categories, 0 thru 5 and saves the current category to a text file. I'm using the 'w' mode during the write operation, so there is only one character in the text file at any time. The server program listed above reads the text file and sends it to a client that turns on the air filter for categories above 2. The client sends the word "done" back to the server to end the transaction.
Until you mentioned it, I didn't realize my mistake, clientsocket.recv(2). I'll fix that and try again.
So, the listener socket should go outside the while loop, leaving the send and receive inside???
Troubleshooting: I start the two programs using nice nohup python3 xxx.py & nice nohup python3 yyy.py. The program that reads the PMS7003 continues running and updating the text file with current category, but the server program falls out of existence after a few days. top -c -u pi reveals only the PMS7003 program running, while the server program is missing. Also, there's nothing in nohup.out or in socketexceptions.txt and I tried looking through system logs in /var/log but was overwhelmed by information and found nothing that made any sense to me.
Since writing to the socketexceptions.txt file is not in a try/except block, the crash might be happening there.
import socket
import time
index = " "
clientsocket = ""
def getmaxindex():
try:
with open('/home/pi/pm25/fan.txt','r')as f:
stat = f.read() #gets max_index from pm25b.py
return(stat)
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to read max index")
def setup(index):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind(("192.168.1.70", 5050))
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to bind")
try:
s.listen(1)
clientsocket, address = s.accept()
clientsocket.send(index)
rx = clientsocket.recv(2)
if rx == "done":
clientsocket.close()
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to communicate with flient")
while True:
index = getmaxindex().encode('utf-8')
setup(index)
time.sleep(5)
enter code here
It is unknown what program is supposed to do and where exactly you run into problems, since there is only a code dump and no useful error description (what does "stop" mean - hang or exit, where exactly does it stop). But the following condition can never be met:
rx = clientsocket.recv(2)
if rx == "done":
The will receive at most 2 bytes (recv(2)) which is definitely not enough to store the value "done".
Apart from that it makes not real sense to recreate the same listener socket again and again, just to accept a single client and exchange some data. Instead the listener should only be created once and multiple accept should be called on the same listener socket, where each will result in a new client connection.

Reading registers using Pymodbus (Modbus RTU)

I am trying to communicate a Modbus slave device that uses Modbus RTU protocol, with RPi having pymodbus lib in python 3.8.
Here's a link for this library
http://riptideio.github.io/pymodbus/
My concern is when printing holding registers responses. I have read other posts regarding it and people have suggested using '.registers' with the print command.
How to read from Registers with pymodbus
At times, I get the value printed out but other times I see this error
Traceback (most recent call last):
File "/home/user123/VsCode/Modbus_Rtu_sample.py", line 65, in <module>
value = result.registers
AttributeError: 'ModbusIOException' object has no attribute 'registers'
Firstly, I want to know why does this works sometimes and not other times. as I can see in the pymodbus documentation, there's no attribute called registers then how does it work the first time? this is my code:
with ModbusClient(method='rtu', port='/dev/ttyUSB0', stopbits=1, bytesize=8 , parity='N', baudrate=9600, timeout=2) as client:
if not client.connect():
connection = client.connect()
connection = client.connect()
print("Your connection is set to",connection)
if client.connect():
read=client.read_holding_registers(address = 0x1000,count =4,unit=1)
value = read.registers
print(value)
#print(read.registers)
else:
print("Cannot connect to modbus slave")
client.close()
Is there any better lib/method/way to overcome this issue? all I want is to have stable read / write outputs for registers. later I want to call a class made from this so don't want to have a situation where sometimes I do get output and other times an error is thrown.

docker-py reading container logs as a generator hangs

I am using docker-py to read container logs as a stream. by setting the stream flag to True as indicated in the docs. Basically, I am iterating through all my containers and reading their container logs in as a generator and writing it out to a file like the following:
for service in service_names:
dkg = self.container.logs(service, stream=True)
with open(path, 'wb') as output_file:
try:
while True:
line = next(dkg).decode("utf-8")
print('line is: ' + str(line))
if not line or "\n" not in line: # none of these work
print('Breaking...')
break
output_file.write(str(line.strip()))
except Exception as exc: # nor this
print('an exception occurred: ' + str(exc))
However, it only reads the first service and hangs at the end of the file. It doesn't break out of the loop nor raise an exception (e,g. StopIteration exception). According to the docs if stream=True it should return a generator, I printed out the generator type and it shows up as a docker.types.daemon.CancellableStream so don't think it would follow the traditional python generator and exception out if we hit the end of the container log generator and call next().
As you can see I've tried checking if eol is falsy or contains newline, even see if it'll catch any type of exception but no luck. Is there another way I can. determine if it hits the end of the stream for the service and break out of the while loop and continue writing the next service? The reason why I wanted to use a stream is because the large amount of data was causing my system to run low on memory so I prefer to use a generator.
The problem is that the stream doesn't really stop until the container is stopped, it is just paused waiting for the next data to arrive. To illustrate this, when it hangs on the first container, if you do docker stop on that container, you'll get a StopIteration exception and your for loop will move on to the next container's logs.
You can tell .logs() not to follow the logs by using follow = False. Curiously, the docs say the default value is False, but that doesn't seem to be the case, at least not for streaming.
I experienced the same problem you did, and this excerpt of code using follow = False does not hang on the first container's logs:
import docker
client = docker.from_env()
container_names = ['container1','container2','container3']
for container_name in container_names:
dkg = client.containers.get(container_name).logs(stream = True, follow = False)
try:
while True:
line = next(dkg).decode("utf-8")
print(line)
except StopIteration:
print(f'log stream ended for {container_name}')

ConnectionResetError: [Errno 54] Connection reset by peer

I was reading << Black Hat Python >> and trying the first bhnet.py program.
On one terminal, I run script
./bhnet.py -l -p 9999 -c
And on another terminal, run script
./bhnet.py -t localhost -p 9999
then type
<ctrl-D>
or
ls -alt
<ctrl-D>
The first terminal would return
File "bhnet.py", line 186, in client_handler
cmd_buffer += client_socket.recv(1024).decode('utf-8')
ConnectionResetError: [Errno 54] Connection reset by peer
Below are the codes for the program
def client_handler(client_socket):
global upload
global execute
global command
# check for upload
if len(upload_destination):
# read all the buffer and write to destination
file_buffer = ""
# keep reading til none is available
while True:
data = client_socket.recv(1024)
if not data:
break
else:
file_buffer += data
# take the bytes and write them out
try:
file_descriptor = open(upload_destination,'wb')
file_descriptor.write(file_buffer)
file_descriptor.close()
# acknowledge that file being wrote out
client_socket.send(f"Successfully save file to {upload_destination}.\r \n")
except:
client_socket.send(f"Failed to save file to {upload_destination}.\r \n")
# check for command execution
if command:
while True:
#pop up a window
client_socket.send(b"<BHP:#> ")
# keep receiving data until \n
cmd_buffer = ""
while "\n" not in cmd_buffer:
cmd_buffer += client_socket.recv(1024).decode('utf-8')
response = run_command(cmd_buffer)
client_socket.send(response)
I googled and even tried to upgrade openssl and none of these work...
Thanks in advance!
It's hard to be certain since you don't provide the client side code. However, I'm fairly confident this is happening:
When you type Ctrl-D, you're giving an end of file to the client's input. That is causing the client to close the socket it had previously connected to the server. Doing this causes the client's operating system to send a TCP FIN packet to the server. The FIN only tells the server that the client is finished sending data; there is no way in the normal TCP session termination of telling a peer that the peer may not send any more data.
But then the server is trying to send to the client, after the client has closed its socket. When you try to send further data on a closed socket, then the destination peer's operating system sends a TCP RST packet. That isn't actually reported to the server on the send because the send function call is complete when the data is copied into the kernel -- whereas the RST is probably received by the kernel a few milliseconds later after it actually sent a data packet to the peer.
Hence that condition will be reported on the next operation on the socket which, here, is recv. Hence your program gets back an ECONNRESET error, which python translates into ConnectionResetError exception.
In other words:
Client Server
------ ------
close()
FIN => <OS receives FIN>
send(data)
<= "data"
RST =>
recv
<ECONNRESET>
One more thing: Depending on the exact timing, it's possible that your first call to recv in that loop is actually getting an end-of-file indicator (i.e. zero bytes). However, you're not checking for that, you just keep calling recv as long as there is no newline in the buffer. You really should be checking whether you got a zero byte string back from recv and terminate your loop in that case.
Once you've gotten the end-of-file indicator on the socket, you'll never get a newline added to the buffer. If the client had actually managed to receive the data you sent it before it closed the socket, then no RST would have been sent. In that case, your recv loop would run potentially forever, getting back zero bytes continuously but never finding the newline.

solution1_s() missing 1 required positional argument: 'sol_s1'

Does anyone know why this doesn't work?
Instead of printing the solution, it produces this error:
Traceback (most recent call last):
File "C:\Program Files\Python34\lib\tkinter__init__.py", line 1533,
in call return self.func(*args)
TypeError: solution1_s() missing 1 required positional argument: 'sol_s1'
solutions_s={
"sol_s1":"if this is happeing to you, there is a bug in the software. call up your supplier and they will try and troubleshoot the phone. make sure you have all the latest updates installed",
"sol_s2":"these are all signs of a virus. the deleting of applications is virus munching on your data, and pop ups on your scren is also a virus symptom. immiditely use your antivirus to look for the problem or take it to a repair shop where they can delete the virus",
"sol_app":"check if you have enogh storage on your device, if you dont and that was the problem, then see if you can get a storage upgrade. However, if it isnt there is a good chance you have a virus on your phone. scan your phone with an antivirus, or let your local repair shop do it",
"sol_pop":"if the pop ups are on a web browser, this is normal. try getting an ad blocker if it is bothering you, but do not click on them. however, if it is happening on the main screen, you have contracted a virus. use your antivirus orget it fixed at a repair shop",
"sol_s3":"this is another sign of a software fault. the only one who can fix this is your supplier. contact them as soon as possible"}
def solution1_s(sol_s1):
label20=Label(screen,text=solutions_s[sol_s1])
label20.pack()
sys.exit()
def solution2_s(sol_s2):
label22=Label(screen,text=solutions_s[sol_s2])
label22.pack()
sys.exit()
def solution_app(sol_app):
label23=Label(screen,text=solutions_s[sol_app])
label23.pack()
sys.exit()
def solution_pop(sol_pop):
label24=Label(screen,text=solutions_s[sol_pop])
label24.pack()
sys.exit()
def solution3_s(sol_s3):
label26=Label(screen,text=solutions_s[sol_s3])
label26.pack()
sys.exit()
When you put a variable in the function header like this:
def solution1_s(sol_s1):
Python expects that you pass it an argument, which can be anything, and names it sol_s1 within the scope of that function.
However, you appear to want to look up the key sol_s1 in the dictionary solutions_s, which you have declared in your script, instead.
Try this instead:
def solution1_s():
label20=Label(screen,text=solutions_s['sol_s1'])
label20.pack()
sys.exit()
Here's some reading material with in-depth discussion on scopes and dicts:
Scopes
Dictionaries

Resources