[{"element_type":"alert","content":"\u003cp\u003e\u003cstrong\u003eVIBES ALERT:\u003c/strong\u003e Have I read all the code that was generated on my behalf? Pretty much / Not really, maybe 60-70% of the final JS / webpage thoroughly and over 80% of the circuitpython code (plus plenty more before the current version so about 85% of codebase minus cruft). Although in my defence I scan agent sessions in verbose mode and examine changes as they are made, manually fix or suggest techniques, and generally architect and direct debugging. I also do review PRs, but prioritise my time on the useful productive areas when prototyping like this.\u003c/p\u003e\n","metadata":{"markdown":"**VIBES ALERT:** Have I read all the code that was generated on my behalf? Pretty much / Not really, maybe 60-70% of the final JS / webpage thoroughly and over 80% of the circuitpython code (plus plenty more before the current version so about 85% of codebase minus cruft). Although in my defence I scan agent sessions in verbose mode and examine changes as they are made, manually fix or suggest techniques, and generally architect and direct debugging. I also do review PRs, but prioritise my time on the useful productive areas when prototyping like this.","alert_type":"yellow","icon":"thinking"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eFor CircuitPython Day 2026 I wanted to create an E-ink CO2 monitoring project for a friend.\u003c/p\u003e\n\u003cp\u003eThey wanted to be able to see how long things were in bad ranges after the fact, mostly not looking at the data until a problem occurred and was rectified, like a post-mortem of their setup. There's no internet over wifi (just phone data), and they prefer / are used to using Bluetooth on the phone with another data logger.\u003c/p\u003e\n\u003cp\u003eTo make it more exciting for me I wanted to play with a few technologies, namely an *ESP-Now data collector, Offline SSL WiFi + Web-BLE control/analysis page, and Web-AI thrown in for local offline model use in the browser!*\u003cbr\u003e\u003cbr\u003e\u003csub\u003e*\u003cem\u003eWeb-AI and the Prompt API are limited to Chrome currently, and \u003ca href=\"https://developer.chrome.com/docs/ai/webmcp\"\u003eWeb-MCP\u003c/a\u003e is a time restricted origin-trial for now.\u003c/em\u003e\u003c/sub\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eHardware / Parts Used\u003c/h2\u003e\n\u003cp\u003e\u0026nbsp;It was appropriate timing as Adafruit have just released the Sensirion SCD43 (most accurate so far) along with a plethora of E-ink panels + displays, my most recent acquisition being the Quad colour 3.52\" panel.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/6512","metadata":{"quantity":"1","description":"","name":"","url":"","featured":"true"}},{"element_type":"product","content":"https://www.adafruit.com/product/6414","metadata":{"quantity":"1","description":"","name":"","url":"","featured":"true"}},{"element_type":"markdown","content":"\u003cblockquote\u003e\n\u003ch3\u003eAlternative components\u003c/h3\u003e\n\n\u003cp\u003eThis project was designed for two displays, the 2.9\" or 3.52\" e-inks, but anything should work (adjusting display layout a bit).\u003c/p\u003e\n\n\u003cp\u003eThe Host controller can be anything really, all transports (BLE/WiFi/esp-now) should be optional (Espressif WiFi for espnow), but this was set up for the Feather C6 or S3, combined with the \u003ca href=\"https://www.adafruit.com/product/4446\"\u003eE-ink Feather Friend\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe remote sensor nodes are a similar spec, without displays or e-ink feather friends.\u003c/p\u003e\n\n\u003cp\u003eSensor-wise, the code expects a Sensirion SCD4x series (\u003ca href=\"https://www.adafruit.com/product/5187\"\u003e40\u003c/a\u003e/\u003ca href=\"https://www.adafruit.com/product/5190\"\u003e41\u003c/a\u003e/\u003ca href=\"https://www.adafruit.com/product/6512\"\u003e43\u003c/a\u003e) or \u003ca href=\"https://www.adafruit.com/product/4867\"\u003eSCD30\u003c/a\u003e Carbon dioxide sensor, or alternatively a combined Air Quality CO2 sensor like the SEN66.\n-# \u003cem\u003e(The SEN63c is not recommended for remote nodes, but is fine for the host controller where it's more about approximate household values rather than more accurate data)\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n","metadata":{"markdown":"\u0026gt; ### Alternative components\n\u0026gt; \n\u0026gt; This project was designed for two displays, the 2.9\" or 3.52\" e-inks, but anything should work (adjusting display layout a bit).\n\u0026gt; \n\u0026gt; The Host controller can be anything really, all transports (BLE/WiFi/esp-now) should be optional (Espressif WiFi for espnow), but this was set up for the Feather C6 or S3, combined with the [E-ink Feather Friend](https://www.adafruit.com/product/4446).\n\u0026gt;\n\u0026gt; The remote sensor nodes are a similar spec, without displays or e-ink feather friends.\n\u0026gt; \n\u0026gt; Sensor-wise, the code expects a Sensirion SCD4x series ([40](https://www.adafruit.com/product/5187)/[41](https://www.adafruit.com/product/5190)/[43](https://www.adafruit.com/product/6512)) or [SCD30](https://www.adafruit.com/product/4867) Carbon dioxide sensor, or alternatively a combined Air Quality CO2 sensor like the SEN66.\n-# _(The SEN63c is not recommended for remote nodes, but is fine for the host controller where it's more about approximate household values rather than more accurate data)_"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eHow does it work and what does it do?\u003c/h2\u003e\n\u003cp\u003eSo this project is an ESP-Now based communication device (less bandwidth and no real need for WiFi AP dependency although can host one if needed), with it's own offline sensor and SD card, and acts as a data collector for remote sensor nodes.\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;The devices are set up with a wifi AP to begin with (and a captive portal) allowing setting of the device name and sensor polling frequency. If the esp-now based host controller cannot be accessed the the nodes create the setup wifi AP again (@tyeth remember to check what happens if host goes offline and returns - they happily resume but more testing worth doing).\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;The main host controller is expected to be powered (ideally with battery backup) and displays it's data along with the remote nodes, compared against high_warn / high_bad / low_warn and low_bad thresholds for each metric (co2, RH, temp, particles, battery).\u003c/p\u003e\n\u003ch3\u003eSo how do we view the data on a bigger screen, or historical data?\u003c/h3\u003e\n\u003cp\u003eThere's a GitHub Pages site mirrored on the host controllers Wifi AP captive portal. It supports https and http locally, so that web-AI is supported* [\u003cem\u003ehttps is mandated by Browsers for certain technologies like Web-BLE or phone sensor access\u003c/em\u003e].\u003c/p\u003e\n\u003cp\u003eThis is a demo link that prepopulates data files into the page (no syncing from device needed):\u003cbr\u003e\u003ca href=\"https://tyeth.github.io/deepsleep_espnow_wifi_and_ble_env_collector/webapp/?demo=1\" target=\"_blank\"\u003ehttps://tyeth.github.io/deepsleep_espnow_wifi_and_ble_env_collector/webapp/?demo=1\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;The host has the page available served over the captive wifi portal just in case no internet is around, and the needed plotly.js is stored in flash or on the SD card for easy serving. \u003cbr\u003e\u0026nbsp; \u0026nbsp;Lastly pyodide is available with access to the data and sqlite3 and numpy, but being 10mb it's an SD or online only option.\u003c/p\u003e\n\u003cp\u003eThe final treat on the page is the use of the \u003cem\u003eexperimental \u003c/em\u003e\u003ca href=\"https://developer.chrome.com/docs/ai/prompt-api\" target=\"_blank\"\u003eWeb-AI Prompt API\u003c/a\u003e in Google Chrome, allowing the downloading of a model (nano-flash or Gemma4*) and then interrogating your data via the \u003cspan style=\"text-decoration: underline;\"\u003e\u003cstrong\u003elocally run model in the browser\u003c/strong\u003e\u003c/span\u003e.\u003c/p\u003e\n\u003ch3\u003eOffline web page resources (for graphs and AI analysis)\u003c/h3\u003e\n\u003cp\u003eWhat's clever is that by visiting the webpage from github first, you can get the https support allowing Web-AI, download the Model if needed along with all the JS libraries super quickly.\u003c/p\u003e\n\u003cp\u003eThen you switch to the Sensors WiFi network (no internet) and the page remains working. It's also possible to just visit the webpage initially, but then the JS files will be slowly served from the ESP32.\u003c/p\u003e\n\u003cp\u003eThe page still allows accessing the esp32 host controller on a known fixed address (192.168.4.1) via an openly free SSL certificate from 192dot168dot4dot1.gundryconsultancy.com which is updated by LetsEncrypyt every 60days. Of course you can also use BLE instead to transfer the data from host.\u003c/p\u003e\n\u003ch3\u003ePoor forgetful little computer... Keeping Time and new SSL certs updated\u003c/h3\u003e\n\u003cp\u003eBecause the Host controller device boots with no time, and maybe an out of date SSL certificate, the phone or PC provide updated time (and optionally SSL certs) simply by connecting to the website and then using web-ble or the wifi REST API to connect to the host controller. At that point all the stored data with \u0026lt;2020 timestamps get synced with the relative offset to current time giving correct dates.\u003cbr\u003eThe days of data are also synced to the browser in the same way, over BLE or WiFi API, saved to localstorage.\u003c/p\u003e\n\u003cp\u003eIt would be relatively cheap and easy to include a Real-Time Clock (RTC) as they have battery backups and would retain time after power loss. As it is, the first visit to the portal page syncs time from the browser 🧠\u003c/p\u003e\n\u003ch2\u003eRoom for improvement\u003c/h2\u003e\n\u003cp\u003eAlas the phone (android) doesn't support Web-AI yet* (\u003ca href=\"https://developers.google.com/ml-kit/genai/prompt/android/get-started\"\u003eML-Kit promises in future\u003c/a\u003e), and other browsers have \u003ca href=\"https://github.com/mozilla/standards-positions/issues/1213#issuecomment-4347988313\"\u003eso far refused the temptation of the Prompt API\u003c/a\u003e (with good reasons), so if no internet exists then a Laptop is required to use the Web-AI model (and have downloaded a model previously or be briefly online).\u003c/p\u003e\n\u003cp\u003eThe Nano-flash model was at times really quick and effective, although pretty inconsistent and at times ran for minutes before a response. Gemma4 model will likely be much better (will update after testing).\u003c/p\u003e\n\u003cp\u003eOf course as models get better there will eventually be something worth running locally on the esp32.\u003c/p\u003e\n\u003cp\u003eFor kicks pretty early on I wanted to think about \u003ca href=\"https://developer.chrome.com/docs/ai/structured-output-for-prompt-api\" target=\"_blank\"\u003ethis structured output schema\u003c/a\u003e and then move onto Tool calls from the agents responses, and it worked surprisingly well (not to spec / hacky).\u003cbr\u003e\u003ca href=\"https://github.com/tyeth/deepsleep_espnow_wifi_and_ble_env_collector/compare/main...ai-tool-loop\"\u003ehttps://github.com/tyeth/deepsleep_espnow_wifi_and_ble_env_collector/compare/main...ai-tool-loop\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eLonger term, it would be nice if the agent had access to more tooling (\u003ca href=\"https://github.com/webmachinelearning/webmcp?tab=readme-ov-file\"\u003eWeb-MCP\u003c/a\u003e), like the whole of pyodide, and the ability to reuse the graphs to highlight a related region of interest. Sadly it probably wants a sidebar or floating widget so the \"Ask\" and response summary is always available...\u003c/p\u003e\n\u003cp\u003eLastly CircuitPython itself comes with some occasional gotchas, community contributions (esp-now), bleeding edge features (like BLE improvements), and heavy memory usage woes if using graphics and radio, especially when you're a glutton for punishment like myself and attempt an SSL server + BLE + espnow + display!\u003cbr\u003e🪞🔫🦶🤓\u003cbr\u003e\u003cbr\u003eI \u003ca href=\"https://github.com/tyeth/circuitpython/pull/3\" target=\"_blank\"\u003eended up\u003c/a\u003e compiling circuitpython with extra flags for memory radio buffer size, adding SSL session tickets for cheaper connection resuming, turning off BLE workflow in settings.toml, and using the new\u0026nbsp;\u003ca href=\"https://docs.circuitpython.org/en/latest/shared-bindings/storage/index.html#storage.unsafe_disable_usb_drive\" target=\"_blank\"\u003esupervisor.\u003cem\u003eunsafe_\u003c/em\u003edisable_usb_storage\u003c/a\u003e to allow removing MSC write permission from the PC and restoring to the MCU (to save settings changes etc).\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n        \u003ch2\u003eTo Be Continued...\u003c/h2\u003e\n\u003cp\u003eI love the optimisation of running things via local models, avoid burning the world unnecessarily and all that, but clearly there's work to be done. I figure by next year it'll be worth revisiting \u003ca href=\"https://docs.google.com/forms/d/e/1FAIpQLSdP83TpEv85ZH54hX4QyvNHdvP9zzyJARQN9ictLNdX5ra-uQ/viewform\"\u003ethe mailing list - \u003cspan\u003eBuilt-in AI: Early Preview Program (Chrome)\u003c/span\u003e\u003c/a\u003e for some mobile-centric ML-Kit stuff.\u003c/p\u003e\n\u003cp\u003eWell this was also a massive excuse to practise using my CNC for more complex toolpaths, mixing Wood and 3d printing, and attempting to probe workpieces.\u003c/p\u003e\n      \n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/002/591/original/Screenshot_2026-09-11_171403.png?1789143255","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/002/590/original/IMG_20260911_170447_622_%28Custom%29_%281%29.jpg?1789143075","metadata":{}},{"element_type":"text","content":"\n  \n        \u003cp\u003eThere will be a follow up post showing the enclosure / composite design, and in the mean-time here's the latest yak shaving mission en-route, in my tangential quest for CNC probing accuracy and convenience:\u003c/p\u003e\n      \n","metadata":{}},{"element_type":"button","content":"https://adafruit-playground.com/u/tyeth/pages/bringing-new-conveniences-to-an-old-cnc-v1-unmodified-machine","metadata":{"button_type":"info","icon":"","target":"_blank","zip_folder":"false"}}]