Network Initialization
The Ethernet interface uses the WIZnet W5500 driver through network.WIZNET5K.
Default SPI and control pins:
Signal |
Pin |
|---|---|
SPI bus |
|
CS |
|
RST |
|
PWR / enable |
|
By default, the server tries to use DHCP. If DHCP fails, it falls back to the static IP configuration defined in setup.py.
Default static configuration:
Field |
Default value |
|---|---|
IP address |
|
Subnet mask |
|
Gateway |
|
DNS |
|
The network mode can also be changed from the HTTP network configuration page.
HTTP Server Behavior
The server is a lightweight MicroPython socket server. It listens on port 80, accepts one client connection at a time, handles the request, sends the response, and then closes the client socket.
Supported behavior:
GETandPOSTroute registration throughserver.add_route().Static file serving for JavaScript, CSS, and images.
JSON request body handling for configuration and control APIs.
HTTP responses with
Content-Type,Connection: Close, and optionalContent-Lengthheaders.Deferred network reconfiguration through a pending-task queue, so the server can respond to the current request before restarting the socket.
Static paths:
URL prefix |
Content type |
|---|---|
|
|
|
|
|
|
Page Routes
These routes return HTML pages from the template/ directory.
Method |
Path |
Description |
|---|---|---|
|
|
Main index page. |
|
|
Network configuration page. |
|
|
Digital input/output control page. |
|
|
Analog input/output page. |
|
|
Serial UART page. |
|
|
EEPROM storage page. |
Network Configuration API
Read current network configuration
GET /netconfig/config
Returns the active Ethernet configuration.
Example response:
{
"ip_addr": "10.0.1.109",
"subnet_mask": "255.255.255.0",
"default_gateway": "10.0.1.254",
"dns": "8.8.8.8",
"dhcp": true
}
Change network configuration
POST /netconfig/config
Content-Type: application/json
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"
}
When static mode is selected, the program validates each IPv4 field before applying the new configuration. A valid IPv4 address must contain four number segments separated by dots, and each segment must be between 0 and 255.
Example success response:
{
"redirect": true
}
Note
The redirect behavior depends on the selected network mode.
Static IP mode: The webpage automatically redirects to the IP address you entered.
DHCP mode: The IP address is assigned by the network, so you need to open the URL shown in the REPL console manually.
Example validation error response:
{
"error": true,
"error_message": "Ensure address are all in ipv4. (x.x.x.x)"
}
After a successful change, the server schedules the network reinitialization as a pending task. This allows the HTTP response to be sent before the old socket is closed and recreated.
Digital I/O API
The program supports eight digital inputs and eight digital outputs.
Type |
Names |
|---|---|
Digital inputs |
|
Digital outputs |
|
Open digital I/O page
GET /io
Returns the digital I/O control page.
Set digital output
POST /io
Content-Type: application/json
Example request:
{
"pin": "RY1",
"value": 1
}
Example response:
{
"value": 1
}
Read all digital I/O states
GET /io/state
Example response:
{
"output": [
["RY1", 1],
["RY2", 0]
],
"input": [
["IN1", 0],
["IN2", 1]
]
}
The actual response contains all eight outputs and all eight inputs.
Read digital inputs only
GET /din
Example response:
{
"IN1": 0,
"IN2": 1,
"IN3": 0,
"IN4": 0,
"IN5": 0,
"IN6": 0,
"IN7": 0,
"IN8": 0
}
Analog I/O API
The program supports four analog inputs and two analog outputs.
Type |
Names |
|---|---|
Analog inputs |
|
Analog outputs |
|
Analog input mode mapping:
Input |
Mode |
Returned unit |
|---|---|---|
|
Current mode |
mA |
|
Current mode |
mA |
|
Voltage mode |
V |
|
Voltage mode |
V |
Open analog I/O page
GET /analog_io
Returns the analog I/O page.
Read all analog input states
GET /analog_io/state
Example response:
{
"AI0": {
"value": 12.3,
"mode": 1
},
"AI1": {
"value": 11.8,
"mode": 1
},
"AI2": {
"value": 2.5,
"mode": 0
},
"AI3": {
"value": 3.1,
"mode": 0
}
}
Mode values:
Value |
Meaning |
|---|---|
|
Voltage mode |
|
Current mode |
Set analog output voltage
POST /analog_out
Content-Type: application/json
Example request:
{
"index": 0,
"voltage": 5.0
}
The program converts the requested voltage into a 12-bit DAC value. The DAC output is calibrated with the measured minimum and maximum voltage values stored in DAC_CONFIG.
Calibration values:
Output |
Minimum measured voltage |
Maximum measured voltage |
|---|---|---|
|
|
|
|
|
|
Values outside the supported range are clamped to the DAC range.
Serial UART API
The program exposes UART communication through the HTTP interface. The UART is initialized as UART(3), and its default settings come from UART_CONFIGS in config.py.
Default UART setting fallback values:
Field |
Fallback value |
|---|---|
Baudrate |
|
Data bits |
|
Parity |
|
Stop bits |
|
Open serial page
GET /serial
Returns the serial communication page.
Note
You can test the UART section with our WIZnet Serial Debugging Tool.
Send UART message
POST /serial
Content-Type: application/json
Example request:
{
"message": "hello"
}
The message is encoded as UTF-8 and written to UART3.
Receive UART message
GET /serial/recv
Example response when data is available:
{
"message": "OK"
}
Example response when no data is available:
{
"message": null
}
The receive function reads all currently available UART bytes and decodes them as UTF-8. Invalid bytes are replaced during decoding.
EEPROM Storage API
The board checks for an EEPROM device on the I2C bus during startup. The I2C bus is created with software I2C on PB6 and PB7.
Signal |
Pin |
|---|---|
SCL |
|
SDA |
|
The EEPROM is considered connected when the scan result contains addresses from 0x50 to 0x57.
EEPROM limits:
Operation |
Maximum size |
|---|---|
Write |
|
Read |
|
Open storage page
GET /storage
Returns the EEPROM storage page.
Write data to EEPROM
POST /storage/write
Content-Type: text/plain
Example body:
hello from WIZ-RTU
The program writes the received body to EEPROM starting at address index 0. String data is encoded to UTF-8 before writing.
Example response:
Success.
Read data from EEPROM
GET /storage/read
Reads up to 1000 bytes from EEPROM starting at address index 0.
Example response:
hello from WIZ-RTU
CAN API
The CAN bus is enabled when CAN_USED = True in config.py.
Default CAN configuration:
Field |
Value |
|---|---|
CAN interface |
|
Baudrate |
|
Mode |
|
RX pin |
|
TX pin |
|
Note
CAN and serial UART use the same PB8 and PB9 pins. Set CAN_USED = True to enable CAN, or CAN_USED = False to use the serial UART interface instead.
Open CAN page
GET /can
Returns the CAN communication page.
Send CAN frame
POST /can/send
Content-Type: application/json
Example request:
{
"id": "0x123",
"data": "0x1122334455667788"
}
Field |
Description |
|---|---|
|
CAN identifier as a hexadecimal string. |
|
CAN payload as a hexadecimal string. Maximum 8 bytes. |
Supported CAN identifier ranges:
Frame type |
Range |
|---|---|
Standard |
|
Extended |
|
Example response:
{
"success": true
}
A failed send returns:
{
"success": false
}
Receive CAN frames with WebSocket
CAN receive data is sent through the WebSocket server at port 8080.
ws://<device-ip>:8080
CAN messages use type: 1.
{
"type": 1,
"frames": [
{
"id": "0x123",
"data": "1122334455667788",
"frame_type": "standard"
}
]
}
Field |
Description |
|---|---|
|
|
|
List of received CAN frames. |
|
CAN identifier in hexadecimal format. |
|
CAN payload as hexadecimal bytes without the |
|
|
The server reads and broadcasts up to three received CAN frames at a time to all connected WebSocket clients.
Example JavaScript:
const socket = new WebSocket("ws://10.0.1.109:8080");
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.type !== 1) {
return;
}
for (const frame of message.frames) {
console.log(frame.id, frame.data, frame.frame_type);
}
});
Modem API
The 4G modem is enabled when MODEM_CONFIGS is defined in config.py.
Default modem configuration:
Field |
Value |
|---|---|
UART interface |
|
Baudrate |
|
Data bits |
|
Parity |
|
Stop bits |
|
Power-control pin |
|
The modem starts in transparent mode after it is powered on.
Open modem page
GET /4G
Returns the modem communication page.
Send modem data or AT command
POST /4G
Content-Type: text/plain
The request body is written directly to the modem UART.
Example AT command:
AT
Example response:
{
"success": true
}
To enter AT command mode, send:
+++
To restart the modem and return to transparent mode, send:
AT+Z
Note
Newline characters in a modem command are converted to \r\n before being sent to the modem.
Receive modem data with WebSocket
Modem receive data is sent through the WebSocket server at port 8080.
ws://<device-ip>:8080
Modem messages use type: 2.
{
"type": 2,
"msg": "QVQNCk9LDQo=",
"work_mode": 0
}
Field |
Description |
|---|---|
|
|
|
Modem data encoded as a Base64 string. |
|
Current modem work-mode value. |
The msg field must be Base64-decoded before it is displayed or processed.
Example JavaScript:
const socket = new WebSocket("ws://10.0.1.109:8080");
const decoder = new TextDecoder("utf-8");
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.type !== 2 || !message.msg) {
return;
}
const binary = atob(message.msg);
const bytes = Uint8Array.from(
binary,
(character) => character.charCodeAt(0)
);
console.log(decoder.decode(bytes));
});
CAN and Modem WebSocket Protocol
The application starts a separate WebSocket server on port 8080 when the HTTP server starts.
ws://<device-ip>:8080
For example, when the device IP address is 10.0.1.109:
ws://10.0.1.109:8080
The WebSocket is a receive-only event stream for the web application. CAN transmission and modem transmission use HTTP POST requests; received CAN frames and modem bytes are broadcast from the device to connected WebSocket clients.
Direction |
CAN |
Modem |
|---|---|---|
Browser to device |
|
|
Device to browser |
WebSocket message |
WebSocket message |
Every WebSocket message is JSON and contains a numeric type field.
WebSocket |
Source |
Required payload |
|---|---|---|
|
CAN bus |
|
|
4G modem |
|
CAN WebSocket message: type: 1
{
"type": 1,
"frames": [
{
"id": "0x123",
"data": "1122334455667788",
"frame_type": "standard"
}
]
}
Field |
Type |
Description |
|---|---|---|
|
Number |
Always |
|
Array |
One or more received CAN frames. |
|
String |
CAN identifier with a |
|
String |
Payload as hexadecimal byte pairs, without a |
|
String |
|
The server reads at most three CAN frames per processing cycle. A message is sent only when at least one CAN frame is available.
Modem WebSocket message: type: 2
{
"type": 2,
"msg": "QVQNCk9LDQo=",
"work_mode": 0
}
Field |
Type |
Description |
|---|---|---|
|
Number |
Always |
|
String |
Base64-encoded raw bytes received from the modem UART. |
|
Number |
The current modem transparent work-mode value reported by the modem manager. |
The server reads modem UART data in chunks of up to 2048 bytes. A message is sent for each non-empty chunk. Decode msg from Base64 to recover the received modem bytes.
Client handling example
const socket = new WebSocket("ws://10.0.1.109:8080");
const decoder = new TextDecoder("utf-8");
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.type === 1) {
for (const frame of message.frames ?? []) {
console.log("CAN", frame.id, frame.data, frame.frame_type);
}
return;
}
if (message.type === 2 && message.msg) {
const binary = atob(message.msg);
const bytes = Uint8Array.from(
binary,
(character) => character.charCodeAt(0)
);
console.log("Modem", decoder.decode(bytes, { stream: true }));
}
});
Note
The WebSocket server supports up to two connected clients. The server broadcasts each CAN or modem message to all connected clients.
Error Handling
Common error behavior:
Situation |
Response |
|---|---|
Unknown route |
|
Invalid JSON body |
|
Header larger than maximum size |
Plain text response: |
Invalid IPv4 field |
JSON response with |
Invalid digital output pin |
JSON response with |
The server closes the client connection after each response.
Example Usage With curl
Read network configuration:
curl http://10.0.1.109/netconfig/config
Set static IP configuration:
curl -X POST http://10.0.1.109/netconfig/config \
-H "Content-Type: application/json" \
-d '{"dhcp":false,"ip":"10.0.1.109","subnet_mask":"255.255.255.0","default_gateway":"10.0.1.254","dns":"8.8.8.8"}'
Turn on RY1:
curl -X POST http://10.0.1.109/io \
-H "Content-Type: application/json" \
-d '{"pin":"RY1","value":1}'
Read digital I/O state:
curl http://10.0.1.109/io/state
Set AO0 to 5.0 V:
curl -X POST http://10.0.1.109/analog_out \
-H "Content-Type: application/json" \
-d '{"index":0,"voltage":5.0}'
Send a UART message:
curl -X POST http://10.0.1.109/serial \
-H "Content-Type: application/json" \
-d '{"message":"hello"}'
Read UART data:
curl http://10.0.1.109/serial/recv
Write EEPROM data:
curl -X POST http://10.0.1.109/storage/write \
-H "Content-Type: text/plain" \
-d 'hello from WIZ-RTU'
Read EEPROM data:
curl http://10.0.1.109/storage/read
Send a standard CAN frame:
curl -X POST http://10.0.1.109/can/send \
-H "Content-Type: application/json" \
-d '{"id":"0x123","data":"0x11223344"}'
Send an extended CAN frame with an eight-byte payload:
curl -X POST http://10.0.1.109/can/send \
-H "Content-Type: application/json" \
-d '{"id":"0x18FF50E5","data":"0x0102030405060708"}'
Send a basic AT command:
curl -X POST http://10.0.1.109/4G \
-H "Content-Type: text/plain" \
--data-binary "AT\n"
Enter modem command mode:
curl -X POST http://10.0.1.109/4G \
-H "Content-Type: text/plain" \
--data-binary "+++"
Restart the modem and return to transparent mode:
curl -X POST http://10.0.1.109/4G \
-H "Content-Type: text/plain" \
--data-binary "AT+Z\n"
Send modem data in transparent mode:
curl -X POST http://10.0.1.109/4G \
-H "Content-Type: text/plain" \
--data-binary "hello from WIZ-RTU"
Note
curl can send CAN frames and modem data through HTTP, but it cannot receive the application’s CAN or modem events through an HTTP endpoint. Receive data through ws://<device-ip>:8080 using a WebSocket client.