Micropython Second Thread Hangs Starting WIFI - multithreading

I have found an example to publish a very simple HTML page from my Raspberry Pi Pico W. I plan to use it to monitor and control the Pico from a browser within my LAN.
If I un-comment the last in the code below and run it directly in Thony (not as a thread) it works no problem and I can turn on and off the LED on the Pico W from my browser.
Sorry this code is long. I created a cut down thread test example and that worked, so it's something about the way this code runs in a thread.
# Test Web server based on an example found here
# https://how2electronics.com/raspberry-pi-pico-w-web-server-tutorial-with-micropython/
import machine
import socket
#import math
import network
import time
import secretsHouse
# Temperature Sensor
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)
# Pin Definitions
ledPin = 5 # Physical Pin 7 Gnd = 8
led = machine.Pin(ledPin, machine.Pin.OUT) # Define pin as output
def webServerMain():
print("*Thread2* Main()")
wlan = network.WLAN(network.STA_IF)
print("*Thread2* wlan="+str(wlan))
try:
print("*Thread2* try wlan.active(True)")
wlan.active(True)
print("*Thread2* wlan=active")
except Exception as errorMsg:
print("*Thread2* Error="+str(errorMsg))
wlan.connect(secretsHouse.ssid,secretsHouse.password)
# Wait for connect or fail
print("*Thread2* WIFI Connect Wait=10")
wait = 10
while wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
wait -= 1
print('*Thread2* waiting for wifi connection...'+str(wait))
time.sleep(2)
# Handle connection error
if wlan.status() != 3:
print("*Thread2* "+str(wlan.status()))
raise RuntimeError('*Thread2 *wifi connection failed')
else:
print('*Thread2* Wifi Connected')
ip=wlan.ifconfig()[0]
print('*Thread2* IP: ', ip)
try:
if ip is not None:
connection=open_socket(ip)
serve(connection)
except KeyboardInterrupt:
machine.reset()
def temperature():
temperature_value = sensor_temp.read_u16() * conversion_factor
temperature_Celcius = 27 - (temperature_value - 0.706)/0.00172169/ 8
print("*Thread2* Temp=" + str(temperature_Celcius))
time.sleep(2)
return temperature_Celcius
def webpage(value):
html = f"""
<!DOCTYPE html>
<html>
<body>
<form action="./led">
<input type="submit" value="Led On" />
</form>
<form action="./off">
<input type="submit" value="Led Off" />
</form>
<p>Temperature is {value} degrees Celsius</p>
</body>
</html>
"""
return html
def serve(connection):
while True:
print("*Thread2* serve() While Loop.... (Wait for web request)")
client = connection.accept()[0]
print("*Thread2* serve() client=" + str(client))
print()
request = client.recv(1024)
print("*Thread2* request="+str(request))
request = str(request)
print()
try:
request = request.split()[1]
except IndexError:
print("IndexError")
pass
print()
print("*Thread2* Request=" + str(request))
if request == '/off?':
led.low()
elif request == '/led?':
led.high()
value='%.2f'%temperature()
html=webpage(value)
client.send(html)
client.close()
def open_socket(ip):
# Open a socket
print("*Thread2* open_socket()")
address = (ip, 80)
connection = socket.socket()
connection.bind(address)
connection.listen(1)
print("*Thread2* connection="+str(connection))
return(connection)
#webServerMain() # Enable this line to run not in a thread
If I comment out the last line in the above sample and run the code in a separate thread using the example below, the foreground thread runs but the background thread stops with the output as below.
*Thread2* Main()
*Thread1* Count=1
*Thread2* wlan=<CYW43 STA down 0.0.0.0>
*Thread2* try wlan.active(True)
*Thread1* Count=2
*Thread1* Count=3
*Thread1* Count=4
*Thread1* Count=5
*Thread1* Count=6
*Thread1* Count=7
*Thread1* Count=8
*Thread1* Count=9
*Thread1* Count=10
*Thread1* Count=11
*Thread1* Count=12
*Thread1* Count=13
*Thread1* Count=14
*Thread1* Count=15
Here is the code that executes the thread
The simple testTone.beep also works correctly just to make sure my thread stuff is reasonable.
# Test Thread
import _thread
import time
import webServerExample
import testTone
_thread.start_new_thread(webServerExample.webServerMain,())
#_thread.start_new_thread(testTone.beep,())
count=0
while True:
count=count+1
print("*Thread1* Count="+str(count))
time.sleep(2)
I'm stuck now. Any ideas what I'm doing wrong or how I can further narrow the field to find the problem.
Thanks in advance
David.
UPDATE
I think it's only the Wifi starting that stops the second thread so I updated the Title.

