Session 3: Deployment, Configuration, and Field Maintenance
Synopsis
Covers organizing project files, managing device configuration, updating code safely, and preparing async systems for real-world support scenarios.
Session Content
Session 3: Deployment, Configuration, and Field Maintenance
Session Overview
In this session, learners will move from development to real-world use of a Raspberry Pi Pico 2 W project. The focus is on preparing a device for deployment, configuring it for reliable operation, and performing basic field maintenance tasks such as logging, troubleshooting, and safe updates.
By the end of this session, learners will be able to: - Configure a Pico 2 W for deployment in a stable, repeatable way - Store and load runtime configuration from a file - Add basic device identification and startup diagnostics - Implement resilient networking logic with reconnect behavior - Log operational events for field troubleshooting - Perform safe maintenance tasks such as firmware updates and config changes
Session Duration
Approximately 45 minutes
Prerequisites
- Raspberry Pi Pico 2 W
- USB cable
- Thonny IDE installed
- MicroPython firmware installed on the Pico 2 W
- Basic familiarity with Python, GPIO, and Wi-Fi setup
- A working Wi-Fi network
Development Environment Setup
Thonny Setup
- Connect the Pico 2 W to your computer via USB.
- Open Thonny.
- Go to Tools > Options > Interpreter.
- Select:
- MicroPython (Raspberry Pi Pico)
- Confirm the correct port is selected.
- Open the Shell and verify the device responds.
Recommended Project Files
Create a small project structure on the Pico:
main.py— application entry pointconfig.json— deployment configurationboot.py— optional boot-time setuplogger.py— simple logging helper
Theory: Deployment and Field Maintenance
1. What Deployment Means
Deployment is the step where a development prototype becomes a device that can run unattended. A deployed device should: - Start automatically on power-up - Reconnect to Wi-Fi if needed - Use stored configuration instead of hardcoded values - Provide useful diagnostics for troubleshooting - Be easy to update in the field
2. Why Configuration Matters
Hardcoding values like Wi-Fi credentials, device names, and thresholds makes maintenance difficult. A configuration file allows changes without editing code.
Typical configuration values: - Device name - Wi-Fi SSID and password - Reporting interval - Sensor thresholds - Server endpoint
3. Field Maintenance Goals
A maintainable device should support: - Log messages for failures and status - Safe file-based configuration changes - Recovery after reboot - Minimal manual intervention - Clear separation between code and data
4. Common Deployment Issues
- Incorrect Wi-Fi credentials
- Missing or corrupted config files
- Weak signal or unreliable network
- Unexpected resets due to memory use
- Inconsistent startup behavior
Hands-On Exercise 1: Create a Configuration File
Goal
Store deployment settings in a JSON file and load them at startup.
config.json
Create this file on the Pico:
{
"device_name": "pico2w-field-node",
"wifi_ssid": "YOUR_WIFI_NAME",
"wifi_password": "YOUR_WIFI_PASSWORD",
"report_interval_s": 30,
"debug": true
}
Hands-On Exercise 2: Load Configuration Safely
Goal
Read configuration from a file, fall back to defaults if needed, and print startup diagnostics.
main.py
# main.py
# Load deployment configuration safely and show startup diagnostics.
import json
import time
import machine
import os
CONFIG_FILE = "config.json"
DEFAULT_CONFIG = {
"device_name": "pico2w-default",
"wifi_ssid": "",
"wifi_password": "",
"report_interval_s": 60,
"debug": False
}
def load_config():
"""
Load configuration from config.json.
If the file is missing or invalid, return default settings.
"""
config = DEFAULT_CONFIG.copy()
try:
with open(CONFIG_FILE, "r") as f:
user_config = json.load(f)
config.update(user_config)
print("Config loaded successfully.")
except OSError:
print("Config file not found. Using defaults.")
except ValueError:
print("Config file is invalid JSON. Using defaults.")
return config
def print_system_info(config):
"""
Print basic diagnostic information useful for field maintenance.
"""
print("\n=== Device Startup ===")
print("Device name:", config["device_name"])
print("Machine unique ID:", machine.unique_id())
print("Free memory:", machine.mem_free(), "bytes")
try:
stat = os.statvfs("/")
free_space = stat[0] * stat[3]
print("Approx free storage:", free_space, "bytes")
except:
print("Storage information unavailable.")
print("======================\n")
def main():
config = load_config()
print_system_info(config)
# Keep the program alive for testing.
while True:
print("Device running...")
time.sleep(config["report_interval_s"])
main()
Expected Output
Config loaded successfully.
=== Device Startup ===
Device name: pico2w-field-node
Machine unique ID: b'\x00\x00\x00...'
Free memory: 120000 bytes
Approx free storage: 3000000 bytes
======================
Device running...
Device running...
Hands-On Exercise 3: Add a Simple Logger
Goal
Create a reusable logger that prints messages with timestamps and writes them to a file.
logger.py
# logger.py
# Simple file-based logger for field debugging.
import time
LOG_FILE = "device.log"
def _timestamp():
"""
Return a simple elapsed-time timestamp string.
"""
t = time.localtime()
return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(
t[0], t[1], t[2], t[3], t[4], t[5]
)
def log(message, level="INFO"):
"""
Print a log message and append it to device.log.
"""
line = "[{}] {}: {}".format(_timestamp(), level, message)
print(line)
try:
with open(LOG_FILE, "a") as f:
f.write(line + "\n")
except OSError:
print("Warning: log file could not be written.")
Updated main.py
import time
import json
import machine
from logger import log
CONFIG_FILE = "config.json"
DEFAULT_CONFIG = {
"device_name": "pico2w-default",
"wifi_ssid": "",
"wifi_password": "",
"report_interval_s": 60,
"debug": False
}
def load_config():
config = DEFAULT_CONFIG.copy()
try:
with open(CONFIG_FILE, "r") as f:
config.update(json.load(f))
log("Configuration loaded.")
except OSError:
log("Config file missing, using defaults.", "WARN")
except ValueError:
log("Config file invalid, using defaults.", "WARN")
return config
def main():
config = load_config()
log("Device started: {}".format(config["device_name"]))
log("Unique ID: {}".format(machine.unique_id()))
while True:
log("Heartbeat")
time.sleep(config["report_interval_s"])
main()
Example Output
[2026-03-22 10:15:00] INFO: Configuration loaded.
[2026-03-22 10:15:00] INFO: Device started: pico2w-field-node
[2026-03-22 10:15:00] INFO: Unique ID: b'\x00\x00\x00...'
[2026-03-22 10:15:00] INFO: Heartbeat
Hands-On Exercise 4: Wi-Fi Connection with Retry Logic
Goal
Connect to Wi-Fi reliably with retries and logging.
main.py
import time
import network
import json
from logger import log
CONFIG_FILE = "config.json"
def load_config():
with open(CONFIG_FILE, "r") as f:
return json.load(f)
def connect_wifi(ssid, password, max_retries=10):
"""
Connect to Wi-Fi with retry logic.
"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if wlan.isconnected():
log("Already connected to Wi-Fi.")
return wlan
log("Connecting to Wi-Fi SSID '{}'...".format(ssid))
wlan.connect(ssid, password)
retries = 0
while not wlan.isconnected() and retries < max_retries:
log("Waiting for connection... attempt {}".format(retries + 1))
time.sleep(1)
retries += 1
if wlan.isconnected():
log("Wi-Fi connected: {}".format(wlan.ifconfig()[0]))
return wlan
else:
log("Wi-Fi connection failed.", "ERROR")
return None
def main():
config = load_config()
wlan = connect_wifi(
config["wifi_ssid"],
config["wifi_password"]
)
if wlan is None:
log("Continuing in offline mode.", "WARN")
else:
log("Online services can now start.")
while True:
log("Main loop active")
time.sleep(config.get("report_interval_s", 30))
main()
Expected Output
[2026-03-22 10:20:00] INFO: Connecting to Wi-Fi SSID 'HomeNetwork'...
[2026-03-22 10:20:01] INFO: Waiting for connection... attempt 1
[2026-03-22 10:20:02] INFO: Waiting for connection... attempt 2
[2026-03-22 10:20:03] INFO: Wi-Fi connected: 192.168.1.50
[2026-03-22 10:20:03] INFO: Online services can now start.
Hands-On Exercise 5: Add a Startup LED Status Indicator
Goal
Use the onboard LED to signal startup and network status.
main.py
import time
import network
import machine
import json
LED = machine.Pin("LED", machine.Pin.OUT)
def blink(times, on_time=0.2, off_time=0.2):
"""
Blink the onboard LED a given number of times.
"""
for _ in range(times):
LED.value(1)
time.sleep(on_time)
LED.value(0)
time.sleep(off_time)
def load_config():
with open("config.json", "r") as f:
return json.load(f)
def connect_wifi(ssid, password):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
for _ in range(10):
if wlan.isconnected():
return wlan
blink(1, 0.1, 0.1)
time.sleep(1)
return None
def main():
blink(3, 0.1, 0.1) # startup indication
config = load_config()
wlan = connect_wifi(config["wifi_ssid"], config["wifi_password"])
if wlan:
LED.value(1) # solid on for connected
else:
blink(5, 0.05, 0.05) # error indication
while True:
time.sleep(5)
main()
Field Maintenance Tasks
1. Updating Configuration
If Wi-Fi changes, edit only config.json rather than code. Keep usernames, passwords, and endpoints outside application logic.
2. Reviewing Logs
Open device.log in Thonny or download it to inspect:
- Boot sequence
- Wi-Fi status
- Error messages
- Unexpected resets
3. Restarting the Device
A simple reboot can resolve temporary connection or memory issues:
import machine
machine.reset()
4. Checking Free Memory
Useful during troubleshooting:
import machine
print(machine.mem_free())
5. Checking Files on the Device
List files using:
import os
print(os.listdir())
Best Practices for Deployment
- Keep
main.pysmall and stable - Store settings in a separate config file
- Log major startup and failure events
- Retry network connections before failing
- Use the LED for simple operational status
- Avoid long blocking operations where possible
- Test reboot behavior before field deployment
Common Troubleshooting Checklist
- Confirm
config.jsonis valid JSON - Verify Wi-Fi credentials
- Check signal strength near the deployment location
- Inspect
device.log - Ensure
main.pydoes not crash at startup - Re-flash MicroPython if the filesystem becomes unstable
- Restart the board after updates
Mini Challenge
Modify the logger and configuration file so that:
1. The device name appears in every log line
2. The report interval can be changed without editing main.py
3. The LED blinks twice when Wi-Fi connects successfully
Review Questions
- Why is storing configuration in a file better than hardcoding it?
- What should a deployed IoT device do after a reboot?
- Why are logs important for field maintenance?
- What is the purpose of Wi-Fi retry logic?
- How can the onboard LED help during deployment?
Summary
In this session, learners built the foundations for a maintainable Pico 2 W deployment: - Loaded configuration from a file - Added logging for troubleshooting - Implemented Wi-Fi reconnect behavior - Used the onboard LED for status indication - Learned practical field maintenance techniques
Suggested Next Steps
- Add a sensor reading to the main loop
- Send logged data to a cloud endpoint
- Implement MQTT publishing
- Add an HTTP configuration interface
- Store logs with rotation to prevent storage exhaustion
Back to Chapter | Back to Master Plan | Previous Session | Next Session