Skip to the content
PCBKitNew board
2 layers71.1 × 38.1 mmMower, the printed circuit board in 3D

Some notes + hardware designs for my OpenMower Mowgli build

Size
71.1 × 38.1 mm
Copper layers
2
Thickness
1.6 mm
Parts
28
Nets
15
Pads
67
Tracks, vias
77, 0
Routed
Yes
KiCad
KiCad 9.0 (file 20241229)
Created
24 Oct 2025
Updated
10 Aug 2026

Overview

Mower is an open-source printed circuit board by ondras12345, from the ondras12345/mower repository on GitHub, published under the MIT licence. It is a 2-layer board of 71.1 × 38.1 mm with 28 parts on 15 nets, routed with 77 tracks and 0 vias.

It is built around the NID65-5, Π122M30 and P4SMAJ5.0A. Its parts include 13 connectors, 4 capacitors, 2 resistors, 2 ICs and 2 diodes.

Main components

3

README

README.md

My Mowgli build

This repository contains various notes about my Mowgli OpenMower build.

Hardware

Mowers

I bought two broken mowers:

  • Yardforce 500

    • This one was driving around, but the blade motor wasn't spinning.
    • The fault was caused by bad contact in one of the connectors on the ribbon cable between the mainboard and the display board.
    • The motor debug script and discord question were for this mower.
    • I continued the build with this one.
  • Yardforce 500 B

    • Drives along the cable, turns around and goes back to the dock.
    • I haven't investigated this one further yet.

Hardware modifications

USB connector
Connections
  • STM32 pins are 5V tolerant, that's why people are able to get away with connecting 5V IMUs
  • J18 pin 6 has continuity to J14 (USB) 5V, and no other 5V supplies

RPi pins

  • 1: mowgli debug 3V3 (to level shifter)
  • 2,4: 5v power in and 5v fan
  • 6: power in GND
  • 7: GPIO4 DS18B20 data
  • 9: DS18B20 GND
  • 12: GPIO18 fan PWM
  • 14: fan GND
  • 17: DS18B20 3v3
  • 25: GPS GND
  • 27: GPIO0 GPS TXD
  • 28: GPIO1 GPS RXD
  • 32: GPIO12 mowgli debug TXd
  • 33: GPIO13 mowgli debug RXd
  • 34: mowgli debug GND

System install instructions

  • ~/Downloads/NAS/completed/2025-05-13-raspios-bookworm-arm64-lite.img.xz
  • use raspi imager:
    • hostname openmower.local
    • username + pw
    • SSID: pISS ; WLAN country CZ
    • timezone: Europe/Prague
    • enable SSH; add key
    • it already did this: sudoedit /etc/ssh/sshd_config: PasswordAuthentication no
  • sudo apt update && sudo apt upgrade
  • sudo apt install tmux vim zsh git minicom etckeeper
  • sudo reboot

Docker

sh
# https://docs.docker.com/engine/install/debian/#install-using-the-repository
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
#sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt install -y docker-ce docker-compose-plugin

Set docker to also log through standard systemd facilities:

sh
sudo tee /etc/docker/daemon.json >/dev/null << "EOF"
{
  "log-driver": "journald"
}
EOF

sudo systemctl restart docker.service

Firewall

sh
sudo apt install ufw
sudo ufw allow ssh
sudo ufw enable


# 4006 = mowgli GUI, 9002 = xbot_remote
sudo tee /etc/ufw/applications.d/openmower >/dev/null << "EOF"
[openmower]
title=openmower
description=Some openmower services, others need to be listed in /etc/ufw/after.rules
ports=4006/tcp|9002/tcp
EOF

sudo ufw app update openmower

sudo ufw allow in to any app openmower from 192.168.1.4 comment "ondra-lab"
sudo ufw allow in to any app openmower from 192.168.1.5 comment "ondra mobil"
sudo ufw allow in to any app openmower from 192.168.1.55 comment "tablet"
sudo ufw allow in to any app openmower from 192.168.54.111 comment "wg ondra-TP"
sudo ufw allow in to any app openmower from 192.168.54.112 comment "wg ondra mobil"

Restrict traffic to docker containers not unit --network=host:

