lewisfw
@lewis-e · Joined March 2026
Embedded developer · energy · 14 years in the field
- Topics
- 1
- Replies
- 37
- Points
- 0
Forum Replies Created
Volume is most of it. Wi-Fi chips ship in billions, Zigbee in a fraction of that.
Partly right on the radio half. Contiki-NG does have IPv6 multicast for RPL networks: two engines, SMRF and MPL. You turn one on at build time in the nodes’ project-conf.h and in the border router’s:
/* every node and the border router */
#define UIP_MCAST6_CONF_ENGINE UIP_MCAST6_ENGINE_MPL
/* for SMRF instead you also need storing mode with multicast: */
/* #define UIP_MCAST6_CONF_ENGINE UIP_MCAST6_ENGINE_SMRF */
/* #define RPL_CONF_MOP RPL_MOP_STORING_MULTICAST */
Where he’s right is that the stock rpl-border-router does not forward multicast arriving from the outside network into the mesh. So the multicast has to originate on the border router itself: a small process on that box that, on a command from Hermetry, sends one MPL datagram to a group the nodes joined. It means reflashing all 30 nodes with multicast enabled, which on a cold store install is a site visit.
Honestly, for 30 nodes I’d skip multicast. Have the border router loop over the 30 addresses and send unicast with a retry. 30 small packets on a mesh is nothing, it finishes in seconds, and every node acks so you know who missed it. No reflash.
Then in Hermetry: add a command like sampling.set with a period_s parameter on the gateway’s profile, answer it from that process once all nodes acked, and put two buttons on the QA dashboard. Have the nodes report their current period as a client attribute so QA can see it took.
Once every 5 minutes from one box? No. And reads don’t count toward the message quota, that’s telemetry and attributes the devices send.
Right. Keep it in RTC memory across resets, or send a boot counter as well and let the chart handle the drop. Simplest is RTC memory.
It’s not your topic. Hermetry only lets a device subscribe to its own downlink topics: v1/attributes/set, v1/attributes/res/+, v1/rpc/req/+, v1/rpc/up/res/+ and v1/cert (plus the gw ones for gateways). A device never gets another device’s messages and never sees its own uplinks. The table is on MQTT topics and payload reference. Bit me too on my first project.
For 1 you don’t need an echo at all. The PUBACK is the confirmation: Hermetry only sends it once the message is safely queued, and in the order you published. Keep the reading in your buffer until its PUBACK arrives, drop it then. If the session dies before that, send it again after reconnect, duplicates with the same key and timestamp are stored once anyway.
For 2 the display shouldn’t be listening on MQTT at all. Give it an API key and let it read the latest value over REST every few minutes:
GET https://<your host>/tenant/<tenantId>/api/telemetry/latest?kind=iot_device&entityId=<cabinet3 id>&keys=kwh
Authorization: Bearer sk_live_...
Make the key read only (telemetry read is enough), put the depot’s public address in the key’s IP allowlist, and note the expiry: 90 days by default, a year at most, so put a reminder in for the rotation. Then you can delete the display’s device too, it doesn’t need one.
Had this exact situation with meter boards a contractor did. You can ask the running firmware: the partition API in ESP-IDF works fine from the Arduino core. Push one build to the boards you can reach (the ones from your own batch), or wait until the others are at the depot, that reports the table as client attributes once after connecting:
#include "esp_partition.h"
void reportPartitions() {
String json = "{";
esp_partition_iterator_t it =
esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
bool first = true;
while (it != NULL) {
const esp_partition_t *p = esp_partition_get(it);
if (!first) json += ",";
json += ""part." + String(p->label) + "":"" +
String(p->address, HEX) + "/" + String(p->size) + """;
first = false;
it = esp_partition_next(it);
}
esp_partition_iterator_release(it);
json += "}";
mqtt.publish("v1/attributes", json.c_str());
}
Then the Attributes tab, Client scope, shows part.app0, part.app1, part.otadata and so on per board. What you want to see is two app slots (ota_0 and ota_1, called app0/app1 in the Arduino tables) plus otadata, each at least as big as your image. If you see one app0 of 3 MB and no app1, that’s the “Huge APP” scheme, no OTA possible.
For the boards you can’t get a new build on, emily’s way works at the depot:
esptool.py --port /dev/ttyUSB0 read_flash 0x8000 0xc00 pt.bin
python gen_esp32part.py pt.bin
Boards with the wrong scheme need one USB reflash with a two-slot table. After that OTA works for good.
Is anything else enabled in config.txt? enable_uart, other overlays? Paste the whole file.
That’s a counterbalance valve (CBV = counterbalance valve). The check valve lets oil flow freely into the cylinder to lift. Coming down, oil has to push past a relief spring, and the dashed pilot line from the other side helps open it, so the load can’t run away or drop if a hose fails. The arrow across the spring means the setting is adjustable. On a lift table it’s the part that stops the platform falling, so it’s worth showing clearly.
On the drawing: the core symbol set has a check valve in the water network family, it’s in every organisation with the SCADA module. I’d place that, label it “CBV-1 counterbalance” with a text, and you’re honest about what it is. The palette search matches names and common other names, so search “check” rather than the part number.
If you want a proper hydraulic/process valve symbol, the Process library covers pumps and valves and ISA instruments. You can browse it before buying: symbols of a locked library show in the palette with a padlock and a still picture. Just see whether one fits before paying. Details: Symbols and industry packs.
Worth putting your pressure transmitter between the CBV and the cylinder on the drawing too, that’s where a dropping load would show first.
Owen has it. The press is shorter than your loop, so polling misses it. Use an interrupt so the count happens the moment the pin falls, and do the debounce by time inside it. I’ve had pulse counters on gas meters for years done like this:
volatile uint32_t rejects = 0; // since boot
volatile uint32_t lastEdgeUs = 0;
void IRAM_ATTR onPress() {
uint32_t now = micros();
if (now - lastEdgeUs > 30000) { // ignore bounce within 30 ms
rejects++;
}
lastEdgeUs = now;
}
void setup() {
pinMode(D5, INPUT_PULLUP); // button to GND
attachInterrupt(digitalPinToInterrupt(D5), onPress, FALLING);
}
Remove the delay(50) from loop(). In the publish, copy the counter with interrupts briefly off.
Hardware: on a factory floor the button cable is often metres long next to motor cables. Add a 1 kΩ series resistor and 100 nF at the ESP pin, or better, run the button at 24 V into an optocoupler. Otherwise you’ll start counting phantom rejects.
Send a cumulative counter, not “rejects this minute”: {"rejects.total": 1532}. If a message is lost, the next one still has the right total, and a chart with the increase aggregate gives you rejects per hour.
Kevin’s right that HC-12 is the easy way. With bare modules, the piece most people miss is that the Si446x chips don’t have a register map you just poke. You load a configuration blob that Silicon Labs’ WDS tool generates (radio_config.h) for your frequency, modulation and data rate, and send it to the chip at startup.
The library that works for me is RadioHead, driver RH_RF24, which supports Si4460/4461/4463. It ships with a set of ready configurations, so you don’t need WDS unless you want something unusual. Minimal sender:
#include <SPI.h>
#include <RH_RF24.h>
RH_RF24 rf24(10, 2, 9); // CS, nIRQ, SDN
void setup() {
Serial.begin(9600);
if (!rf24.init()) Serial.println("init failed");
rf24.setFrequency(433.92);
rf24.setTxPower(0x4f);
}
void loop() {
uint8_t msg[] = "lvl=182";
rf24.send(msg, sizeof(msg));
rf24.waitPacketSent();
delay(60000);
}
Two traps. SDN is active high, so if you leave it floating the chip sits in shutdown and init fails. And these modules run at 3.3V: a 5V Pro Mini needs level shifting on SPI, or use the 3.3V/8 MHz Pro Mini, which is also better for battery life.
On the Pi end, read the packet and publish lvl as a key the same way you do for your other devices.
Makes sense. Measured it: GND2 to GND2 drifted around 0.6 V on the bench, so plenty of margin there and none to count on outdoors. Ordered cable with an extra pair, reference through 100 ohm at each end, shield earthed at the gateway end. Thanks Ciaran, and Tyler for the supply capacitance hint.
120 ohm at both ends, bias on one end only (the master).
Megan’s right. What I’d do:
1. Keep sending co2, temperature and motion as numbers (motion as 0/1, not true/false, so it has a history).
2. Pull the raw data out with the history API, one day per call is comfortable:
curl -s -H "Authorization: Bearer $KEY" "https://$HOST/tenant/$TENANT/api/telemetry/series?kind=iot_device&entityId=$ROOM&keys=co2,temperature,motion&from=$FROM&to=$TO&bucket=raw"
If a key comes back with 10,000 points, ask again from the last ts + 1.
3. Join it with the front desk export on a laptop, train a logistic regression or a 3-input, 8-neuron net in Python. That is plenty for CO2 slope + motion count.
4. Quantise the weights to int8, paste them as a const int8_t w[] PROGMEM array, and the Uno only runs the forward pass. Send the result as occupied_p (0-100) alongside the other keys.
Honestly, for occupancy the CO2 rise over 20 minutes plus “any motion in the last 30 min” gets you most of the way before any neural net. Start with that as a baseline so you know if the model is worth it. API side is here: The history API.
Megan’s right. The Personnel tracking page calls Hermetry Safe the dedicated worker-safety product but doesn’t say what it does, so I wouldn’t promise anything from it. The Personnel tracking ready solution is what I’d use, and room-level is exactly what it’s built for. I set it up for a pumping station last year.
The way zones work decides your hardware. Hermetry does not work out zones from positions or radio signals, and it has no geofence polygons. The tracker, or the gateway that hears it, decides which zone the person is in and sends the zone name in the key zone. So the zone logic lives in your hardware: a small BLE transmitter fixed in each room, the worker’s tracker reports the strongest one it hears, and the tracker or the gateway maps that to a zone name like switchgear-2.
The solution’s tracker profile then gives you the rest:
keys: lat, lng, zone, event, battery, air_temp, light
event: normal | motion | fall | motionless | sos | press_once
alarms: fall, motionless, sos (Critical), help_call,
forbidden_zone (zone == the tracker's forbiddenZone attribute),
battery_low, device_offline (15 min)
The event words must be exactly those, lower case. If your tracker sends something like FALL, translate it in the profile’s root flow. Each tracker gets a server attribute forbiddenZone with one room name, so set it to the switchgear room for everyone without a permit. Only one forbidden zone per tracker out of the box, a second needs an extra rule.
For the map, use the Map card in image mode with the building’s floor plan, positions in pixels. Full description: Personnel tracking.
It hides the destination from someone watching the depot line, yes, Rahul. But the destination was never the secret, and Tor costs you real things on the Hermetry side.
What actually protects you today: the MQTT session is TLS 1.2 minimum with forward-secret suites, so contents and the device token aren’t readable on the wire. Someone sniffing the depot uplink learns there’s a device talking to a broker every N seconds. Tor hides the broker name and adds a lot of latency and connection churn.
Now the costs. Every Tor circuit exits from a different address. Hermetry sends your admins a notice when a device logs in from a new network (at most once an hour per device), so 9 nodes hopping exits means a steady stream of those, and you train people to ignore exactly the warning that would tell them a token was stolen. You also lose the profile’s Allowed networks option, which is one of the best controls you have for fixed-site devices: list the depot’s public address and a stolen token used from anywhere else gets CONNACK 5. Plus repeated failed logins block an address for every device behind it, and on shared exits that’s not under your control.
If the boss wants more, I’d do this instead, all on the device profile:
- Allowed networks: the depot’s public IP
- TLS only, Token in the URL off, Commands allowed without TLS empty
- move from access tokens to X.509 if your firmware can, otherwise MQTT basic, one credential per device
- lower the profile Limits to what the nodes really send so a hijacked one gets cut off fast
And put the ESP32s on their own VLAN so a compromised node can’t reach the office PCs. Checklist is on Device transport security.

