How to make this work in parallel with _thread on a pico w and micropython? - micropython

So, im trying to make a webserver and processing data with a pico in parallel, my goal is to reach the pico from my browser using the local network ip to see in what step the pico is working and what data is on the current loop, however i have two issues and i have no idea how to make it work:
When running the two process in parallel using _thread, the webserver function hangs until the dataprocess function finish, so i cant see in realtime what is going on, the webserver respond only when the other process is finished and it hangs again, i need to press f5 on my browser at the exact time when the dataprocess function finish and start again only to see part of the process because it hangs if i refresh my browser to see the progress
When running the webserver, the urequets.get function of the dataprocess function does not work, it throws [Errno 103] ECONNABORTED
Here is the part of my code that is not working:
import utime, machine, urequests, json, network, socket, _thread
led = machine.Pin("LED", machine.Pin.OUT)
def connect():
global wlan
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("SSID", "PASSS")
while wlan.isconnected() == False:
print("Connecting...")
led.off()
utime.sleep_ms(100)
led.on()
utime.sleep_ms(100)
led.off()
utime.sleep_ms(100)
led.on()
utime.sleep_ms(100)
led.on()
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')
return ip
def open_socket(ip):
address = (ip, 80)
connection = socket.socket()
connection.bind(address)
connection.listen(1)
return connection
def webpage(steps):
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Pico 2</title>
</head>
<body>
<p>{steps}</p>
</body>
</html>
"""
return str(html)
def pushgetdata():
while wlan.isconnected() == True:
try:
global steps
led.off()
utime.sleep_ms(300)
led.on()
steps = "Step 1: Reading values from sensor one...<br>"
#function to read data from one sensor here
#...
#...
#...
led.off()
utime.sleep_ms(100)
led.on()
steps = steps + "Step 2: Reading values from sensor two...<br>"
#function to read data from other sensor here
#...
#...
#...
led.off()
utime.sleep_ms(100)
led.on()
steps = steps + "Step 3: Pushing and getting results...<br>"
jsondata = urequests.get("https://xxx.xxx.xxx/api/?device=pico2&sensor1=valulesfromsensor1&sensor2=valuesfromsensor2")
proceseddata = jsondata.json()
steps = steps + proceseddata + "<br>"
steps = steps + "Step 4: Doing things with results...<br>"
#function to do conditions and things with results...
#...
#...
#...
jsondata.close()
steps = steps + "Step 5: Finished, sleeping for the next round...<br>"
utime.sleep_ms(100)
led.off()
utime.sleep_ms(100)
led.on()
utime.sleep(900)
except OSError as e:
steps = steps + e
def serve(connection):
while True:
try:
client, addr = connection.accept()
print('client connected from', addr)
request = client.recv(1024)
request = str(request)
html = webpage(steps)
client.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
client.send(html)
client.close()
except OSError as e:
client.close()
def webserver():
ip = connect()
connection = open_socket(ip)
_thread.start_new_thread(serve,(connection,))
try:
webserver()
pushgetdata()
except KeyboardInterrupt:
machine.reset()

I'm not sure exactly what's going on with your code, but using the same threading idea, please find here my solution for a similar problem I've encountered today if it helps.
from machine import Pin
from time import sleep
import wlan_data, network
import socket, _thread
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(wlan_data.SSID, wlan_data.PASSWORD)
print("WLAN is connected: " + str(wlan.isconnected()))
page = open("index.html", "r")
html = page.read()
page.close()
sta_if = network.WLAN(network.STA_IF)
print(sta_if.ifconfig()[0])
def setUpSocket():
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)
return s
def serve(s):
while True:
try:
cl, addr = s.accept()
cl_file = cl.makefile('rwb', 0)
while True:
line = cl_file.readline()
if not line or line == b'\r\n':
break
response = html
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()
except OSError as e:
cl.close()
def webserver():
s = setUpSocket()
_thread.start_new_thread(serve,(s,))
webserver()
led = machine.Pin("LED", machine.Pin.OUT)
led.toggle()
while 1:
led.toggle()
sleep(10)

_thread library is experimental and buggy. From the micropython documentation https://docs.micropython.org/en/latest/library/_thread.html
This module is highly experimental and its API is not yet fully settled and not yet described in this documentation.
From my own testing, some of the functionality works for a while, then you might add another import and things stop working. Best to steer clear until it is no longer an experimental feature.

Related

Run more than one thread in micropython(pico w)

i am gettinig this error below when i try to run the second thread on my pico w.
Traceback (most recent call last):
File "<stdin>", line 114, in <module>
OSError: core1 in use
I am trying to display some content in one thread, and in another, server a simple web page with the same content.
"""BME688 / BME680 demo
This demo will work for both the BME680 and BME688.
"""
print("lets import and use the libraries")
import time
from time import sleep
import network
import socket
import _thread
from breakout_bme68x import BreakoutBME68X, STATUS_HEATER_STABLE
from pimoroni_i2c import PimoroniI2C
from picographics import PicoGraphics, DISPLAY_LCD_240X240, PEN_P8
display = PicoGraphics(display=DISPLAY_LCD_240X240, pen_type=PEN_P8)
display.set_backlight(1.0)
WIDTH, HEIGHT = display.get_bounds()
ssid = 'netgear_2.4g' #Your network name
password = '9xc4prce' #Your WiFi password
print("setting pin breakouts etc")
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
print("finish setting pins and now set i2c")
i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN)
print("i2c set and now set bme")
bme = BreakoutBME68X(i2c)
print("bme set")
# If this gives an error, try the alternative address
# bme = BreakoutBME68X(i2c, 0x77)
temperatureReadingData = ["","0.0c"]
print("Testing sensors on the while loop")
def connect():
#Connect to WLAN
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
while wlan.isconnected() == False:
print('Waiting for connection...')
sleep(1)
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')
return ip
def open_socket(ip):
# Open a socket
address = (ip, 80)
connection = socket.socket()
connection.bind(address)
connection.listen(1)
return connection
def readTemperature():
temperature, pressure, humidity, gas, status, _, _ = bme.read()
heater = "Stable" if status & STATUS_HEATER_STABLE else "Unstable"
temperatureReadingData[0] = "Temperature is {:0.2f}c, Pressure is {:0.2f}Pa, humidity is {:0.2f}%, gas is {:0.2f} Ohms, Heater: {}".format(temperature, pressure, humidity, gas, heater)
temperatureReadingData[1] = "{:0.2f}c".format(temperature)
def webpage(reading):
#Template HTML
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Pico W Weather Station</title>
<meta http-equiv="refresh" content="10">
</head>
<body>
<p>{reading}</p>
</body>
</html>
"""
return str(html)
def serve():
#Start a web server
ip = connect()
connection = open_socket(ip)
while True:
client = connection.accept()[0]
request = client.recv(1024)
request = str(request)
html = webpage(temperatureReadingData[0])
client.send(html)
client.close()
def displayTemp():
while True:
readTemperature()
print("display temp" + temperatureReadingData[1])
display.clear()
display.set_pen(15)
display.text(temperatureReadingData[1], 0, 0, scale=4)
display.update()
time.sleep(1.0)
if __name__ == "__main__":
try:
tempDisplay = _thread.start_new_thread(displayTemp, ())
webServer = _thread.start_new_thread(serve, ())
except KeyboardInterrupt:
machine.reset()

