MqttSensor with WebServer (Exercise)

MQTT Sensor with Web Server
##################################################################################
import network                        # mqtt needs network connection            #
import framebuf                       # we also connect a display                #
from umqtt.simple import MQTTClient   # MQTT library                             #
from SH1107_OLED import OLED          # our driver for the OLED display          #
from BME280  import BME280            # our library for the BME280 sensor (T,P)
from utils   import Config            # to read a configurattion file            #
from machine import Pin, I2C          # API for the I2C interfaces               #
import time                           # standard package for timing              #
import json                           # we publish our data in json format       #
import uasyncio
from random import uniform
##################################################################################




##################################################################################
def wifi_connect(oled):
##################################################################################
    # this code you can reuse from a previous exercise.
    fb = oled.getFramebuffer()
    fb.fill(0)

    # get a "station interface" (opposed to access point interface) from the
    # netwrok library. This object has the magic methods to connect to the
    # wireless network and then to the LAN on the IP level.
    sta_if = network.WLAN( network.STA_IF )
    # If it is already active de-activate it first so that we always start
    # from the same base state.
    if  sta_if.active():
        sta_if.active(False)

    # Now try to connect to the WIFI

    sta_if.active( True )
    fb.text( "Connecting...", 0, 10 )
    oled.copyFramebuf()

    sta_if.connect( config.get("ssid"), config.get("password") )

    # Poll to know when the connection succeeds

    connected = sta_if.isconnected()

    # create some dotted lines on the display to
    # indicate the process which takes time.

    x = 0
    col = 1
    while not connected:
        fb.pixel( x, 0, col)
        fb.pixel( x, 1, col)
        fb.pixel( x+1, 0, col)
        fb.pixel( x+1, 1, col)
        oled.copyFramebuf()
        x += 4
        if x > 122:
            x=0
            col = (col+1)%2
        connected = sta_if.isconnected()
        time.sleep(0.1) # this is 100ms

    # If we arrive here we should be connected

    fb.text( "Success !", 0, 25 )
    oled.copyFramebuf()
    mac = sta_if.config('mac')
    # Show the IP address we got from the DHCP server

    ifparm = sta_if.ifconfig()

    iptxt = "IP:%s" % ifparm[0]
    fb.text( iptxt, 0,40 )
    oled.copyFramebuf()
    print ("IP : %s" % ifparm[0] )
    print ("SUB: %s" % ifparm[1] )
    print ("GAT: %s" % ifparm[2] )
    print ("DNS: %s" % ifparm[3] )
    # short break to read the display before we move on

    time.sleep(2)

##################################################################################



##################################################################################
def mqtt_connect(client_id, mqtt_server, oled):
##################################################################################
    # Also this routine can be reused from the previous exercise.

    # Now set up the connection to the MQTT Broker
    print("connecting to %s" % mqtt_server)
    fb = oled.getFramebuffer()
    client = MQTTClient(client_id, mqtt_server, keepalive=3600)
    if oled:
        fb.fill(0)
        fb.text( "mqtt connect...", 0,10 )
        oled.copyFramebuf()
    try:
        client.connect()
    except Exception as e:
        print(e)
        if oled:
            fb.text("...failed...", 0,20 )
            fb.text("check broker!", 0,30 )
            fb.text("cont. wo MQTT!", 0,45 )
            oled.copyFramebuf()
            time.sleep(5)
            fb.fill(0)
            return False
        else:
            return False
    if oled:
        fb.text("mqtt broker :", 0,30 )
        fb.text(mqtt_server, 0,40 )
        oled.copyFramebuf()
        time.sleep( 4 )

    return client

##################################################################################


##################################################################################
def webpage( temp, pressure, humidity ):
    # this can be the same webpage as in our webserver example

    html = f"""<!DOCTYPE html>
<html lan='en'>
    <head>
        <title>A sensor simulation</title>
        <link rel="stylesheet" href="mystyle.css">
    </head>
    <body>
        <h1>Sensor values</h1>
        <p></p>
        <table class="sensor">
            <thead>
            </thead>
            <tbody>
                <tr><th>Temperature</th><th>:</th><td>{temp:.1f}</td><td class="left">C</td></tr>
                <tr><th>Pressure</th><th>:</th><td>{pressure:.1f}</td><td class="left">mb</td></tr>
                <tr><th>Humidity</th><th>:</th><td>{humidity:.0f}</td><td class="left">%</td></tr>    
            </tbody>
        </table>
    </body>
</html>
"""
    return html


##################################################################################

##################################################################################
# also recycled from the webserver exercise

async def sendfile( fn, writer, ctype ):

    # read the file
    content = open( fn, 'r' ).read()

    # send the header to the HTTP client 
    writer.write( ('HTTP/1.0 200 OK\r\nContent-type: ' + ctype + '\r\n\r\n').encode() )

    # send the contents to the HTTP client
    writer.write(content.encode())

    # Now wait until everything is written
    await writer.drain()

    # Finally close the stream and wait until it is closed
    writer.close()
    await writer.wait_closed()

##################################################################################

##################################################################################
async def handle_request(reader, writer):
    # read the header from the reader
    request_line = await reader.readline()
    # skip all other header lines (until the empty line)
    stop = False
    while not stop:
            li = await reader.readline()
            if li  != b"\r\n":
                pass
            else:
                stop=True
    request = str(request_line, 'utf-8').split()[1]

    print("request: %s" % request )

    if request[-3:] == "css":
        await sendfile( request[1:], writer, "text/css" )
    elif request[-4:] == "html":
        # Here is a small difference to the webserver example:
        # instead of random values we need to use the sensor values which we 
        # have obtained from a sensor in another task. They are saved in global
        # variables...
        response = ???
        writer.write( 'HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n'.encode())
        writer.write(response.encode())
        await writer.drain()
        writer.close()
        await writer.wait_closed()
    else:
        writer.write( 'HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n'.encode())
        writer.write(("<html><body><h2>Unknown request : %s</h2></body></html>" % request).encode())
        await writer.drain()
        writer.close()
        await writer.wait_closed()
##################################################################################


##################################################################################
# Now comes a small difference to our previous MQRR sensor example without web server:
# We want to use the asyncio based webserver which will run in a separate task. The
# main task instead will contain the loop for the sensor measurement. This was in the 
# main program of the Network Sensor example. However now we need to declare this routine
# as a coroutine and we need to yield when we wait for one second between two sensor
# measurements.
# Hence I recommend to start with the code from the main programm of the MQTT Sensor 
# program. Convert this main program into a co-routine. Then before the endless loop
# for the sensor measurements start, also create a new task for the web server. You
# can call this routine "main()" (but this is not required of course)
# Remember to change the sleep statement into the asyncio form of the sleep so that
# the webserver gets also some CPU time !
#################################################################################

####################### Here the main programme starts ###########################

# We use global variables for the three sensor values:
temp = 0
p0 = 0
hum = 0
# read the configuration file. We want "config" to be in the global scope
# therefore we create it here and not in the "main()" function.
config = Config( "config_mqttSensor.json" )

# Put here the command to start the EventLoop with the main co-routine