Hi
I'm trying to add a flowmeter to my Raspberry Pi web app.
The flowmeter is attached via GPIO and I'm listening for GPIO Events.
When an event occurs the callback method gets called.
Inside this method I like to send an update via websocket to the client.
Unfortunately I don't receive an event on client site.
Any idea why I don't get data on the client site?
def callback(channel):
socketio.emit('UPDATE_FLOWMETER', "DATA", namespace='/test')
def initFlowmeter(data):
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
try:
# SETUP GPIO PIN
GPIO.setup(data.gpio, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# ADD GPIO CALLBACK
GPIO.add_event_detect(data.gpio, GPIO.RISING, callback=callback, bouncetime=20)
except Exception as e:
print e
I don't have enough information to tell you what's wrong. I recommend that you verify that the callback is being invoked, and also that the event is being emitted (you can add logger=True to your SocketIO constructor to enable logging to your console). If the event is being emitted, then I would enable logging on the browser side to see if the event is being received (see this SO question). If the client logs show that the event was received, but yet your javascript callback isn't invoked, then it is your client app that is set up incorrectly, maybe a mismatch in the namespace name.
I've this kind of issue too.
If I put "socketio.emit" command elsewhere in my application, it works fine.
Inside GPIO callback it doesn't. Maybe because GPIO.add_event_detect is creating a background thread to control I/O and to invoke the callback and ( maybe) inside the thread there are some issues.
I'm using eventlet, with eventlet.moneky_patch() invocation just after imports.
Workaround:
Basically do not use add_event_detect, but create yourself a thread (using eventlet.spawn) that check IO status:
def checkIO():
while(True):
currentIO = GPIO.input(pinNumber)
if currentIO != previousIO:
previousIO = currentIO
if currentIO == 1: # RISING
socketio.emit(...)
eventlet.sleep(0.1)
@sierrodc I have been working on a similar project which uses the GPIO library to detect when a button is pressed and then update the client side. After trying many different configurations, I ultimately found a solution by forcing the websocket to use gevent with monkey patching instead of eventlet. I'm not sure why this made a difference, but it worked for me.
`from gevent import monkey
monkey.patch_all()
app = Flask(__name__)
socketio = SocketIO(app, async_mode='gevent')`
I'm happy to share more of my code with you if that doesn't help.
I got the same issue with my GPIO callback and even the monkey patch its not working. When I use asyn_mode="threading" it works with transport polling.
This is how I start my app:
from app import app, socketio
from gevent import monkey
monkey.patch_all()
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=80, debug=False)
This is my app __init__.py :
from flask import Flask
import psycopg2
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'gjr39dkjn344_!67#'
socketio = SocketIO(app, async_mode='gevent')
#socketio = SocketIO(app, async_mode='treading')
db = psycopg2.connect(database="counter", user="pi", password="", host="127.0.0.1", port="5432")
from app import routes
This is how I call the emit function:
import json
from app import socketio
def updateSensorsState(sensor):
print "Update"
socketio.emit('state', json.dumps(sensor))
When I start the app with debug=True it works well...!
@nicolasfoisy monkey patching helps for standard library modules such as socket and threading. I'm not familiar with the Python GPIO code, but if it provides blocking and non-blocking interfaces you should definitely go non-blocking.
Im using GPIO.add_event_detect witch is executing a callback function when my button is pressed. This is where the emit function is called. I assume it should be non-blocking. When I start the app with debug=True it works well with websocket...
witch is executing a callback function
How does this work exactly? The most used pattern is to run a blocking function that reads the GPIO state in another thread and invokes the callback when appropriate. That would not work with gevent.
This is what is probably under the hood of the GPIO.add_event_detect funtion. A while thru in a seperate thread waiting for a gpio input and invoking the callback. However it works perfectly well in debug wich is kind of strange for me.
You may want to move the monkey patching to the top of the file, above all other imports. The main difference between debug and non-debug modes for gevent is that in debug mode there is monkey patching, which is required by the Flask reloader. Any chance your monkey patching isn't being applied, so then things work only when I apply it from my side?
I have put the monkey patch at the top of my run.py file and nothing. When I print socketIo right before the emit function I receive flask_socketio.SocketIO object at 0x757871f0 so the object kind of exist in this context. And when I wait 10 to 15 sec the client receive the message.
When I start in debug and print socketio, I see that the function is called twice and on the screen prints the 2 socketio object have distincts memory adresses.
This makes me believe that it works only when the app is instantiated twice with the reloader. One version of the socketIo object fail to emit the message and the other one manage to get is way to the client. But why? Even if there is two instances of the app working in concurence shouldn't I normaly receive the message twice at worst?
When you use the reloader two applications are created, but only one is active. This is a side effect of how the reloader works. I don't think that is related to the issue.
So if I sumarize:
If debug is True -> works perfectly well
If debug is False -> Works but the client receive the message with some delai (variable delai)
So it looks like the emit function is stuck on a stack somewhere and its not doing that behavior when in debug. This only occur when the function is called from my GPIO callback. I guess I will run my program in debug because I dont have the knowlege to go beyond this diagnostic.
But is there a way to force the execution of this function on the main thread?
I cannot explain the difference in behavior between debug and non-debug modes, but if you are using the official GPIO library then I'm pretty sure it isn't compatible with greenlet frameworks such as gevent or eventlet. This library is written in C and uses threads, so it cannot be force to comply with greenlets with monkey patching.
Ok thanks Miguel, I will give one more try to monkey something in my code to make it work and if I find something I'll let you know.
Thanks again for your support, I understand a little bit more how and why it's not working. If you have a genius idea on your side let me know!
I'm having the exact same problem as nicolasfoisy, i.e. in debug=True mode it works. Using RPi.GPIO interrupts to call callbacks, which then emit to the front-end. I also experienced the exact same problem as scottofthescience regarding eventlets. gevent seems to fare a little better.
"The most used pattern is to run a blocking function that reads the GPIO state in another thread and invokes the callback when appropriate. That would not work with gevent." has me concerned, but it does seem to work in debug mode!
Perhaps a different GPIO library would help.
From this blog post:
https://beenje.github.io/blog/posts/experimenting-with-asyncio-on-a-raspberry-pi/
I found this async version of the popular pigpio library:
https://github.com/PierreRust/apigpio
@snowbord if you are willing to drop Flask, then you can use the base Socket.IO package python-socketio, which can be used with asyncio.
Thanks. I've migrated to Tornado, which leverages Python 3's async await and am using that to drive Socket.IO.