Threading Timer - NoneType error on raspberry pi - passing variable to another thread

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


Threading Timer - NoneType error on raspberry pi - passing variable to another thread



I'm junior python dev, mainly backend Django. One of my first tasks in work to write program on raspberry pi that set state to LOW and after 5 sec to HIGH.


LOW


HIGH



Seems not a problem but.... I think the proper way of waiting 5 sec to change to HIGH is not via time.sleep() but via threading.Timer (I found it on stackoverflow)... It will be connected via web socket so I think one thread must listen and another must change states


HIGH


time.sleep()


threading.Timer



My problem is half pythonic, half raspberrian... this is my code:


import RPi.GPIO as GPIO
from threading import Timer
from time import sleep

pin = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT)

def set_high():
GPIO.output(pin, GPIO.HIGH)

def web_socket_loop():
GPIO.output(pin, GPIO.LOW)
t = Timer(5, set_high(GPIO))
t.start()


web_socket_loop()

GPIO.cleanup()



When I run this code states are changing, but after few sec I also got an error:


Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.7/threading.py", line 1158, in run
self.function(*self.args, **self.kwargs)
TypeError: 'NoneType' object is not callable



I found on stack that I have to do something like this: (but it does not work properly): ;/


t = Timer(5, set_high, args=[GPIO])



And it does repair error about NoneType... but I got another error:


Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.7/threading.py", line 1158, in run
self.function(*self.args, **self.kwargs)
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)



I probably know why this error is but have no idea how to deal with it. I think that on another thread RPi.GPIO is not configured (GPIO.setmode(GPIO.BCM), GPIO.setup(pin, GPIO.OUT))


RPi.GPIO


(GPIO.setmode(GPIO.BCM), GPIO.setup(pin, GPIO.OUT))



Thats why I pass GPIO as a variable to the function set_high(GPIO), without it on another thread it thinks that RPi is not configured... passing this variable is cool but... I got an NoneType error... ;/


GPIO


set_high(GPIO)


RPi


NoneType



When I try second way with Timer(5, set_high, args=[GPIO]) the NoneType error disappears but another occurs (RuntimeError with info about pin setup)... this is the same error which I get when I don't pass GPIO to the function like this:


Timer(5, set_high, args=[GPIO])


NoneType


RuntimeError with info about pin setup


GPIO


def set_high(#i_dont_pass_GPIO):
#things
pass



What is the proper way of passing the GPIO variable to another thread? So that it will recognize this variable properly and set pin 22 to HIGH.



Or maybe you recommend to try do it another way? Maybe sleep is better? But it will be connected via web socket so I think one must listen and another must change states?



I appreciate any help!!





The problem with t = Timer(5, set_high(GPIO) is that it's calling set_high(GPIO) right now, then passing the result of that call (which is None) as the target function to Timer. And you can't call None as a function. But your fix to call t = Timer(5, set_high, args=[GPIO]) solves that. If you get a different error, please edit your question to be about the problem you want help with, instead of being about the problem you already solved and only mentioning your actual problem two thirds of the way down.
– abarnert
yesterday


t = Timer(5, set_high(GPIO)


set_high(GPIO)


None


Timer


None


t = Timer(5, set_high, args=[GPIO])




1 Answer
1



Your first problem, and your third one, really aren't relevant, because you've already correctly solved them.



When you call Timer(5, set_high(GPIO)), that calls set_high(GPIO) right now, and passes whatever it returns as the target function for the Timer to call. Since it returns None, 5 seconds later, your Timer tries to call None as a function, hence the TypeError.


Timer(5, set_high(GPIO))


set_high(GPIO)


Timer


None


Timer


None


TypeError



The fix is exactly what you already did:


Timer(5, set_high, [GPIO])



Now, once you've fixed that, think through the flow of control you're asking for.



First, you do this:


GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT)



Then you call web_socket_loop, and it does this:


web_socket_loop


GPIO.output(pin, GPIO.LOW)



And then it creates and starts a Timer, and returns. And you do this:


Timer


GPIO.cleanup()



Then, 5 seconds later, the timer does off and calls your set_high function, which does this:


set_high


GPIO.output(pin, GPIO.HIGH)



You can't set the pin high after you've cleaned up the GPIO.



The error message is a bit misleading:


RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)



This is because the state post-cleanup is the same as the state pre-initialization, so it assumes your problem is that you haven't initialized things by calling setmode yet.


setmode



How can you fix that? Your main thread shouldn't try to clean up until you're done.



Normally, you have other stuff for the main thread to do. That's why you use a Timer, or a Thread, or some other form of concurrency. But when you do that, you need to give your main thread some way to wait on that background work to finish, or you need to find some way to chain things up so that the cleanup happens only after the last thread is done using GPIO.


Timer


Thread


cleanup



When you add the code that deals with communicating over a websocket, you'll need to do one of those things. But it's impossible to show you what you should do without knowing how you plan to organize that code.



Meanwhile, you really have nothing else to do but wait for 5 seconds, so you don't need any concurrency; just sleep:


sleep


def web_socket_loop():
GPIO.output(pin, GPIO.LOW)
time.sleep(5)
set_high(GPIO)





Ohhh gosh!!! You are right! I'm cleaningup before timer ends... Thank you! This device will be connected via websocket, on mobile user clicks "DOIT!" then request goes to server and server sends message to raspberry (websocket), when raspbery get message it run this script (set GPIO.LOW), and it takes 15 sec, to complete "task", after this script sets GPIO.HIGH and sends back info to server about success (and server to mobile).
– Adrian Kurzeja
yesterday





I thought about multithreating becouse what if another person sends click "DOIT!"? Raspberry should react somehow I think (not sure)? lets say "Now i'm bussy" and in the same time it should still do script. Or maybe sleep() won't destroy it at all?
– Adrian Kurzeja
yesterday





Heh... sorry for spam... but now I found out that I should block sending requests to raspberry directly on server... and block it untill raspberry answers with succes or error. Now I have to confirm on raspberry that "double" request won't hurt it just to be sure. Thanks again for help ;)
– Adrian Kurzeja
yesterday





@AdrianKurzeja As I said, once you write the code that actually does some websocket stuff, you'll have to work out how to integrate the timer into that. If you're using threaded I/O, you probably want a threaded timer, and just have the main thread wait by joining your main I/O thread. If you're using asyncio, you probably want to schedule a timer in that asyncio event loop, and you'll just do the cleanup after the event loop. And so on.
– abarnert
yesterday


join


asyncio






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Stripe::AuthenticationError No API key provided. Set your API key using “Stripe.api_key = ”

CRM reporting Extension - SSRS instance is blank

Keycloak server returning user_not_found error when user is already imported with LDAP