This is more of a work around than an answer.
If I put the WIFI stuff in the main thread and then start a second thread that does something trivial it works no problem. I'll rebuild my actual application to function this way and see if something else kills the sub thread.
I'm not sure if this behavior is a bug in Micropython threading?
I'll create a new question with a bare bones example that demonstrates the problem with just the WIFI connection and not much more than a counter or two.
David

Related

Interrupting a bluetooth connection in a programmable way

I am wanting to interrupt this bluetooth connect in a programmable way in python if I can. I have read many articles online and cannot seem to find a way to send an interrupt, other than a keyboard interrupt, to the client_sock, clientInfo = server_sock.accept() so that this line of code stops its bluetooth connectivity. The end game is to use my GUI side of this program by implementing a "Stop Connection" button in my GUI to halt the bluetooth connection. Is there a way to do that in python3 at all, or is this something that can only be handled via command line???
size = 1024
while True:
self.bluetooth_information.append("Waiting for connection")
self.bluetooth_information.append(str(datetime.now().time()))
client_sock, clientInfo = server_sock.accept()
try:
data = client_sock.recv(size) # receives data from client
if len(data) == 0:
break
client_sock.send(self.parse.process_data(data)) # Echo response back to client
# except bluetooth.btcommon.BluetoothError:
# pass
if self.stop:
client_sock.close()
server_sock.close()
self.stop = False
self.bluetooth_information.clear()
break
except KeyboardInterrupt:
client_sock.close()
server_sock.close()
break

Using Multiprocessing to create a pyserial .write() , .read() processes in python3

Devices used : Beaglebone_AI and 2 STM32 nucleos connected to the Beaglebone AI using USB hub.
The idea:
The Beaglebone uses a python3 script that uses multiprocessing and pyserial to create 3 processes:
Process 1 - Request a string from STM32 #1 and adds the string to FIFO Queue
Process 2 - Function that writes all responses from STM32 #2
Process 3 - .get() from FIFO Queue go through a switch statement to process either strings from STM32 #1 or user commands.
The Problem:
- It seems like sometimes pyserial .write() dosnt send the user command or data.
can Pyserial .read() and .write() at the same time ??
This is my function process that prints everything that it receives:
def writeall(serial_port, q2):
all_bytes = []
while True:
try:
bytesToRead = serial_port.inWaiting()
data = serial_port.read(bytesToRead).decode('utf-8')
if not data:
pass
else:
if data == '\n':
data = ''.join(all_bytes)
#Print to screen everything
print(data, end = '\033[K\n\n', flush=True)
all_bytes.clear()
#serial_port.reset_input_buffer()
else:
#append all the bytes that are being received
all_bytes.append(str(data))
except (OSError,serial.serialutil.SerialException) as e:
print(f'Actuator Controller: {e}') ```
**This is my serial port config:**
``` def opening_port(q2):
global serial_port
try:
a =""
for p in ports:
#if 'COM9' in p.device: #for windows
if "066BFF343633464257245637" in p.serial_number: #my nucleo
a = p.device
# Port initialization
serial_port = serial.Serial(
port= a,
baudrate=9600,
bytesize= serial.EIGHTBITS,
parity= serial.PARITY_NONE,
stopbits= serial.STOPBITS_ONE,
timeout= 1)
# Discarding anything in the I/O buffer.
#serial_port.reset_output_buffer()
#serial_port.reset_input_buffer()
return serial_port ```
**ANd this is pyserial .write() function that uses the same port as the writeall function process**
``` def write_comand(comand):
try:
#Reset the buffer before sending a command
serial_port.reset_output_buffer()
serial_port.reset_input_buffer()
serial_port.write(bytes(comand,'utf8'))
# Testing wheter the Queue is getting the user commands and if we are actually sending them
if '3E12' in str(comand):
pass
else :
print(f" cmd sent {comand}\n")
#time.sleep(0.005)
except (OSError,serial.serialutil.SerialException) as e:
print (f" There was a problem communicating with AC: {e}\n")
print('Resetting Actuator Controller...\n')
write_comand('B6D8\r')# reset command ```

asyncio with multiple methods in micropython

