socketio emit not working from gpio callback - raspberry-pi

I am pulling my hair out trying to figure out what I am doing wrong.
Server shows emit is being sent, but the browser never gets it. After about a minute, the client disconnects and re-connects.
However, emit after on.connect works perfect. It's almost like the client has to send the server a request and immediately get a response to work.
For some reason, StackOverflow wants me to type more info because it looks like all code, but I'm not really sure what else I can add. I googled and tried different versions and tutorials to get this working, and I believe it should be good. It must be a typo somewhere.
Okay, that still isn't enough detail, so let's go through the entire process.
I have a Raspberry Pi 3 with an SD card, running Bullseye x64 that is up to date.
I am also using a logic level shifter to drive a few 5v transistors that turn on 4 different LEDs on 4 different buttons. The buttons are grounded when pressed, and using the Raspberry Pi's internal pull up resistor. There are also 2 relays that operate solenoid air valves, the relays are separated from the Raspberry Pi/ logic level with an optocoupler.
Essentially, I am trying to use flask-socket io to display a question on a tablet. The answer is then entered using 1 of the 4 colored buttons. If the answer is wrong then relays/transistors are activated to let air out. If the answer is correct then different relays/transistors are activated to trigger the compressor and build pressure again.
The LEDs, buttons presses, and compressors all work. I just can't seem to get socketio to receive emit messages outside of an on event.
As a workaround, I may just have Javascript poll for button status every second, until I can figure out what I'm doing wrong. At least I can get that part to work.
Sorry for the long intro. Maybe it helps, to me it seems like a lot of boring text to read through while I'm sure someone with more knowledge only needed to look at my code to see what was wrong.
Nope, still not enough text.... so let's see. I'm new to Python, and not sure what else to put in here. Actually, I'll write more boring stuff at the bottom so you don't actually have to read any of this.
Raspberry Pi3 Bullseye x64
Dependancies
bidict==0.22.0
cachelib==0.9.0
click==8.1.3
Flask==2.2.1
Flask-Login==0.6.2
Flask-Session==0.4.0
Flask-SocketIO
importlib-metadata==4.12.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
python-engineio
python-socketio
six==1.11.0
Werkzeug==2.2.1
zipp==3.8.1
RPi.GPIO==0.7.1
Python code:
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from random import random
from threading import Lock
from datetime import datetime
import RPi.GPIO as GPIO
import time
"""
define gpio class
this holds all info for pins
"""
class rio:
def __init__(self, label, pin, state, btnPin=None):
self.label = label
self.pin = pin
self.state = state
self.btnPin = btnPin
self.checking = False
self.timeLast = time.time()
GPIO.setup(self.pin, GPIO.OUT)
if (btnPin!=None):
GPIO.setup(btnPin,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(self.btnPin, GPIO.FALLING, callback=self.stateChange)
GPIO.output(self.pin, self.state)
def stateChange(self,channel):
global count
duration = time.time() - self.timeLast
self.timeLast = time.time()
print(self.label + " was pressed")
count+=1
print("Sending counter event...")
socketio.emit('updateSensorData', {'value': count, "date": get_current_datetime()})
"""
Define Globals
"""
count = 0
"""
label and define what gpio
we want to use
"""
GPIO.setmode(GPIO.BCM) #enable GPIO BCM Module
yellow = rio("yellow", 22, GPIO.LOW, 14)
green = rio("green", 10, GPIO.LOW, 16)
blue = rio("blue", 9, GPIO.LOW, 20)
red = rio("red", 11, GPIO.LOW, 21)
compressor = rio("compressor", 13, GPIO.LOW)
LogicShift = rio("shifter", 26, GPIO.HIGH)
valve1 = rio("valve in", 27, GPIO.HIGH)
valve2 = rio("valve out", 17, GPIO.HIGH)
"""
create websocket
"""
app = Flask(__name__)
app.config['SECRET_KEY'] = 'donsky!'
socketio = SocketIO(app, cors_allowed_origins='*', logger=True)
"""
Get current date time
"""
def get_current_datetime():
now = datetime.now()
return now.strftime("%m/%d/%Y %H:%M:%S")
"""
Serve root index file
"""
#app.route('/')
def index():
return render_template('index.html')
"""
Decorator for connect
"""
#socketio.on('connect')
def connect():
global thread
print('Client connected')
emit('after connect', {'data':'Lets dance'})
"""
Decorator for disconnect
"""
#socketio.on('disconnect')
def disconnect():
print('Client disconnected', request.sid)
if __name__ == '__main__':
# Access app in all IP not just localhost
socketio.run(app, host="0.0.0.0")
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Synchronized</title>
<link rel="stylesheet" href='/static/style.css' />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<!--script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.3/socket.io.js"></script-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
// sending a connect request to the server.
var socket = io.connect('http://10.100.180.15:5000');
// An event handler for a change of value
$('#btn1').on('input', function(event) {
console.log('button changed');
socket.emit('Button value changed', {
who:$(this).attr('id'),
data: $(this).val()
});
return false;
});
socket.on('after connect', function(msg){
console.log('After connect', msg);
});
socket.on('update value', function(msg) {
console.log('Slider value updated');
$('#'+msg.who).val(msg.data);
});
socket.on('message', function(msg) {
console.log(msg);
});
socket.on('updateSensorData', function (msg) {
console.log('Received sensorData :: ' + msg.date + ' :: ' + msg.value);
$('#current-count').text(msg.value);
});
});
</script>
</head>
<body>
<h1>Data:</h1>
<div class="">
<h1>Demo</h1>
<form class="mt-5">
<div class="form-group">
<label for"formControlRange">Btn1</label>
<input type="checkbox" class"form-control-range sync" id="btn1" value="{{btn1}}">
</div>
</form>
</div>
<button class="btn-close btn btn-sm">×</button>
<div class="container">
<div class="hero">
<h1 class="title">Real-Time Conveyor Counter Using Raspberry Pi</h1>
<div class="counter">
<h1 id="current-count">0</h1>
</div>
</div>
</div>
</body>
</html>
I tried different dependencies, and different JavaScript versions of socketio, but all of them respond the same way. The browser is Chrome 107.0.5304.110. Safari responds the same way, as well.

