Step 1: Managing Credentials Off-Chip

As with previous builds, authentication credentials remain isolated from the primary firmware logic in a local secrets.py file. Copy and paste the code below into a new code window, enter your own username and key, and save it as secrets.py on the MicroPython device.

# secrets.py
ADAFRUIT_IO_USERNAME = "your_username_here"
ADAFRUIT_IO_KEY      = "your_aio_key_here"

This keeps personal API tokens off version control while allowing the main script to reference dynamic feed paths.

Step 2: The Firmware Breakdown

Let's dissect the implementation details and hardware considerations behind this autonomous edge controller.

1. Imports and Setpoint Configuration

import gc
import network
import time
from machine import I2C, SPI, Pin
import ahtx0
from umqtt.simple import MQTTClient

from secrets import ADAFRUIT_IO_KEY, ADAFRUIT_IO_USERNAME

# MQTT Broker Details
MQTT_HOST = "io.adafruit.com"
MQTT_PORT = 1883
TEMP_TOPIC = ADAFRUIT_IO_USERNAME + "/feeds/temperature"
HUMIDITY_TOPIC = ADAFRUIT_IO_USERNAME + "/feeds/humidity"
FAN_TOPIC = ADAFRUIT_IO_USERNAME + "/feeds/fan"
PUBLISH_INTERVAL_MS = 30000

# Temperature thresholds for fan control (Celsius)
TEMP_FAN_ON = 25.0
TEMP_FAN_OFF = 24.0   # Hysteresis band
  • Target Feeds: Three streams are prepared—temperature, humidity, and a dedicated fan feed to mirror the hardware's autonomous decisions back to our remote dashboard.

  • Hysteresis Logic: Turning a fan on and off at a single target (say, exactly 25.0°C) causes rapid relay or MOSFET chattering as the temperature hovers around the setpoint. Introducing a 1.0°C deadband (TEMP_FAN_ON = 25.0 and TEMP_FAN_OFF = 24.0) stabilizes mechanical and electrical components. You can change the numbers to whatever temperature you would like the fan to turn on and off at.

Quick Note:
Make sure you have the ahtx0 and MQTT client libraries installed. The MQTT client library can be installed by navigating to Tools -> Manage Packages and searching for micropython-umqtt.simple.
The ahtx0 library can be installed using the mip module. Once your board is connected to the internet (via your Ethernet setup), open the REPL and run:

import mip
mip.install("github:targetblank/micropython_ahtx0/blob/master/ahtx0.py")

 2. Robust W5500 Bring-Up with Hardware Reset

# Configure SPI 2 for the internal W5500 controller spi = SPI(2, baudrate=8_000_000, polarity=0, phase=0) cs = Pin("PB12", Pin.OUT) rst = Pin("PD9", Pin.OUT) # Explicit hardware reset toggle rst.value(0) time.sleep_ms(10) rst.value(1) time.sleep_ms(200) nic = network.WIZNET5K(spi, cs, rst) nic.active(True) nic.ifconfig("dhcp") print("Waiting for DHCP...") deadline = time.ticks_add(time.ticks_ms(), 30000) while nic.ifconfig()[0] == "0.0.0.0": if time.ticks_diff(deadline, time.ticks_ms()) <= 0: raise RuntimeError("DHCP timeout") time.sleep_ms(500) # Verify physical link state timeout = 10 while not nic.isconnected() and timeout > 0: time.sleep(1) timeout -= 1 if nic.isconnected(): print("→ Ethernet Link UP!") print(" IP Address:", nic.ifconfig()[0]) else: print("→ Failed to detect physical link. Check cable and switch.")

 

  • Physical Reset: Toggling PD9 low for 10 ms before high-impedance initialization clears internal state registers on the W5500, guaranteeing a fresh start after soft reboots in MicroPython.

  • Link State Polling: Beyond obtaining a DHCP lease, calling nic.isconnected() checks whether physical Ethernet carrier pulses are detected before trying to initiate TCP transactions.

3. Peripherals: I2C Sensor and High-Power Actuator

# Initialize I2C bus and AHT20 sensor i2c = I2C(1) print("I2C devices:", i2c.scan()) sensor = ahtx0.AHT20(i2c) # Configure fan actuator output fan = Pin("PD13", Pin.OUT) # Connected to MOSFET TRIG/PWM pin fan.off()

  • I2C Bus Init: I2C(1) scans the lines and latches the AHT20 at address 0x38.
  •  Actuator Isolation: Microcontroller GPIOs cannot drive DC fans directly without frying pins....
Read more »