Microcontroller serial stops working when mu serial is closed - python

I am trying to control some NeoPixel lights, and because the Raspberry Pi can only control one light at any given time, I decided I would just get a microcontroller to do it, specifically the Adafruit ItsyBitsy M4 Express, and just send serial data through the USB connecting the two to control them. I got that working, using PySerial to send data to the microcontroller. However, after the RPi script sends data 8 times, it stops working and I have to reload the script. Additonally, by stop working, I mean that the script gets stuck sending data and never ends. The strange part is that if I open the MU serial after my script has started running, I can A: see the data being sent in, and B: it never stops. It goes past the 8 issue and keeps going.
RPi Code:
import serial
import time
from random import randint
a = time.time() #Unimportant, was looking to see how quickly Serial could be re-defined
try:
ser = serial.Serial(port='/dev/ttyACM1', baudrate=115200, write_timeout=0.05)
except:
ser = serial.Serial(port='/dev/ttyACM0', baudrate=115200, write_timeout=0.05)
print(time.time() - a)
EnterKey = "\x1A\r\n"
e = 0
while True:
e+=1
d = []
for number in range(0, 30):
d.append([randint(0, 255), randint(0, 255), randint(0, 255)])
d = ((str(d)) + EnterKey).encode()
ser.write(d)
time.sleep(1)
print(e)
Microcontroller code:
import board
import supervisor
import neopixel
import time
a = neopixel.NeoPixel(board.D5, 30, auto_write=False)
supervisor.diable_autoreload()
while True:
b = input("automated")
RST = []
c = ""
R3T = []
started = False
for value in b:
if started == True:
if value == '[':
started2 = True
ending = False
elif value == ']':
if ending == True:
started=False
else:
RST.append(int(c))
c = ""
ending = True
started2 = False
R3T.append(RST)
RST = []
elif started2 == True:
if value.isdigit():
c += value
elif value == ",":
RST.append(int(c))
c = ""
elif value == '[':
started = True
for value in range(0, 30):
a[value] = R3T[value]
a.show()
print(a)
I tried being lazy and just re-defining the serial object after a write timeout, but that does not work. (Errno 16] Device or resource busy). I then went around looking for Raspberry Pi settings seeing if anything was getting in my way, but found nothing. I ultimately gave up and came here. If you have any ideas, please tell me!

Related

Is there a way to sync sending information across sockets on a network?

I am programming ada fruit ring LED lights in python, I have setup a local network for a midi controller to read data into my Raspberry Pi 3 and send to the 3 Pi Zero W through web sockets. I am running into the issue where some of the functions I have that run on threads will get out of sync with each other. In other words, one of the LED lights will go at a slightly faster or slower rate and eventually look not synced. I was curious if anyone has run into an issue like this or if I am just using the threads and events the incorrect way.
Here is some of my server side code:
import socket
from socket import *
from threading import Thread
import threading
import board
import neopixel
import sys
import time
import random
import os
from adafruit_led_animation.animation.comet import Comet
def handle_client(client):
p = None
connected = True
lights_on_default()
while True:
global thread_running
data=client.recv(1024).decode()
if not data:
connected = False
thread_running = False
break
if 'Pad' in data:
thread_running = False
thread = threading.Event()
if data == 'Pad 1':
thread_running = False
if p:
p.join()
p = Thread(target=slow_circles, args=(thread,))
p.daemon = True
p.start()
def slow_circles(event):
global thread_running
thread_running = True
while not event.isSet():
if not thread_running:
event.set()
break
for i in range(num_of_pixels):
// goes through the lighting of pixels
if not thread_running:
event.set()
break
pixels.show()
Here is some of my client side code:
import rtmidi.midiutil as midiutil
import time
import socket
buttons = {
36: 'Pad 1',
}
try:
s1 = socket.socket()
s2 = socket.socket()
except:
print('Error making socket')
port1 = 12346
port2 = 12347
serverOne = '192.168.1.18' // 1st Pi Zero W
serverTwo = '192.168.1.17' // 2nd Pi Zero W
def start():
global s1
global s2
notConnected = True
while True:
while notConnected:
connected = False
try:
s1.connect((serverOne, port1))
s2.connect((serverTwo, port2))
connected = True
break
except:
s1.close()
s2.close()
s1.socket.socket()
s2.socket.socket()
if connected:
midiin, port = midiutil.open_midiinput(1)
midiin.set_callback(midiCallback) // gets pad from midi controller
while True:
retry = False
try:
s1.sendall('red connected'.encode('utf8'))
s2.sendall('yellow connected'.encode('utf8'))
except:
retry = True
if retry:
s1.close()
s2.close()
midiin.delete()
break
else:
time.sleep(15)
def midiCallback(message, data):
control = message[0][1]
try:
value = message[0][2]
if (control in button.keys()):
name = buttons[control]
if (value > 0):
return buttonDown(name)
except IndexError:
pass
def buttonDown(button):
s1.sendall(f'{button}'.encode('utf8'))
s2.sendall(f'{button}'.encode('utf8'))
start()
And when I hit the pad and do the function I send data to the two PIs and they start at the same time but over time get out of sync. All help would be appreciated thanks.