Related

Best way to stream audio data between the browser and FastAPI

I have some basic Server and Client Side code with FastAPI, but I am trying to find the best way to read in that data for a live application that does some audio processing. I am not sure how to approach this, as FastAPI can only read the data from the client as bytes. Should I even bother converting it to a Blob after recording? Maybe use HTTP for simplicity (just building a prototype so it does not need to be optimal)? I also read about RTSP but I can't seem to find any good resources for it, so I would appreciate some way to point me in the right direction. Thanks for any advice!
Client
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>helloworld</h1>
<script>
var ws = new WebSocket("ws://localhost:8000/ws", 'echo-protocol');
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start(5000);
mediaRecorder.addEventListener("dataavailable", event => {
console.log("sending audio");
const audioBlob = new Blob([event.data], {type: 'audio/mp3'});
ws.send(audioBlob);
console.log("sent audio");
});
});
</script>
</body>
</html>
Server
from fastapi import FastAPI, WebSocket, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
#app.get("/")
def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
#app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_bytes()

How do i properly install flask-socketIO?

I have installed multiple times Flask-socketio on my mac, closely reading the instructions and installing the requirements (eventlet/gevent). Athough when i run my simple code to test, it either says that i have not imported the modules or show nothing until i open index.html in my browser where it then displays :
The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
Here is my app.py code:
from flask import Flask
from flask_socketio import SocketIO, send
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hello'
socketio = SocketIO(app, cors_allowed_origins='*')
#socketio.on('message')
def handle(msg):
print("message: "+msg)
send(msg, bradcast=True)
if __name__ == '__main__':
socketio.run(app)
And here is my terminal window:
Here is my index.html code (if needed):
<html>
<head>
<title>Chat Room</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
var socket = io.connect('http://127.0.0.1:5000');
socket.on('connect', function() {
socket.send('User has connected!');
});
socket.on('message', function(msg) {
$("#messages").append('<li>'+msg+'</li>');
console.log('Received message');
});
$('#sendbutton').on('click', function() {
socket.send($('#myMessage').val());
$('#myMessage').val('');
});
});
</script>
<ul id="messages"></ul>
<input type="text" id="myMessage">
<button id="sendbutton">Send</button>
</body>
</html>
Thank you for your help
Check the Flask-SocketIO docs for information about version compatibility.
You have installed Flask-SocketIO version 5, so you need version 3 of the JavaScript client, but you have 1.4.8.
Use this CDN URL instead for version 3.0.5: https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.5/socket.io.min.js

react-router - server side rendering match

