Get Weather on an OLED Display

In this lesson we will use the Pico W to get a weather forecast from a web service and display the current and forecasted temperatures on a 128x64 OLED display. The display above shows the city and current temperature on the top row of the display and then a plot of the predicted temperatures for the next 120 hours. The max temperature is 87 degrees and the minimum is 60 degrees Fahrenheit.

Calling the Weather API

We will use the same method as in the previous lesson to get the weather forecast. However, in this lesson we will not just plot the temperature on the Thonny plot screen, we will use a OLED screen.

To do this we will need to write a function that will display the temperature data. The function will display the current location city name, the current temperature, and then draw a plot of the next 40 3-hour intervals. This lesson is a bit harder because we have to manually do all the work of scaling and plotting our data. This is all done for us in the prior Thonny plotting lab.

To achieve this we will need to scale the data to fit the display grid. We will reserve the top 10 pixels for the city and current temp and then draw the plot on the remaining 54 pixel high and 128 pixel wide plot region.

To scale the data we will need to find the min and max temperatures. These will also be displayed on the screen. The scale is then the ratio of the graph height over the temperature range. For example if the range of temperatures is 27 degrees and the height of the display is 54 we will need to scale the temperature vertically by 2.

The horizontal axis will go from 0 to 120. Since we have 40 points, each point will occur every 3rd pixel. We can then look at the difference between the current point and the next point to interpolate the dots between the two points.

The math can be a little confusing since higher temperatures are closer to the top of the display, so they have a lower Y coordinate value. Note the y_delta is subtracted from the next value:

 `1` `````` y_delta = -round((y - y_next)/3) # a small positive or negative number for interpolation ``````
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52``` ``````def display_weather(): global weather, city, current_temp oled.fill(0) min = 120 max = -50 for i in range(0, 39): temp = round(weather['list'][i]['main']['temp']) if temp < min: min = temp if temp > max: max = temp min = round(min) max = round(max) temp_range_height = max - min graph_height = 54 scale = graph_height/temp_range_height print('min/max/range/scale:', min, max, temp_range_height, scale) # display city name, current temp, min and max oled.text(city + ': ' + str(current_temp), 0, 0, 1) oled.text(str(min), 0, 57, 1) # bottom left corner oled.text(str(max), 0, 10, 1) # under top row max_points = 39 # graph temps for the next n periods print('Date Tmp TNx Y Y2 Del') for i in range(0, max_points): temp = round(weather['list'][i]['main']['temp']) x = i * 3 # scaled x y = 63 - round((temp - min)*scale) oled.pixel(x, y, 1) # now draw the next two points if i < max_points: temp_next = round(weather['list'][i+1]['main']['temp']) y_next = 63 - round((temp_next - min)*scale) y_delta = -round((y - y_next)/3) # a small positive or negative number # for debugging - fixed with columns with leading spaces print(weather['list'][i]['dt_txt'][0:13], '{: 3.3d}'.format(temp), '{: 3.3d}'.format(temp_next), '{: 3.3d}'.format(y), '{: 3.3d}'.format(y_next), '{: 3.3d}'.format(y_delta)) # should be 1/3 of the way to the next point oled.pixel(x+1, y + y_delta, 1) # should be 2/3 of the way to the next point oled.pixel(x+2, y + 2*y_delta, 1) oled.show() ``````

The Main Loop

The main loop repeats forever, pausing every hour between updates. It gets first calls the rest service, extracts the city name and current temperature and then calls the display_weather() function using global variables for the JSON file, city and current temperature.

 ```1 2 3 4 5 6 7 8 9``` ``````while True: # globals: weather, city, current_temp weather = urequests.get(url).json() # print(weather) city = weather['city']['name'] current_temp = round(weather['list'][0]['main']['temp']) display_weather() print('Going to sleep for one hour') sleep(3600) # sleep one hour ``````

Full Sample Code

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127``` ``````import network import ssd1306 import secrets import urequests from utime import sleep, ticks_ms, ticks_diff # startup print('Connecting to WiFi Network Name:', secrets.SSID) wlan = network.WLAN(network.STA_IF) wlan.active(True) WIDTH = 128 HEIGHT = 64 SCK=machine.Pin(2) SDL=machine.Pin(3) spi=machine.SPI(0,baudrate=100000,sck=SCK, mosi=SDL) CS = machine.Pin(0) DC = machine.Pin(1) RES = machine.Pin(4) oled = ssd1306.SSD1306_SPI(WIDTH, HEIGHT, spi, DC, RES, CS) oled.poweron() def display_startup(counter): oled.fill(0) oled.text('Running startup', 0, 10, 1) oled.text('Connecting to', 0, 20, 1) oled.text(secrets.SSID, 0, 30, 1) oled.text(str(counter), 0, 40, 1) oled.show() def display_status(counter): oled.fill(0) # display the network name oled.text('n:' + secrets.SSID, 0, 0, 1) # display the connection time oled.text('t:', 0, 10, 1) oled.text(str(connection_time)+ ' ms', 15, 10, 1) oled.show() def display_weather(): global weather, city, current_temp oled.fill(0) min = 120 max = -50 for i in range(0, 39): temp = round(weather['list'][i]['main']['temp']) if temp < min: min = temp if temp > max: max = temp min = round(min) max = round(max) temp_range_height = max - min graph_height = 54 scale = graph_height/temp_range_height print('min/max/range/scale:', min, max, temp_range_height, scale) # display city name, current temp, min and max oled.text(city + ': ' + str(current_temp), 0, 0, 1) oled.text(str(min), 0, 57, 1) # bottom left corner oled.text(str(max), 0, 10, 1) # under top row max_points = 39 # graph temps for the next n periods print('Date Tmp TNx Y Y2 Del') for i in range(0, max_points): temp = round(weather['list'][i]['main']['temp']) x = i * 3 # scaled x y = 63 - round((temp - min)*scale) oled.pixel(x, y, 1) # now draw the next two points if i < max_points: temp_next = round(weather['list'][i+1]['main']['temp']) y_next = 63 - round((temp_next - min)*scale) y_delta = -round((y - y_next)/3) # a small positive or negative number print(weather['list'][i]['dt_txt'][0:13], '{: 3.3d}'.format(temp), '{: 3.3d}'.format(temp_next), '{: 3.3d}'.format(y), '{: 3.3d}'.format(y_next), '{: 3.3d}'.format(y_delta)) # should be 1/3 of the way to the next point oled.pixel(x+1, y + y_delta, 1) # should be 2/3 of the way to the next point oled.pixel(x+2, y + 2*y_delta, 1) oled.show() start = ticks_ms() # start a millisecond counter if not wlan.isconnected(): wlan.connect(secrets.SSID, secrets.PASSWORD) print("Waiting for connection...") counter = 0 while not wlan.isconnected(): sleep(1) print(counter, '.', sep='', end='', ) counter += 1 display_startup(counter) delta = ticks_diff(ticks_ms(), start) #print("Connect Time:", delta, 'milliseconds') #print("IP Address:", wlan.ifconfig()[0]) base = 'http://api.openweathermap.org/data/2.5/forecast?units=imperial&' location = '5037649' # Minneapolis, MN USA url = base + 'id=' + location + '&appid=' + secrets.appid #print(url) max_times = 39 #for i in range(0, max_times): #print(' Temp: ', weather['list'][i]['main']['temp']) while True: # globals: weather, city, current_temp weather = urequests.get(url).json() # print(weather) city = weather['city']['name'] current_temp = round(weather['list'][0]['main']['temp']) display_weather() print('Going to sleep for one hour.') sleep(3600) # sleep one hour ``````