When I run the following code, it runs and print ("Listening, connect your APP to http://192.168.4.1:8080/") and waiting request as web server. During the web server mode, I want the LED to blink that's why I have applied asyncio.
However, unless it receives any request (which activates While True: loop in web server), LED does not respond. I have tried many ways but I could not find a way to toggle of LED during web server mode. You can see the comment regarding to await asyncio.sleep(20) in the code below:
import uasyncio as asyncio
from machine import Pin
import time
LED_PIN = 13
led = Pin(LED_PIN, Pin.OUT, value=1)
async def toggle():
while True:
await asyncio.sleep_ms(500)
led.value(not led.value()) # toggling
async def webServer(ipAddress):
s = socket.socket()
ai = socket.getaddrinfo(ipAddress, 8080)
print("Bind address info:", ai)
addr = ai[0][-1]
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(2)
print("Listening, connect your APP to http://%s:8080/" % ipAddress)
counter = 0
# await asyncio.sleep(20) # !! if i applied await here, LED toggling 20 secs but web server does not accept any request because "while True" below is not activated during 20 secs.
while True:
res = s.accept()
client_sock = res[0]
client_addr = res[1]
print("Client address:", client_addr)
print("Client socket:", client_sock)
req = client_sock.recv(1024)
print("Payload: %s" % req.decode())
client_sock.send(CONTENT % counter)
client_sock.close()
counter += 1
print()
loop = asyncio.get_event_loop()
loop.create_task(toggle())
loop.create_task(webServer('192.168.4.1'))
loop.run_forever()
Your webServer async function is not really async because it uses blocking IO. At a minimum you need to set the socket to non-blocking mode and use the socket operations provided by asyncio, or even better you should use asyncio.start_server to implement an asynchronous network server.
See the asyncio documentation or e.g. this answer for examples.

Simultaneous input and output for network based messaging program

In python, I am creating a message system where a client and server can send messages back and forth simeltaneously. Here is my code for the client:
import threading
import socket
# Global variables
host = input("Server: ")
port = 9000
buff = 1024
# Create socket instance
s = socket.socket()
# Connect to server
s.connect( (host, port) )
print("Connected to server\n")
class Recieve(threading.Thread):
def run(self):
while True: # Recieve loop
r_msg = s.recv(buff).decode()
print("\nServer: " + r_msg)
recieve_thread = Recieve()
recieve_thread.start()
while True: # Send loop
s_msg = input("Send message: ")
if s_msg.lower() == 'q': # Quit option
break
s.send( s_msg.encode() )
s.close()
I have a thread in the background to check for server messages and a looping input to send messages to the server. The problem arises when the server sends a message and the user input is immediately bounced up to make room for the servers message. I want it so that the input stays pinned to the bottom of the shell window, while the output is printed from the 2nd line up, leaving the first line alone. I have been told that you can use curses or Queues to do this, but I am not sure which one would be best in my situation nor how to implement these modules into my project.
Any help would be appreciated. Thank you.
I want it so that the input stays pinned to the bottom of the shell
window, while the output is printed from the 2nd line up, leaving the
first line alone. I have been told that you can use curses
Here's a supplemented version of your client code using curses.
import threading
import socket
# Global variables
host = input("Server: ")
port = 9000
buff = 1024
# Create socket instance
s = socket.socket()
# Connect to server
s.connect( (host, port) )
print("Connected to server\n")
import sys
write = sys.stdout.buffer.raw.write
from curses import *
setupterm()
lines = tigetnum('lines')
change_scroll_region = tigetstr('csr')
cursor_up = tigetstr('cuu1')
restore_cursor = tigetstr('rc')
save_cursor = tigetstr('sc')
def pin(input_lines): # protect input_lines at the bottom from scrolling
write(save_cursor + \
tparm(change_scroll_region, 0, lines-1-input_lines) + \
restore_cursor)
pin(1)
class Recieve(threading.Thread):
def run(self):
while True: # Recieve loop
r_msg = s.recv(buff).decode()
write(save_cursor+cursor_up)
print("\nServer: " + r_msg)
write(restore_cursor)
recieve_thread = Recieve()
recieve_thread.daemon = True
recieve_thread.start()
while True: # Send loop
s_msg = input("Send message: ")
if s_msg.lower() == 'q': # Quit option
break
s.send( s_msg.encode() )
pin(0)
s.close()
It changes the scrolling region to leave out the screen's bottom line, enters the scrolling region temporarily to output the server messages, and changes it back at the end.

Python 3.4 - How to 'run' another script python script continuously, How to pass http get / post to socket

