Raspberry Pi Pico keeps crashing every since I started using both cores - multithreading

I'm new to pico, only using arduinos before. I'm trying to make a simple rotary encoder program that displays a value from 0-12 on an 0.96 oled display, and lights up that many leds on a strip.
I wanted to try out using multiple cores, as interrupts made the leds not run smooth when I had them just cycling (everything would be paused while the encoder was being turned)
However, when I run this program, aside from the encoder being bouncy, the pico would crash maybe 30 seconds into running the program, making a mess on the display and stopping the code. I feel like there's some rule of using multiple cores that I completely ignored.
Here's the code:
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import _thread
import utime
import neopixel
#general variables section
numOn = 0
#Encoder section
sw = Pin(12,Pin.IN,Pin.PULL_UP)
dt = Pin(11,Pin.IN)
clk = Pin(10,Pin.IN)
encodeCount = 0
lastClk = clk.value()
lastButton = False
#Encoder thread
def encoder(): #don't mind the indentation here,
#stackoverflow kinda messed up the code block a bit.
while True:
#import stuff that I shouldn't need to according to tutorials but it doesn't work without
global encodeCount
global lastClk
global clk
import utime
if clk.value() != lastClk:
if dt.value() != clk.value():
encodeCount += 1
else:
encodeCount -= 1
if encodeCount > 12:
encodeCount = 0
elif(encodeCount < 0):
encodeCount = 12
lastClk = clk.value()
print(encodeCount)
utime.sleep(0.01)
_thread.start_new_thread(encoder,())
#LED section
numLed = 12
ledPin = 26
led = neopixel.NeoPixel(machine.Pin(ledPin),numLed)
#Screen Section
WIDTH = 128
HEIGHT = 64
i2c = I2C(0,scl=Pin(17),sda=Pin(16),freq=200000)
oled = SSD1306_I2C(WIDTH,HEIGHT,i2c)
#loop
while True:
for i in range(numLed):
led[i] = (0,0,0)
for i in range(encodeCount):
led[i] = (100,0,0)
led.write()
#Display section
oled.fill(0)
oled.text(f'numLed: {numOn}',0,0)
oled.text(f'counter: {encodeCount}',0,40)
oled.show()
I'm probably doing something stupid here, I just don't know what.
Also, any suggestions on simply debouncing the encoder would be very helpful.
Any help would be appreciated! Thanks!
Update: The code above bricked the pico, so clearly I'm doing something very very wrong. _thread start line stopped it from crashing again, so the problem is there.

Same issue with very similar code on a Raspberry Pico W. I specify the 'W' because my code works without crashing on an earlier Pico.
I'm wondering if the low level networking functions might be using the 2nd core and causing a conflict.
I'm adding thread locking to see if passing a baton helps, the link below has an example, eg:
# create a lock
lck= _thread.allocate_lock()
# Function that will block the thread with a while loop
# which will simply display a message every second
def second_thread():
while True:
# We acquire the traffic light lock
lck.acquire()
print("Hello, I'm here in the second thread writting every second")
utime.sleep(1)
# We release the traffic light lock
lck.release()

Related

Get screnn shot in python3.7 by win32gui will cause memory leak

Win10, python3.7
My code like this:
import win32gui, win32ui, win32con, win32api
screen_w = 2880
screen_h = 1800
x0 = int(screen_w * 0.866)
y0 = int(screen_h * 0.14)
ww = 220
hh = 70
def window_capture(filename):
hwnd = 0
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, ww, hh)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0, 0), (ww, hh), mfcDC, (x0, y0), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, filename)
#------------------
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
#------------------
count = 0
while 1:
window_capture('temp.png')
count += 1
if int(count/1000) == float(count/1000):
print(count)
Firstly, I didn`t add DeleteObject and DeleteDC to release memory. The process will stop after about 10000 times window_capture.
But after I add them, I still can see the memory increase in task manager. Finally, after about 130000 times, the process stop again.
Does that mean I haven`t release all the memories? In my project, I need capture screen shot for a long time. How can I solve this problem?
Thank you!
Caveat: I don't know Python.
From Win32 perspective, you should not delete a GDI object while it is still selected into any DC (Device Context). You are doing precisely that here:
win32gui.DeleteObject(saveBitMap.GetHandle())
The typical Win32 scenario is to save a handle of the object previously selected into DC (any of the Select...() functions return that), and re-select it into the DC when you are done with your own object.
Alternatively, you can use SaveDC / RestoreDC pair if you need to restore multiple objects.