I have this on my server
app.get('*', function(req, res) {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
const body = renderToString(<RouterContext {...renderProps} />)
res.send(`
<!DOCTYPE html>
<html>
<head>
<link href="//cdn.muicss.com/mui-0.6.5/css/mui.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="root">${body}</div>
<script defer src="assets/app.js"></script>
</body>
</html>
`)
})
})
And this on the client side
import { Router, hashHistory, browserHistory, match } from 'react-router'
let history = browserHistory
//client side, will become app.js
match({ routes, location, history }, (error, redirectLocation, renderProps) => {
render(<Router {...renderProps} />, document.getElementById('root'))
})
the problem
It works only when I remove the (let history = browserHistory), but it adds the /#/ hash prefix to my url(which I don't want to happen).
When I leave the let (history = browserHistory) there, it throws an error
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) < ! -- react-empty: 1 -
(server) < section data-reactro
The error message is pretty clear, however, I don't understand why it works with the hashHistory but fails with the browserHistory
version incompatibility issue
solution
{
"history": "^2.1.2",
"react-router": "~2.5.2"
}
links:
https://github.com/reactjs/react-router/issues/3003

Auto complete with multiple keywords

I want . Auto complete text box with multiple keyword. it's from database. if I use jQuery and do operation in client side mean. If the database size is huge, it leads to some issues. I need to know how this is done on the server side and get proper result.
I have already seen this topic but the operation is done on the client side. I need it from the database directly.
<html>
<head>
<title>Testing</title>
<link href="css/jquery-ui-1.10.3.custom.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.srchHilite { background: yellow; }
</style>
<script src="scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.10.3.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
NewAuto();
});
function NewAuto() {
var availableTags = ["win the day", "win the heart of", "win the heart of someone"];
alert(availableTags); // alert = win the day,win the heart of,win the heart of someone
$("#tags").autocomplete({
source: function(requestObj, responseFunc) {
var matchArry = availableTags.slice(); // Copy the array
var srchTerms = $.trim(requestObj.term).split(/\s+/);
// For each search term, remove non-matches.
$.each(srchTerms, function(J, term) {
var regX = new RegExp(term, "i");
matchArry = $.map(matchArry, function(item) {
return regX.test(item) ? item : null;
});
});
// Return the match results.
responseFunc(matchArry);
},
open: function(event, ui) {
// This function provides no hooks to the results list, so we have to trust the selector, for now.
var resultsList = $("ul.ui-autocomplete > li.ui-menu-item > a");
var srchTerm = $.trim($("#tags").val()).split(/\s+/).join('|');
// Loop through the results list and highlight the terms.
resultsList.each(function() {
var jThis = $(this);
var regX = new RegExp('(' + srchTerm + ')', "ig");
var oldTxt = jThis.text();
jThis.html(oldTxt.replace(regX, '<span class="srchHilite">$1</span>'));
});
}
});
}
</script>
</head>
<body>
<div>
<label for="tags">
Multi-word search:
</label>
<input type="text" id="tags" />
</div>
</body>
</html>
take a look to Select2 it may help you.
Select2 is a jQuery based replacement for select boxes. It supports
searching, remote data sets, and infinite scrolling of results.
link
In your code, you have provided source as array. As you mentioned in comments, problem is how to get the data to source in jquery.
To make this work,
You need to do following
load jquery in header, which is you have already done.
Provid array,string or function for the source tag. [See api for
the source tag][1]
[1]: http://api.jqueryui.com/autocomplete/#option-source
In your serverside script, provid Jason encoded string.
If you check the API, you can see they have clear mentioned this.
Here is the jquery code
$(function() {
$( "#option_val" ).autocomplete({
dataType: "json",
source: 'search.php',
minLength: 1,
select: function( event, ui ) {
log( ui.item ?
"Selected: " + ui.item.value + " aka " + ui.item.id :
"Nothing selected, input was " + this.value );
}
});
});
</script>
Here is the php code, Sorry if you use differnt serverside script language.
<?php
// Create connection
$con=mysqli_connect("localhost","wordpress","password","wordpress");
// Check connection
if (mysqli_connect_errno($con))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result=mysqli_query($con,"select * from wp_users");
while($row = mysqli_fetch_array($result))
{
$results[] = array('label' => $row['user_login']);
}
echo json_encode($results);
mysqli_close($con);
?>

Errors in IE 8 from connect.facebook.net/en_US/all.js caused by credits callback

