Breadboard
docs
Example project · Wi-Fi & internet

Weather over the sandbox internet (OLED)

The board is really online, inside the sandbox internet. The ESP32 joins Wi-Fi, resolves api.weather.test with a real DNS lookup, and GETs a weather JSON over a genuine TCP socket — then fills a 128×128 OLED like a proper station: location, sky, temperature, humidity, wind, a live temperature-trend sparkline, and a UTC clock from a second REST call. Everything is deterministic and offline (the sandbox mocks the services), so this bench needs no account and stays on the sandbox internet — its api.weather.test host exists only in the emulator. For the same demo against a live API, open “Weather over the real internet (Pro)”. Follow every lookup and HTTP exchange in the 📡 Sniffer.

The Weather over the sandbox internet (OLED) circuit as rendered by the simulator

How it works

The ESP32 joins BreadboardNet and talks to the simulator's sandbox internet: a deterministic, fully offline mock of the real thing built into the emulated network core. The script does a real DNS lookup for api.weather.test (the sandbox resolver answers for any name), opens a genuine TCP socket, and sends a plain HTTP GET; the mock weather service replies with a JSON report, and a second request to /time reads the sandbox's virtual clock. MicroPython parses both with the json module and fills the 1.5″ 128×128 SH1107 like a proper station readout — location, sky, temperature, humidity, wind, a live temperature-trend sparkline drawn bar by bar with framebuf, and the UTC clock. Because the services are mocked in-core, the demo runs signed-out, offline, and reproducibly. Note that api.weather.test is a sandbox-only name — it cannot resolve on the real internet, so this bench stays in the sandbox; the sibling example "Weather over the real internet (Pro)" runs the same socket code against a live API through the secure gateway.

What's on the bench

  • Battery
  • ESP32-C3
  • OLED 1.5″ 128×128

How it's wired

  • hole a10hole B-5
  • hole a11hole B+5
  • hole a40hole B-9
  • hole a41hole f10
  • hole a42hole g11
  • hole a43hole b20

The code

This MicroPython script runs on the emulated board every boot; edit it in the Code tab.

from machine import I2C, Pin
import framebuf, time
i = I2C(0, scl=Pin(9), sda=Pin(8))
buf = bytearray(2048)
fb = framebuf.FrameBuffer(buf, 128, 128, framebuf.MONO_VLSB)
for c in b'\xae\x20\xa8\x7f\xd3\x00\xdc\x00\x81\x4f\xad\x8a\xa4\xa6\xaf':
    i.writeto(0x3d, bytes([0, c]))
def show():
    for p in range(16):
        i.writeto(0x3d, bytes([0x00, 0xb0 + p, 0x00, 0x10]))
        i.writeto(0x3d, b'\x40' + buf[p*128:(p+1)*128])

import network, socket, json
w = network.WLAN(network.STA_IF)
w.active(True)
fb.fill(0); fb.text('joining WiFi...', 0, 60); show()
w.connect('BreadboardNet')
while not w.isconnected():
    time.sleep_ms(100)
def fetch(host, path):
    # Real DNS against the sandbox resolver, then a genuine TCP socket.
    addr = socket.getaddrinfo(host, 80)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(('GET ' + path + ' HTTP/1.0\r\nHost: ' + host + '\r\n\r\n').encode())
    r = b''
    while True:
        c = s.recv(256)
        if not c:
            break
        r += c
    s.close()
    return json.loads(r.split(b'\r\n\r\n', 1)[1])
hist = []
n = 0
while True:
    wx = fetch('api.weather.test', '/weather')
    t = fetch('worldtimeapi.test', '/time')
    n += 1
    hist.append(wx['temperature_c'])
    if len(hist) > 21:
        hist.pop(0)
    fb.fill(0)
    fb.text('INTERNET WEATHER', 0, 0)
    fb.hline(0, 10, 128, 1)
    fb.text(wx['location'][:16], 0, 16)
    fb.text(wx['conditions'][:16], 0, 26)
    fb.text('TEMP %s C' % wx['temperature_c'], 0, 40)
    fb.text('HUM  %d %%' % wx['humidity'], 0, 50)
    fb.text('WIND %s kph' % wx['wind_kph'], 0, 60)
    fb.hline(0, 72, 128, 1)
    fb.text('temp trend', 0, 76)
    # Sparkline: one bar per refresh, scaled to the window seen so far.
    # (bi, not i: the display driver owns the global i = I2C bus.)
    if len(hist) > 1:
        lo = min(hist); span = (max(hist) - lo) or 1
        for bi, v in enumerate(hist):
            h = 2 + int(20 * (v - lo) / span)
            fb.fill_rect(bi * 6, 110 - h, 4, h, 1)
    fb.hline(0, 114, 128, 1)
    fb.text(t['utc_datetime'][11:19] + ' #%d' % n, 0, 119)
    show()
    print('wx', n, wx['temperature_c'], 'C,', wx['conditions'])
    time.sleep_ms(2000)

Try this

  • Watch the last line of the OLED: the UTC clock and request counter advance on every 2-second refresh.
  • Open the 📡 Sniffer and follow a full round trip — DNS query and answer, TCP SYN/ACK, the GET, the JSON reply.
  • Edit the script in the Code tab to fetch '/api' (the mock cloud dashboard) or POST to '/echo' instead.
  • Click the ESP32 and look at the top of its Wi-Fi inspector: that's where a Pro bench switches from the sandbox to real internet access — on a bench whose script targets a real host, like the live-weather example.

Related projects