thompsonplc
@lthompson · Joined May 2025
Automation engineer · pharma · 14 years in the field
- Topics
- 7
- Replies
- 65
- Points
- 0
Forum Replies Created
Both right. I had to explain this to our own purchasing last year, so the longer version:
The price stacks up from: smaller volumes than Wi-Fi/BLE parts, a pre-certified module (FCC/CE modular approval is a big cost you’d otherwise carry per product), the Zigbee stack and the vendor’s firmware/tools, and the alliance membership and certification for a product that wants the logo. A bare EFR32MG or CC2652 module is maybe $5-8, but then the RF certification and the stack integration are yours.
Then there’s the path into Hermetry. Zigbee devices don’t connect by themselves; you need a coordinator, and something on site that reports for the sensors. In Hermetry terms that’s a gateway publishing on the v1/gw/* topics so each sensor becomes a child device. That’s another box and another piece of software to own.
For pump stations specifically I’d at least price LoRaWAN sensors: Hermetry runs its own LoRaWAN network server and the frequency plan follows your gateway, so in the US you’d buy US915 gateway and sensors. Sensors run years on a battery, and one gateway covers a lot of ground. If LoRaWAN doesn’t fit the sites, wired 4-20 mA into a gateway is the other realistic choice.
Good. Keep the operator’s approval email with the design file, the next hardware revision will need it again.
US work is mostly the ANSI/NFPA-79 habits (TB1-3 style), Europe is IEC. Since your customers are UK/IE, go IEC.
Module certification lowers what the end product has to repeat, it doesn’t replace it. Ask the operator’s IoT sales team what they require for a device on their network. Some just want the module’s approval and a registration of your device, some want lab testing on the finished product.
0x3F. Changed the constructor and it reads “dP: 12.4 Pa” on the first line. Going with a BSS138 level shifter board for the final build as you suggest. Bit embarrassed I didn’t scan first.
Yes, turned it end to end. Blocks get lighter and darker, no text.
Custom topics are inbound only, they become telemetry of the device that published. And devices never get each other’s messages in Hermetry, so a sensor publishing for a fan will never work.
Also from 14 years of plant work: you want the actuator to answer. Fire and forget to a relay is how you end up with a fan that shows on in the control room and is off in the room.
Aisha, the single low-side MOSFET has a catch I’ve been bitten by. When it’s off, the bottom of the divider floats, and the top resistor pulls the ADC pin up to the full 12 V through 100k. That’s above the ESP32’s 3.3 V limit; the pin’s protection diode clamps it and current leaks into the 3.3 V rail. So you save the divider current and stress the pin instead.
The two-transistor version switches the top: a P-channel (AO3401 or similar) between the battery and the divider, gate pulled up to the battery with 100k so it’s off by default. A small N-channel (2N7000) driven by a GPIO pulls that gate down to switch it on. You need the N-channel because a 3.3 V GPIO can’t pull the gate of a P-channel sitting at 12 V high enough to turn it off. With the top switched off, the whole divider sits at 0 V, the ADC pin too.
In the firmware: switch on, wait about 10 ms, take 16 readings and average them, switch off. Use the calibrated reading (analogReadMilliVolts) and send battery_v as a number with two decimals.
For the flapping: a lead-acid battery’s voltage jumps when charging starts, so a plain threshold will always bounce. Give the rule hysteresis so it only clears well above the threshold, and two severities:
{
"type": "battery_low",
"title": "{{device.name}}: battery {{value}} V",
"severities": [
{ "severity": "MAJOR", "condition": { "key": "battery_v", "op": "<", "value": 11.5 } },
{ "severity": "MINOR", "condition": { "key": "battery_v", "op": "<", "value": 11.8 } }
],
"hysteresis": 0.4,
"debounce": { "mode": "count", "raise": 2, "clear": 2 },
"notify": { "recipients": [ { "kind": "email", "email": "field-team@example.com" } ], "minSeverity": "MINOR" },
"enabled": true
}
It opens below 11.8 and clears only at 12.2 or above, two readings in a row. Kevin’s idea would hide the problem, not fix it. Hysteresis and debounce are explained on this page.
Short helps, but 17 dB is not “short trace” territory, that’s a broken RF path. Did this on a sensor board for a cleanroom monitor and lost about the same.
Questions to check: is there solid ground under the trace on the other layer? Is the trace 50 ohm? On 1.6 mm FR4 a 50 ohm microstrip is around 3 mm wide, so a 0.25 mm signal trace is closer to 120 ohm. Over 6 mm that alone isn’t the whole 17 dB, but add no ground plane under it and the trace is half an antenna radiating into your own board.
What I’d do on the next revision: coplanar waveguide with ground on both sides and a row of vias, width from a calculator for your stackup, a pi network footprint between module and connector (0 ohm in series, others not fitted until you can measure), and the U.FL ground pads stitched straight to the plane. Keep the switching regulator away from that corner.
Then compare again in the frames log, it shows the best RSSI and SNR per uplink so you can check both boards on the same gateway without extra tools. See The frames log.
Bus 0 is reserved for the HAT ID EEPROM on pins 27/28. Bus 1 is the user one. Aoife, when you say “GPIO 3”, do you mean physical pin 3 or BCM GPIO 3?
Are you sending one line per sample, so 200 radio packets a second?
Rules card. I run lab ventilation schedules this way.
First give the Pi’s profile a command, e.g. heaters.set with a Yes/no param on, Basic tier. On the Pi:
from rpi_rf import RFDevice
tx = RFDevice(17); tx.enable_tx()
CODES = {True: [5592405, 5592407, 5592411, 5592413],
False: [5592404, 5592406, 5592410, 5592412]}
def on_rpc(req_id, body):
if body.get("method") == "heaters.set":
for code in CODES[bool(body["params"]["on"])]:
tx.tx_code(code, 1, 350) # protocol 1, pulse length from the sniff
client.publish(f"v1/rpc/res/{req_id}", '{"ok": true}', qos=1)
(Your codes and pulse length come from the sniff, mine are placeholders.)
Then on the terrace asset’s Rules card:
- Rule “Heaters on”: When Sunrise or sunset, Sunset, offset -15. Then Send a command
heaters.seton = yes to the Pi. - Rule “Heaters off”: When A time of day 23:00, every day. Then the same with on = no.
Sunset needs the place’s location, so put latitude/longitude on the terrace asset and place the Pi under it, otherwise the rule won’t save. Also, 433 MHz has no feedback, so send the command twice a minute apart if you want to be sure. Doc: Time, sunrise/sunset, dates and holidays.
Using the shield as your signal reference is the main problem. Noise current from the drives flows in it and straight into your receiver’s ground.
Close, but it’s the other way round, and it trips everyone. The names are from the Pi’s point of view: MOSI is the Pi sending to the chip, MISO is the chip sending to the Pi. DO on your breakout means “data out of the MAX31855”, so DO goes to MISO, GPIO 9, pin 21.
The MAX31855 never receives anything, you just pull CS low and clock out 32 bits, so it has no input pin at all. Leave MOSI (pin 19) unconnected. Wiring:
VIN -> 3.3 V (pin 1)
GND -> GND (pin 6)
DO -> GPIO 9 MISO (pin 21)
CLK -> GPIO 11 SCLK (pin 23)
CS -> GPIO 8 CE0 (pin 24)
Enable SPI in raspi-config, then read 4 bytes with spidev at 1 MHz or below. Bits 31..18 are the thermocouple temperature in 0.25 C steps, bit 16 is the fault flag. Check that flag before you send anything, an open thermocouple otherwise shows up as a strange number on your chart.
RS-485, Modbus RTU, one 4-core cable. I’ve wired a few greenhouses like this.
Cable: a shielded twisted pair cable with two pairs, for example a 2x2x0.5 mm². One pair for A/B, the other pair for power: 12 V and 0 V. The 0 V also serves as the RS-485 common. At 80 m and 12 Nanos drawing maybe 40 mA each through a small buck converter, the voltage drop on 0.5 mm² is under a volt, fine for 12 V in.
Topology: a daisy chain along the benches, with short stubs (under 30 cm) to each node. No star. 120 Ω termination at both ends of the main run, bias resistors only once, at the ESP32 end. Each Nano gets a MAX485 (or better, an auto-direction module so you save a pin).
Protocol: each Nano is a Modbus RTU slave with its own address 1-12 (the ModbusRTUSlave library is fine), holding registers for moisture, temp x10, RH x10, lux. The ESP32 is master, polls every node every 30 s at 19200, and publishes:
v1/gw/connect {"device": "bench-03", "type": "greenhouse-node"}
v1/gw/telemetry {"bench-03": {"ts": 1767225600000, "values": {"soil": 41, "t": 23.4, "rh": 71.2, "lux": 8200}},
"bench-04": {"ts": 1767225600000, "values": {"soil": 38, "t": 23.1, "rh": 70.8, "lux": 7900}}}
One gateway message for all twelve, and megan_b40 is right about ts, only if the ESP32 has NTP. Make a device profile named greenhouse-node and every child lands in it with the same alarms.
If a node stops answering, publish v1/gw/disconnect for it so it shows offline instead of silently going stale.

