{"id":19388356,"url":"https://github.com/mk-fg/hwctl","last_synced_at":"2026-04-13T01:07:35.639Z","repository":{"id":207261685,"uuid":"718824072","full_name":"mk-fg/hwctl","owner":"mk-fg","description":"Local hardware-control setup using microcontrollers as peripherals","archived":false,"fork":false,"pushed_at":"2025-02-12T09:48:27.000Z","size":122,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-12T10:32:17.144Z","etag":null,"topics":["automation","controller","led","mcu","micropython","neopixel","nfc","peripheral","ppps","python","rp2040","usb-tty"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mk-fg.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"COPYING","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,"publiccode":null,"codemeta":null}},"created_at":"2023-11-14T21:47:34.000Z","updated_at":"2025-02-12T09:48:31.000Z","dependencies_parsed_at":"2024-01-10T15:47:30.128Z","dependency_job_id":"2ac65c2c-9662-4d8b-a849-0a16dfac6aad","html_url":"https://github.com/mk-fg/hwctl","commit_stats":null,"previous_names":["mk-fg/hwctl"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fhwctl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fhwctl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fhwctl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fhwctl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mk-fg","download_url":"https://codeload.github.com/mk-fg/hwctl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240549434,"owners_count":19819131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["automation","controller","led","mcu","micropython","neopixel","nfc","peripheral","ppps","python","rp2040","usb-tty"],"created_at":"2024-11-10T10:12:31.420Z","updated_at":"2026-04-13T01:07:30.612Z","avatar_url":"https://github.com/mk-fg.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"hwctl\n=====\n------------------------------------------------------------------\nLocal hardware-control setup using microcontrollers as peripherals\n------------------------------------------------------------------\n\nMicrocontroller firmware and linux userspace scripts/services to control things\nlike LEDs, relays, buttons/switches and sensors connected to pins on those mcus\nin my local setup.\n\nCheap hobbyist microcontrollers like Arduinos_ and `RP2040 boards`_ are easy to\nuse as USB peripherals for normal PCs, to provide smart programmable interface\nto a lot of simpler electrical hardware, electronics and common embedded buses\nlike I²C.\n\nE.g. for stuff like switching power of things on-and-off with relays by sending\na command byte to /dev/ttyACM0, when it is needed/requested by whatever regular\nOS - for example on click in some GUI, desktop keyboard shortcut, or when some\ncronjob runs and needs external USB drive powered on.\n\nScripts here implement firmware and software side for some of my local setup,\nand are probably not useful as-is to anyone else, as they have specific pins\nconnected to specific local hardware, so probably only useful for reference\nand/or as a random example snippets.\n\nIt's very much in a \"local hardware/firmware projects dump\" or \"dotfiles\" category.\n\n.. contents::\n  :backlinks: none\n\nRepository URLs:\n\n- https://github.com/mk-fg/hwctl\n- https://codeberg.org/mk-fg/hwctl\n- https://fraggod.net/code/git/hwctl\n\n.. _Arduinos: https://www.arduino.cc/\n.. _RP2040 boards:\n  https://www.raspberrypi.com/documentation/microcontrollers/rp2040.html\n\n\n`rp2040-usb-ppps`_\n------------------\n.. _rp2040-usb-ppps: rp2040-usb-ppps.py\n\nRP2040 firmware used to control USB per-port-power-switching, but NOT via actual\nbuilt-in ppps protocol that some USB Hub devices support (and can be done via\nsysfs on linux, or uhubctl_ tool), and instead via cheap MOSFET solid-state-relays,\nsoldered to a cheap simple USB Hub with push-button power controls on ports.\n\nHubs with ppps have two deal-breaking downsides for me:\n\n- They're impossible to find or identify - e.g. any model on uhubctl_ list of\n  \"known working\" is either too old, can't be sourced from here, or is ridiculously\n  expensive, while USB3 Hubs with port power dpdt switches are dirt-common and\n  cost like $10.\n\n  Chips in many hubs support ppps, but sometimes it toggles data lines and not\n  VBUS, sometimes it only toggles one \"fast charging\" port, most times it\n  doesn't do anything at all (control tracks not connected to anything), all this\n  changes between minor hw revisions, and afaict not mentioned anywhere by vendors.\n\n- PPPS in USB Hubs has default port power state as ON.\n\n  So whenever you reboot the machine, dual-boot it into gaming-Windows or\n  whatever, all the junk plugged into all ports powers-on all at once,\n  which is dumb and bad - that's kinda the idea behind port power control to\n  avoid this, and it takes its toll on devices (esp. spinning-rust ext-hdds).\n\n  Also usually don't want non-main OS accessing stuff like Yubikeys at all\n  (that lock themselves up after N access attempts), so power to those should\n  always be default-disabled.\n  In some cases, whole point of this per-port power control is to avoid\n  seldom-used USB devices getting jerked around all the time, and ppps with\n  default-on state is actually worse than simple always-on ports.\n\nControlling power via $1 SSRs soldered to buttons neatly fixes the issue -\nnothing gets randomly powered-on, and when it does, code on the rp2040\ncontroller can be smart enough to know when to shut down devices if whatever\nusing them stops sending it \"this one is still in use\" pings.\n\nImplemented using mostly-stateless protocol sending single-byte commands over\nttyACM (usb tty) back-and-forth, so that there can be no \"short read\" buffering\nissues.\n\nWhen using mpremote with RP2040, ``mpremote run rp2040-usb-ppps.py``\nwon't connect its stdin to the script (at least current 2023 version of it),\nso right way to actually run it seem to be uploading as ``main.py`` and do\n``mpremote reset`` or something to that effect.\n\nFor deploying script as long-term firmware, pre-compiling it via\n`mpy-cross tool`_ is probably a good idea::\n\n  % mpy-cross -march=armv6m -O2 rp2040-usb-ppps.py -o usb_ppps.mpy\n  % echo 'import usb_ppps; usb_ppps.run()' \u003eloader.py\n  % mpremote cp usb_ppps.mpy :\n  % mpremote cp loader.py :main.py\n  % mpremote reset\n\n(mpy-cross binary used there is trivial to build - see `Arch PKGBUILD here`_)\n\nAlso wrote-up some extended thoughts on this subject in a\n`\"USB hub per-port power switching done right\" blog post`_.\n\n.. _uhubctl: https://github.com/mvp/uhubctl/\n.. _mpy-cross tool:\n  https://github.com/micropython/micropython/tree/master/mpy-cross\n.. _Arch PKGBUILD here:\n  https://github.com/mk-fg/archlinux-pkgbuilds/blob/master/mpy-cross/PKGBUILD\n.. _\"USB hub per-port power switching done right\" blog post:\n  https://blog.fraggod.net/2023/11/17/usb-hub-per-port-power-switching-done-right-with-a-couple-wires.html\n\n\n`hwctl`_\n--------\n.. _hwctl: hwctl.py\n\nLinux userspace part of the control process - a daemon script to talk to\nconnected microcontrollers and send them commands, received via whatever\nsimple unixy IPC mechanisms.\n\nCurrently itself controlled via signals (e.g. ``pkill -USR1 -F hwctl.pid``, see\n``-p/--pid-file`` option) and any space/line-separated plaintext commands to a FIFO\npipe (``echo usb3=on \u003ehwctl.fifo``, ``-f/--control-fifo`` option) from terminal or scripts.\n\nUses serial_asyncio module from `pyserial/pyserial-asyncio`_ for ttyACMx communication.\n\n`Older version`_ used to poll /proc/self/mountinfo fd and do some \"don't forget\nto unmount\" indication via LEDs connected to Arduino Uno board (running `hwctl.ino`_),\nread/debounce physical buttons, as well as similar usb-control wdt logic as\nrp2040-usb-ppps script.\n\n.. _pyserial/pyserial-asyncio: https://github.com/pyserial/pyserial-asyncio\n.. _Older version: https://github.com/mk-fg/hwctl/blob/0e60923/hwctl.py\n.. _hwctl.ino: https://github.com/mk-fg/hwctl/blob/0e60923/hwctl.ino\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk-fg%2Fhwctl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmk-fg%2Fhwctl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk-fg%2Fhwctl/lists"}