How come when I try to run 2 python scripts that give and take data from another and one's reading serial port data I get a permission error?

So I have 2 python scripts, one is reading serial port data and the other is receiving a list from that data, however once I run the second script I get a permission error and can no longer read from the serial port. These are my 2 scripts and I'm doing stuff with Arduino, I know that I can have all this in 1 file but for latency purposes I separated the logic from the read. Here are my scripts:
read.py:
import serial
from time import sleep
ser = serial.Serial('COM7', 9600)
while True:
ser_bytes = ser.readline()
decoded_bytes = ser_bytes[0:len(ser_bytes)-2].decode('utf-8')
if len(decoded_bytes) == 0:
ser.flushInput()
if len(decoded_bytes) != 0:
find1 = decoded_bytes.find('&')
find2 = decoded_bytes.find('|')
find3 = decoded_bytes.find('/')
left = decoded_bytes[0:find1]
right = decoded_bytes[find1+1:find2]
fire = decoded_bytes[find2+1: find3]
jump = decoded_bytes[find3+1:]
keypresses = [left, right, fire, jump]
ser.flushInput()
do.py:
from read import keypresses
import pyautogui as pg
while True:
if keypresses[0] == 1:
pg.keyDown('a')
else:
pg.keyUp('a')
if keypresses[1] == 1:
pg.keyDown('d')
else:
pg.keyUp('d')
if keypresses[2] == 1:
pg.mouseDown()
else:
pg.mouseUp()
if keypresses[3] == 1:
pg.keyDown('w')
else:
pg.keyUp('w')
If you could help that'd be nice, thanks.

Opening 2 serial ports simultaneously in python (one tx one for rx)

I am making a throughput test for a bluetooth link, and I need to send data through a serial port to one bluetooth device which will then transport that data wirelessly to another bluetooth device. The other device will then complete the circuit by sending the data back to the host PC via a different serial port.
The problem seems to be when I attempt to open up 2 different instances of PySerial, the program simply hangs. I have isolated it down to running vs. hanging when I comment out one of the two serial port instantiations. Anyone see a problem with how I'm doing this? If so, what is the proper way to do this? See code below:
#/usr/bin/python
import serial
import time
import sys
DEFAULT_BAUD = 115200
SEND_SIZE = 100
def addPath(file):
pth, fl = os.path.split(__file__)
return os.path.join(pth, file)
def is_number(s):
try:
int(s, 16)
return True
except:
return False
class SerialReader():
def __init__(self, portRx, portTx):
self.portTx = portTx
self.portRx = portRx
self.start_time__sec = time.time()
self.interval__sec = 0
self.buffer = []
self.sendtext = ''.join([str(i) for i in range(SEND_SIZE)])
# send first batch of data
self.portTx.write(self.sendtext)
def didDataArrive(self):
# Read port
self.buffer.extend(list(self.portRx.read(1024)))
# Step through the buffer byte and byte and see if the tick text
# is at the front.
while len(self.buffer) >= len(self.sendtext):
if self.buffer[:len(self.sendtext)] == self.sendtext:
# Discard the tick text
self.buffer = self.buffer[len(self.sendtext):]
# Record time
snapshot__sec = time.time()
self.interval__sec = snapshot__sec - self.start_time__sec
self.start_time__sec = snapshot__sec
# send more data
self.portTx.write(self.sendtext)
return True
else:
self.buffer.pop(0)
return False
def main(port1, port2, baudrate1 = DEFAULT_BAUD, baudrate2 = DEFAULT_BAUD):
try:
import serial
except:
traceback.print_exc()
print "="*60
print "You need to install PySerial"
print "Windows: easy_install pyserial"
print "Mac/Linux: sudo easy_install pyserial"
try:
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)
print "Loading serial ports"
except:
print "Serial port error"
exit()
plot_stop = False
dataread = SerialReader(s2, s1)
try:
while plot_stop == False:
if dataread.didDataArrive():
print dataread.interval__sec
except KeyboardInterrupt:
print "Keyboard Interrupt"
plot_stop = True
finally:
print "Closing"
s1.close()
s2.close()
if __name__ == '__main__':
if (len(sys.argv) < 3):
print "Usage: python extract_data.py phonelink_serialport phonelinkclient_serialport [baudrate1] [baudrate2]"
else:
main(*sys.argv[1:])
If I remove one of the following lines (doesn't matter which one), the python script runs (although it eventually crashes because in the code it eventually tries to reference both ports). If I leave these lines in, the program seems to just hang (it just seems to sit there and run indefinitely):
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)

