MQTT Application Overview

The WIZ-RTU MQTT application initializes the board hardware, configures the WIZnet Ethernet interface, connects to the MQTT broker, subscribes to registered command topics, publishes the initial device state, and then starts its polling loop.

Board boots
    |
    v
Initialize hardware and Ethernet
    |
    v
Load mqtt.json
    |
    v
Connect to MQTT broker
    |
    v
Subscribe to command topics
    |
    v
Publish initial states
    |
    v
Poll hardware and process MQTT messages

The MQTT loop performs:

self.poll()
self.check_msg()
time.sleep_ms(100)

This means the device checks peripherals and incoming MQTT messages approximately every 100 ms, excluding processing and network time.


Network and Broker Configuration

The Ethernet interface uses network.WIZNET5K with the following default pins:

Signal

Pin

SPI bus

SPI(2)

SPI baud rate

8,000,000

CS

PB12

RST

PD9

PWR / enable

PE15

The device first attempts to use DHCP. If DHCP fails, it falls back to:

Field

Default value

IP address

10.0.1.109

Subnet mask

255.255.255.0

Gateway

10.0.1.254

DNS

8.8.8.8

MQTT broker settings are loaded from mqtt.json:

{
  "host": "10.0.1.238",
  "username": null,
  "password": null,
  "port": 1883
}

To create mqtt.json, simply rename the file mqtt.default.json to mqtt.json and enter your MQTT broker’s corresponding configurations.

Note

Here you should input your broker’s IP address and other information. If you use your personal PC as the broker, run ipconfig to get your PC IP under the local network and enter the IP to mqtt.json.

If a value is missing, the following defaults are used:

Field

Fallback value

Host

127.0.0.1

Username

None

Password

None

Port

1883

The MQTT client ID is generated from the board MAC address.

If umqtt.simple is unavailable, the application attempts to install it into /flash using mip.

Note

In newer firmware builds, umqtt should already be frozen into the firmware, so an online installation should normally not be required.


MQTT Topic Summary

The application uses command topics for requests received by the device and state or response topics for data published by the device.

Subscribed command topics

Topic

Payload

Description

network_config/set

JSON

Changes the Ethernet configuration.

digital_output/+/set

JSON

Changes one digital output.

analog_output/+/set

JSON

Changes one analog output voltage.

uart/write

UTF-8 data

Sends data through UART3.

eeprom/write

UTF-8 data

Writes data to EEPROM.

eeprom/read/request

JSON

Requests EEPROM data from an index.

eeprom/wipe

Text

Wipes EEPROM when the payload is exactly True.

Published state and response topics

Topic

Payload

Description

network_config/state

JSON

Current Ethernet configuration.

digital_input/INx/state

JSON

Current digital input pin value.

digital_output/RYx/state

JSON

Resulting digital output value.

analog_input/state

JSON

Averaged analog input values and modes.

analog_output/AOx/state

JSON

Requested analog output voltage.

uart/read

UART data

Data received from UART3.

eeprom/read/response

Raw bytes

EEPROM data returned after a read request.

Topics are registered with the add_topic() decorator:

@client.add_topic("uart/write")
def write_to_uart(msg):
    pass

For normal topics, the handler receives the payload:

def handler(msg):
    pass

For the current digital-output and analog-output wildcard topics, the handler receives the captured pin and the payload:

def handler(pin, msg):
    pass

For example, a message sent to:

digital_output/RY1/set

is matched against:

digital_output/+/set

and calls:

handler(b"RY1", msg)

Note

The current wildcard dispatcher only handles topic families beginning with digital_output or analog_output.


Initial State and Retained Messages

After connecting, the device publishes:

  • Current network configuration.

  • All eight digital input states.

  • All eight digital output states.

  • All four analog input values and modes.

  • Initial analog output states.

This allows an MQTT dashboard to display the current device state immediately after subscribing.

The general publish() helper uses:

retain=True

Therefore, state messages published through this helper are retained by the broker.

This includes:

network_config/state
digital_input/IN1/state
digital_output/RY1/state
analog_input/state
analog_output/AO0/state

The following topics are published directly and are not explicitly retained by the application:

uart/read
eeprom/read/response

Command topics should normally not be retained because a retained command could be delivered again when the device reconnects.


Network Configuration Topics

Read the current network state

The device publishes its network configuration to:

network_config/state

Example payload:

{
  "ip": "10.0.1.109",
  "mask": "255.255.255.0",
  "gateway": "10.0.1.254",
  "dns": "8.8.8.8",
  "dhcp": true
}

Change the network configuration

Publish to:

network_config/set

Use DHCP:

{
  "dhcp": true
}

Use a static IP address:

{
  "dhcp": false,
  "ip": "10.0.1.109",
  "subnet_mask": "255.255.255.0",
  "default_gateway": "10.0.1.254",
  "dns": "8.8.8.8"
}

After receiving the request, the application disconnects MQTT, reinitializes Ethernet, sets the restart flag, and starts the MQTT runtime again with the new configuration.

Warning

The current MQTT handler does not call validate_ipv4() before applying a static configuration. Ensure all submitted IPv4 values are valid.


Digital I/O Topics

The program supports:

Type

Names

Digital inputs

IN1 to IN8

Digital outputs

RY1 to RY8

Set a digital output

Topic pattern:

digital_output/<pin>/set

Example:

digital_output/RY1/set

Payload:

{
  "value": 1
}

If value is missing, the handler uses 0.

The resulting pin state is published to:

digital_output/RY1/state

Example response:

{
  "value": 1
}

Monitor digital inputs

Digital inputs are published to:

digital_input/<pin>/state