Setup:
Got a working facebook app and am correctly setup for facebook credits transactions (i.e. everything on the serverside is working fine).
In Firefox and chrome transactions complete without issue, however in IE8 the callback upon completing/closing the purchase dialog throws the following errors:
Error 1:
Line: 52 Error: Object doesn't support this property or method
Object doesn't support this property or method JScript - script block, line 52 character 37
Where the function it points to is:
ui: function( params )
{
obj = FB.JSON.parse( params );
method = obj.method;
cb = function( response ) { FBAS.getSwf().uiResponse( FB.JSON.stringify( response ), method ); }
FB.ui( obj, cb );
},
Specifically highlighting this bit:
FBAS.getSwf().uiResponse( FB.JSON.stringify( response ), method )
in the http://connect.facebook.net/en_US/all.js file
Error 2:
Line: 65 Error: Object doesn't support this action
Object doesn't support this action all.js, line 65 character 2198
[The line it points to is a stupidly long unformatted unreadable mess so I'll omit it unless requested]
Specifically highlighting this bit:
delete d._old_visibility
again in the http://connect.facebook.net/en_US/all.js file
The html I'm using (with the app identifying stuffs removed) is as follows:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="https://www.facebook.com/2008/fbml">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Expires" content ="0" />
<meta http-equiv="Pragma" content ="no-cache" />
<meta http-equiv="Cache-Control" content ="no-cache" />
<title>[ APP NAME ]</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
</head>
<body>
<div id="fb-root"></div>
<script type="text/javascript">
$(document).ready
(
function()
{
var appId = [ APP ID ];
var host = [ APP HOST ];
// If the user did not grant the app authorization go ahead and
// tell them that. Stop code execution.
if (0 <= window.location.href.indexOf ("error_reason"))
{
$(document.body).append ("<p>Authorization denied!</p>");
return;
}
// Loads the Facebook SDK script.
(function(d)
{
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
// When the Facebook SDK script has finished loading init the
// SDK and then get the login status of the user. The status is
// reported in the handler.
window.fbAsyncInit = function()
{
//debugger;
FB.init(
{
appId : appId,
status : true,
cookie : true,
oauth : true,
});
FB.getLoginStatus (onCheckLoginStatus);
};
// Handles the response from getting the user's login status.
// If the user is logged in and the app is authorized go ahead
// and start running the application. If they are not logged in
// then redirect to the auth dialog.
function onCheckLoginStatus (response)
{
if (response.status != "connected")
{
top.location.href = "https://www.facebook.com/dialog/oauth?client_id=" + appId + "&redirect_uri=" + encodeURIComponent (host+"[ RELATIVE APP PATH ]") + "&scope=publish_stream,user_about_me,read_friendlists,user_photos";
}
else
{
// Start the application
loadGame();
}
}
}
);
function loadGame()
{
var flashvars = {};
var params = {};
var attributes = {};
params.allowscriptaccess = "always";
attributes.id = 'flashContent';
attributes.name = 'flashContent';
swfobject.embedSWF("[ APP SWF ]?"+Math.floor(Math.random()*10000), "flashContent", "100%", "99%", "10.0", null, flashvars, params, attributes);
}
</script>
<div id="flashContent" >
Loading...
</div>
</body>
This is just a problem for IE 8 but is stopping the app going live since a significant number of users transactions would fail (or rather would complete, be charged and not take effect due to the failed callback).
For the past few days I've been searching for others with this or a similar problem but to no avail.
I've seen some similar issues where people are warned about javascript variables being created globally and causing interfereance or variables being names using keywords reserved in IE but as far as I can tell neither is the case here. The facebook javascript code is fairly boilerplate stuff lifted from facebook dev pages and reliable sources. It may be JQuery (which I have little experience with), however, again, this is lifted from working examples and if there is a problem I can't see it.
Can anyone help?
SOLVED
I won't accept this answer because I honestly don't think the question was answerable/solvable with the info provided and feel it would be bad form. But I want to leave this here for anyone that might be looking for a solution.
Cause of the error
The problem is the result of the combination of facebook hiding the app during 'normal' facebook actions (in this case, displaying the pay prompt) and external interface calls not working in Internet explorer when the app is hidden/not visible.
Solution
Found at http://flassari.is/2012/02/external-interface-error-object-expected/#comment-1743
All of these steps may not be neccessary but in the end what I did was:
Stop facebook hiding the app by overriding the visibility using
<style>
#[ ID OF THE FLASH OBJECT]
{
visibility: visible !important;
}
</style>
Adding wmode = "opaque"; to the swfobject params
Using the optional flash_hide_callback by adding hideFlashCallback:"OnHideFlash" to the FB.init options in the actionscript to move/hide the app instead, where OnHideFlash is a javascript function:
function OnHideFlash(params)
{
if (params.state == 'opened')
{
getSwf().style.top = '-10000px';
} else
{
getSwf().style.top = '';
}
}
Where getSwf() is your prefered method of getting the flash app object.
Hopefully this will save some people the suffering of pouring through the endless 'reasons that XYXY doesn't work in IE' questions and solutions that has been my last few days.
I suggest putting your code through a JavaScript Lint tool and correcting any errors you find. IE8 is extremely picky about how JavaScript is coded, while Firefox and Chrome are ok with minor mistakes. If your code is error free (after linting), it should work properly.