{"id":17269170,"url":"https://github.com/vladak/fusebox","last_synced_at":"2026-02-02T15:36:40.049Z","repository":{"id":64842327,"uuid":"578729133","full_name":"vladak/fusebox","owner":"vladak","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-20T20:39:40.000Z","size":5564,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-12T10:27:28.721Z","etag":null,"topics":["adafruit","circuitpython","electricity-consumption","microcontroller","mqtt","sensors"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vladak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-12-15T18:40:03.000Z","updated_at":"2024-04-20T20:39:43.000Z","dependencies_parsed_at":"2023-01-29T18:01:21.721Z","dependency_job_id":"b3c7b201-b203-41aa-b73e-7541aeb833b0","html_url":"https://github.com/vladak/fusebox","commit_stats":{"total_commits":42,"total_committers":1,"mean_commits":42.0,"dds":0.0,"last_synced_commit":"7f17aedb50ba26854be040ed7ea603879cfc6220"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vladak/fusebox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladak%2Ffusebox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladak%2Ffusebox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladak%2Ffusebox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladak%2Ffusebox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vladak","download_url":"https://codeload.github.com/vladak/fusebox/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladak%2Ffusebox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29014382,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T14:58:54.169Z","status":"ssl_error","status_checked_at":"2026-02-02T14:58:51.285Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adafruit","circuitpython","electricity-consumption","microcontroller","mqtt","sensors"],"created_at":"2024-10-15T08:15:30.370Z","updated_at":"2026-02-02T15:36:40.033Z","avatar_url":"https://github.com/vladak.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Python checks](https://github.com/vladak/fusebox/actions/workflows/python-checks.yml/badge.svg)](https://github.com/vladak/fusebox/actions/workflows/python-checks.yml)\n\n# Fusebox - power consumption monitoring\n\nThis repository contains code for ESP32 V2 Adafruit Feather to measure temperature and send it to MQTT broker via WiFi.\nThe Feather is located in a fuse box, running from a 5V power. It reads pulses from a wire connected to a power meter.\n\nThis is somewhat similar to the [shield](https://github.com/vladak/shield) project.\nLikewise, I used this project as a way to learn more about electronic circuitry,\n[CircuitPython](https://circuitpython.org/), MQTT, microcontrollers, etc. and \nI mostly enjoyed the process of building it.\n\n## Hardware\n\nHere is a bill of materials:\n\nPurpose | Name\n---|---\nmicrocontroller | [ESP32 Feather V2 with w.FL antenna connector](https://www.adafruit.com/product/5438)\nantenna | [RP-SMA: 2 dBi](https://botland.cz/usb-wifi-karty/4750-wifi-usb-n-600mbps-sitova-karta-s-antenou-5907621809676.html)\npigtail | [RP-SMA to w.FL / MHF3 / IPEX3 Adapter](https://www.adafruit.com/product/5444)\ntemperature/humidity sensor | [Adafruit AHT20 - Temperature \u0026 Humidity Sensor Breakout Board - STEMMA QT / Qwiic](https://www.adafruit.com/product/4566)\npower meter | [SDM 72D 0,25-100A MID](https://www.elektromery.com/product/trifazove-elektromery-na-listu-din-neove/elektromer-sdm-72d--0_25-100a-mid/177)\n5V power supply | [Meanwell HDR-15-5](https://www.meanwell.com/webapp/product/search.aspx?prod=HDR-15)\nresistor | 1k Ohm\nwires | 22 AWG, black/red/blue\nWago connectors | 3 x [221-413](https://www.wago.com/global/installation-terminal-blocks-and-connectors/compact-splicing-connector/p/221-413)\nenclosure | [3D Printed Case for Adafruit Feather](https://learn.adafruit.com/3d-printed-case-for-adafruit-feather/parts) - solid lid, bottom with wings\nmounts | [nylon screw and stand-off set](https://www.adafruit.com/product/3299)\n\nMost of the stuff comes from [Adafruit](https://www.adafruit.com/).\n\n## Genesis\n\nIn the midst of the [energy crisis](https://en.wikipedia.org/wiki/2021%E2%80%932022_global_energy_crisis),\nI wondered what the power consumption of our household looks like in detail,\nesp. with running a small [home lab](https://www.reddit.com/r/homelab/).\n\nI came across a web that completed [just that](https://www.berlinger.cz/blog/mereni-elektriny/) using Arduino.\nBeing a fan of Adafruit and CircuitPython, I wanted to replicate this with these.\n\n### ESP32 V2 and WiFi\n\nTesting with [ESP32-S2](https://www.adafruit.com/product/5303) that has built-in antenna placed inside the fuse box,\nrevealed once again that there is some problem in the stack as some of the\nMQTT messages sent from the Feather to the MQTT broker are lost.\n\nFrom the last Adafruit order, I had the antenna pigtail, and I found a USB WiFi adapter\nthat came with small 2 dBi antenna. Since the WiFi adapter cost like 5 EUR,\nI used the antenna and trashed the USB dongle (of course, I recycled it properly\nwith other electronic waste I had).\n\n### Temperature/humidity monitoring\n\nInitially, to test the code that was still heavily based on the [shield](https://github.com/vladak/shield) code,\nI connected temperature/humidity sensor to have some data sent out.\n\nThen I figured why not to leave the sensor in place. There will not be many variations\ninside the fuse box, however it might be interesting to see the values in the long term.\n\n### Physical packaging\n\nTo place the microcontroller inside the fuse box securely, I needed a box.\nSince the local manufacturer of 3D printers has a [map](https://world.prusa3d.com)\nwith places where it is possible to have the models printed, it was quite easy.\nI found a school in walkable distance that has a workshop with more than 10 printers,\ncalled the teacher responsible for the workshop, and I had the box ready in 2 days for cheap.\n\n\u003cimg src=\"img/box-outside.JPG\" alt=\"drawing\" width=\"400\"/\u003e\n\nTo fasten the microcontroller inside the case, I used the [nylon screw and stand-off set](https://www.adafruit.com/product/3299).\nI am glad that I ordered the screw set earlier without having a firm plan what to use it for.\n\nThe nice thing about the case is that the Neopixel blinking (in blue, for half a second),\nwhich happens whenever a message is published to MQTT broker,\nis visible through the lid of the enclosure (also blue color) as it is a bit translucent.\n\n### Wiring\n\nThe color coding I choose is based on the [STEMMA QT spec](https://learn.adafruit.com/introducing-adafruit-stemma-qt/technical-specs) -\nred for V+ power, black for ground. The pulse wire connected to GPIO pin 37 (`board.D37` in the code) is blue (matching the color \nof the box for a bonus !). There are several [tricks](https://forums.adafruit.com/viewtopic.php?t=196848) on how to determine and maintain the mapping between pin on the board and the CircuitPython `board` attribute. One is to look into CircuitPython source code, another is to\nsave the `dict(board)` to a `.txt` file on the microcontroller (or elsewhere) for easy reference.\n\n\u003cimg src=\"img/box-inside.JPG\" alt=\"drawing\" width=\"400\"/\u003e\n\nTo get reliable pulse readings, a [pullup resistor](https://en.wikipedia.org/wiki/Pull-up_resistor) is needed.\nIn order to connect all the wires and the resistor together I used the Wago connectors.\nI cut the leads to the resistor a bit so it almost completely hides inside the Wago connectors.\nThe only soldering necessary was for the 3 wires attached to the Feather.\n\n\u003cimg src=\"img/wago.JPG\" alt=\"drawing\" width=\"400\"/\u003e\n\nThe only struggle I had was powering the Feather. It turned out that both +V and -V outputs from the\npower supply have to be used. \nThere is very nice tear down video of the DR-15-5 on https://www.youtube.com/watch?v=2S5OFr9VSnY , \nwhich actually made me think about how to connect it properly.\nThe +V is connected to the USB input/output pin and -V is connected to GND pin.\nThus, the -V is connected to the Wago connector together with the GND from Feather and the '-' impluse contact\nfrom the power meter.\n\nThe +V connected to the USB pin has some [caveats](https://forums.adafruit.com/viewtopic.php?p=933318#p933318) -\ncare needs to be taken not to connect USB cable while the +V is connected.\n\n\u003cimg src=\"img/power.JPG\" alt=\"drawing\" width=\"400\"/\u003e\n\nThis is a crappy diagram of how the wires are connected:\n\n\u003cimg src=\"img/diagram.jpg\" alt=\"drawing\" width=\"400\"/\u003e\n\nAnd here is the final result:\n\n\u003cimg src=\"img/fusebox-final.JPG\" alt=\"drawing\" width=\"400\"/\u003e\n\nThe power meter and the power supply were installed by an electrician, however\nthen I needed to add a wire to -V on the power supply and one needs to be really\ncareful while the fuse box is under current. Dangling wire with exposed contact\nis a no-no - just add a empty Wago connector to prevent it from touching the phase wires.\nOr shut the whole fuse box altogether and grab a head lamp.\n\n### Code\n\nThe code is simple - in endless cycle it collects and sends the pulse count (and sensor data)\nto MQTT broker. Since it has to count the pulses, it would be undesirable to enter any kind\nof hardware sleep that would prevent that. It is also not needed, given the microcontroller is connected to 5V\npower supply inside the fuse box.\n\nAs for the pulse counting, it is made easy thanks to CircuitPython \n[wrapping interrupts in the countio module](https://forums.adafruit.com/viewtopic.php?p=855334),\nwhich I believe happens on ESP32 V2.\n\n## Software/firmware install\n\nFirstly, the microcontroller needs to be converted to run CircuitPython. To do that, for ESP32 V2, initially I chose the [command line `esptool`](https://learn.adafruit.com/circuitpython-with-esp32-quick-start/command-line-esptool) on a Linux computer (since macOS appeared to have flaky serial connection for some reason).\n\nSpecifically, this command was used:\n```\nesptool.py --port /dev/ttyACM0 write_flash -z 0x0 \\\n    ~/Downloads/adafruit-circuitpython-adafruit_feather_esp32_v2-en_US-x.y.z.bin\n```\n\nThese days it can be also quite conveniently done using [WebFlasher](https://adafruit.github.io/Adafruit_WebSerial_ESPTool/) with the `.bin` file provided on https://circuitpython.org/board/adafruit_feather_esp32_v2/\n\nThe web workflow for this case is actually a boon, since going into the fusebox cabinet\nin order to change the code would be undesirable and potentially dangerous.\n\nHaving static IP address for the microcontroller is handy.\n\nOnce CicuitPython is installed, perform the initial set up by [creating the `settings.toml` file](https://learn.adafruit.com/circuitpython-with-esp32-quick-start/setting-up-web-workflow\n) in the root directory (using `screen` when the board is connected via USB data cable):\n```\nf = open('settings.toml', 'w')\nf.write('CIRCUITPY_WIFI_SSID = \"wifissid\"\\n')\nf.write('CIRCUITPY_WIFI_PASSWORD = \"wifipassword\"\\n')\nf.write('CIRCUITPY_WEB_API_PASSWORD = \"XXX\"\\n')\nf.close()\n```\nand restart the microcontroller.\n\nThen the following can be used:\n- copy `*.py` files to the root directory using web workflow, assumes system with `curl` installed:\n  ```\n  for f in *.py; do\n      curl -v -u :XXX -T $f -L --location-trusted http://172.40.0.9/fs/$f;\n  done\n  ```\n- create `secrets.py` in the root directory (using the same technique as in the previous step)\n- install necessary libraries from Adafruit CircuitPython bundle to the `lib` directory:  \n  ```\n  circup --host 172.40.0.9 --password XXX install -r requirements.txt\n  ```\n\n## Configuration\n\nThe number of pulses is stored in a [counter](https://prometheus.io/docs/concepts/metric_types/#counter) in Prometheus.\n\n### Prometheus MQTT exporter\n\nThe contents of `/etc/prometheus/mqtt-exporter.yaml` should look like this:\n\n```yml\nmqtt:\n  # The MQTT broker to connect to\n  server: tcp://localhost:1883\n  # The Topic path to subscribe to. Be aware that you have to specify the wildcard.\n  topic_path: devices/#\n  # Optional: Regular expression to extract the device ID from the topic path. The default regular expression, assumes\n  # that the last \"element\" of the topic_path is the device id.\n  # The regular expression must contain a named capture group with the name deviceid\n  # For example the expression for tasamota based sensors is \"tele/(?P\u003cdeviceid\u003e.*)/.*\"\n  device_id_regex: \"(.*/)?(?P\u003cdeviceid\u003e.*)\"\n  # The MQTT QoS level\n  qos: 0\ncache:\n  # Timeout. Each received metric will be presented for this time if no update is send via MQTT.\n  # Set the timeout to -1 to disable the deletion of metrics from the cache. The exporter presents the ingest timestamp\n  # to prometheus.\n  timeout: 15m\n# This is a list of valid metrics. Only metrics listed here will be exported\nmetrics:\n  -\n    # The name of the metric in prometheus\n    prom_name: temperature\n    # The name of the metric in a MQTT JSON message\n    mqtt_name: temperature\n    # The prometheus help text for this metric\n    help: temperature reading\n    # The prometheus type for this metric. Valid values are: \"gauge\" and \"counter\"\n    type: gauge\n  -\n    # The name of the metric in prometheus\n    prom_name: humidity\n    # The name of the metric in a MQTT JSON message\n    mqtt_name: humidity\n    # The prometheus help text for this metric\n    help: humidity reading\n    # The prometheus type for this metric. Valid values are: \"gauge\" and \"counter\"\n    type: gauge\n    # A map of string to string for constant labels. This labels will be attached to every prometheus metric\n    #const_labels:\n    #  sensor_type: dht22\n  -\n    # The name of the metric in prometheus\n    prom_name: pulses\n    # The name of the metric in a MQTT JSON message\n    mqtt_name: pulses\n    help: Pulse count\n    # The prometheus type for this metric. Valid values are: \"gauge\" and \"counter\"\n    type: counter\n```\n\n### Prometheus\n\nThe MQTT exporter by itself sets the `sensor` tag to the last component of the topic,\nwhich in this case is the device name. To get also the location (2nd component of the topic), \nre-labeling in Prometheus itself is used.\n\nUnder the `scrape_configs` section in `/etc/prometheus/prometheus.yml` there should be:\n```yml\n\n  - job_name: mqtt\n    # If prometheus-mqtt-exporter is installed, grab metrics from external sensors.\n    static_configs:\n      - targets: ['localhost:9641']\n    # The MQTT based sensor publish the data only now and then.\n    scrape_interval: 1m\n    # Add the location as a tag.\n    metric_relabel_configs:\n     - source_labels: [topic]\n       target_label: location\n       regex: 'devices/([[:alnum:]]*)/[[:alnum:]]*'\n       action: replace\n       replacement: \"$1\"\n\n```\n\n## Grafana\n\nThe dashboard is rather simple:\n\n\u003cimg src=\"img/grafana-dashboard.jpg\" alt=\"drawing\" width=\"800\"/\u003e\n\nIt captures only the first day of measurement, so the per month values are not real yet.\n\nThe dashboard can be downloaded from [Electricity.json](/Electricity.json).\n\nFor getting the data displayed in Grafana the way I wanted, the Explore tab is immensely useful.\nIn my case, the power consumption graph displayed only wide bars and I found out that\nthe scaping interval has to be made smaller in order to get enough data points,\nwhich in turn makes finer grained resolution and thus narrower columns.\n\n## Usage\n\nThere needs to be a `secrets.py` file that contains Wi-Fi credentials and information about the MQTT broker.\nIt can look like this:\n```python\n# This file is where you keep secret settings, passwords, and tokens!\n# If you put them in the code you risk committing that info or sharing it\n\nsecrets = {\n    \"ssid\": \"foo\",\n    \"password\": \"bar\",\n    \"broker\": \"172.40.0.3\",\n    \"broker_port\": 1883,\n    \"mqtt_topic\": \"devices/fusebox/esp32\",\n    \"sleep_duration\": 30,\n    \"log_level\": \"INFO\",\n    \"log_topic\": \"logs/fusebox/esp32\",\n}\n```\n\nTo transfer the file to the microcontroller, the same method as in the Install section should be used.\n\n## Guide links\n\nAdafruit has largely such a good documentation that the links are worth putting here for quick reference:\n- CircuitPython [countio](https://docs.circuitpython.org/en/latest/shared-bindings/countio/index.html)\n- [ATH20 guide](https://learn.adafruit.com/adafruit-aht20)\n\n## Future\n\nAfter completing the project, I found there are many sophisticated setups out there that\nallow electricity consumption monitoring for individual rooms, basically per breaker.\nThese are usually done by having multiple power meters and/or dedicated gadgets that have multiple\nsensors placed on individual phase wires in the fuse box.\n\nRether than going this way, further cluttering the fuse box, I might use electricity consumption\nreadings from various smart plugs around the house that are already connected to the IoT network.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladak%2Ffusebox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvladak%2Ffusebox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladak%2Ffusebox/lists"}