5. Adding an accurate Timestamp with NTP - ControlBits/EMIT GitHub Wiki

Having good quality environmental data is important for many applications, but in order to use this data to help us spot trends over time, we need to understand when each of our data points were measured - we need to add an accurate 'Timestamp' to each of our measurements.

As we now we have an Internet connection, we can use the 'Network Time Protocol' (NTP) to get accurate date and time information. From wikipedia.com:

The Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems over packet-switched, variable-latency data networks. In operation since before 1985, NTP is one of the oldest Internet protocols in current use.

NTP is intended to synchronize all participating computers to within a few milliseconds of Coordinated Universal Time (UTC).

Source: https://en.wikipedia.org/wiki/Network_Time_Protocol

EMIT's ESP32's already has an onboard clock, so all we need to do is synchronise our 'local time' with the NTP server network to ensure it's accurate.

Once that has been done, we can safely rely on our local time to create a 'Timestamp' for each of our sensor readings.

5.1 Synchronising local time to NTP network

The first thing we need to do is import the 'ntptime' module in our boot.py. To keep the code compact and organised, I've appended it to the import statement for the related 'network' module:

import network, ntptime

Next up, we need to create a function, in boot.py, that will check to see if we are connected to the NTP servers and if so, synchronise our local time to the NTP network. All this is done, with a single 'ntptime.settime()' command:

    ntptime.settime()

The rest of the function just needs to manage the NTP connection and print some debug messages to our Shell. The fully commented 'sync_to_NTP()' function is shown below:

# define function to synchronise local clock with NTP time
def sync_to_NTP():
    ntpConnected = 0   # initialise an 'ntpConnected' variable to hold status, set it to 

    while ntpConnected == 0:   # while not connected to NTP network ...
  
        try:
            print('Trying to Sync with NTP servers')   # print debug message to Shell
            ntptime.settime()   # set localtime to NTP time
            ntpConnected = 1   # set ntpConnected status to 1
  
        except:
            print ('NTP error')   # print debug message to Shell
            ntpConnected = 0   # set ntpConnected status to 0

    print('Local time synchronised: ', get_time_str()) # print the new localtime as a string

Oops! The last line of our 'sync_to_NTP()' function (above) calls a helper function 'get_time_str()' that we've not written yet!

This function simply gets the 'local time' and converts it into a formatted string that we can use as our Timestamp. Let's create it now:

# define function for getting local time as string
def get_time_str():
    my_time = time.localtime()   # get 'localtime' and store it in a variable 'my_time'
    my_time_string = '{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(my_time[0], my_time[1], my_time[2], my_time[3], my_time[4], my_time[5])   # convert localtime to a formatted text string 'my_time_string'
    return my_time_string  # return the formatted string

Once these two new functions have been appended to the end of our boot.py, it should look like this:

# general board setup
import machine, time, dht
import network, ntptime

# configure GPIO pins
RedLED = machine.Pin(16, machine.Pin.OUT)
RedLED.value(0)

GreenLED = machine.Pin(17, machine.Pin.OUT)
GreenLED.value(0)

AM2302 = dht.DHT22(machine.Pin(14))

# define Wi-Fi settings
wifiSSID = 'your-WiFi-SSID-goes-here'
wifiPasswd = 'your-WiFi-password-goes-here'

# define function for setting up Wi-Fi network
def wifi_connect():
    wifi = network.WLAN(network.STA_IF)  # create our 'wifi' network object
    wifi.active(True)  # turn on the Wi-Fi hardware 

    # if not connected ...
    while wifi.isconnected() == False:
        
        GreenLED.value(1)  # turn Green LED ON
        print('trying WiFi connection ', wifiSSID)  # print 'trying..' message to Shell 
  
        wifi.connect(wifiSSID, wifiPasswd)  # try connecting to wifi 
    
        time.sleep(1)  # wait 1 second 

        GreenLED.value(0)  # turn Green LED OFF
    
        time.sleep(2)  # wait 2 second 
        
    # if connected ...
    GreenLED.value(1)  # turn Green LED ON
    print('WiFi connection successful')  # print 'WiFi connection successful' message to Shell 
    print(wifi.ifconfig())  # print WiFi network settings (inc. IP Address) to Shell
    
    
# define function to synchronise local clock with NTP time
def sync_to_NTP():
    ntpConnected = 0   # initialise an 'ntpConnected' variable to hold status, set it to 

    while ntpConnected == 0:   # while not connected to NTP network ...
  
        try:
            print('Trying to Sync with NTP servers')   # print debug message to Shell
            ntptime.settime()   # set localtime to NTP time
            ntpConnected = 1   # set ntpConnected status to 1
  
        except:
            print ('NTP error')   # print debug message to Shell
            ntpConnected = 0   # set ntpConnected status to 0

    print('Local time synchronised: ', get_time_str()) # print the new localtime as a string
    
    
# define function for getting local time as string
def get_time_str():
    my_time = time.localtime()   # get 'localtime' and store it in a variable 'my_time'
    my_time_string = '{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(my_time[0], my_time[1], my_time[2], my_time[3], my_time[4], my_time[5])   # convert localtime to a formatted text string 'my_time_string'
    return my_time_string  # return the formatted string

5.2 Using our new, accurate localtime to Timestamp our data

Now we've created the NTP functions, all we need to do is 1) connect to the Wi-Fi network (already in our code), 2) wait for a few seconds for the network connection to settle and then 3) run our sync_to_NTP function.

This is all done in the top few lines of our main.py like this:

# connect to wifi
wifi_connect()

# wait 5 seconds for network to settle
time.sleep(5)

# synchronise local clock with NTP time
sync_to_NTP()

Once that is done, we're ready to use our accurate 'localtime' whenever we need to, just by calling our 'get_time_str()' function.

To add the Timestamp to our measurement output, we just need to add the following print statement to our code:

   print("Time Stamp: " + get_time_str())

Our completed main.py should look like this:

# connect to wifi
wifi_connect()

# wait 5 seconds for network to settle
time.sleep(5)

# synchronise local clock with NTP time
sync_to_NTP()


# this is the main program loop
while True:

  time.sleep(5)     # wait 5 seconds 
    
  RedLED.value(1)   # turn RedLED ON
  print("Reading AM2302 ...")
  
  AM2302.measure()  # start AM2302 measurement
  
  RedLED.value(0)   # turn RedLED OFF

  tempC = AM2302.temperature()   # get temperature (Celsius) from AM2302
  humidity = AM2302.humidity()   # get humidity from AM2302

  tempF = (tempC * 9/5) + 32.0   # convert Celcius result into Fahrenheit

  print("Time Stamp: " + get_time_str())
  print("Temperature (C): " + str(tempC))
  print("Temperature (F): " + str(tempF))
  print("Humidity (%RH): " + str(humidity))  

As the code that we've created is the foundation of your IoT sensor code, we strongly recommend you upload and test what we've created before moving on to the next section: Transmitting data over MQTT