MicroPython: How to auto reconnect STA to AP if AP gets power cycled?

I am writing a python code on ESP8266 using MicroPython to configure it as STA, connect it to an AccessPoint and then send some data on UDP Socket.
I want to implement a scenario where in case if AccessPoint due to some reason goes down and comes back after some time, the the ESP8266 acting as STA should automatically reconnect to specific AP.
I am not sure if there's a functionality to setup a callback on STA if it misses few Beacons (e.g. upto 5 or 10)
This is the code that I have written so far:
import network
import esp
import gc
import time
try:
import usocket as socket
except ModuleNotFoundError as e:
import socket
def main():
count = 0
esp.osdebug(None)
gc.collect()
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect('HumidityServer', 'password#123')
while not sta.active():
pass
print('Connection successful')
print(sta.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('192.168.45.1', 9000))
while True:
str_to_send = "Hello ESP8266: " + str(count)
s.sendto(str_to_send, ('192.168.45.1', 9000))
request = s.recv(1024)
print('Content = %s' % str(request))
count = count + 1
time.sleep(5)
if __name__ == "__main__":
main()

Dual core on Raspberry Pi PICO W with MicroPython

I have an issue with running part of a code to be more specific defined as new thread CoreTask().
I have no idea why I cannot turn on/off built-in LED. The rest of the code looks to be working as expected (WIFI integration and 'httpd service' work fine).
I use Raspberry Pi Pico W with latest MicroPython loaded.
Please advice... Thanks!
import machine
import _thread
import network
import socket
import utime
from machine import Pin
led = machine.Pin('LED', machine.Pin.OUT)
ssid = 'someSSID'
password = 'somePASSWORD'
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
html = """<!DOCTYPE html>
<html>
<body>
<div id="humidity">100</div>
<div id="temperature">21</div>
</body>
</html>
"""
sLock = _thread.allocate_lock()
def CoreTask():
while True:
sLock.acquire()
print('LED...')
led.on()
utime.sleep(1)
led.off()
utime.sleep(1)
sLock.release()
_thread.start_new_thread(CoreTask, ())
while True:
sLock.acquire()
# Wait for connect or fail
max_wait = 10
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('waiting for connection...')
utime.sleep(1)
# Handle connection error
if wlan.status() != 3:
raise RuntimeError('network connection failed')
else:
print('connected')
status = wlan.ifconfig()
print( 'ip = ' + status[0] )
# Open socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
# Listen for connections
while True:
try:
cl, addr = s.accept()
print('client connected from', addr)
request = cl.recv(1024)
print(request)
request = str(request)
response = html
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()
except OSError as e:
cl.close()
print('connection closed')
sLock.release()
I'm also keen to know a solution, so far the best try I have got the onboard led working in seperate thread is the following simple code, every other more complicated try was failed.
I have only tested this with onboard LED, so if anyone may try if threading works on real GPIO? If it does, I think this is not an issue as most of time we send out "status blinks" on other LED in project not the onboard one.
import machine
import _thread
import time
def led():
led = machine.Pin('LED', machine.Pin.OUT)
while True:
led.on()
time.sleep(.5)
led.off()
time.sleep(.5)
def pnt():
i=0
while True:
i += 1
print(f"\rThis is the {i} cycle......", sep="", end="")
time.sleep(1)
if __name__ == "__main__":
th1 = _thread.start_new_thread(pnt, ())
#th2 = _thread.start_new_thread(led, ()) This line did not work
led()
I think someone said in a video that the threading is still in experiment and has problems on pico w, and they are working on it.
I also could not workout how to achieve same result with uasycncio, it looks like calling the LED to blink is not about programming but real physical movement, so cannot be "await". Can anyone show ma a correct way?
It's is such an important function to waiting for program (connection loop) while sending "status blinks"

