Wednesday, February 2, 2022

[SOLVED] Trying to get realtime sensor data from python into html using Flask and jquery without refreshing entire page

Issue

I am trying to read in data from a load sensor using a raspberry pi. I can successfully get the data from the python file but when I try and pass it to a html file using flask it wont update the data correctly. it acts like its not getting current data just loading the same data over and over.

*SEE BOTTOM FOR UPDATE

here is my main.py file -

#! /usr/bin/python3
import time
import sys
from flask import Flask, render_template
import datetime
app = Flask(__name__)

@app.route("/main")
def main():  
    EMULATE_HX711=False
    referenceUnit = 1

    if not EMULATE_HX711:
        import RPi.GPIO as GPIO
        from hx711 import HX711
    else:
        from emulated_hx711 import HX711

    hx = HX711(5, 6)
    hx.set_reading_format("MSB", "MSB")
    hx.set_reference_unit(-23000)

    #this clears the data on startup 
    hx.reset()
    hx.tare()

    #this is the only data I would like to refresh and stream into html
    while True:
        try:
            val = hx.get_weight(5)
            lbs = val * 2.2046
            templateData = {
                'data' : lbs
                }
            return render_template('index.html', **templateData)
                 
            hx.power_down()
            hx.power_up()
            time.sleep(1)

        except (KeyboardInterrupt, SystemExit):
            cleanAndExit()

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

I am trying to pass lbs as data into index.html -

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Flask App</title>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>

<div id='test'></div>

<script>
function loadlink(){

    $('#test').load('/main',function () {
        $(this).unwrap();
        $('#test').replaceWith('{{ data }}');
    });
}

loadlink();
setInterval(function(){
    loadlink()
}, 1000);
 
 </script>

 </body>
 </html>

UPDATE I have figured out that the data is being reset with each refresh because of the -

hx.reset()
hx.tare()

Which is needed to start the sensor at zero, but once started I want it to stream the sensor data as it changes. How could I accomplish this without refreshing the page?


Solution

Your python code return the entire page of index.html upon receiving each request from the browser, what you should do is instead of return render_template('index.html', **templateData), you only return the data with something like return jsonify(templateData), 200. In order to do this, create a separate route for handling the request.

#! /usr/bin/python3
from flask import Flask, render_template, jsonify

app = Flask(__name__)
EMULATE_HX711=False
referenceUnit = 1

if not EMULATE_HX711:
   import RPi.GPIO as GPIO
   from hx711 import HX711
else:
   from emulated_hx711 import HX711

hx = HX711(5, 6)
hx.set_reading_format("MSB", "MSB")
hx.set_reference_unit(-23000)

#this clears the data on startup 
hx.reset()
hx.tare()

# this route only handle the rendering of index.html
@app.route("/main")
def main():
   return render_template('index.html')

# this route handling the request send to the /update uri
@app.route("/update")
def update():
    val = hx.get_weight(5)
    lbs = val * 2.2046
    templateData = {'data' : lbs}
    return jsonify(templateData), 200


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

Modify the JavaScript accordingly to send the request to the new route /update, as I have not used the jQuery for ages, so I used my own pure JavaScript here:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Flask App</title>
</head>
<body>

  <div id='test'></div>

<script>
  document.addEventListener("DOMContentLoaded", function(event) {

    const getSensorReading = function() {
      fetch(`http://${location.host}/update`)  // send request to route /update
        .then((resp) => resp.json())
        .then(function(response) {
          document.getElementById('test').innerHTML =response.data.toFixed(2);
      });
    }

    getSensorReading();
    setInterval(getSensorReading, 1000);  //request for update every 1 second
  });
</script>

</body>
</html>

Please test the code yourself as I did not test the code. This is mostly copy-and-pasted from my project which offers more sophisticated usage cases on sensor reading and web development that you might find beneficial.



Answered By - hcheung
Answer Checked By - Willingham (WPSolving Volunteer)