Sample Debugging

To allow you to see the math for the plotting and interpolation we have added a print that prints the temperatures and y coordinates in fixed with format. Here is an example of this output:

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43``` ``````Connecting to WiFi Network Name: anndan-2.4 min/max/range/scale: 60 87 27 2.0 Date Tmp Tnx Y Y2 Del 2022-08-13 03 68 68 47 47 0 2022-08-13 06 68 68 47 47 0 2022-08-13 09 68 65 47 53 2 2022-08-13 12 65 72 53 39 -5 2022-08-13 15 72 83 39 17 -7 2022-08-13 18 83 87 17 9 -3 2022-08-13 21 87 82 9 19 3 2022-08-14 00 82 71 19 41 7 2022-08-14 03 71 66 41 51 3 2022-08-14 06 66 62 51 59 3 2022-08-14 09 62 60 59 63 1 2022-08-14 12 60 71 63 41 -7 2022-08-14 15 71 82 41 19 -7 2022-08-14 18 82 85 19 13 -2 2022-08-14 21 85 82 13 19 2 2022-08-15 00 82 73 19 37 6 2022-08-15 03 73 69 37 45 3 2022-08-15 06 69 66 45 51 2 2022-08-15 09 66 64 51 55 1 2022-08-15 12 64 74 55 35 -7 2022-08-15 15 74 83 35 17 -6 2022-08-15 18 83 85 17 13 -1 2022-08-15 21 85 81 13 21 3 2022-08-16 00 81 75 21 33 4 2022-08-16 03 75 70 33 43 3 2022-08-16 06 70 69 43 45 1 2022-08-16 09 69 66 45 51 2 2022-08-16 12 66 66 51 51 0 2022-08-16 15 66 79 51 25 -9 2022-08-16 18 79 80 25 23 -1 2022-08-16 21 80 70 23 43 7 2022-08-17 00 70 65 43 53 3 2022-08-17 03 65 64 53 55 1 2022-08-17 06 64 63 55 57 1 2022-08-17 09 63 61 57 61 1 2022-08-17 12 61 72 61 39 -7 2022-08-17 15 72 82 39 19 -7 2022-08-17 18 82 85 19 13 -2 2022-08-17 21 85 81 13 21 3 Going to sleep for one hour ``````