Python Socket Multiple Clients

So I am working on an iPhone app that requires a socket to handle multiple clients for online gaming. I have tried Twisted, and with much effort, I have failed to get a bunch of info to be sent at once, which is why I am now going to attempt socket.
My question is, using the code below, how would you be able to have multiple clients connected? I've tried lists, but I just can't figure out the format for that. How can this be accomplished where multiple clients are connected at once and I am able to send a message to a specific client?
Thank you!
#!/usr/bin/python # This is server.py file
import socket # Import socket module
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 50000 # Reserve a port for your service.
print 'Server started!'
print 'Waiting for clients...'
s.bind((host, port)) # Bind to the port
s.listen(5) # Now wait for client connection.
c, addr = s.accept() # Establish connection with client.
print 'Got connection from', addr
while True:
msg = c.recv(1024)
print addr, ' >> ', msg
msg = raw_input('SERVER >> ')
c.send(msg);
#c.close() # Close the connection
Based on your question:
My question is, using the code below, how would you be able to have multiple clients connected? I've tried lists, but I just can't figure out the format for that. How can this be accomplished where multiple clients are connected at once and I am able to send a message to a specific client?
Using the code you gave, you can do this:
#!/usr/bin/python # This is server.py file
import socket # Import socket module
import thread
def on_new_client(clientsocket,addr):
while True:
msg = clientsocket.recv(1024)
#do some checks and if msg == someWeirdSignal: break:
print addr, ' >> ', msg
msg = raw_input('SERVER >> ')
#Maybe some code to compute the last digit of PI, play game or anything else can go here and when you are done.
clientsocket.send(msg)
clientsocket.close()
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 50000 # Reserve a port for your service.
print 'Server started!'
print 'Waiting for clients...'
s.bind((host, port)) # Bind to the port
s.listen(5) # Now wait for client connection.
print 'Got connection from', addr
while True:
c, addr = s.accept() # Establish connection with client.
thread.start_new_thread(on_new_client,(c,addr))
#Note it's (addr,) not (addr) because second parameter is a tuple
#Edit: (c,addr)
#that's how you pass arguments to functions when creating new threads using thread module.
s.close()
As Eli Bendersky mentioned, you can use processes instead of threads, you can also check python threading module or other async sockets framework. Note: checks are left for you to implement how you want and this is just a basic framework.
accept can continuously provide new client connections. However, note that it, and other socket calls are usually blocking. Therefore you have a few options at this point:
Open new threads to handle clients, while the main thread goes back to accepting new clients
As above but with processes, instead of threads
Use asynchronous socket frameworks like Twisted, or a plethora of others
Here is the example from the SocketServer documentation which would make an excellent starting point
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
Try it from a terminal like this
$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello
HELLOConnection closed by foreign host.
$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Sausage
SAUSAGEConnection closed by foreign host.
You'll probably need to use A Forking or Threading Mixin too
This program will open 26 sockets where you would be able to connect a lot of TCP clients to it.
#!usr/bin/python
from thread import *
import socket
import sys
def clientthread(conn):
buffer=""
while True:
data = conn.recv(8192)
buffer+=data
print buffer
#conn.sendall(reply)
conn.close()
def main():
try:
host = '192.168.1.3'
port = 6666
tot_socket = 26
list_sock = []
for i in range(tot_socket):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host, port+i))
s.listen(10)
list_sock.append(s)
print "[*] Server listening on %s %d" %(host, (port+i))
while 1:
for j in range(len(list_sock)):
conn, addr = list_sock[j].accept()
print '[*] Connected with ' + addr[0] + ':' + str(addr[1])
start_new_thread(clientthread ,(conn,))
s.close()
except KeyboardInterrupt as msg:
sys.exit(0)
if __name__ == "__main__":
main()
def get_clients():
first_run = True
startMainMenu = False
while True:
if first_run:
global done
done = False
Thread(target=animate, args=("Waiting For Connection",)).start()
Client, address = objSocket.accept()
global menuIsOn
if menuIsOn:
menuIsOn = False # will stop main menu
startMainMenu = True
done = True
# Get Current Directory in Client Machine
current_client_directory = Client.recv(1024).decode("utf-8", errors="ignore")
# beep on connection
beep()
print(f"{bcolors.OKBLUE}\n***** Incoming Connection *****{bcolors.OKGREEN}")
print('* Connected to: ' + address[0] + ':' + str(address[1]))
try:
get_client_info(Client, first_run)
except Exception as e:
print("Error data received is not a json!")
print(e)
now = datetime.now()
current_time = now.strftime("%D %H:%M:%S")
print("* Current Time =", current_time)
print("* Current Folder in Client: " + current_client_directory + bcolors.WARNING)
connections.append(Client)
addresses.append(address)
if first_run:
Thread(target=threaded_main_menu, daemon=True).start()
first_run = False
else:
print(f"{bcolors.OKBLUE}* Hit Enter To Continue.{bcolors.WARNING}\n#>", end="")
if startMainMenu == True:
Thread(target=threaded_main_menu, daemon=True).start()
startMainMenu = False
#!/usr/bin/python
import sys
import os
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 50000
try:
s.bind((socket.gethostname() , port))
except socket.error as msg:
print(str(msg))
s.listen(10)
conn, addr = s.accept()
print 'Got connection from'+addr[0]+':'+str(addr[1]))
while 1:
msg = s.recv(1024)
print +addr[0]+, ' >> ', msg
msg = raw_input('SERVER >>'),host
s.send(msg)
s.close()

socket.SO_REUSEADDR: packets received by every vs by newest listener

I got multiple processes listening on the same port subscribed to a multicast address. Packets to this address reach every process. However, when I contact them via unicast, only the newest process gets the message. Where is this behavior documented? How can I change it?
Demo program (Python):
import socket,os,struct,sys
def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 4242))
mreq = '\xef\x01\x02\x03' + struct.pack('=I', socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
d = sock.recvfrom(1024)
print('[s' + str(os.getpid()) + '] ' + repr(d))
def client():
caddr = '239.1.2.3'
caddr = '127.0.0.1' # Uncomment this and all servers print
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
csock.sendto('data from ' + str(os.getpid()), 0, (caddr, 4242))
def main():
if sys.argv[1] == 's':
server()
else:
client()
if __name__ == '__main__':
main()
The MSDN states that the behaviour where multiple sockets are listening to the same port for unicast messages is undefined and that there's no way to know which one will receive the data. I tested a similar setup using C++ and winsock2.2 and had similar results as when I ran your program under python (namely the process-blocking effect).
Here's the MSDN article