[{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/521/original/greenhouse-logger-9.jpeg?1737107419","metadata":{"caption":""}},{"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        \u003ch2\u003eOverview\u003c/h2\u003e\n      \n\n\n\n\n\n\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        \u003cp class=\"p1\"\u003eThis guide shows how to build cheap data loggers to help keep plants happy in greenhouses or other indoor growing spaces. I developed this design to help a community garden group control temperatures in greenhouses for starting new plants in winter. By charting the temperatures, we were able to identify and fix problems with air sealing and heater thermostat settings. This logger design uses manual data collection over USB serial because there is no WiFi at the greenhouses.\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","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        \u003ch2\u003eParts\u003c/h2\u003e\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/5323","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/381","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/2886","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eThis project uses a Lithium-ion polymer battery. For longer battery life, avoid exposing the battery to temperatures above 35°C (95°F). To avoid risk of fire (🔥!), do not puncture, crush, or bend the battery, and don't expose it to temperatures above 60°C (140°F). In particular, do not leave the data logger in a car, near a heater, or in a spot exposed to direct sunlight. When using the logger in a greenhouse, you can make a shade structure with reflective foil insulation, a shelf, a ventilated crate, or similar.\u003c/p\u003e","metadata":{"class":"element alert-element build-alert alert-warning","markdown":"This project uses a Lithium-ion polymer battery. For longer battery life, avoid exposing the battery to temperatures above 35°C (95°F). To avoid risk of fire (🔥!), do not puncture, crush, or bend the battery, and don't expose it to temperatures above 60°C (140°F). In particular, do not leave the data logger in a car, near a heater, or in a spot exposed to direct sunlight. When using the logger in a greenhouse, you can make a shade structure with reflective foil insulation, a shelf, a ventilated crate, or similar.","alert_type":"warning"}},{"element_type":"product","content":"https://www.adafruit.com/product/3898","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/1214","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3525","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/4154","metadata":{"name":"0.1\" Male Header Pins","url":"https://www.adafruit.com/product/4154","description":"Adafruit #4154, scrap, or whatever (will be used with a 2-position jumper shunt)","quantity":"1","featured":false}},{"element_type":"alert","content":"\u003cp\u003eThe IP65 junction box and desiccant pack are meant to protect against condensation due to temperature changes and against light incidental spray, splashes, or drips. This design is not weatherproof or waterproof. It's fine for the temperature probe and cable to get wet, but be careful about the box. Don't put the box in a location that gets direct spray from misters or sprinklers.\u003c/p\u003e","metadata":{"class":"element alert-element build-alert alert-warning","markdown":"The IP65 junction box and desiccant pack are meant to protect against condensation due to temperature changes and against light incidental spray, splashes, or drips. This design is not weatherproof or waterproof. It's fine for the temperature probe and cable to get wet, but be careful about the box. Don't put the box in a location that gets direct spray from misters or sprinklers.","alert_type":"warning"}},{"element_type":"parts","content":"\n  \u003cdiv class=\"parts-details\"\u003e\n    \u003cdiv\u003e\n      \u003cspan class=\"parts-quantity\"\u003e1\u003c/span\u003e\u003cspan\u003e x \u003c/span\u003e\n        \u003ca href=\"https://www.amazon.com/dp/B0D5B73XFV\" class=\"parts-name\" target=\"_blank\"\u003eIP65 (or IP67) Junction Box with Cable Gland\u003c/a\u003e\n      \u003cdiv class=\"parts-description\"\u003eprotect from sprinklers or condensation (before you buy, check that inside dimensions will fit proto PCB)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"parts-action\"\u003e\n  \u003cdiv\u003e\n      \u003ca href=\"https://www.amazon.com/dp/B0D5B73XFV\" class=\"parts-url btn-secondary\" target=\"_blank\"\u003eBuy Now\u003c/a\u003e\n\n  \u003c/div\u003e\n\u003c/div\u003e\n\n  \u003cdiv class=\"clearfix\"\u003e\u003c/div\u003e\n","metadata":{"name":"IP65 (or IP67) Junction Box with Cable Gland","url":"https://www.amazon.com/dp/B0D5B73XFV","description":"protect from sprinklers or condensation (before you buy, check that inside dimensions will fit proto PCB)","quantity":"1"}},{"element_type":"parts","content":"\n  \u003cdiv class=\"parts-details\"\u003e\n    \u003cdiv\u003e\n      \u003cspan class=\"parts-quantity\"\u003e1\u003c/span\u003e\u003cspan\u003e x \u003c/span\u003e\n        \u003ca href=\"https://www.amazon.com/gp/product/B07B9FYGPN/?th=1\" class=\"parts-name\" target=\"_blank\"\u003eIndicating Silica Gel Packets\u003c/a\u003e\n      \u003cdiv class=\"parts-description\"\u003eDry \u0026amp; Dry 2 gram or similar (to prevent condensation inside enclosure)\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"parts-action\"\u003e\n  \u003cdiv\u003e\n      \u003ca href=\"https://www.amazon.com/gp/product/B07B9FYGPN/?th=1\" class=\"parts-url btn-secondary\" target=\"_blank\"\u003eBuy Now\u003c/a\u003e\n\n  \u003c/div\u003e\n\u003c/div\u003e\n\n  \u003cdiv class=\"clearfix\"\u003e\u003c/div\u003e\n","metadata":{"name":"Indicating Silica Gel Packets","url":"https://www.amazon.com/gp/product/B07B9FYGPN/?th=1","description":"Dry \u0026amp; Dry 2 gram or similar (to prevent condensation inside enclosure)","quantity":"1"}},{"element_type":"parts","content":"\n  \u003cdiv class=\"parts-details\"\u003e\n    \u003cdiv\u003e\n      \u003cspan class=\"parts-quantity\"\u003e1\u003c/span\u003e\u003cspan\u003e x \u003c/span\u003e\n        \u003cspan class=\"parts-name\"\u003e(optional) Tamiya Universal Plate Set #70157\u003c/span\u003e\n      \u003cdiv class=\"parts-description\"\u003e3mm thick, 160x60mm ABS plates with 3mm holes on 5mm grid\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"parts-action\"\u003e\n  \u003cdiv\u003e\n\n  \u003c/div\u003e\n\u003c/div\u003e\n\n  \u003cdiv class=\"clearfix\"\u003e\u003c/div\u003e\n","metadata":{"name":"(optional) Tamiya Universal Plate Set #70157","url":null,"description":"3mm thick, 160x60mm ABS plates with 3mm holes on 5mm grid","quantity":"1"}},{"element_type":"product","content":"https://www.adafruit.com/product/4685","metadata":{"name":"(optional) M3 Nylon Standoff Set","url":"https://www.adafruit.com/product/4685","description":"misc. M3 machine screws, nuts, and washers","quantity":"1","featured":false}},{"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  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eAdditional Tools \u0026amp; Materials\u003c/h3\u003e\n\u003cp\u003eTools:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSoldering tools (soldering iron, diagonal flush cutters, wire strippers, etc.)\u003c/li\u003e\n\u003cli\u003eScrewdriver with Phillips bit (or whatever matches your IP65 enclosure screws)\u003c/li\u003e\n\u003cli\u003eDrill and hole bit set to make a hole in IP65 enclosure for cable gland\u003c/li\u003e\n\u003cli\u003eKnife, file, or deburring tool to clean up edges of the cable gland hole\u003c/li\u003e\n\u003cli\u003eAnti-static wrist strap and work mat\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMaterials:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSolder\u003c/li\u003e\n\u003cli\u003ePolyimide\u0026nbsp;\u003cspan\u003e(Kapton) Tape, 1 cm wide: Adafruit #3057 or similar to protect against shorts and battery punctures\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003e3M Scotch Vinyl Electrical Tape 35: Good quality electrical tape for sealing\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003e\u003cspan class=\"parts-name\"\u003eZiploc bag: small \u003c/span\u003eresealable zip-top bag to hold desiccant packs while enclosure is unsealed\u003c/span\u003e\u003c/li\u003e\n\u003c/ul\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\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  \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  \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  \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  \n  \n  \n  \n  \n  \n        \u003ch3\u003eOptional\u003c/h3\u003e\n\u003cp\u003eBecause I didn't want this guide to be too long, it only documents how to build a temperature logger using an Adafruit ESP32-S3 Feather dev board. But, I've written the code to work on other ESP32-S3 boards. Adafruit Metro ESP32-S3 works, including built-in battery voltage monitoring. Adafruit QT Py ESP32-S3 works, but it lacks the \u003cspan\u003eMAX17048 battery monitor chip, so \u003c/span\u003eyou'll have to check voltage manually with multimeter. Other ESP32-S3 boards may also work. If you want to use a different ESP32-S3 board, just adapt the enclosure, jumper, and 1-wire sensor connections as needed.\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\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\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\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\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        \u003ch2\u003eWiring\u003c/h2\u003e\n\u003cp\u003eIf you are unfamiliar with soldering stacking headers, you might want to read:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools\"\u003eAdafruit Guide To Excellent Soldering\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://learn.adafruit.com/how-to-solder-headers\"\u003eHow To Solder Headers\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eFritzing Diagram\u003c/h3\u003e\n\u003cp\u003eThis diagram shows the core circuit for the temperature logger, omitting the pin header and perma-proto stuff, and using a toggle switch to represent the jumper:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/546/original/logger-fritzing-diagram.png?1737599513","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eHeaders, Jumper, and 1-Wire Resistor\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eSolder male header pins onto the \u003cstrong\u003eFeather\u003c/strong\u003e board. (use a \u003ca href=\"https://www.adafruit.com/product/4539\" target=\"_blank\"\u003ebreadboard\u003c/a\u003e to hold the pins)\u003c/li\u003e\n\u003cli\u003ePut female headers onto the male pins.\u003c/li\u003e\n\u003cli\u003eAssemble the Feather board and headers onto a \u003ca href=\"https://www.adafruit.com/product/1214\" target=\"_blank\"\u003e\u003cstrong\u003esmall mint tin size perma-proto\u003c/strong\u003e board\u003c/a\u003e. Secure the Feather and headers to the perma-proto board with electrical tape or Kapton tape, being careful not to cover the pins on the bottom of the perma-proto.\u003c/li\u003e\n\u003cli\u003eSolder the female headers to the perma-proto board and remove the tape.\u003c/li\u003e\n\u003cli\u003ePrepare a 2-position chunk of male header and solder it to the perma-proto board on the breadboard rows for Feather pins\u0026nbsp;\u003cstrong\u003eGND\u003c/strong\u003e and \u003cstrong\u003eA0\u003c/strong\u003e. This is where you will attach the jumper shunt that wakes the logger from deep sleep.\u003c/li\u003e\n\u003cli\u003eSolder the \u003cstrong\u003e4.7 kΩ\u003c/strong\u003e resistor from your DS18B20 kit to the perma-proto board on the rows for Feather pins \u003cstrong\u003e3V\u003c/strong\u003e and \u003cstrong\u003eA1\u003c/strong\u003e. (The Feather ESP32-S3 has two 3V pins. Use the one next to RST.)\u003c/li\u003e\n\u003cli\u003eUnplug the Feather board from the perma-proto board and set it aside.\u003c/li\u003e\n\u003cli\u003ePrepare to trim the \u003cstrong\u003eDS18B20\u003c/strong\u003e wires to length by routing them through the perma-proto board as shown in the picture below:\u0026nbsp;\u003cstrong\u003ered\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003e3V\u003c/strong\u003e\u003cspan\u003e pin,\u0026nbsp;\u003c/span\u003e\u003cstrong\u003eblue or black\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003eGND\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;pin,\u0026nbsp;\u003c/span\u003e\u003cstrong\u003eyellow or white\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003eA1\u003c/strong\u003e\u003cspan\u003e pin. \u003c/span\u003eTo avoid interfering with the battery, route the wires up through the bottom of the board, then back down. To avoid possible shorts if the wire insulation gets damaged, don't route the wires through two holes on the same breadboard row (check traces on the bottom of the board).\u003c/li\u003e\n\u003c/ol\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/523/original/greenhouse-logger-3.jpeg?1737107500","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/522/original/greenhouse-logger-1.jpeg?1737107456","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/524/original/greenhouse-logger-4.jpeg?1737107545","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/525/original/greenhouse-logger-5.jpeg?1737107610","metadata":{"caption":""}},{"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  \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  \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  \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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eDS18B20 Temperature Sensor\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eUse diagonal flush cutters to trim the temperature sensor wires to a suitable length.\u003c/li\u003e\n\u003cli\u003eCarefully remove the wires from the perma-proto board.\u003c/li\u003e\n\u003cli\u003eUse wire strippers to remove a short length of insulation from the end of each wire.\u003c/li\u003e\n\u003cli\u003eTin the ends of the wires with solder.\u003c/li\u003e\n\u003cli\u003ePut the wires back in the perma-proto board in the same arrangement as before.\u003c/li\u003e\n\u003cli\u003eSolder the \u003cstrong\u003eDS18B20\u003c/strong\u003e wires in place:\u0026nbsp;\u003cstrong\u003ered\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003e3V\u003c/strong\u003e\u003cspan\u003e pin,\u0026nbsp;\u003c/span\u003e\u003cstrong\u003eblue or black\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003eGND\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;pin,\u0026nbsp;\u003c/span\u003e\u003cstrong\u003eyellow or white\u003c/strong\u003e\u003cspan\u003e\u0026nbsp;wire to \u003cstrong\u003eFeather\u0026nbsp;\u003c/strong\u003e\u003c/span\u003e\u003cstrong\u003eA1\u003c/strong\u003e\u003cspan\u003e pin\u003c/span\u003e\u003cspan\u003e.\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003eGently arrange the wires flush against the perma-proto board.\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003eTape the wires down with Kapton tape on both sides of the perma-proto board. Use Kapton tape to make a strain relief where the sensor wire meets the perma-proto board. This is meant to avoid wiring damage when installing the battery and handling the board to download data logs over USB.\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003eOn the bottom side of the perma-proto board, cover all the pins and breadboard connecting traces with Kapton tape. This is meant to protect against possible shorts or battery punctures.\u003c/span\u003e\u003c/li\u003e\n\u003c/ol\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\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\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\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\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\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":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/526/original/greenhouse-logger-6.jpeg?1737107650","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/527/original/greenhouse-logger-7.jpeg?1737107699","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eEnclosure \u0026amp; Final Assembly\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eDrill a hole, approximately in the center, on one of the short ends of the \u003cstrong\u003eIP65 junction box\u003c/strong\u003e. Be sure to check the outside diameter of the threads on the cable glands that come with your box and use a suitable sized hole drill bit. On my boxes, the outside thread diameter was 15.7 mm, so I used a 5/8\" spade bit.\u003c/li\u003e\n\u003cli\u003eIf the hole has rough edges that might interfere with the cable gland seal, smooth them out with a knife, file, or deburring tool. Be careful not to enlarge the hole.\u003c/li\u003e\n\u003cli\u003eInstall a \u003cstrong\u003ecable gland\u003c/strong\u003e in the hole: remove nut, insert threaded section through the hole you just drilled, replace nut, tighten nut.\u003c/li\u003e\n\u003cli\u003ePlug the Feather board into the proto-board.\u003c/li\u003e\n\u003cli\u003eInstall the \u003cstrong\u003ejumper shunt\u003c/strong\u003e for the \u003cstrong\u003eGND\u003c/strong\u003e and \u003cstrong\u003eA0\u003c/strong\u003e pins.\u003c/li\u003e\n\u003cli\u003e(optional) Cut a piece of Tamiya Universal Plate (or other similar material) to fit in the bottom of the junction box, attach the perma-proto board to it with Nylon M3 screws, then attach standoffs to the plate to make the whole assembly fit just inside the height of the closed junction box lid. The point is to make it so the board can't move around inside the box, reducing the possibility of accidents like unintentionally pressing the RESET button by jamming it against the sensor cable or enclosure lid.\u003c/li\u003e\n\u003cli\u003ePut the \u003cstrong\u003ebattery\u003c/strong\u003e in the gap between the Feather and perma-proto boards.\u003c/li\u003e\n\u003cli\u003ePlug in the battery.\u003c/li\u003e\n\u003cli\u003eThread the \u003cstrong\u003etemperature sensor cable\u003c/strong\u003e through the cable gland, leaving enough slack to make a loop or two of cable inside the box.\u003c/li\u003e\n\u003cli\u003ePut the logger board assembly in the box and arrange the cable loops so everything will fit when the lid is shut. Be careful not to accidentally press the RESET button.\u003c/li\u003e\n\u003cli\u003eCheck the inside diameter of the cable gland seal against the outside diameter of the temperature sensor cable. If the gap is too big, carefully adjust the cable loop and pull out some of the slack, wrap the temperature sensor cable in several layers of electrical tape to make up the difference, then push the cable back in so the tape fits inside the seal. Be careful to avoid creases in the tape.\u003c/li\u003e\n\u003cli\u003eTighten the cable gland seal.\u003c/li\u003e\n\u003cli\u003eGet a 2 gram indicating \u003cstrong\u003esilica gel desiccant pack\u003c/strong\u003e, seal it in a Ziploc bag, and set it aside for later\u003c/li\u003e\n\u003cli\u003eIf your junction box did not come with a pre-installed \u003cstrong\u003elid seal\u003c/strong\u003e, install the seal.\u003c/li\u003e\n\u003c/ol\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/528/original/greenhouse-logger-8.jpeg?1737107744","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/529/original/greenhouse-logger-9.jpeg?1737107840","metadata":{"caption":""}},{"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  \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  \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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eInstall CircuitPython 9.2.1\u003c/h2\u003e\n\u003cp class=\"p1\"\u003eThe code for this project was developed and extensively tested on CircuitPython 9.2.1, which works well for both USB serial connectivity and low deep sleep current. CircuitPython 9.2.2 has a\u0026nbsp;\u003ca href=\"https://github.com/adafruit/circuitpython/issues/9956\" target=\"_blank\"\u003eknown issue\u003c/a\u003e with USB connectivity on ESP32-S3, so you should avoid 9.2.2. Probably 9.2.3 and later will be fine, but I haven't tested that.\u003c/p\u003e\n\u003cp class=\"p1\"\u003eTo download the CircuitPython 9.2.1 .BIN files you can use this direct link to circuitpython.org:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003ca href=\"https://downloads.circuitpython.org/bin/adafruit_feather_esp32s3_nopsram/en_US/adafruit-circuitpython-adafruit_feather_esp32s3_nopsram-en_US-9.2.1.bin\"\u003eAdafruit Feather ESP32-S3 No PSRAM\u003c/a\u003e (9.2.1 .BIN file)\u003c/p\u003e\n\u003cp class=\"p1\"\u003eIf you prefer to try running the latest version of CircuitPython, you can use the .BIN download button from the board detail page at circuitpython.org:\u003c/p\u003e\n\u003cp class=\"p1\" style=\"padding-left: 40px;\"\u003e\u003ca href=\"https://circuitpython.org/board/adafruit_feather_esp32s3_nopsram/\" target=\"_blank\"\u003eAdafruit Feather ESP32-S3 No PSRAM\u003c/a\u003e (board page)\u003c/p\u003e\n\u003cp class=\"p1\"\u003eOnce you have the .BIN file, follow the instructions in the\u0026nbsp;\u003ca href=\"https://learn.adafruit.com/circuitpython-with-esp32-quick-start/web-serial-esptool\" target=\"_blank\"\u003eWeb Serial ESPTool\u003c/a\u003e section of the \"CircuitPython on ESP32 Quick Start\" learn guide to update your board: first erase the flash, then program the .BIN file.\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\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\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\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\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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eCircuitPython Code\u003c/h2\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"button","content":"https://github.com/samblenny/greenhouse-logger/releases/download/v0.2.0/greenhouse-logger-95368a9.zip","metadata":{"class":"btn btn-large btn-block btn-primary","button_type":"primary","target":"_blank","zip_folder":"false"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3 class=\"p1\"\u003eInstall Project bundle\u003c/h3\u003e\n\u003cp class=\"p2\"\u003e\u003cspan class=\"s1\"\u003eTo copy the project bundle files to your CIRCUITPY drive:\u003c/span\u003e\u003c/p\u003e\n\u003col class=\"ol1\"\u003e\n\u003cli class=\"li3\"\u003e\u003cspan class=\"s1\"\u003eDownload the project bundle .zip file using the \"Download Project Bundle\" button above.\u003c/span\u003e\u003c/li\u003e\n\u003cli class=\"li3\"\u003e\u003cspan class=\"s1\"\u003eExpand the zip file by opening it, or use\u0026nbsp;unzip\u0026nbsp;in a Terminal. The zip archive should expand to a folder. When you open the folder, it should contain a\u0026nbsp;README.txt\u0026nbsp;file and a\u0026nbsp;CircuitPython 9.x\u0026nbsp;folder.\u003c/span\u003e\u003c/li\u003e\n\u003cli class=\"li3\"\u003e\u003cspan class=\"s1\"\u003eOpen the CircuitPython 9.x folder and copy all of its contents to your CIRCUITPY drive.\u003c/span\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp class=\"p2\"\u003e\u003cspan class=\"s1\"\u003eTo learn more about copying libraries to your \u003cstrong\u003eCIRCUITPY\u003c/strong\u003e drive, check out the\u0026nbsp;\u003ca href=\"https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-libraries\" target=\"_blank\"\u003e\u003cspan class=\"s2\"\u003eCircuitPython Libraries\u003c/span\u003e\u003c/a\u003e\u0026nbsp;section of the\u0026nbsp;\u003ca href=\"https://learn.adafruit.com/welcome-to-circuitpython\" target=\"_blank\"\u003e\u003cspan class=\"s2\"\u003eWelcome to CircuitPython!\u003c/span\u003e\u003c/a\u003e\u0026nbsp;learn guide.\u003c/span\u003e\u003c/p\u003e\n\u003cp class=\"p2\"\u003e\u003cspan class=\"s1\"\u003eAfter copying the files from the project bundle, your \u003cstrong\u003eCIRCUITPY\u003c/strong\u003e drive should contain the files and directories shown in this screenshot:\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/552/original/CIRCUITPY-screenshot.png?1737853993","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n        \u003ch3\u003eCIRCUITPY/code.py\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003ecode.py\u003c/strong\u003e file has \u003ccode\u003emain()\u003c/code\u003e, which runs each time the Feather wakes from deep sleep, and \u003ccode\u003eadmin_mode()\u003c/code\u003e, which runs when the GND to A0 jumper is active.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIn logging mode (A0 not grounded), the logger runs \u003ccode\u003emain()\u003c/code\u003e which records a temperature measurement then enters deep sleep for 20 minutes (see \u003ccode\u003eINTERVAL_S\u003c/code\u003e). When the logger wakes up from deep sleep, it starts over at the beginning of \u003ccode\u003emain()\u003c/code\u003e, which is why \u003ccode\u003emain()\u003c/code\u003e does not have the normal type of event loop.\u003c/li\u003e\n\u003cli\u003e\u003cspan\u003eIn admin mode (A0 is grounded), the logger wakes from deep sleep, activates the USB interface, and begins flashing the current battery voltage in Morse code on the LED. The LED voltage messages are disabled to conserve power when the battery voltage drops below 3.55V (\u003ccode\u003eLOW_CV\u003c/code\u003e).\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003eSee\u003cstrong\u003e util.py\u003c/strong\u003e below for a description of additional features available by manually importing \u003cstrong\u003eutil.py\u003c/strong\u003e from the CircuitPython REPL.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n","metadata":{}},{"element_type":"embed","content":"https://github.com/samblenny/greenhouse-logger/blob/v0.2.0/code.py"},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eCIRCUITPY/datalogger.py\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003edatalogger.py\u003c/strong\u003e file has functions to check the battery voltage and the DS18B20 temperature. Since I wrote the firmware to work on four different ESP32-S3 boards, there is some logic to detect which hardware features are present according to the value of \u003ccode\u003eboard.board_id\u003c/code\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOn boards which lack a MAX17048 battery monitor chip, the \u003ccode\u003ebattery_centivolts()\u003c/code\u003e returns \u003ccode\u003eNone\u003c/code\u003e instead of a voltage.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003etemp_f()\u003c/code\u003e function returns DS18B20 temperature, converted to Fahrenheit, as a signed 8-bit integer. If there is a problem communicating with the the sensor, the return value is \u003ccode\u003e0x80\u003c/code\u003e (\u003ccode\u003e-128\u003c/code\u003e).\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"embed","content":"https://github.com/samblenny/greenhouse-logger/blob/v0.2.0/datalogger.py"},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eCIRCUITPY/redled.py\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003eredled.py\u003c/strong\u003e file implements the \u003ccode\u003eRedLed\u003c/code\u003e class for sending numeric Morse code messages on the board's LED or Neopixel.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ccode\u003eRedLED.__init__()\u003c/code\u003e checks for whether \u003ccode\u003eboard.LED\u003c/code\u003e, \u003ccode\u003eboard.NEOPIXEL\u003c/code\u003e, and \u003ccode\u003eboard.NEOPIXEL_POWER\u003c/code\u003e are present to decide which type of LED to use.\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eRedLED.morse_char()\u003c/code\u003e sends one numeric Morse code character (alphabet is limited to digits 0-9 along with the KA and AR prosigns). This is meant for sending 3-digit battery voltages using centi-Volt units.\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"embed","content":"https://github.com/samblenny/greenhouse-logger/blob/v0.2.0/redled.py"},{"element_type":"text","content":"\n        \u003ch3\u003eCIRCUITPY/sleepmem.py\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003esleepmemp.py\u003c/strong\u003e file implements the \u003ccode\u003eSleepMem\u003c/code\u003e class for managing access to the 4096 byte ESP32-S3 sleep memory region (\u003ccode\u003ealarm.sleep_memory\u003c/code\u003e) which stores the log of temperature measurements. Many of the methods in \u003ccode\u003eSleepMem\u003c/code\u003e have to do with scaling timestamps, battery voltages, and temperatures so that each measurement can be saved as 4 bytes.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe first 96 bytes of sleep memory are reserved for header values\u003c/li\u003e\n\u003cli\u003eThe logger's maximum capacity is (4000 bytes) / (4 bytes per measurement) = 1000 measurements. Since the interval between measurements is approximately 20 minutes, the memory will fill up after (1000 x 20 minutes) /\u0026nbsp; (60 x 24 minutes per day) = 13.8 days. Based on my testing, there's a reasonable chance the battery might actually last that long, but usually I only log for 3-4 days at a time.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003esleep_memory[EPOCH]\u003c/code\u003e header value stores a custom timestamp epoch value that gets set by \u003ccode\u003eutil.set_clock()\u003c/code\u003e. Timestamps typically use at least 32 bits (4 bytes) with a standard epoch of Jan 1, 1970. Using a custom epoch lets timestamps fit in fewer bits, so the logger can record more measurements.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003esleep_memory[END]\u003c/code\u003e header value stores the index to the end of the data log\u003c/li\u003e\n\u003cli\u003eVoltage measurements in the range of 2.70V (270cV) to 4.30V (430cV) are scaled to fit into an 8-bit unsigned integer by the \u003ccode\u003escale_centivolts()\u003c/code\u003e and \u003ccode\u003eunscale_centivolts()\u003c/code\u003e methods.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003eappend_data()\u003c/code\u003e method saves a timestamped temperature and voltage measurement using bitwise operations and \u003ccode\u003estruct.pack\u003c/code\u003e to fit a 16-bit time, an 8-bit temperature, and an 8-bit voltage into a single 32-bit value.\u003c/li\u003e\n\u003c/ul\u003e\n      ","metadata":{}},{"element_type":"embed","content":"https://github.com/samblenny/greenhouse-logger/blob/v0.2.0/sleepmem.py"},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eCIRCUITPY/util.cpy\u003c/h3\u003e\n\u003cp\u003eThe \u003cstrong\u003eutil.py\u003c/strong\u003e file provides administrative functions that are meant to be used from the CircuitPython REPL. For examples, refer to the \"Logger Usage\" section of this guide.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ccode\u003eutil.batt()\u003c/code\u003e: print the battery voltage (this will usually be inaccurate when a USB cable is connected)\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eutil.dump()\u003c/code\u003e: dump the log of timestamped measurements in CSV format (to copy and paste into CSV file)\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eutil.set_clock()\u003c/code\u003e: interactively set the RTC time, reset the log memory, and set the epoch value in sleep memory\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eutil.now()\u003c/code\u003e: print the RTC time\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"embed","content":"https://github.com/samblenny/greenhouse-logger/blob/v0.2.0/util.py"},{"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  \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  \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  \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  \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  \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  \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  \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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eLogger Usage\u003c/h2\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\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\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\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\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\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\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\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\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\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        \u003ch3\u003eSet Clock \u0026amp; Seal Enclosure\u003c/h3\u003e\n\u003cp\u003eTo set the clock and seal the logger enclosure, you will need:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eScrewdriver\u003c/li\u003e\n\u003cli\u003eComputer and USB data cable (not a charge-only cable!)\u003c/li\u003e\n\u003cli\u003eZiploc bag to store the desiccant pack while you have the logger open\u003c/li\u003e\n\u003cli\u003e(Optional) Anti-static wrist strap\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo keep the parts cost for low for this logger, it uses the ESP32-S3 sleep memory to store measurements and the built-in RTC to keep time. The sleep memory is small and the RTC isn't super accurate. So, for good results, you should clear the memory and set the clock each time you want to start a new data logging session:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIf you haven't already done so as part of downloading CSV data, open the enclosure, set the lid aside, and move the desiccant pack to a Ziploc bag.\u003c/li\u003e\n\u003cli\u003eCarefully lift the board out of the enclosure and move the jumper shunt to the GND pin only (A0 pin should be visible and unconnected).\u003c/li\u003e\n\u003cli\u003ePlug the logger into your computer. Be sure to use a USB data cable. Charge-only cables won't work.\u003c/li\u003e\n\u003cli\u003eWait for the \u003cstrong\u003eCIRCUITPY\u003c/strong\u003e drive to appear, then eject it. Leave the USB cable connected. This is to prepare for resetting the board.\u003c/li\u003e\n\u003cli\u003e(CAUTION: this will clear the log, so download your old log first if needed) Press the board's RESET button.\u003c/li\u003e\n\u003cli\u003eIn a terminal shell on your computer, connect to the logger's serial console with \u003ccode\u003escreen\u003c/code\u003e, \u003ccode\u003etio\u003c/code\u003e, or your favorite serial monitor program. \u003cspan\u003eOn my Mac, I use\u0026nbsp;\u003c/span\u003e\u003ccode\u003escreen -h 9999 -fn /dev/tty.usbmodem* 115200\u003c/code\u003e\n\u003c/li\u003e\n\u003cli\u003eType any key to start the CircuitPython REPL.\u003c/li\u003e\n\u003cli\u003eIn the REPL, run the command \u003ccode\u003eimport util\u003c/code\u003e to load the data logger admin interface functions.\u003c/li\u003e\n\u003cli\u003eRun\u0026nbsp;\u003ccode\u003eutil.set_clock()\u003c/code\u003e to enter the clock setting mode, then answer the prompts to set the date and time (see example below).\u003c/li\u003e\n\u003cli\u003eUse the \u003ccode\u003eCtrl-D\u003c/code\u003e key combo to exit the CircuitPython REPL. When \u003cstrong\u003ecode.py\u003c/strong\u003e starts running, you should see a message with an initial temperature reading.\u003c/li\u003e\n\u003cli\u003eDisconnect your serial monitor program. If you use \u003ccode\u003escreen\u003c/code\u003e, you can do \u003ccode\u003eCtrl-A k y\u003c/code\u003e (press and hold \u003ccode\u003eCtrl\u003c/code\u003e key, press \u003ccode\u003eA\u003c/code\u003e key, release \u003ccode\u003eCtrl\u003c/code\u003e and \u003ccode\u003eA\u003c/code\u003e, press and release\u0026nbsp;\u003ccode\u003eK\u003c/code\u003e key, then press and release \u003ccode\u003eY\u003c/code\u003e key).\u003c/li\u003e\n\u003cli\u003e(optional) Wait until the battery charging LED turns off. Or, if you just downloaded a log and saw that the battery has at least 3.8V (380 cV), you can probably skip this step. ESP32-S3 deep sleep is very easy on batteries.\u003c/li\u003e\n\u003cli\u003eEject the logger's \u003cstrong\u003eCIRCUITPY\u003c/strong\u003e drive and unplug the USB cable.\u003c/li\u003e\n\u003cli\u003eDouble check that the jumper shunt is only connected to the GND pin\u003c/li\u003e\n\u003cli\u003eTake a silica gel desiccant pack out of the Ziploc bag, verify the indicator beads show the gel is still able to absorb water, then put the gel pack in the logger enclosure. Be careful not to accidentally press RESET.\u003c/li\u003e\n\u003cli\u003eDouble check that both halves of the enclosure lid are clear of anything that might interfere with the seal (hair, dirt, corner of silica gel pack, temperature sensor cable, etc).\u003c/li\u003e\n\u003cli\u003eTighten the lid screws to seal the enclosure.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThis CircuitPython REPL log shows an example of what it looks like to set the clock over USB serial:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\n\nPress any key to enter the REPL. Use CTRL-D to reload.\n\nAdafruit CircuitPython 9.2.1 on 2024-11-20; Adafruit Feather ESP32S3 No PSRAM with ESP32S3\n\u003e\u003e\u003e import util\n\u003e\u003e\u003e util.set_clock()\nThis will delete your data, are you sure? [y/N]: y\nSLEEP MEMORY CLEARED\nSet RTC time...\n   year: 2025\n  month: 1\n    day: 17\n   hour: 12\n minute: 48\nseconds: 25\nnew RTC time:  2025-01-17 12:48:25 (epoch + 0)\nnew epoch is:  1737118105\n\u003e\u003e\u003e\n\nAuto-reload is on. Simply save files over USB to run them or enter REPL to disable.\ncode.py output:\nDS18B20: 70 °F, batt: 418 cV\nsleep_memory[96] = 70 F, 418 cV\n\nCode done running.\n\nPress any key to enter the REPL. Use CTRL-D to reload.\nPretending to deep sleep until alarm, CTRL-C or file write.","metadata":{"language":"terminal","linenums":false}},{"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  \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  \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  \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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eWhen you have sealed up the logger, be sure to double check that the desiccant pack is inside the enclosure and that the jumper shunt is only on the GND pin (can you see the A0 pin?).\u003c/p\u003e\n\u003cp\u003eA fully assembled logger should look similar to this picture:\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\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\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\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\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\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/530/original/greenhouse-logger-10.jpeg?1737107864","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eDownload Log \u0026amp; Save to CSV\u003c/h3\u003e\n\u003cp\u003eTo collect data, you will need:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eScrewdriver\u003c/li\u003e\n\u003cli\u003eComputer and USB data cable (not a charge-only cable!)\u003c/li\u003e\n\u003cli\u003eZiploc bag to store the desiccant pack while you have the logger open\u003c/li\u003e\n\u003cli\u003e(Optional) Anti-static wrist strap\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo collect your temperature data:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eUnscrew and remove the enclosure lid. Check seal for debris and clean it if needed. Set the lid aside.\u003c/li\u003e\n\u003cli\u003eMove the desiccant pack to a Ziploc bag and set it aside.\u003c/li\u003e\n\u003cli\u003eLift the logger far enough out of the enclosure so you can access the jumper shunt and the USB port. CAUTION: To avoid data loss, be careful not to hit the \u003ccode\u003eRESET\u003c/code\u003e button or zap the board with static.\u003c/li\u003e\n\u003cli\u003eMove the jumper shunt so that it connects the \u003ccode\u003eGND\u003c/code\u003e and \u003ccode\u003eA0\u003c/code\u003e pins. This will wake the logger out of deep sleep and enable USB serial.\u003c/li\u003e\n\u003cli\u003ePlug in the USB cable.\u003c/li\u003e\n\u003cli\u003eConnect to the logger's USB serial port with your favorite serial monitor (\u003ccode\u003escreen\u003c/code\u003e, \u003ccode\u003etio\u003c/code\u003e, or whatever). Be sure that the program you use is configured to use a scrollback buffer large enough to hold several hundred lines of CSV data. On my Mac, I use \u003ccode\u003escreen -h 9999 -fn /dev/tty.usbmodem* 115200\u003c/code\u003e for a 9999 line buffer.\u003c/li\u003e\n\u003cli\u003eSend the \u003ccode\u003eCtrl-C\u003c/code\u003e key combo to interrupt \u003cstrong\u003ecode.py\u003c/strong\u003e, then type any key to start the CircuitPython REPL.\u003c/li\u003e\n\u003cli\u003eAt the REPL prompt, run\u0026nbsp;\u003ccode\u003eimport util\u003c/code\u003e to load the logger's admin module functions.\u003c/li\u003e\n\u003cli\u003eRun \u003ccode\u003eutil.now()\u003c/code\u003e to check the logger's RTC clock time. When I do this, I like to also type a Python comment with the time from my watch so I can check how far the RTC time has drifted.\u003c/li\u003e\n\u003cli\u003eRun \u003ccode\u003eutil.dump()\u003c/code\u003e to dump a CSV formatted log of timestamp, temperature, and battery voltage (see example below).\u0026nbsp;\u003c/li\u003e\n\u003cli\u003eCopy the CSV data from your serial monitor program into a text file using the \u003ccode\u003e.csv\u003c/code\u003e file extension so you can use it with a spreadsheet or other charting program. (see \"Details on Copying and Pasting CSV Data...\" below)\u003c/li\u003e\n\u003cli\u003eMake a note of the battery voltage shown on the last column of the last line of the CSV data. Use that battery voltage to decide if you need to charge the battery. For example, if you see 399, that means the MAX17048 measured 3.99V (399 centi-Volts), which should be more than enough to log for several days.\u003c/li\u003e\n\u003cli\u003eTell your serial monitor to disconnect from the serial port. For example, in \u003ccode\u003escreen\u003c/code\u003e, type the \u003ccode\u003eCtrl-a k y\u003c/code\u003e key combo.\u003c/li\u003e\n\u003cli\u003eEject your logger's \u003cstrong\u003eCIRCUITPY\u003c/strong\u003e drive\u003c/li\u003e\n\u003cli\u003eUnplug your logger's USB cable.\u003c/li\u003e\n\u003cli\u003eMove the jumper shunt so it's only on the \u003ccode\u003eGND\u003c/code\u003e pin (\u003ccode\u003eA0\u003c/code\u003e should be visible and unconnected).\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eDetails on Copying and Pasting CSV Data Using \u003ccode\u003escreen\u003c/code\u003e:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eAssuming you have just run \u003ccode\u003eutil.dump()\u003c/code\u003e from the CircuitPython REPL and you want to copy the CSV data dump to a text file: Begin by typing the key sequence \u003ccode\u003eCtrl-a [\u003c/code\u003e to enter \u003ccode\u003escreen\u003c/code\u003e's copy mode (hold \u003ccode\u003eCtrl\u003c/code\u003e key, press \u003ccode\u003eA\u003c/code\u003e key, release \u003ccode\u003eA\u003c/code\u003e and \u003ccode\u003eCtrl\u003c/code\u003e, then press and release the\u0026nbsp;\u003ccode\u003e[\u003c/code\u003e key).\u003c/li\u003e\n\u003cli\u003eUse the arrow keys to move your cursor up to the start of the CSV heading line.\u003c/li\u003e\n\u003cli\u003ePress the spacebar once to mark the start of the text you want to copy.\u003c/li\u003e\n\u003cli\u003eUse the arrow keys to move down to the end of the last line of CSV data.\u003c/li\u003e\n\u003cli\u003ePress the spacebar to copy all the selected text. (NOTE: This uses \u003ccode\u003escreen\u003c/code\u003e's copy buffer, which will probably not be synchronized with your system clipboard. To paste, you need to use \u003ccode\u003escreen\u003c/code\u003e's paste feature.)\u003c/li\u003e\n\u003cli\u003eOpen a second shell window in your \u003ccode\u003escreen\u003c/code\u003e session with \u003ccode\u003eCtrl-a c\u003c/code\u003e key sequence.\u003c/li\u003e\n\u003cli\u003eIn the second shell, open your favorite command line text editor (\u003ccode\u003evim\u003c/code\u003e, \u003ccode\u003eemacs\u003c/code\u003e, \u003ccode\u003enano\u003c/code\u003e, or whatever) and prepare your editor for pasting text into a new file. For example, from the shell prompt, do \u003ccode\u003evim my-log.csv\u003c/code\u003e to start editing a file, run the \u003ccode\u003e:set paste\u003c/code\u003e vim command to avoid auto-indent weirdness, then press the\u0026nbsp;\u003ccode\u003eI\u003c/code\u003e key to enter insert mode.\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003escreen\u003c/code\u003e's paste function to paste the CSV data into your text editor with the key sequence \u003ccode\u003eCtrl-a ]\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSave the CSV file and quit your text editor. For example, in vim, press the \u003ccode\u003eEsc\u003c/code\u003e key, then run the\u0026nbsp;\u003ccode\u003e:wq\u003c/code\u003e command.\u0026nbsp;(Now you should be back at the shell prompt for screen's second shell.)\u003c/li\u003e\n\u003cli\u003eQuit the second shell with the \u003ccode\u003eexit\u003c/code\u003e command. (now you should be back at the CircuitPython REPL)\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIf you don't like \u003ccode\u003escreen\u003c/code\u003e or \u003ccode\u003evim\u003c/code\u003e, that's fine. All you need is a serial monitor with a reasonably large scrollback buffer, a text editor, and a method to copy and paste between them. Lots of programs will work for that.\u003c/p\u003e\n\u003cp\u003eOnce you save the CSV data, if you want to start logging again, refer to the \"Set Clock \u0026amp; Seal Enclosure\" procedure above. If you're done logging, refer to the \"Prep Logger for Storage\" procedure below.\u003c/p\u003e\n\u003cp\u003eThis is an example of what it looks like to dump the CSV data log in a CircuitPython REPL (the first two battery measurements were taken with USB connected and the charger on, the rest were running off the battery):\u003c/p\u003e\n      \n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"Adafruit CircuitPython 9.2.1 on 2024-11-20; Adafruit Feather ESP32S3 No PSRAM with ESP32S3\n\u003e\u003e\u003e import util\n\u003e\u003e\u003e util.batt()\n413 cV\n\u003e\u003e\u003e util.now()\n'2025-01-17 12:59:04 (epoch + 333314)'\n\u003e\u003e\u003e # 12:44:38\n\u003e\u003e\u003e util.dump()\n# NVRAM end index: 1212 (28% of buffer)\nDate Time,°F,centi-Volts\n1/13 16:23,70,420\n1/13 16:23,70,420\n1/13 16:44,72,405\n1/13 17:03,71,406\n1/13 17:24,71,405\n1/13 17:43,71,406\n1/13 18:04,71,405\n1/13 18:24,71,406\n1/13 18:44,71,405\n1/13 19:04,71,406\n1/13 19:24,71,405\n1/13 19:44,71,405\n[---about 250 lines edited out---]\n1/17 07:31,63,403\n1/17 07:51,63,403\n1/17 08:11,63,402\n1/17 08:31,64,402\n1/17 08:51,65,402\n1/17 09:11,66,402\n1/17 09:31,61,402\n1/17 09:51,67,402\n1/17 10:11,64,403\n1/17 10:31,68,402\n1/17 10:51,71,403\n1/17 11:11,70,403\n1/17 11:31,74,403\n1/17 11:51,70,403\n1/17 12:11,71,403\n1/17 12:31,71,403\n1/17 12:51,71,403\n\u003e\u003e\u003e","metadata":{"language":"terminal","linenums":false}},{"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        \u003ch3\u003eLED Morse Code Battery Check\u003c/h3\u003e\n\u003cp\u003eSince it's not possible to get an accurate battery voltage measurement when the USB cable is connected, I implemented a \u003ca href=\"https://en.wikipedia.org/wiki/Morse_code\"\u003eMorse code\u003c/a\u003e LED battery check feature. When the jumper shunt connects \u003ccode\u003eGND\u003c/code\u003e to \u003ccode\u003eA0\u003c/code\u003e, the board wakes up from deep sleep, activates USB connectivity, and starts blinking battery voltage messages on the LED.\u003c/p\u003e\n\u003cp\u003eWhen the battery voltage is 3.80V or higher, the LED stays on between battery check messages. Between 3.55V and 3.80V, the LED stays off between messages. Below 3.55V, the LED stays off and stops sending messages to conserve power.\u003c/p\u003e\n\u003cp\u003eEach battery voltage message includes:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003ccode\u003eKA\u003c/code\u003e start of transmission \u003ca href=\"https://en.wikipedia.org/wiki/Prosigns_for_Morse_code\"\u003eprosign\u003c/a\u003e (\u003ccode\u003e-.-.-\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eThree digit battery voltage in centi-Volts, repeated twice, with a word space in between\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eAR\u003c/code\u003e end of transmission prosign (\u003ccode\u003e.-.-.\u003c/code\u003e)\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIf you wondered why the logger uses centi-Volts instead of Volts for battery units, it's because using cV instead of V makes the Morse code messages shorter (no decimal points).\u003c/p\u003e\n\u003cp\u003eThe main point of the battery voltage messages is to help with discharging the battery to a good storage voltage. To gently discharge the battery, you can jumper \u003ccode\u003eGND\u003c/code\u003e to \u003ccode\u003eA0\u003c/code\u003e, unplug USB, and wait until the voltage goes down.\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","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        \u003ch3\u003ePrep Logger for Storage\u003c/h3\u003e\n\u003cp\u003eTo extend the useable lifetime of your batteries, I recommend following this procedure when you are done using your logger for a while:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIf you haven't already done so, remove the enclosure lid and move the desiccant pack to a Ziploc bag.\u003c/li\u003e\n\u003cli\u003eIf you haven't already done so, carefully lift the logger out of the enclosure and move the jumper shunt so it connects the \u003ccode\u003eGND\u003c/code\u003e and \u003ccode\u003eA0\u003c/code\u003e pins.\u003c/li\u003e\n\u003cli\u003eCheck the LED. If it does not turn on or blink, that means the battery voltage is below 3.55V. If the LED stays off for about 15 seconds between Morse code voltage check messages, that means the voltage is between 3.55V and 3.80V. In that case, plug the USB cable in, wait until the battery is charged, then eject the CIRCUITPY drive and unplug the logger.\u003c/li\u003e\n\u003cli\u003eAt this point, the logger should be unplugged with \u003ccode\u003eGND\u003c/code\u003e connected to \u003ccode\u003eA0\u003c/code\u003e by the jumper shunt. The LED should be blinking battery voltage messages in Morse code. Between messages, the LED should stay on solid for about 8 seconds. Put the logger aside in a place where you can periodically look at the battery messages. Wait until the message shows the voltage is close to 3.8V (it may take several hours).\u003c/li\u003e\n\u003cli\u003eStart checking the LED more frequently and wait until the LED turns off between battery check messages. When the LED stays off between messages, it means the voltage is below 3.80V, so the battery is ready to store.\u003c/li\u003e\n\u003cli\u003eUnplug the battery pack from the ESP32-S3 Feather board.\u003c/li\u003e\n\u003cli\u003e(optional) Put the desiccant back back in the enclosure and re-seal the lid.\u003c/li\u003e\n\u003c/ol\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","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  \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  \n  \n  \n        \u003ch2\u003eMaking Charts\u003c/h2\u003e\n\u003cp\u003eMy ultimate goal for this greenhouse data logger project was making charts so the folks from the community garden group could compare how the temperatures in their three greenhouses change over time.\u003c/p\u003e\n\u003cp\u003eIn most spreadsheet programs, making a 2D line chart is fairly easy when you have a 2-column CSV file with X-axis values in the first column and Y-axis values in the second column. But, in this case, it's trickier because I need to make a 2D scatter plot with 3 data series where the X-axis values are out of phase from each other. (It takes me 5 minutes or so to set the clock on a logger, seal it up, and move on to the next one.)\u003c/p\u003e\n\u003ch3\u003eSpreadsheet Programs\u003c/h3\u003e\n\u003cp\u003eLots of software can read data from CSV files and make 2D scatter plots with timestamps on the X axis and temperatures on the Y axis. For my purposes, the easiest thing to use was Apple's \"Numbers\" spreadsheet app that comes with macOS, so I will explain how to use Numbers. Some possible alternatives include:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMicrosoft Excel\u003c/li\u003e\n\u003cli\u003eGoogle Sheets\u003c/li\u003e\n\u003cli\u003e\n\u003ca href=\"https://manpages.ubuntu.com/manpages/trusty/man1/gnuplot.1.html\"\u003egnuplot\u003c/a\u003e command line program (Ubuntu, Debian, etc)\u003c/li\u003e\n\u003cli\u003e\n\u003ca href=\"https://pypi.org/project/matplotlib/\" target=\"_blank\"\u003eMatplotlib (pypi)\u003c/a\u003e Python charting library\u003c/li\u003e\n\u003c/ul\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\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\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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003e2D Scatter Plot for 3 CSV Files\u003c/h3\u003e\n\u003cp\u003eThe Apple Numbers app is powerful, but some chart features that you might expect to have their own settings end up being determined as a non-obvious side effect of some other setting elsewhere in the program. This guide isn't about spreadsheets, so I'm just going to list off the steps of the chart-making incantation I use without getting into the rationale for why each step is necessary.\u003c/p\u003e\n\u003cp\u003eFirst Part: Combine CSV data into a single spreadsheet with the formatting that makes 2D scatter plots happy:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIn the macOS Finder, select your 3 CSV files (or 1 for each logger you built) that you copied and pasted from the serial monitor with the CircuitPython REPL and\u0026nbsp;\u003ccode\u003eutil.dump()\u003c/code\u003e. To select multiple files at once, you can hold down the command key while left-clicking on the files.\u003c/li\u003e\n\u003cli\u003eOpen the CSV files with Command-O, a double-click, or whatever. You should see 3 spreadsheet windows open in Numbers.\u003c/li\u003e\n\u003cli\u003eCreate a new blank spreadsheet file with File menu \u0026gt; New. In later steps, this is \"the chart spreadsheet\". The leftmost column and topmost row should both be gray, indicating that you have the default 1 header row and 1 header column.\u003c/li\u003e\n\u003cli\u003eIn right sidebar \u0026gt; Table tab \u0026gt; Header \u0026amp; Footer section: set the leftmost pulldown menu to 0 (remove row headers). The background color of Column A should turn from gray to white.\u003c/li\u003e\n\u003cli\u003eSwitch windows to your first CSV data log file.\u003c/li\u003e\n\u003cli\u003eSelect and copy all cells of the first two columns,\u0026nbsp;\u003ccode\u003eDate Time\u003c/code\u003e and \u003ccode\u003e°F\u003c/code\u003e (skip the centi-Volts column).\u003c/li\u003e\n\u003cli\u003eSwitch windows to the chart spreadsheet.\u003c/li\u003e\n\u003cli\u003eClick on cell A1 (first row of first column) and paste the CSV data.\u003c/li\u003e\n\u003cli\u003eChange the text of A1 from \"Date Time\" to something useful like \"Greenhouse 1\" or whatever.\u003c/li\u003e\n\u003cli\u003eClick on cell A1 to make sure that Numbers shows the letter and number row and column labels.\u003c/li\u003e\n\u003cli\u003eClick on the \"A\" column label to select the entire column of timestamp data.\u003c/li\u003e\n\u003cli\u003eIn right sidebar \u0026gt; Cell tab \u0026gt; Data Format: change the pulldown menu from \"Automatic\" to \"Date \u0026amp; Time\"\u003c/li\u003e\n\u003cli\u003eRepeat the previous 8 steps for each of the remaining CSV files, pasting the second file's data into columns C \u0026amp; D, the third file's data into columns E \u0026amp; F, etc. Be sure to set the timestamp columns to the \"Date \u0026amp; Time\" data format.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eWhen you've finished pasting data series into the chart spreadsheet, it should look similar to this screenshot:\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\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/547/original/01-combined-data.png?1737607763","metadata":{"caption":""}},{"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  \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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eSecond Part: Make the 2D scatter plot chart:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIn the window for the chart spreadsheet, select all 6 columns (or 2 for each CSV file you pasted).\u003c/li\u003e\n\u003cli\u003eFrom the menubar, select Insert \u0026gt; Chart \u0026gt; 2D Scatter. You should see a chart next to your data, but it will look wrong.\u003c/li\u003e\n\u003cli\u003eClick the green \"Edit Data References\" button below the chart.\u003c/li\u003e\n\u003cli\u003eYou should see a new control overlay appear on the column headings for columns B, C, D, E, and F. The overlay has a dark gray background with rounded corners on the top and colored dots in the middle.\u003c/li\u003e\n\u003cli\u003eClick the colored dot for the column heading overlay of column B. It should open a context menu.\u003c/li\u003e\n\u003cli\u003eFrom the context menu, uncheck the item for \"Share X Values\". This should change the appearance of the chart and reduce the number of column heading overlays.\u003c/li\u003e\n\u003cli\u003eWith the chart still selected, in the right sidebar, switch to the Series tab.\u003c/li\u003e\n\u003cli\u003eIn Series tab \u0026gt; Data Symbols \u0026gt; Size: click once on the up arrow control, changing size from \"Auto\" to \"2\".\u003c/li\u003e\n\u003cli\u003eIn Series tab \u0026gt; Connection Lines: change \"No Lines\" to \"Curved\".\u003c/li\u003e\n\u003cli\u003eIn the right sidebar, switch to the Axis tab\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (X) \u0026gt; Label Angle: change \"Horizontal\" to \"Left Diagonal\"\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (X) \u0026gt; Axis Scale \u0026gt; Min: enter a date time string, such as \"1/14 12 pm\" where the time is the earliest timestamp in your data, rounded down to the previous multiple of 4 hours.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (X) \u0026gt; Axis Scale \u0026gt; Max: enter a date time string, such as \"1/17 12 pm\" where the time is the latest timestamp in your data, rounded up to the next multiple of 4 hours.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (X) \u0026gt; Axis Scale \u0026gt; Steps \u0026gt; Major: click the up arrow button until the second X axis label on the chart is exactly 4 hours later than the first label. For example, if your locale is set to 24 hour time and the first label is \"1/14 12:00\", the second label should be \"1/14 16:00\".\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (X) \u0026gt; Major Gridlines: change \"None\" to the solid line and change the color to light gray. You should now have X-axis grid lines dividing your chart into 4 hour intervals.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (Y) \u0026gt; Axis Scale \u0026gt; Min: enter a temperature number, such as \"10\", where the number is the lowest temperature in your data rounded down to the previous multiple of 10.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (Y) \u0026gt; Axis Scale \u0026gt; Max: enter a temperature number, such as \"90\", where the number is the highest temperature in your data rounded up to the next multiple of 10.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (Y) \u0026gt; Axis Scale \u0026gt; Steps \u0026gt; Major: click the up arrow button until the second Y axis label on the chart is exactly 10 degrees higher than the first label. For example, if your first label is 10, the second should be 20.\u003c/li\u003e\n\u003cli\u003eIn Axis tab \u0026gt; Value (Y) \u0026gt; Major Gridlines: change \"None\" to the solid line and change the color to light gray. You should now have Y-axis grid lines dividing your chart into 10 degree intervals.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThe resulting chart should look something like this:\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\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\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/549/original/02-combined-chart.png?1737609577","metadata":{"caption":""}},{"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  \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        \u003ch3\u003eInterpreting Charts\u003c/h3\u003e\n\u003cp\u003eThe chart above shows a temperature scatter plot for 3 greenhouses over a 3 day period (phase starts at noon):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGreenhouse 1 is blue\u003c/li\u003e\n\u003cli\u003eGreenhouse 2 is green\u003c/li\u003e\n\u003cli\u003eGreenhouse 3 is gray\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSome observations:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe peaks between 8 AM and 5 PM are from solar heating\u003c/li\u003e\n\u003cli\u003eThe dense nighttime zigzag pattern on the blue line is because that greenhouse has a potent heater with a thermostat that initially maintained the temperature between about 62°F and 73°F. On the second day, somebody adjusted the thermostat to maintain temperature between about 60°F and 68°F.\u003c/li\u003e\n\u003cli\u003eFor the green line, the nights start with a zigzag pattern that quickly begins to sag down to below the thermostat setting. This greenhouse has good double layer insulated plastic glazing which retains heat well. But, the heater is undersized and unable to hold the requested temperature.\u003c/li\u003e\n\u003cli\u003eThe gray line is for an unheated greenhouse with a single layer of plastic. It gets pretty cold.\u003c/li\u003e\n\u003c/ul\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\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":{}}]