Compare commits

...

2 Commits

Author SHA1 Message Date
bcb7c56b1a Update config, README, board for v2.x 2026-01-03 03:54:32 -05:00
07eefdb466 Add configurable room health settings
Port in from SuperSensor configuration.
2026-01-03 03:54:10 -05:00
6 changed files with 14983 additions and 9087 deletions

View File

@@ -1,56 +1,61 @@
# MicroEnv v1.x # MicroEnv v2.x
The MicroEnv is a compact and simplistic environmental monitoring sensor, ![MicroEnv](cover.png)
based on the ESP32-C3 and the Sensirion SGP41 and SHT45 sensors, using
ESPHome for integration with HomeAssistant
Use a MicroEnv near a source of emissions to monitor air quality, or as The MicroEnv is a compact and simplistic environmental monitoring sensor, based on the ESP32-C3 and the Sensirion SGP41 VOC/NOx and SHT45 temperature/humidity sensors, using ESPHome for integration with HomeAssistant.
a general room environmental tracking sensor. It is, in effect, a cut
down version of the environmental sensor package of my [Supersensor 2.x](https://github.com/joshuaboniface/supersensor2)
project in a much smaller footprint.
Power is provided by USB-C, though a very slim connector is required when Use a MicroEnv near a source of emissions to monitor air quality, evaluate temperatures, or as a general room environmental tracking sensor. It is, in effect, a cut down version of the environmental sensor package of my [SuperSensor](https://github.com/joshuaboniface/supersensor) project in a much smaller, standalone footprint.
using SMD soldering without a buffer.
The Sensirion sensors are elevated with through-hole pins, to ensure Power is provided by USB-C, which is also used for initial flashing. Once running, the device connects to WiFi to provide data to Home Assistant or via the ESPHome WebUI/API.
optimal distance between the (warm) board and the sensors themselves.
You can either directly solder the sensors to the board or, as I do, The Sensirion sensors are directly soldered onto the board along with their power management circuitry, using SMD techniques, which is in contrast to the socketed GY-form-factor sensors from [version 1.x](https://github.com/joshuaboniface/microenv/tree/v1.x). This provides more compactness and flexibility in terms of positioning to ensure the temperature sensor is properly isolated from the rest of the system.
use 4-pin female sockets for easy changing should it be necessary.
Version 2.x features an extended 5mm-width, 40mm-length "neck", the end of which contains the SHT45 temperature sensor, for maximum isolation from the ESP32 and SGP41 sensor, both of which produce heat which can negatively affect the SHT45 temperature. This emulates the design of many temperature probe devices which use similar long boards for isolation purposes and work quite well in this regard.
![MicroEnv Board Design](/board/pcb.svg) ![MicroEnv Board Design](/board/pcb.svg)
Since this revision uses directly SMD-mounted components, it requires fine soldering using solder paste and either a hotplate or, with extreme care, hot air soldering techniques. It is strongly recommended that anyone attempting to build one read the datasheets for the [SHT45](https://sensirion.com/media/documents/33FD6951/67EB9032/HT_DS_Datasheet_SHT4x_5.pdf) and [SGP41](https://sensirion.com/media/documents/5FE8673C/61E96F50/Sensirion_Gas_Sensors_Datasheet_SGP41.pdf) sensors thoroughly to ensure they are not subject to overheating which can result in potential damage or incorrect operation. We recommend using low-temperature GA-SN solder paste for this purpose, and suggest ordering the PCB with lead-free HASL to facilitate this.
## v1.x Compatibility
The only changes between v1.x and v2.x are in the board layout and component mounting; for software purposes, both versions are functionally identical aside from versioning. Thus all changes should be ported to both the `v1.x` and `v2.x` branches. There is no `master` branch.
## Parts List ## Parts List
| Qty | Component | Cost (mid-2025 CAD, ex. shipping) | Links | **Note:** All prices are $CAD, as of late November 2025, excluding shipping, sales, and bulk discounts beyond those indicated.
|-------|---------------------|-----------------------------------|-------|
| 1 | GY-SGP41 | $9.58 | [AliExpress](https://www.aliexpress.com/item/1005007958589642.html) |
| 1 | GY-SHT45 | $5.67 | [AliExpress](https://www.aliexpress.com/item/1005008175340220.html)* |
| 1 | ESP32-C3 | $3.12 | [AliExpress](https://www.aliexpress.com/item/1005007205044247.html) |
| 2 | Female 4-pin header | $0.37 ($14.99/40) | [Amazon](https://www.amazon.ca/dp/B08LF3S5S8) |
| 1 | Custom PCB (JLC) | $0.50 ($5.00/10) | [GitHub](https://github.com/joshuaboniface/microenv/tree/master/board) |
| **TOTAL** | | **$19.24** | |
`*` Ensure you select the correct device on the page as it shows multiple options. **Note:** For AliExpress item links marked `*`, ensure you select the correct device; multiple different models share the page.
## Configurable Options | Qty | PCB ID(s) | Component | Cost | Links |
|-------|-----------|------------------------------------------------|---------------------|-------|
| 1 | N/A | PCB - "MicroEnv" | $0.64 ($3.20/5) | [EasyEDA/JLCPCB](/board/pcb.easyeda.json) |
| 1 | ESP32-C3 | ESP32-C3 development board (SMD) | $2.46 ($12.28/5) | [AliExpress](https://www.aliexpress.com/item/1005007205044247.html)* |
| 1 | SHT4x | Sensirion SHT45-AD1F-R2 | $9.37 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/sensirion-ag/SHT45-AD1F-R2/17180856) |
| 1 | SGP4x | Sensirion SGP41-D-R4 | $13.00 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/sensirion-ag/SGP41-D-R4/15652788) |
| 1 | R1 | CR1206-FX-10R0ELFCT-ND (10Ω 1% 1/4W 1206) | $0.16 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/bourns-inc/CR1206-FX-10R0ELF/2562695) |
| 1 | C1 | 399-C0805C106K8PACTUCT-ND (10μF 10V X5R 0805) | $0.19 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/kemet/C0805C106K8PACTU/1090830) |
| 1 | C2 | 399-C0805C104K5RACTUCT-ND (0.1μF 50V X7R 0805) | $0.12 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/kemet/C0805C104K5RACTU/411169) |
| 1 | C3 | 399-C0805C105K3RACTUCT-ND (1μF 50V X7R 0805) | $0.18 | [DigiKey.ca](https://www.digikey.ca/en/products/detail/kemet/C0805C105K3RACTU/2211765) |
| **T** | | | **$26.14** | *plus tools, solder, etc.* |
There are several UI-configurable options with the MicroEnv to help you get ## Case
the most out of the sensor for your particular use-case.
### Temperature Offset (selector, -30 to +10 @ 0.1, -5 default) The MicroEnv also includes a case to enclose the sensor while still providing good airflow to cool the microcontroller and provide quality exposure for the sensors. This case can be printed in any material, though ABS is recommended for rigidity and strength.
Allows calibration of the SHT45 temperature sensor with an offset from -30 to +10 ## Calibration
degrees C. Useful if the sensor is misreporting actual ambient tempreatures. Due
to internal heating of the SHT45 by the ESP32, this defaults to -5; further
calibration may be needed for your sensors and environment.
### Humidity Offset (selector, -20 to +20 @ 0.1) Due to its design explicitly considering temperature accuracy, the MicroEnv should not require any Temperature Offset corrections in normal use; the option is however still provided just in case this is required in your environment.
Allows calibration of the SHT45 humidity sensor with an offset from -20 to +20 The heat component of the SHT45 is explicitly disabled, as this should only be required in extreme humidity conditions.
percent relative humidity. Useful if the sensor is misreporting actual humidity.
## Home Assistant View Like its parent SuperSensor, the MicroEnv provides several additional configurable options for a "Room Health" metric system. For more detail, see [the main SuperSensor README](https://github.com/joshuaboniface/supersensor/tree/v3.x/configuration#room-health) section on the Room Health system.
This image shows the various available sensors in Home Assistant. # Contributing
![MicroEnv Home Assistant](/microenv-homeassistant.png) If you wish to contribute to the MicroEnv project, please open a pull request in this repository. All pull requests will be carefully reviewed before inclusion, as we are extremely selective about what we modify. Note that any signifiant logical changes should also be compatible with the SuperSensor, and changes from that project will be occasionally integrated here.
Please ensure you target the correct branch(es) for your changes. Currently we are supporting both `v1.x` and `v2.x` in software as noted above, and there is no `master`/`main` branch. This support of both `v1.x` and `v2.x` means a 1-to-1 correlation of functionality between those two versions, so any PR to one will be back- or forward-ported to the other, and your changes(s) must be compatable with both.
Any changes must be **globally applicable to all users** of the project; if you wish to maintain your own customizations that are only applicable for yourself, please fork the project and adjust your package import URL accordingly.
"AI" (LLM) contributions are not prohibited, but you - a human - **must** at least review, reformat, and test the changes yourself before submitting them. Obviously pure-LLM-generated PRs that do not function, mangle formatting or functionality, or otherwise clearly have no human review will be rejected with prejudice. This extends to the PR body itself: write in your own words, not the output of an LLM, and if you can't concisely sumarize the changes yourself, then we're not interested in them.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 71 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
--- ---
############################################################################### ###############################################################################
# MicroEnv v1.0 ESPHome configuration # MicroEnv v2.0 ESPHome configuration
############################################################################### ###############################################################################
# #
# Copyright (C) 2025 Joshua M. Boniface <joshua@boniface.me> # Copyright (C) 2025 Joshua M. Boniface <joshua@boniface.me>
@@ -26,11 +26,11 @@ esphome:
friendly_name: "MicroEnv Sensor" friendly_name: "MicroEnv Sensor"
project: project:
name: "Joshua Boniface.microenv" name: "Joshua Boniface.microenv"
version: "1.0" version: "2.0"
min_version: 2025.11.0 min_version: 2025.11.0
dashboard_import: dashboard_import:
package_import_url: github://joshuaboniface/microenv/microenv.yaml@v1.x package_import_url: github://joshuaboniface/microenv/microenv.yaml@v2.x
esp32: esp32:
board: esp32-c3-devkitm-1 board: esp32-c3-devkitm-1
@@ -52,6 +52,54 @@ globals:
restore_value: true restore_value: true
initial_value: "0.0" initial_value: "0.0"
#
# Room Health settings
#
- id: room_health_temperature_min
type: float
restore_value: true
initial_value: "21.0"
- id: room_health_temperature_max
type: float
restore_value: true
initial_value: "24.0"
- id: room_health_temperature_penalty
type: float
restore_value: true
initial_value: "10.0"
- id: room_health_humidity_min
type: float
restore_value: true
initial_value: "40.0"
- id: room_health_humidity_max
type: float
restore_value: true
initial_value: "60.0"
- id: room_health_humidity_penalty
type: float
restore_value: true
initial_value: "5.0"
- id: room_health_voc_weight
type: float
restore_value: true
initial_value: "0.4"
- id: room_health_temperature_weight
type: float
restore_value: true
initial_value: "0.3"
- id: room_health_humidity_weight
type: float
restore_value: true
initial_value: "0.3"
logger: logger:
level: INFO level: INFO
baud_rate: 115200 baud_rate: 115200
@@ -292,6 +340,35 @@ text_sensor:
mac_address: mac_address:
name: "WiFi MAC Address" name: "WiFi MAC Address"
- platform: template
name: "Chemical Pollution"
id: sgp41_chemical_pollution
icon: mdi:molecule
lambda: |-
float voc = id(sgp41_tvoc_ppb).state;
if (isnan(voc) || voc < 1) return {"Unknown"};
else if (voc <= 200) return {"Excellent"};
else if (voc <= 400) return {"Good"};
else if (voc <= 600) return {"Moderate"};
else if (voc <= 1500) return {"Unhealthy"};
else return {"Hazardous"};
update_interval: 15s
- platform: template
name: "Room Health"
id: room_health_text
icon: mdi:home-heart
lambda: |-
float score = id(room_health_score).state;
if (score < 0) return {"Unknown"};
else if (score >= 100.0) return {"Excellent"};
else if (score >= 95.0) return {"Great"};
else if (score >= 90.0) return {"Good"};
else if (score >= 80.0) return {"Fair"};
else if (score >= 60.0) return {"Poor"};
else return {"Bad"};
update_interval: 15s
button: button:
- platform: restart - platform: restart
name: "ESP32 Restart" name: "ESP32 Restart"
@@ -336,3 +413,150 @@ number:
id: humidity_offset id: humidity_offset
value: !lambda 'return float(x);' value: !lambda 'return float(x);'
# Room Health Calibration Values
# These values allow the user to tweak the values of the room health calculation
- platform: template
name: "Room Health Min Temperature"
id: room_health_temperature_min_setter
min_value: 15
max_value: 30
step: 0.5
lambda: |-
return id(room_health_temperature_min);
set_action:
then:
- globals.set:
id: room_health_temperature_min
value: !lambda 'return float(x);'
- platform: template
name: "Room Health Max Temperature"
id: room_health_temperature_max_setter
min_value: 15
max_value: 30
step: 0.5
lambda: |-
return id(room_health_temperature_max);
set_action:
then:
- globals.set:
id: room_health_temperature_max
value: !lambda 'return float(x);'
- platform: template
name: "Room Health Temperature Penalty"
id: room_health_temperature_penalty_setter
min_value: 1
max_value: 20
step: 1
lambda: |-
return int(id(room_health_temperature_penalty));
set_action:
then:
- globals.set:
id: room_health_temperature_penalty
value: !lambda 'return float(x);'
- platform: template
name: "Room Health Min Humidity"
id: room_health_humidity_min_setter
min_value: 20
max_value: 80
step: 1.0
lambda: |-
return id(room_health_humidity_min);
set_action:
then:
- globals.set:
id: room_health_humidity_min
value: !lambda 'return float(x);'
- platform: template
name: "Room Health Max Humidity"
id: room_health_humidity_max_setter
min_value: 20
max_value: 80
step: 1.0
lambda: |-
return id(room_health_humidity_max);
set_action:
then:
- globals.set:
id: room_health_humidity_max
value: !lambda 'return float(x);'
- platform: template
name: "Room Health Humidity Penalty"
id: room_health_humidity_penalty_setter
min_value: 1
max_value: 10
step: 1
lambda: |-
return int(id(room_health_humidity_penalty));
set_action:
then:
- globals.set:
id: room_health_humidity_penalty
value: !lambda 'return float(x);'
- platform: template
name: "Room Health VOC Weight"
id: room_health_voc_weight_setter
min_value: 0.00
max_value: 1.00
step: 0.01
lambda: |-
return id(room_health_voc_weight);
set_action:
- if:
condition:
lambda: |-
float total = x + id(room_health_temperature_weight) + id(room_health_humidity_weight);
return (total > 0.0) && (total <= 1.0);
then:
- globals.set:
id: room_health_voc_weight
value: !lambda 'return float(x);'
else:
- logger.log:
format: "Rejected VOC weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
args: [ 'x' ]
- platform: template
name: "Room Health Temperature Weight"
id: room_health_temperature_weight_setter
min_value: 0.00
max_value: 1.00
step: 0.01
lambda: |-
return id(room_health_temperature_weight);
set_action:
- if:
condition:
lambda: |-
float total = x + id(room_health_voc_weight) + id(room_health_humidity_weight);
return (total > 0.0) && (total <= 1.0);
then:
- globals.set:
id: room_health_temperature_weight
value: !lambda 'return float(x);'
else:
- logger.log:
format: "Rejected Temperature weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
args: [ 'x' ]
- platform: template
name: "Room Health Humidity Weight"
id: room_health_humidity_weight_setter
min_value: 0.00
max_value: 1.00
step: 0.01
lambda: |-
return id(room_health_humidity_weight);
set_action:
- if:
condition:
lambda: |-
float total = x + id(room_health_temperature_weight) + id(room_health_voc_weight);
return (total > 0.0) && (total <= 1.0);
then:
- globals.set:
id: room_health_humidity_weight
value: !lambda 'return float(x);'
else:
- logger.log:
format: "Rejected Humidity weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
args: [ 'x' ]