Example:

digital_input/IN1/state

Payload:

{
  "value": 1
}

The device publishes a new message only when the physical input value changes.

Note

The payload contains the physical pin value. The MQTT layer does not invert active-low inputs.


Analog I/O Topics

The program supports:

Type

Names

Analog inputs

AI0 to AI3

Analog outputs

AO0 to AO1

Default analog input modes:

Input

Mode

Unit

AI0

Current

mA

AI1

Current

mA

AI2

Voltage

V

AI3

Voltage

V

Mode values:

Value

Meaning

0

Voltage mode

1

Current mode

Monitor analog inputs

All channels are published together on:

analog_input/state

Example payload:

{
  "AI0": {
    "value": 12.3,
    "mode": 1
  },
  "AI1": {
    "value": 11.8,
    "mode": 1
  },
  "AI2": {
    "value": 2.5,
    "mode": 0
  },
  "AI3": {
    "value": 3.1,
    "mode": 0
  }
}

Each channel collects ten readings before publishing an average. With the default loop delay, analog data is normally published about once per second.

Reported values are limited to:

Mode

Maximum reported value

Voltage mode

10 V

Current mode

20 mA

The conversion formulas are:

# Current mode
current_ma = (voltage / 120) * 1000

# Voltage mode
input_voltage = (voltage * 14.7) / 4.7

Set an analog output

Topic pattern:

analog_output/<pin>/set

Example:

analog_output/AO0/set

Payload:

{
  "voltage": 5.0
}

If voltage is missing, the handler uses 0.

The requested value is published to:

analog_output/AO0/state

The DAC uses 12-bit values from 0 to 4095.

Calibration values:

Output

Minimum measured voltage

Maximum measured voltage

AO0

0.024 V

9.89 V

AO1

0.079 V

9.88 V

Requested voltages outside the supported range are clamped to the DAC limits.


Serial UART Topics

UART communication uses UART(3).

Fallback settings:

Field

Fallback value

Baud rate

9600

Data bits

8

Parity

None

Stop bits

1

Send UART data

Publish UTF-8 data to:

uart/write

Example:

hello from MQTT

Receive UART data

UART data is published to:

uart/read

The device checks UART during every polling cycle, reads all available bytes, and decodes them as UTF-8 with replacement characters.


EEPROM Topics

The EEPROM uses software I2C:

Signal

Pin

SCL

PB6

SDA

PB7

The EEPROM is considered connected only when the I2C scan contains every address from 0x50 to 0x57.

Limits:

Operation

Maximum size

Write

1000 bytes

Read

1000 bytes

Write data

Publish UTF-8 data to:

eeprom/write

Example:

hello from WIZ-RTU

The default write index is 0.

Read data

First, request a read from eeprom by publishing to:

eeprom/read/request

Payload:

{
  "index": 0
}

If index is missing, the handler uses 0.

THen, the eeprom read result will be published as raw bytes to:

eeprom/read/response

Wipe data

Publish to:

eeprom/wipe

Required payload:

True

The comparison is case-sensitive.

Warning

Wiping EEPROM removes its stored contents and normally cannot be undone.


Runtime, Restart, and Error Behavior

When the network configuration changes, the application:

  1. Disconnects from MQTT.

  2. Reinitializes Ethernet.

  3. Sets client.restart = True.

  4. Exits the current MQTT loop.

  5. Starts the MQTT runtime again.

Common error behavior:

Situation

Behavior

MQTT connection fails

run() returns -1.

Unknown topic is received

The message is ignored.

Invalid JSON command payload

The handler returns without applying the command.

Invalid output name

The device prints an error and returns -1.

EEPROM is unavailable

EEPROM operations fail their assertion check.

Runtime exception

The error is printed, MQTT disconnects, and the application exits.

KeyboardInterrupt

MQTT disconnects and the application exits without printing a normal runtime error.

Debug output is controlled by:

DEBUG = False

Set it to True to enable dprint() messages.


Customizing the MQTT Application

Edit mqtt_app.py to add, remove, or change subscribed command topics and their handlers.

Example:

@client.add_topic("custom/topic")
def handle_custom_topic(msg):
    print("Received:", msg)

Use device_mqtt.py only when changing lower-level behavior such as:

  • MQTT connection handling.

  • Subscription and dispatch logic.

  • Polling intervals.

  • Automatic state publication.

  • Retained-message behavior.

Use device.py only when changing the underlying GPIO, ADC, DAC, UART, I2C, or EEPROM behavior.


Example Usage With Mosquitto

Replace 10.0.1.238 with your broker address.

Monitor all topics:

mosquitto_sub -h 10.0.1.238 -t "#" -v

Turn on RY1:

mosquitto_pub -h 10.0.1.238 \
  -t "digital_output/RY1/set" \
  -m '{"value":1}'

Set AO0 to 5.0 V:

mosquitto_pub -h 10.0.1.238 \
  -t "analog_output/AO0/set" \
  -m '{"voltage":5.0}'

Monitor analog inputs:

mosquitto_sub -h 10.0.1.238 \
  -t "analog_input/state" -v

Send UART data:

mosquitto_pub -h 10.0.1.238 \
  -t "uart/write" \
  -m "hello"

Write EEPROM data:

mosquitto_pub -h 10.0.1.238 \
  -t "eeprom/write" \
  -m "hello from WIZ-RTU"

Request EEPROM data:

mosquitto_pub -h 10.0.1.238 \
  -t "eeprom/read/request" \
  -m '{"index":0}'

Request DHCP:

mosquitto_pub -h 10.0.1.238 \
  -t "network_config/set" \
  -m '{"dhcp":true}'