PySerial: how to understand that the timeout occured while reading from serial port?

I'm using PySerial to read from serial port like in the code below.
CheckReadUntil() read output of the command that I send to serial port until the sequence of symbols readUntil are in the serial output.
...
self.ser = serial.Serial(comDev, 115200, timeout=10)
...
#Function that continue to read from Serial port until 'readUntil'
#sequence of symbols appears
def CheckReadUntil(self, readUntil):
outputCharacters = []
while 1:
ch = self.ser.read()
outputCharacters += ch
if outputCharacters[-len(readUntil):]==readUntil:
break
outputLines = ''.join(outputCharacters)
return outputLines
However, if there is no sequence readUntil (for any reason), I'm just stuck in the function CheckReadUntil() forever. The setting timeout=10 sets up timeout so I'm stuck in a loop that iterates every 10 seconds and does nothing, just waiting.
How it is possible to understand that there was a timeout event so I may exit the infinite loop? Output length may be different.
UPDATE (previous answer was not correct, this is the working code from #konstantin):
...
self.ser = serial.Serial(comDev, 115200, timeout=10)
...
#Function that continue to read from Serial port until 'readUntil'
#sequence of symbols appears
def CheckReadUntil(self, readUntil):
outputCharacters = []
while 1:
ch = self.ser.read()
if len(ch) == 0:
break
outputCharacters += ch
if outputCharacters[-len(readUntil):]==readUntil:
break
outputLines = ''.join(outputCharacters)
return outputLines

crashing out in a while loop python

How to solve this error? i want to pass the values from get_robotxya() and get_ballxya()
and use it in a loop but it seems that it will crash after awhile how do i fix this? i want to get the values whithout it crashing out of the while loop
import socket
import os,sys
import time
from threading import Thread
HOST = '59.191.193.59'
PORT = 5555
COORDINATES = []
def connect():
globals()['client_socket'] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST,PORT))
def update_coordinates():
connect()
screen_width = 0
screen_height = 0
while True:
try:
client_socket.send("loc\n")
data = client_socket.recv(8192)
except:
connect();
continue;
globals()['COORDINATES'] = data.split()
if(not(COORDINATES[-1] == "eom" and COORDINATES[0] == "start")):
continue
if (screen_width != int(COORDINATES[2])):
screen_width = int(COORDINATES[2])
screen_height = int(COORDINATES[3])
return
def get_ballxy():
update_coordinates()
ballx = int(COORDINATES[8])
bally = int(COORDINATES[9])
return ballx,bally
def get_robotxya():
update_coordinates()
robotx = int(COORDINATES[12])
roboty = int(COORDINATES[13])
angle = int(COORDINATES[14])
return robotx,roboty,angle
def print_ballxy(bx,by):
print bx
print by
def print_robotxya(rx,ry,a):
print rx
print ry
print a
def activate():
bx,by = get_ballxy()
rx,ry,a = get_robotxya()
print_ballxy(bx,by)
print_robotxya(rx,ry,a)
Thread(target=update_coordinates).start()
while True:
activate()
this is the error i get:
I think you'll find that, because you're continuously creating new connections without closing them down, you'll eventually run out of resources.
That may be a local limitation or it may be the server end getting fed up with too many connections from a single IP.
Regardless, either create one connection and use it over and over, or shut down connections when you're done with them.
One possible way of doing this is to connect() within the main code before calling activate():
connect()
update_coordinates()
while True:
activate()
Then remove the initial connect() from the start of the update_coordinates() function since that's the bit that does a new connection every time you try to update the coordinates.
If the session goes down for some reason, the except bit will re-create it and try again.
That should hopefully alleviate your resource problem quite a bit.

Categories

Resources