WiFi#
Some microcontrollers come with built-in WiFi radios. Others use coprocessors for this purpose.
SSID and Passwords#
Create a file $IOT_PROJECTS/libs/secrets.py
to store your SSID
and password
.
Sample secrets.py
with wifi credentials and other “secrets”:
# secrets.py
# wifi
wifi_ssid = 'MY_SSID'
wifi_pwd = 'MY_PASSWORD'
# timezone
tz_offset = -8*3600 # PST
# webrepl password, 4 .. 9 characters
webrepl_pwd = 'er93xa'
# https://openweathermap.org/
openweathermap_apiid = "1c336ac61add531d32b73af8f2b"
Now create or update a device description (don’t forget to update the uid to match your microcontroller). In addition to secrets.py
it also includes code used elsewhere in this guide.
%%writefile $IOT_PROJECTS/devices/esp32.yaml
esp32:
uid: 30:ae:a4:23:ab:d4
path: internet
include-patterns:
- "./**/*.py"
- "./**/*.mpy"
- "./**/"
- "./**/*.key"
- "./**/*.crt"
resources:
- secrets.py:
path: libs
- code
Writing /home/iot/iot49.org/docs/projects/devices/esp32.yaml
%connect esp32
%rsync
Connected to esp32 @ serial:///dev/ttyUSB0
Directories match
Connecting#
The help
command on the esp32 shows sample code for configuring the WiFi:
help()
Show code cell output
Welcome to MicroPython on the ESP32!
For generic online docs please visit http://docs.micropython.org/
For access to the hardware use the 'machine' module:
import machine
pin12 = machine.Pin(12, machine.Pin.OUT)
pin12.value(1)
pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)
print(pin13.value())
i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))
i2c.scan()
i2c.writeto(addr, b'1234')
i2c.readfrom(addr, 4)
Basic WiFi configuration:
import network
sta_if = network.WLAN(network.STA_IF); sta_if.active(True)
sta_if.scan() # Scan for available access points
sta_if.connect("<AP_name>", "<password>") # Connect to an AP
sta_if.isconnected() # Check for successful connection
Control commands:
CTRL-A -- on a blank line, enter raw REPL mode
CTRL-B -- on a blank line, enter normal REPL mode
CTRL-C -- interrupt a running program
CTRL-D -- on a blank line, do a soft reset of the board
CTRL-E -- on a blank line, enter paste mode
For further help on a specific object, type help(obj)
For a list of available modules, type help('modules')
Search available networks (optional):
import network, secrets
wlan = network.WLAN(network.STA_IF);
wlan.active(True)
for net in wlan.scan():
ssid, bssid, channel, RSSI, authmode, hidden = net
ssid = ssid.decode()
if len(ssid) == 0: ssid = "?"
authmode = [ "open", "WEP", "WPA-PSK", "WPA2-PSK", "WPA/WPA2-PSK" ][authmode]
bssid = ":".join("{:02x}".format(x) for x in bssid)
hidden = [ "visible", "hidden" ][hidden]
print(f"{ssid:12} {bssid} ch:{channel:2} {RSSI:4}dB {authmode:12} {hidden}")
TPA-Secure d6:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
TPA-IoT c6:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
TPA-Server b4:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
TPA b6:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
? e6:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
? f6:fb:e4:77:a3:f4 ch:11 -58dB WPA2-PSK visible
ATT-Router dc:7f:a4:c2:f2:d9 ch:11 -83dB WPA2-PSK visible
Connect to wlan secrets.wifi_ssidsecrets.wifi_ssid
:
import network, secrets
wlan = network.WLAN(network.STA_IF);
wlan.active(True)
wlan.connect(secrets.wifi_ssid, secrets.wifi_pwd)
print("connected:", wlan.isconnected())
connected: True
Let’s check the IP address:
wlan = network.WLAN(network.STA_IF)
print(wlan.ifconfig()[0])
10.39.40.168
We can now ping the device:
!ping -c 3 10.39.40.168
PING 10.39.40.168 (10.39.40.168): 56 data bytes
64 bytes from 10.39.40.168: icmp_seq=0 ttl=255 time=217.643 ms
64 bytes from 10.39.40.168: icmp_seq=1 ttl=255 time=129.098 ms
64 bytes from 10.39.40.168: icmp_seq=2 ttl=255 time=49.519 ms
--- 10.39.40.168 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 49.519/132.087/217.643/68.669 ms
Internet time#
Now that we are connected, we can fetch the current time from the internet. Let’s check first what it is without initialization:
import time
print(time.gmtime())
Let’s fetch the current time from the internet.
import ntptime, time, machine, secrets
# fetch time from internet
tm = time.localtime(ntptime.time() + getattr(secrets, 'tz_offset', 0))
print("time", tm)
# set the time
machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0))
time (2021, 8, 1, 20, 27, 19, 6, 213)
Formatted:
rtc = machine.RTC()
t = rtc.datetime()
print('{:04d}-{:02d}-{:02d} {:02d}:{:02d}'.format(t[0], t[1], t[2], t[4], t[5]))
2021-08-01 20:27
Webrepl#
Once the microcontroller is connected to the internet, we can program it wirelessly. Great for robot projects! Make sure that webrepl_pwd
is defined in secrets.py
, then create and upload webrepl_cfg.py
.
!mkdir -p $IOT_PROJECTS/internet/code
%%writefile $IOT_PROJECTS/internet/code/webrepl_cfg.py
from secrets import webrepl_pwd
PASS = webrepl_pwd
Writing /home/iot/iot49.org/docs/projects/internet/code/webrepl_cfg.py
%%writefile $IOT_PROJECTS/devices/esp32.yaml
esp32:
uid: 30:ae:a4:30:84:34
path: internet
resources:
- secrets.py:
path: libs
- code
Writing /home/iot/iot49.org/docs/projects/devices/example.yaml
%rsync
Directories match
Start the webrepl daemon on the microcontroller:
import webrepl
webrepl.start()
WebREPL daemon started on ws://10.39.40.135:8266
Started webrepl in normal mode
Advertise the webrepl with regular broadcast messages so ide49 discovers it.
from socket import socket, AF_INET, SOCK_DGRAM
import _thread
def broadcaster(msg, port):
so = socket(AF_INET, SOCK_DGRAM)
try:
# pre-allocate address (on heap)
addr = ('255.255.255.255', port)
while True:
so.sendto(msg, addr)
time.sleep(1)
finally:
so.close()
msg = "ws://{}:8266\n{}".format(
network.WLAN(network.STA_IF).ifconfig()[0],
":".join("{:02x}".format(x) for x in machine.unique_id())
)
th = _thread.start_new_thread(broadcaster, (msg, getattr(secrets, 'broadcast_port', 50000)))
Connect wirelessly:
%discover
esp32 serial:///dev/ttyUSB0
esp32 ws://10.39.40.135:8266
%connect esp32 ws
print("connected to webrepl")
Connected to esp32 @ ws://10.39.40.135:8266
connected to webrepl
Unfortunately wireless connection via webrepl is slow. The primary reason is that webrepl lacks a feature to tell when it’s ready to receive code and when not. ide49 takes a conservative approach and sends code only in small chunks with pauses inbetween.
Note: if you rather not use the broadcaster code to advertise the webrepl, you can instead connect with the url:
%connect "ws://10.39.40.135:8266"
Connected to esp32 @ ws://10.39.40.135:8266
boot.py#
To automatically connect the ESP32 whenever it boots, place the instructions in a file boot.py
stored in the root directory of the microcontroller. MicroPython executes this file whenever it starts.
%%writefile $IOT_PROJECTS/internet/code/boot.py
import micropython, machine, network, ntptime, time
import secrets
# handle errors in interrupts
micropython.alloc_emergency_exception_buf(100)
def connect():
"""connect to wifi, get ntp time, advertise hostname (if not None)"""
wlan = network.WLAN(network.STA_IF)
if wlan.isconnected(): return True
wlan.active(True)
# wlan.scan()
print("Connecting to WLAN ... ", end="")
wlan.connect(getattr(secrets, 'wifi_ssid', 'SSID'),
getattr(secrets, 'wifi_pwd', ''))
# wait for connection to be established ...
for _ in range(30):
if wlan.isconnected(): break
time.sleep_ms(100)
if not wlan.isconnected():
print("Unable to connect to WiFi!")
wlan.disconnect()
return False
# set clock to local time
tm = time.localtime(ntptime.time() + getattr(secrets, 'tz_offset', 0))
print("time", tm)
machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0))
return True
# connect to WiFi
conneced = connect()
if connected and hasattr(secrets, 'webrepl_pwd'):
# start webrepl
import webrepl
webrepl.start()
# advertise
from socket import socket, AF_INET, SOCK_DGRAM
import _thread
def broadcaster(msg, port):
so = socket(AF_INET, SOCK_DGRAM)
try:
# pre-allocate address (on heap)
addr = ('255.255.255.255', port)
while True:
so.sendto(msg, addr)
time.sleep(1)
finally:
so.close()
msg = "ws://{}:8266\n{}".format(
network.WLAN(network.STA_IF).ifconfig()[0],
":".join("{:02x}".format(x) for x in machine.unique_id())
)
th = _thread.start_new_thread(broadcaster, (msg, getattr(secrets, 'broadcast_port', 50000)))
Writing /home/iot/iot49.org/docs/projects/internet/code/boot.py
%rsync
%softreset
UPDATE /boot.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!! softreset ... !!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# switch back to serial connection
%connect esp32 serial
Connected to esp32 @ serial:///dev/ttyUSB0
# and again to wireless
%connect esp32 ws
Connected to esp32 @ ws://10.39.40.135:8266
Try operating the microcontroller from a battery or power-only USB cable to verify that connection is indeed wireless.