Software Testing the UCMOD-0 over RS485
Today’s focus was on the software side of the UCMOD-0 ATtiny1614 module and the Isolated RS485 Building Block Board, creating a basic “Hello World” test program, configuring the ATtiny1614’s clock and brown-out settings, and testing communication over the RS485 bus.
Things went well overall. There was an issue with a jumper wire not being fully seated, which caused instability on the power rail and in turn caused the ATtiny to halt intermittently. Once that was fixed everything looked good.
It’s another reminder that anything I can do to eliminate jumper-wire issues in future test setups and provide more solid connection methods is well worth the effort.
Measuring Voltages#
The first step was to create a sketch that set DE (Driver Enable) high and toggled the DI (Driver Input) pin with eight-second delays. This gave me enough time to measure the resulting voltages on the A and B lines of the RS485 bus with a multimeter.
void setup() {
pinMode(PIN_PA5, OUTPUT);
pinMode(PIN_PA6, OUTPUT); // Configure the driver enable pin
pinMode(PIN_PB2, OUTPUT); // Configure the TX pin as a plain GPIO for now
if (RSTCTRL.RSTFR & RSTCTRL_BORF_bm) {
digitalWrite(PIN_PA5, HIGH);
}
else {
digitalWrite(PIN_PA5, LOW);
}
digitalWrite(PIN_PA6, HIGH); // Enable the driver
}
void loop() {
digitalWrite(PIN_PB2, HIGH);
delay(8000);
digitalWrite(PIN_PB2, LOW);
delay(8000);
}
One important detail with this sketch, and the subsequent tests, is that the UCMOD-0 runs from 3.3V.
Because of this I configured the ATtiny1614 to run at 10MHz rather than the default 20MHz. The ATtiny1614 supports operation at up to 10MHz from 2.7V upwards, whereas running at the full 20MHz requires a higher supply voltage.
Thankfully, quite a lot of this configuration can be done through the Arduino Tools menu when using megaTinyCore.
I used the following settings:
- Clock: 10 MHz internal (not the 10MHz tuning option)
- BOD: 2.6 V
- BOD active mode: Enabled
The upload process was:
- Disconnect the UCMOD-0 module from the carrier board.
- Connect the UPDI Friend to the UCMOD-0 module.
- Set the clock and BOD options as described above.
- Select Burn Bootloader to apply the fuse and configuration settings.
- Select Upload to upload the sketch.
- Disconnect the UPDI Friend and plug the UCMOD-0 module back into the carrier board.
Despite the Arduino menu option being called Burn Bootloader, I’m not actually installing or relying on a traditional bootloader here. Programming is still being performed directly over UPDI; the menu option is being used to apply the selected fuse and configuration settings.
Once the sketch was uploaded, I plugged the UCMOD-0 back into the board, connected a multimeter to the RS485 A and B lines, and recorded the following readings.
With approximately 20m of Cat5 disconnected:
| DI HIGH | DI LOW | |
|---|---|---|
| D0/A | 4.083V | 0.872V |
| D1/B | 0.705V | 3.795V |
| A - B | +3.378V | -2.923V |
With approximately 20m of Cat5 connected:
| DI HIGH | DI LOW | |
|---|---|---|
| D0/A | 3.6V | 1.1V |
| D1/B | 1.0V | 3.4V |
| A - B | +2.6V | -2.3V |
The cable does reduce the measured differential voltage somewhat, which isn’t unexpected, but the bus is still being driven strongly in both states.
The results appeared to be broadly in line with expectations, so I was reasonably comfortable that the board was behaving correctly electrically.
The next step was therefore to send an actual message over the RS485 bus and see whether another board could receive it.
Hello World#
For the receiver I used one of my older isolated RS485 boards connected to a Raspberry Pi Pico 2W.
I deliberately wanted to test a slightly different combination of boards rather than simply connecting two identical setups together. It isn’t strictly necessary, but it does give me a little more confidence that the boards are interoperating correctly.
The Pico ran a modified MicroPython script that waited for incoming messages and printed them to the terminal.
from machine import UART, Pin
import time
uart = UART(
0,
baudrate=9600,
bits=8,
parity=None,
stop=1,
tx=Pin(0),
rx=Pin(1)
)
de_re = Pin(2, Pin.OUT)
de_re.value(0)
buffer = bytearray()
print("Waiting for data...")
while True:
if uart.any():
data = uart.read()
if data:
buffer.extend(data)
while b'\n' in buffer:
line, _, buffer = buffer.partition(b'\n')
# Arduino println() sends \r\n
line = line.rstrip(b'\r')
try:
text = line.decode("ascii")
print("Received:", text)
except UnicodeDecodeError:
print("Invalid data:", line)
time.sleep_ms(10)
The Raspberry Pi Pico sets the receiver-enable control low so that it can receive messages, checks the UART for incoming data, and appends anything received to a buffer.
It then looks for newline characters, extracts complete lines, strips the carriage return sent by Arduino’s println(), and prints the resulting text.
The transmitting side is even simpler.
The ATtiny1614 configures the serial peripheral so that PB2 is used for TX and PB3 for RX, enables the RS485 driver, and sends a message once per second.
void setup()
{
pinMode(PIN_PA6, OUTPUT);
Serial.swap(0); // PB2 = TX, PB3 = RX
Serial.begin(9600);
digitalWrite(PIN_PA6, HIGH); // Enable RS485 transmitter
}
void loop()
{
Serial.println("Hello World!");
delay(1000);
}
Testing the Setup#
With both sketches uploaded and the boards connected, I looked at the serial output from the Raspberry Pi Pico 2W and saw:
Waiting for data...
Received: Hello World!
Received: Hello World!
Received: Hello World!
Received: Hello World!
Received: Hello World!
Received: Hello World!
The result is perhaps not particularly exciting, but it does confirm something important: the UCMOD-0, the carrier board, the isolated RS485 interface and the receiving board are all working together correctly.
I’m reasonably happy with the current implementation, although I’ll continue doing some testing before I finally freeze the design.
I also tested the boards at a range of baud rates:
| Speed | Bits | Parity | Stop |
|---|---|---|---|
| 2400 | 8 | none | 1 |
| 9600 | 8 | none | 1 |
| 460800 | 8 | none | 1 |
| 921600 | 8 | none | 1 |
I also tried going as low as 300 baud, but this did not work with the ATtiny1614 running at 10MHz.
This turns out to be a limitation of the USART baud-rate generator rather than anything to do with the RS485 hardware itself. With megaTinyCore, operating at 300 baud requires a CPU clock of 5MHz or lower, while at 10MHz the minimum practical baud rate is around 600 baud.
For anything I realistically intend to use the board for this isn’t a significant limitation, but it was an interesting edge case to test.
Next Steps#
Testing the boards led me to a few conclusions.
The UCMOD-0 works as expected, although I’ve already created a small revision of the module.
The first change is a jumper that allows the UPDI programmer’s power supply to be isolated from the rest of the board when required.
I’ve also added an AO3401A P-channel MOSFET to the UPDI power input to provide reverse-polarity protection if power is accidentally applied incorrectly through the programmer.
Finally, I’ve increased the local bulk capacitance from 1uF to 4.7uF to give the module a little more supply buffering.
The intermittent power problem I encountered during testing ultimately turned out to be a poorly seated jumper wire rather than a fault with the board itself, so increasing the capacitance isn’t intended as a fix for that particular problem. It does, however, provide a little more local energy storage and supply stability.
More importantly, the experience reinforced my desire to minimise temporary jumper-wire connections in future test setups.
The Isolated RS485 Carrier Board also works as expected.
The only remaining update I’m considering is exposing the SPI and I2C pins from the UCMOD connector through external connectors on the carrier board.
That would make the carrier considerably more useful as a general-purpose interface board, allowing whatever UCMOD module is installed to communicate with external hardware over I2C and SPI as well as RS485.
I’ll send new revisions of both boards for manufacture, but because the changes are relatively minor compared with the versions I’ve now tested successfully, there’s no immediate need to manufacture them. They can wait until I have several more designs ready to send together.
The next major job is the UCMOD-1, based around the STM32G0 family.
I’m also considering getting started on a CAN carrier board, which would give the UCMOD ecosystem another useful industrial communications option.
Finally, I want to create some boards for the Just Boards range that adapt existing development modules to the UCMOD interface, particularly the Raspberry Pi Pico 2 and ESP32 SuperMini.
They’re not especially complicated boards, but I think they’ll be useful. Plenty of people, myself included, already have those modules lying around, so providing a straightforward way to plug them into the UCMOD ecosystem makes sense.
As with the other modules, I’ll make the designs available through the website.
- 2026-10-17 Refining the Protoworks service offerings Lab
- 2026-09-27 Belfast Tech Week Lab
- 2026-09-19 Refining the UCMOD Architecture R&D
- 2026-09-06 Giving AI Agents a Way to Talk to Protoworks Software
- 2026-09-03 A Morning at a Manufacturing Trade Show Lab
- 2026-09-01 Debugging 921600 Baud on the ATtiny1614 R&D