This question is two-fold.
1. So I need to run code for a socket server that's all defined and created in another.py, Clicking run on PyCharm works just fine, but if you exec() the file it just runs the bottom part of the code.
There are a few answers here but they are conflicting and for Python 2.
From what I can gather there are three ways:
- Execfile(), Which I think is Python 2 code.
- os.system() (But I've seen it be said that it's not correct to pass to the OS for this)
- And subprocess.Popen (unsure how to use this either)
I need this to run in the background, it is used to create threads for sockets for the recv portion of the overall program and listen on those ports so I can input commands to a router.
This is the complete code in question:
import sys
import socket
import threading
import time
QUIT = False
class ClientThread(threading.Thread): # Class that implements the client threads in this server
def __init__(self, client_sock): # Initialize the object, save the socket that this thread will use.
threading.Thread.__init__(self)
self.client = client_sock
def run(self): # Thread's main loop. Once this function returns, the thread is finished and dies.
global QUIT # Need to declare QUIT as global, since the method can change it
done = False
cmd = self.readline() # Read data from the socket and process it
while not done:
if 'quit' == cmd:
self.writeline('Ok, bye. Server shut down')
QUIT = True
done = True
elif 'bye' == cmd:
self.writeline('Ok, bye. Thread closed')
done = True
else:
self.writeline(self.name)
cmd = self.readline()
self.client.close() # Make sure socket is closed when we're done with it
return
def readline(self): # Helper function, read up to 1024 chars from the socket, and returns them as a string
result = self.client.recv(1024)
if result is not None: # All letters in lower case and without and end of line markers
result = result.strip().lower().decode('ascii')
return result
def writeline(self, text): # Helper func, writes the given string to the socket with and end of line marker at end
self.client.send(text.strip().encode("ascii") + b'\n')
class Server: # Server class. Opens up a socket and listens for incoming connections.
def __init__(self): # Every time a new connection arrives, new thread object is created and
self.sock = None # defers the processing of the connection to it
self.thread_list = []
def run(self): # Server main loop: Creates the server (incoming) socket, listens > creates thread to handle it
all_good = False
try_count = 0 # Attempt to open the socket
while not all_good:
if 3 < try_count: # Tried more than 3 times without success, maybe post is in use by another program
sys.exit(1)
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create the socket
port = 80
self.sock.bind(('127.0.0.1', port)) # Bind to the interface and port we want to listen on
self.sock.listen(5)
all_good = True
break
except socket.error:
print('Socket connection error... Waiting 10 seconds to retry.')
del self.sock
time.sleep(10)
try_count += 1
print('Server is listening for incoming connections.')
print('Try to connect through the command line with:')
print('telnet localhost 80')
print('and then type whatever you want.')
print()
print("typing 'bye' finishes the thread. but not the server",)
print("eg. you can quit telnet, run it again and get a different ",)
print("thread name")
print("typing 'quit' finishes the server")
try:
while not QUIT:
try:
self.sock.settimeout(0.500)
client = self.sock.accept()[0]
except socket.timeout:
time.sleep(1)
if QUIT:
print('Received quit command. Shutting down...')
break
continue
new_thread = ClientThread(client)
print('Incoming Connection. Started thread ',)
print(new_thread.getName())
self.thread_list.append(new_thread)
new_thread.start()
for thread in self.thread_list:
if not thread.isAlive():
self.thread_list.remove(thread)
thread.join()
except KeyboardInterrupt:
print('Ctrl+C pressed... Shutting Down')
except Exception as err:
print('Exception caught: %s\nClosing...' % err)
for thread in self.thread_list:
thread.join(1.0)
self.sock.close()
if "__main__" == __name__:
server = Server()
server.run()
print('Terminated')
Notes:
This is created in Python 3.4
I use Pycharm as my IDE.
One part of a whole.
2. So I'm creating a lightning detection system and this is how I expect it to be done:
- Listen to the port on the router forever
The above is done, but the issue with this is described in question 1.
- Pull numbers from a text file for sending text message
Completed this also.
- Send http get / post to port on the router
The issue with this is that i'm unsure how the router will act if I send this in binary form, I suspect it wont matter, the input commands for sending over GSM are specific. Some clarification may be needed at some point.
- Recieve reply from router and exception manage
- Listen for relay trip for alarm on severe or close strike warning.
- If tripped, send messages to phones in storage from text file
This would be the http get / post that's sent.
- Wait for reply from router to indicate messages have been sent, exception handle if it's not the case
- Go back to start
There are a few issues I'd like some background knowledge on that is proving hard to find via the old Google and here on the answers in stack.
How do I grab the receive data from the router from another process running in another file? I guess I can write into a text file and call that data but i'd rather not.
How to multi-process and which method to use.
How to send http get / post to socket on router, post needed occording to the router manual is as follows: e.g. "http://192.168.1.1/cgi-bin/sms_send?number=0037061212345&text=test"
Notes: Using Sockets, threading, sys and time on Python 3.4/Pycharm IDE.
Lightning detector used is LD-250 with RLO Relay attached.
RUT500 Teltonica router used.
Any direction/comments, errors spotted, anything i'm drastically missing would be greatly appreciated! Thank you very much in advance :D constructive criticism is greatly encouraged!
Okay so for the first part none of those suggested in the OP were my answer. Running the script as is from os.system(), exec() without declaring a new socket object just ran from __name__, this essentially just printed out "terminated", to get around this was simple. As everything was put into a classes already, all I had to do is create a new thread. This is how it was done:
import Socketthread2
new_thread = Socketthread2.Server() # Effectively declaring a new server class object.
new_thread.run()
This allowed the script to run from the beginning by initialising the code from the start in Socket, which is also a class of Clientthread, so that was also run too. Running this at the start of the parent program allowed this to run in the background, then continue with the new code in parent while the rest of the script was continuously active.

Resources