Detect when no buttons are being pushed PyQt

I have a GUI that is controlling a device over USB.
The way I have it set up is with basically two buttons, forward and back, and while the button is pressed that function is transmitted to the motor, when the button is released, the off signal is triggered once.
def on_release():
print('Off')
self.off()
def on_click():
print('forward')
self.forward()
button = QPushButton('Cut', self)
button.move(100,70)
button.pressed.connect(on_click)
button.released.connect(on_release)
def on_click():
print('back')
self.back()
button = QPushButton('back', self)
button.move(200,70)
button.pressed.connect(on_click)
button.released.connect(on_release)
I've recently encountered an interesting failure mode where if the USB connection is paused (in my case I was using GDB and hit a breakpoint, released the button, then released the breakpoint) at the moment the button is released, the kill signal is never sent and the motor will continue going back or forward forever. (It can be killed by either clicking one of back or forward and releasing, or by killing USB entirely")
I already have protections in place (a threaded heartbeat signal) for turning off the motor in the condition that a USB connection is severed but I'd like to make this fail a little more safely in case that one particular USB off transmission were to fail.
Is there a way for me to check if no buttons are pressed so I can use this to trigger the off signal?
Learning material from tjmarkham stepper.py script at [https://github.com/tjmarkham/python-stepper] for a raspberry Pi which can be put behind your buttons:
#CURRENT APPLICATION INFO
#200 steps/rev
#12V, 350mA
#Big Easy driver = 1/16 microstep mode
#Turn a 200 step motor left one full revolution: 3200
from time import sleep
import RPi.GPIO as gpio #https://pypi.python.org/pypi/RPi.GPIO
#import exitHandler #uncomment this and line 58 if using exitHandler
class stepper:
#instantiate stepper
#pins = [stepPin, directionPin, enablePin]
def __init__(self, pins):
#setup pins
self.pins = pins
self.stepPin = self.pins[0]
self.directionPin = self.pins[1]
self.enablePin = self.pins[2]
#use the broadcom layout for the gpio
gpio.setmode(gpio.BCM)
#set gpio pins
gpio.setup(self.stepPin, gpio.OUT)
gpio.setup(self.directionPin, gpio.OUT)
gpio.setup(self.enablePin, gpio.OUT)
#set enable to high (i.e. power is NOT going to the motor)
gpio.output(self.enablePin, True)
print("Stepper initialized (step=" + self.stepPin + ", direction=" + self.directionPin + ", enable=" + self.enablePin + ")")
#clears GPIO settings
def cleanGPIO(self):
gpio.cleanup()
#step the motor
# steps = number of steps to take
# dir = direction stepper will move
# speed = defines the denominator in the waitTime equation: waitTime = 0.000001/speed. As "speed" is increased, the waitTime between steps is lowered
# stayOn = defines whether or not stepper should stay "on" or not. If stepper will need to receive a new step command immediately, this should be set to "True." Otherwise, it should remain at "False."
def step(self, steps, dir, speed=1, stayOn=False):
#set enable to low (i.e. power IS going to the motor)
gpio.output(self.enablePin, False)
#set the output to true for left and false for right
turnLeft = True
if (dir == 'right'):
turnLeft = False;
elif (dir != 'left'):
print("STEPPER ERROR: no direction supplied")
return False
gpio.output(self.directionPin, turnLeft)
stepCounter = 0
waitTime = 0.000001/speed #waitTime controls speed
while stepCounter < steps:
#gracefully exit if ctr-c is pressed
#exitHandler.exitPoint(True) #exitHandler.exitPoint(True, cleanGPIO)
#turning the gpio on and off tells the easy driver to take one step
gpio.output(self.stepPin, True)
gpio.output(self.stepPin, False)
stepCounter += 1
#wait before taking the next step thus controlling rotation speed
sleep(waitTime)
if (stayOn == False):
#set enable to high (i.e. power is NOT going to the motor)
gpio.output(self.enablePin, True)
print("stepperDriver complete (turned " + dir + " " + str(steps) + " steps)")
teststepper.py:
from Stepper import Stepper
#stepper variables
#[stepPin, directionPin, enablePin]
testStepper = Stepper([22, 17, 23])
#test stepper
testStepper.step(3200, "right"); #steps, dir, speed, stayOn

Python scapy Beacon Frames

I'm trying to build a scapy program that scans for Beacon Frames. Every router should send beacon frames to the air in an interval of X milliseconds so the possible hosts know the router(AP) is alive.
I'm getting nothing, the only kind of Dot11 frames I've been able to get so far is Prob Request, very rarely some data or control frames as well. I setup my wireless card to monitor mode before running the script and it supports it as well. I don't what I might be doing wrong... Here's the code :
from scapy.all import *
global list_prob
list_prob = []
def search_prob(packet1):
if (packet1.haslayer(Dot11)) and (packet1[Dot11].type == 0) and\
(packet1[Dot11].subtype == 8) : #type 4 == ProbRequest
if packet1[Dot11].addr2 not in list_prob:
if packet1[Dot11].info not in list_prob:
print('[>]AP',packet1[Dot11].addr2,'SSID',packet1[Dot11].info)
list_prob.append(packet1[Dot11].addr2)
list_prob.append(packet1[Dot11].info)
sniff(iface='wlan0mon',prn=search_prob)
Ive also tried it with Dot11Beacon instead of subtype 8 and nothing changed . I'm programming with python3.5 on Linux.
Any ideas ?
Code to constantly change channel of network interface using python :
from threading import Thread
import subprocess,shlex,time
import threading
locky = threading.Lock()
def Change_Freq_channel(channel_c):
print('Channel:',str(channel_c))
command = 'iwconfig wlan1mon channel '+str(channel_c)
command = shlex.split(command)
subprocess.Popen(command,shell=False) # To prevent shell injection attacks !
while True:
for channel_c in range(1,15):
t = Thread(target=Change_Freq_channel,args=(channel_c,))
t.daemon = True
locky.acquire()
t.start()
time.sleep(0.1)
locky.release()

Python - queuing one function

I've just started learning python, but I have problem with my code:
import pifacecad
# listener initialization
cad = pifacecad.PiFaceCAD()
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
print('some prints...')
time.sleep(4)
print('and the end.')
blowMyMind() will be fired as many times as listener it tells to. That is okay.
My goal is to deactivate listener UNTIL blowMyMind ends. Pifacecad suggest Barrier() to achieve that, at least I think that it was here for that reason(correct me if I'm wrong).
Now it's working as many times as I activate listener event, but It's not like pushing function 99 times at once, but queues it and runs one by one.
With Barriers I think it should look like this:
# Barrier
global end_barrier
end_barrier = Barrier(1)
# listener initialization
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
global end_barrier
test = end_barrier.wait()
print(test) # returns 0, which should not in about 5 seconds
print('some prints...')
time.sleep(4)
print('and the end.')
The funny part is when I change parties in Barrier initialization it is causing BrokenBarrierError at first listener event.
Actually I think that I completely misunderstood Barrier() I think the problem with it is that all listener events are in one thread instead of their own threads.
It's making me even more confused when I'm reading:
parties The number of threads required to pass the barrier.
from here: https://docs.python.org/3/library/threading.html
My conclusion: when initializing Barrier(X) it would be realeased when there will be X('or less', 'or more'?) number of threads. That sounds VERY stupid :D
I tried to make it that way with no luck:
# listener initialization
global busy
busy = 0
cad = pifacecad.PiFaceCAD()
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
global busy
if busy == 0:
busy = 1
print('some prints...')
time.sleep(4)
print('and the end.')
busy = 0
else:
return None

Raspberry PI with GPIO Input buttons

I have a PI running with 4 GPIO input ports.
The target is, if one the 4 buttons will be pressed, a mp3 file should be played, i.e. button1 = file1.mp3, button2 = file2.mp3 and so on.
It seems to be no so complicate, but 'the devil is in the detail' :-)
This is my code for 2 buttons at moment :
#!/usr/bin/env python
#coding: utf8
import time
from time import sleep
import os
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(24, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(23, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
def my_callback_1(channel):
print("Button 23 Pressed")
os.system('omxplayer -o both /root/1.mp3')
sleep(10)
def my_callback_2(channel):
print("Button 24 Pressed")
os.system('omxplayer -o both /root/2.mp3')
sleep(10)
GPIO.add_event_detect(23, GPIO.RISING, callback=my_callback_1, bouncetime=200)
GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback_2, bouncetime=200)
try:
while 1:
time.sleep(0.5)
except KeyboardInterrupt:
# exits when you press CTRL+C
print(" Bye Bye")
except:
print("Other error or exception occurred!")
finally:
GPIO.cleanup() # this ensures a clean exit
The sleep time is set for the longer of the mp3 file.
Its working, but not like I expected.
The problem is, when a buttons will be pushed while a file is already playing, the PI keeps the button push in a buffer and play the file after the current file in a loop.
Imagine, somebody will push 5 times the same button, 5 times the same mp3 file will be played in a loop.
So I searching for a solution like this:
While a file is playing, all Input buttons should be "disabled" for this time. When the mp3 file paying is finished, the buttons should be "re-enabled" and another button can be pushed.
How can I this ? Thanks for your help.
I don't see a simple way to do this without adding threads. Note that you are implicitly already using threads behind the scenes with add_event_detect(), which runs the callbacks in separate threads. If add_event_detect doesn't support suppressing the button presses (which I don't think it does), then you can thread it in one of two ways - using python threads or processes, or a simpler way by using bash.
To use background processes in bash, remove your add_event_detect calls, and then in your while loop, you'd do something like (untested):
started_23 = 0
while True:
if GPIO.input(23) and time.time() - started_23 > 10:
started_23 = time.time()
print("Button 23 Pressed")
os.system('omxplayer -o both /root/1.mp3 &')
time.sleep(0.200)
Note the ampersand added to the system() call - that will run the omxplayer in the background. And the started_23 variable keeps track of when the sound was started in order to prevent replaying it for another 10 seconds. You may way to increase that to include the length of the file time. You can similarly add in code for GPIO 24 in the same loop.
Thanks for help, Brian. You brought me in the right direction !
I got it. Its working now as I described above.
Here my code :
try:
vtc1 = 8 # Time Audiofile 1
vtc2 = 11 # Time Audiofile 2
vtc3 = 0 # Time Audiofile 3
vtc4 = 0 # Time Audiofile 4
vtc = 0 # Current AudioFileTime
started_t = 0 # Started Time
while True:
if GPIO.input(23) and time.time() - started_t > vtc:
vtc = vtc1
started_t = time.time()
print("Button 23 Pressed")
os.system('omxplayer -o both /root/1.mp3 &')
time.sleep(0.200)
if GPIO.input(24) and time.time() - started_t > vtc:
vtc = vtc2
started_t = time.time()
print("Button 24 Pressed")
os.system('omxplayer -o both /root/2.mp3 &')
time.sleep(0.200)
The problem was, that the seconded file was started before the first was finished. Because the code did not know the longer of currently played file. So I put the time of the audiofile in the "vtc" value when this file were executed.
If you push another button, it calculate the time playing with the current file time "vtc". That's it.
Thanks again.

Resources