ciaran55
@ciaranwalsh55 · Joined June 2025
Smart-city engineer · energy
- Topics
- 9
- Replies
- 68
- Points
- 0
Forum Replies Created
Screen blanking won’t do it. Classic Wi-Fi power saving on the Pi’s Broadcom chip, I had the same with a Pi 3B+ in a pump house. The Pi doesn’t sleep, the Wi-Fi does, and incoming packets get delayed or lost. Your paho client sends a ping every 60 s, the answer doesn’t come back in time, Hermetry sees nothing within the keepalive and closes the session, hence keepalive_timeout. Your SSH wakes the radio up, which is why everything comes back then.
Check it:
iw wlan0 get power_save
Power save: on
Turn it off for good on Bookworm with NetworkManager:
sudo tee /etc/NetworkManager/conf.d/wifi-powersave.conf <<EOF
[connection]
wifi.powersave = 2
EOF
sudo systemctl restart NetworkManager
(2 means disabled.) If you’re on an older image with dhcpcd, put iw wlan0 set power_save off in a script that runs after the network is up.
Also make your client reconnect on its own: client.reconnect_delay_set(1, 120) and loop_forever or loop_start, so a short drop fixes itself.
Or turn it around and have the Pi ask. Pull beats push when the receiver is behind NAT.
For real positions you’d want UWB anchors, BLE RSSI jumps around a lot. Pricey though.
Yeah my bad on the docker bit, I was mixing it up with a local Node-RED box we run. Good to know about the sales route for on-prem though.
That’s typical with those TT motors. No two run at the same speed for the same PWM, the L298N drops a couple of volts differently per channel, the floor and the wheel diameters differ, and all of it changes with battery voltage. A fixed trim only fits one battery level, as you saw. Swapping the motors and still drifting right points at something besides the motors too (wheel diameter, a caster, the floor).
Better motors won’t fix open loop. What fixes it is measuring the wheels: TT motors with encoder discs and slotted optical sensors cost a few pounds. Count ticks on both wheels and correct the PWM so the counts stay equal. A simple proportional correction is enough for a corridor:
from gpiozero import Motor, RotaryEncoder # or DigitalInputDevice per sensor
import time
left, right = Motor(17, 18), Motor(22, 23)
enc_l, enc_r = RotaryEncoder(5, 6), RotaryEncoder(13, 19)
base, kp = 0.7, 0.002
while not arrived():
err = enc_l.steps - enc_r.steps # +: left ahead
left.forward(max(0, min(1, base - kp * err)))
right.forward(max(0, min(1, base + kp * err)))
time.sleep(0.02)
If one cheap optical sensor per wheel (no direction) is all you have, count pulses with a DigitalInputDevice callback instead, direction is known anyway. For really straight runs, add an MPU6050 gyro and hold the heading. And if they’re rebuilding, swap the L298N for a TB6612FNG: much smaller voltage drop, so the battery lasts longer too.
While they’re at it, send the encoder counts per trip to Hermetry. A rising left/right difference on the same route is an early sign of a wheel or gearbox going.
Had nearly the same chat with a client’s IT last year. Pretty sure you can run it on your own box, there’s a docker image somewhere? Don’t quote me though, we’re on the cloud one.
Is the plant AP actually on channel 6? The mesh has to sit on the router’s channel when the root is a station, otherwise the radio keeps hopping and you get exactly that drop and rejoin.
David’s current sensor is a valid feedback source, but most contactors already have an auxiliary contact block, which is cheaper.
Wire an NO auxiliary contact to a PLC input (or use the current relay if the contactor can’t take an aux block). Then publish both values in the same message, every few seconds, not only on change:
{"sf1.cmd": 0, "sf1.fb": 1}
Alarm rule on the profile, in JSON mode since the form only does one comparison per severity:
"severities": [
{ "severity": "MAJOR",
"condition": { "any": [
{ "all": [ { "key": "sf1.cmd", "op": "==", "value": 0 }, { "key": "sf1.fb", "op": "==", "value": 1 } ] },
{ "all": [ { "key": "sf1.cmd", "op": "==", "value": 1 }, { "key": "sf1.fb", "op": "==", "value": 0 } ] } ] } }
],
"debounce": { "mode": "duration", "raise": 5, "clear": 5 }
That covers welded (off but running) and failed to pull in (on but stopped). The 5 s duration debounce covers the contactor’s pick-up and drop-out time. Duration debounce only moves forward when readings arrive, so the PLC has to keep reporting; with change-only reporting the alarm would wait for the next change.
Simpler alternative: let the PLC compute a discrepancy bit with its own timer (it needs it for the local interlock anyway) and alarm on sf1.discrepancy == true. Less JSON, one source of truth. Conditions are explained on Alarm conditions reference.