text
EDITOR=vi sudoedit /etc/ufw/after.rules
# ondra: filter traffic to docker containers
#-I DOCKER-USER -i wlan+ -j DROP
#-I DOCKER-USER -i wlan+ -s 192.168.1.5 -j RETURN
#-I DOCKER-USER -i wlan+ -s 192.168.1.4 -j RETURN
#-I DOCKER-USER -i wlan+ -s 192.168.1.55 -j RETURN
#-I DOCKER-USER -i wlan+ -s 192.168.1.114 -j RETURN
#-I DOCKER-USER -i wlan+ -s 192.168.54.111 -j RETURN
#-I DOCKER-USER -i wlan+ -s 192.168.54.112 -j RETURN

sudo ufw reload

Backup {{{3

sh
sudo apt install borgmatic
sudo mkdir /etc/systemd/system/borgmatic.timer.d/
sudo tee /etc/systemd/system/borgmatic.timer.d/atnight.conf >/dev/null <<"EOF"
[Timer]
OnCalendar=
OnCalendar=*-*-* 00:30:00
RandomizedDelaySec=0
EOF
sudo systemctl daemon-reload
sudo generate-borgmatic-config
sudoedit /etc/borgmatic/config.yaml
sudo tee /root/.ssh/config <<"EOF"
Host rpi-borgbackup
    User ondra
    Hostname rpi.lab.lan
    IdentityFile ~/.ssh/id_borgbackup_ed25519
EOF
sudo ssh-keygen -t ed25519 -f /root/.ssh/id_borgbackup_ed25519 -C "borgbackup mower"
sudo borgmatic init --encryption repokey-blake2
sudo systemctl edit borgmatic.service
#[Unit]
#OnFailure=notify-email@%i.service
#
#[Service]
#TimeoutStartSec=6h

Mowgli

sh
sudo tee /etc/udev/rules.d/50-mowgli.rules >/dev/null <<"EOF"
SUBSYSTEM=="tty" ATTRS{product}=="Mowgli", SYMLINK+="mowgli"
# simpleRTK USB
SUBSYSTEM=="tty" ATTRS{idVendor}=="1546" ATTRS{idProduct}=="01a9", SYMLINK+="gps"
# ESP USB CDC - RTK1010Board
SUBSYSTEM=="tty" ATTRS{idVendor}=="303a" ATTRS{idProduct}=="4001", SYMLINK+="gps"

# ondra
# Mowgli debug
SUBSYSTEM=="tty" KERNEL=="ttyAMA5", SYMLINK+="mowgli_debug"

# WTRTK-960 GPS - uses generic CH340 :-(
SUBSYSTEM=="tty" ATTRS{idVendor}=="1a86" ATTRS{idProduct}=="7523", SYMLINK+="gps"
# same GPS, but via GPIO UART
SUBSYSTEM=="tty" KERNEL=="ttyAMA2", SYMLINK+="gps2"
EOF

cd
git clone https://github.com/cedbossneo/mowgli-docker  # 0ab26b9
cd mowgli-docker
sudo docker compose pull
vi .env  # set both IPs to 127.0.0.1
sudo docker compose up -d

# see mowgli gui @ port 4006
# openmower web @ port 4005

Back up mainboard firmware via stlink:

sh
sudo apt install openocd
lsusb | grep ST-LINK  # note the Bus xxx Device yyy
sudo chmod 666 /dev/bus/usb/001/003  # xxx/yyy

mkdir ~/firmware_backup
cd ~/firmware_backup
tee yardforce500.cfg >/dev/null <<"EOF"
source [find interface/stlink.cfg]
source [find target/stm32f1x.cfg]

transport select hla_swd
#reset_config srst_only srst_nogate


#program "firmware.bin" 0x08000000 verify reset; shutdown;
EOF

openocd -f yardforce500.cfg -c "init" -c "reset halt"   -c " dump_image firmware_backup.bin 0x08000000 0x40000" -c "reset" -c shutdown

# connect ST-LINK to display panel
# Disconnect ribbon cable from main unit, power the target from ST-LINK.
# is seems to have a fake stm32, -c set CPUTAPID 0x2ba01477 is needed
# Not sure what ram backup is for. It is different each time.
openocd -c "set CPUTAPID 0x2ba01477" -f yardforce500.cfg -c "init" -c "reset halt"   -c " dump_image panel_controller_ram.bin 0x20000000 0x10000" -c "reset" -c shutdown
openocd -c "set CPUTAPID 0x2ba01477" -f yardforce500.cfg -c "init" -c "reset halt" -c " dump_image panel_controller_backup.bin 0x08000000 0x10000" -c "reset" -c shutdown

Firmware version:

text
$ sha256sum firmware_backup.bin panel_controller_backup.bin
875bedb32ccb62ee23453581c8af13e61a523bfe758120564d6bdb27cf641998  firmware_backup.bin
b3b1142bf84e09fd506bf4b399632b60d2a6ae3a520c685d3fdbca08e2c1fb88  panel_controller_backup.bin

The upload tool does not work, build firmware manually:

sh
cd
curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
python3 get-platformio.py
mkdir ~/bin
cd ~/bin
ln -s ~/.platformio/penv/bin/pio .
# start a new shell

cd
git clone https://github.com/cedbossneo/mowgli
cd ~/mowgli/stm32/ros_usbnode
pio run -e Yardforce500

lsusb | grep ST-LINK  # note the Bus xxx Device yyy
sudo chmod 666 /dev/bus/usb/001/003  # xxx/yyy

pio run -e Yardforce500 -t upload

Debug UART setup

sh
sudoedit /boot/firmware/config.txt
# # Mowgli debug UART
# dtoverlay=uart5
# # GPS UART
# dtoverlay=uart2
minicom -D /dev/ttyAMA5
# see udev rules above
minicom -D /dev/mowgli_debug

onewire DS18B20

sh
sudoedit /boot/firmware/config.txt
# # onewire temperature sensor
# dtoverlay=w1-gpio

# set resolution
echo 11 | sudo tee /sys/bus/w1/devices/28-000000108239/w1_slave
echo 0 | sudo tee /sys/bus/w1/devices/28-000000108239/w1_slave
# read temperature
cat /sys/bus/w1/devices/28-000000108239/temperature

Disable unnecessary HW to reduce power consumption

sh
sudoedit /boot/firmware/config.txt
# [all]
# dtoverlay=disable-bt
# dtparam=audio=off
# hdmi_blanking=1
# hdmi_ignore_hotplug=1
# # comment out dtoverlay=vc4-kms-v3d
# [pi4]
# dtparam=pwr_led_trigger=none
# dtparam=pwr_led_activelow=off

Fan

sh
# test
sudo gpioset gpiochip0 18=1  # fan on
# sudo raspi-config
# 4 Performance options > Fan > Yes, 18, 75degC

Configure gps

  • WT-RTK960
  • I measured 3.3V DC on the coax => it does send phantom power the antenna
sh
python -m serial.tools.miniterm /dev/gps 115200 -e

The UART on the bottom row of pins and USB are both COM1. The UART on the top row of pins (/dev/gsp2) is COM2.

sh
FRESET
CONFIG COM1 921600
CONFIG COM2 115200
CONFIG COM3 115200
MODE ROVER SURVEY MOW
GPGGA COM1 0.1
GPGSV COM1 2
GPRMC COM1 1
GPGSA COM1 1
GPVTG COM1 1
GPGST COM1 1
GPGGA COM2 1
GNGSV COM2 1
GNGSA COM2 1
GNRMC COM2 1
JAMSTATUSA COM2 1
FREQJAMSTATUSA COM2 1
BESTNAVA COM2 1
ADRNAVA COM2 1
RTCMSTATUSA COM2 ONCHANGED
SAVECONFIG

Here they recommend GPGGA 0.2 and GPGSV 2: https://openmower.de/latest/docs/step-by-step/2-robot-modification/prepare-the-parts/prepare-the-gps/ They seem to recommend MODE ROVER UAV, but let's try MODE ROVER SURVEY MOW instead.

Let's try CONFIG RTK RELIABILITY 3 1. It confidently drove ~1m too far. Let's try CONFIG RTK RELIABILITY 3 2. Drove into the ditch. Let's try CONFIG RTK RELIABILITY 4 2. Why is the default CONFIG RTK RELIABILITY 10 1? That is undocumented.

BESTNAV contains diff_age (age of RTK data from base station)

CONFIG dump before enabling COM2 messages (old, I changed stuff since then):

text
CONFIG
$command,CONFIG,response: OK*54
$CONFIG,ANTENNA,CONFIG ANTENNA POWERON*7A
$CONFIG,NMEAVERSION,CONFIG NMEAVERSION V410*47
$CONFIG,SCENE,CONFIG SCENE DISABLE*70
$CONFIG,RTK,CONFIG RTK TIMEOUT 40 20*79
$CONFIG,RTK,CONFIG RTK RELIABILITY 10 1*44
$CONFIG,RTK,CONFIG RTK MMPL 1*29
$CONFIG,BASE2,CONFIG BASE2 DISABLE*70
$CONFIG,BESTPARA,CONFIG BESTPARA 0 16.00 10.00 10.00 0.00 3.00 0 0 40*3A
$CONFIG,BESTPARA,CONFIG BESTPARA 1 -1.00 4.40 0.00 0.00 6 20 20 0*3F
$CONFIG,FITBESTPOS,CONFIG FITBESTPOS 1 20 3 100 1 1 0*25
$CONFIG,SCENEREG,CONFIG SCENEREG DISABLE*70
$CONFIG,SCENEREGPARA,CONFIG SCENEREGPARA 1.80 40.00 38.00 36.50 51652382 1264872960*3D
$CONFIG,MVTHD,CONFIG MVTHD 20 7.840 6.250 0.250 85 120 2 65*2A
$CONFIG,DGPS,CONFIG DGPS TIMEOUT 300*6C
$CONFIG,RTCMB1CB2A,CONFIG RTCMB1CB2A ENABLE*25
$CONFIG,ANTENNADELTAHEN,CONFIG ANTENNADELTAHEN 0.0000 0.0000 0.0000*3A
$CONFIG,RTK,CONFIG RTK MODE 0 0*27
$CONFIG,RTK,CONFIG RTK CN0THD 0*51
$CONFIG,CHECK,CONFIG CHECK 0 0.000 0.000 0.000 0.000 0 0.000*0A
$CONFIG,PPS,CONFIG PPS ENABLE GPS POSITIVE 500000 1000 0 0*6E
$CONFIG,SIGNALGROUP,CONFIG SIGNALGROUP 2*16
$CONFIG,ANTIJAM,CONFIG ANTIJAM AUTO*2B
text
#VERSION,62,GPS,FINE,2426,121153300,16528,0,18,92;UM960,R4.10Build16528,HRPT00-S10-C-P,2310415000017-MH23C6251910901,ff3b3d643ae0ff0d,2024/10/14*7233682d

Set up gpsd

sh
sudo apt install ser2net
sudoedit /etc/ser2net.yaml
sudo systemctl restart ser2net
sudo apt install netcat-openbsd
# nc -C localhost 5015  # raw connection to gps2 (both rx and tx; gpsd.service can be left running)
sudo apt install gpsd
sudoedit /etc/default/gpsd
# DEVICES="tcp://127.0.0.1:5015"
# GPSD_OPTIONS="-n -b"
GPS antenna offset

Discord c.ez:

set export OM_ANTENNA_OFFSET_X=1.234 export OM_ANTENNA_OFFSET_Y=1.234 in your mower config file. (0/0) is the point between the wheels of your mower, X is to the front and Y is to the left, unit is m. So if your antenna is 10cm to the front relative to the origin, and centered left/right, then you would put 0.1 and 0.0

Setting

sh
export OM_ANTENNA_OFFSET_X=0.290
export OM_ANTENNA_OFFSET_Y=-0.030
GPS base station

TODO rtkbase sucks, just use str2str as packaged by debian. TODO set base cable length 10m (for PPS timing absolute accuracy)

Rear panel getty

sh
sudo tee /etc/udev/rules.d/50-rear-panel-getty.rules >/dev/null <<"EOF"
SUBSYSTEM=="tty", ATTRS{busnum}=="1", ATTRS{devpath}=="1.1", SYMLINK+="rear_panel_tty", TAG+="systemd", ENV{SYSTEMD_WANTS}+="serial-getty@rear_panel_tty.service"
EOF
sudo udevadm control --reload-rules
sudo EDITOR=vi systemctl edit serial-getty@rear_panel_tty.service
# [Service]
# ExecStart=
# ExecStart=-/sbin/agetty -o '-p -- \\u' - 115200 $TERM
# Restart=always
# RestartSec=1
sudo systemctl enable serial-getty@rear_panel_tty.service

Dragonflymail

sh
sudo apt install dma mailutils
sudoedit /etc/dma/dma.conf

sudo tee -a /etc/aliases >/dev/null << "EOF"
# catch-all alias, but only for local users
*: root
root: REDACTED
EOF
sudo newaliases


sudo tee "/etc/systemd/system/notify-email@.service" >/dev/null <<"EOF"
[Unit]
Description=Send email to root

[Service]
Type=oneshot
ExecStart=sh -c 'systemctl status %i | mail -s "[mower SYSTEMD] Failed service: %i" root'
# workaround:
# https://serverfault.com/questions/982318/mail-stuck-in-exim-queue-for-no-reason
# https://systemd-devel.freedesktop.narkive.com/nV1QMO8j/exim4-only-queues-mails-sent-by-systemd-service
KillMode=process
EOF
sudo systemctl daemon-reload

Usage:

text
[Unit]
OnFailure=notify-email@%i.service

Disable SD card swap

The SD card is extremely slow. If we ever need more mem, zram would be a better choice.

sh
sudo systemctl disable --now dphys-swapfile.service
sudo rm /var/swap

Log to influx

sh
# installed in ~/rtkrover_influx1
sudo tee /etc/systemd/system/rtkrover_influx1.service >/dev/null <<"EOF"
[Unit]
Description=Log GPS and system stats to influxdb
After=ser2net.service
Wants=ser2net.service
OnFailure=notify-email@%i.service

[Service]
Restart=on-failure
RestartSec=30
User=openmower
Environment="INFLUX_DSN=influxdb://mowgli1:REDACTED@192.168.1.110:8086/rtkrover"
WorkingDirectory=/home/openmower/rtkrover_influx1/
ExecStart=/home/openmower/rtkrover_influx1/gps_log.py

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now rtkrover_influx1.service

MISC notes

ROS debug

config/om/rosconsole.config: set log4j.threshold=INFO (was OFF). Now it logs both to docker compose logs and rostopic echo /rosout (with details like line numbers).

Parts

28
QtyValueFootprintReferences
13Conn_01x01Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD1.7mm_ReliefJ1, J2, J3, J4, J5, J6, J7, J8, J9, J10, J11, J12, J13
4MountingHoleMountingHole:MountingHole_3.2mm_M3H1, H2, H3, H4
2100nCapacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolderC1, C3
21kResistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolderR1, R2
122u / 25V X5RCapacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolderC4
1EKM227M1HG1BRRSHPCapacitor_THT:CP_Radial_D10.0mm_P5.00mmC2
1NID65-5ondra:Converter_DCDC_MeanWell_NID65_THTU2
1P4SMAJ5.0ADiode_SMD:D_SMA_HandsolderingD1
1PTF/15Fuse:Fuseholder_Cylinder-5x20mm_Stelvio-Kontek_PTF78_Horizontal_OpenF1
1SMAJ28A-13-FDiode_SMD:D_SMA_HandsolderingD2
1Π122M30Package_SO:SOIC-8_3.9x4.9mm_P1.27mmU1

Questions

How big is the Mower PCB?

It measures 71.1 × 38.1 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Mower use?

28 parts, built around the NID65-5, Π122M30 and P4SMAJ5.0A. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Mower KiCad files?

From the ondras12345/mower repository on GitHub, at the commit this page was made from: the board is hardware/mower/mower.kicad_pcb and its schematic hardware/mower/mower.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Mower design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers54.6 × 49.5 mmMulticdc, the printed circuit board in 3D

    Multicdc

    ondras12345/multicdc

    RP2040-based 6-channel USB to UART converter with Zephyr RTOS

    35 parts1MIT
  • 6 layers60 × 55 mmPoE Ethernet GPIB Adapter, the printed circuit board in 3D

    PoE Ethernet GPIB Adapter

    Kofen/PoE_Ethernet_GPIB_Adapter

    This folder contains the necessary hardware files for the PoE Ethernet GPIB Adapter project.

    198 parts120GPL-3.0
  • 2 layers39 × 58.5 mmAtari SIO2SD Mini, the printed circuit board in 3D

    Atari SIO2SD Mini

    RichardRozehnal/Atari-SIO2SD-Mini

    Atari SIO2SD disk drive emulator for 8bit Atari.

    41 parts2GPL-3.0
  • 4 layers115 × 76.3 mmApm, the printed circuit board in 3D

    Apm

    TeHSiGGi/agp-power-monitor

    Power minitoring utility for AGP graphics cards

    119 parts2GPL-3.0
  • 2 layers44.5 × 72.7 mmOSO SWAT C1-jig, the printed circuit board in 3D

    OSO SWAT C1-jig

    joeycastillo/Sensor-Watch

    A board replacement for the classic Casio F-91W wristwatch

    28 parts2.1k
  • 2 layers95 × 54 mmEinkPDA screen, the printed circuit board in 3D

    EinkPDA screen

    TalismanDesign/PocketMage_PDA

    An E-Ink PDA device using the ESP32 S3.

    28 parts2kApache-2.0
  • 2 layers45 × 45 mmSmartbuttons, the printed circuit board in 3D

    Smartbuttons

    hackclub/OnBoard

    💡 Join 1,000 teenagers and make your first PCB with a $100 grant!

    28 parts915