Publishing Sensor data with MQTT (Solution)

MQTT Sensor
##################################################################################
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       #
##################################################################################




##################################################################################
def wifi_connect(oled):
##################################################################################
    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):
##################################################################################

    # 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
##################################################################################








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

# read the configuration file
config = Config( "config_mqttSensor.json" )

# Initialise and configure the first I2C port of the ESP32
# We put both sensors and the display on the same I2C bus.
# The frequency is the I2C default frequency. You can try
# and go higher. At some point things will stop working...
i2c = I2C(0, sda=Pin(33), scl=Pin(32), freq=400000)

# Instantiate the classes for the 2 sensors we have on board

bme = BME280(i2c)

# Setup our super I2C OLED display

oled = OLED( i2c, 0x3c )
oled.init()
oled.setLandscape()
fb = oled.getFramebuffer()

# Setup the network connection via the built in WIFI

wifi_connect( oled )

# Connect to MQTT broker

mqtt = mqtt_connect( config.get("client_id"), config.get("mqtt_server"), oled )


# Now we start out endless loop.
# We take measurements with the sensors and update the display
# However the publishing to the MQTT Broker we do less often.
# It is not super interesting to monitor these values which only
# vary very slowly, with high frequency. (For practical purposes
# the publishing frequency in this example is stil nuch too high.
# But like this you see "something moving" in the MQTT network...)
#
# The absolute altitude can only be roughly estimated. For a precise
# absolute altitude we need to calibrate the formulas, however this
# requires the knowledge of the absolute altitude. But we can very
# precisely measure altitude difference. Therefore we take the mean
# of the first 10 altitude measurements as the reference hight and
# from then on we display the difference to this height. Remember not
# to move the Sensor during these 10 first measurements.

alt_ref = 0
i_measure = 0
ltime = float(time.time_ns()) / 1000000000.0
while True:

    # Get the latest greatest sensor values:
    # Both sensors deliver temperature values. We read both of them.

    [temp, press, hum] = bme.doMeasure()
    alt = bme.getAltitude()

    # convert pressure to see level
    # For this we use the altitude given as a constant to the programme.
    # This normalised altitude is used to compare pressure values
    # measured at different places with different (but known!) altitudes. 
    alt0 = config.get("alt0")
    p0 = press*((1-0.0065*alt0/(temp+0.0065*alt0+273.15))**-5.257)


    fb.fill(0)

    # Now check if we need to publish to MQTT by inspecting the time
    # since the last update.

    if mqtt:  # This if statement is useful if we want to run wo MQTT:
              # We then set the mqtt variable to False in the code above.
        tnow = float(time.time_ns()) / 1000000000.0
        # make a progress bar indicating how much time is left to the
        # next publication
        tdiff = tnow - ltime
        bar_len = int(100.0 * float(tdiff) / float(config.get("mqtt_publish_iv"))+0.5)
        fb.hline(0,1,bar_len,1)
        fb.vline(100,0,3,1)
        fb.vline(0,0,3,1)
        if tdiff >= config.get("mqtt_publish_iv"):
            ltime = tnow
            try:
                message = {
                    "temperature" : temp,
                    "pressure"    : p0,
                    "humidity"    : hum }
                mqtt.publish( config.get("mqtt_topic") + "/data", json.dumps( message ) )

                # Always when we publish values we draw a small box on the
                # bottom right of the display. 
                fb.pixel( 122, 62, 1 )
                fb.pixel( 122, 63, 1 )
                fb.pixel( 123, 62, 1 )
                fb.pixel( 123, 63, 1 )
            except Exception as e:
                # something went wrong: may be the broker went down
                print( str(e) )
                fb.fill(0)
                fb.text("Publish failed !", 0, 10 )
                fb.text("Continue wo MQTT", 0, 20 )
                fb.text("Will re-try...", 0, 40 )
                oled.copyFramebuf()
                mqtt = False
                time.sleep(5)
                fb.fill(0)
    else:
        # A small '-' in the top right shows the user that MQTT publishing is off
        fb.pixel( 120, 1, 1 )
        fb.pixel( 121, 1, 1 )
        fb.pixel( 122, 1, 1 )
        fb.pixel( 123, 1, 1 )

        # Here we try to connect to the mqtt server in case we are not connected already.
        mqtt = mqtt_connect( config.get("client_id"), config.get("mqtt_server"), oled  )

    # Display the values on the OLED 


    temp_str  = " T: %7.2fC"   % temp
    press_str = "P0: %7.2fmb"  % p0
    hum_str   = "rH: %7.2f%%"  % hum

    fb.text( temp_str,  0, 10 );
    fb.text( hum_str,   0, 25 );
    fb.text( press_str, 0, 40 );

    if i_measure < 10:
        alt_ref += alt
        i_measure += 1
    elif i_measure == 10:
        alt_ref = alt_ref / 10.0
        i_measure += 1
    else:
        alt_str   = "dA: %7.2fm"   % (alt - alt_ref)
        fb.text( alt_str,   0, 55 );

    oled.copyFramebuf()

    # Next update in a second.

    time.sleep_ms(1000)