This looks like an interesting board. May be a good upgrade for the 8266
Yes I saw that too. Apparently, pin compatible. I will try and get a board and see how it works.
I have gotten the sketch working on ESP32. So it should be pretty simple to get working.
I have a new board on the way that uses a smaller form factor MCU. It will support either Seeed XIAO RP2040 or Adafruit QT Py ESP32-S2.
I havenāt yet worked on running the sketch on the Seeed XIAO, but as itās pin compatible with the QT Py and runs Ardiuno code, it should be trivial to get working. I like these boards as they are the size of a postage stamp and as powerful, maybe more so than the Wemos boards.
Apparently thereās a hardware issue with V1.0.0
Currently testing V2.1.0 (received it yesterday) and itās great so far.
Any news about compatibility with airgradient board? Iām waiting mine to arrive from JLCPCB
We are working on making the AirGradient Arduino library compatible with ESP32 boards.
The current library does not work with these chips.
Iām really interested in this. Using esphome, it should be possible to use it for not just airgradient, but adding bluetooth_proxy which could be handy given airgradients are likely scattered around. 2 tasks with one device.
I received by Pro (non soldered) kit very recently and have been having trouble uploading firmware with ESPHome as it times out very often. I have absolutely no time out issues with any of my other ~30 ESP boards. The board that came with the kit is a Lolin D1 āMimiā which is based on the 8266 so I figured Iād try to replace it with a pin compatible ESP32 S2 Mini or an ESP32-WROOM-32 ESP Mini 32 (no idea on brand or proper nameā¦).
ESP32 S2:
The OLED display, PMS5003 and SHT3X all worked fine but I just could not get UART1 for the CO2 sensor to work.
ESP32 WROOM 32 (Mini)
Everything works perfectly and it is pin compatible so no mods necessary.
Note: Sorry for terrible pic⦠camera on my S20Ultra is awful.
Edit:
Nevermind⦠it works but now I cannot close the enclosure without modifying it as the corner where the LEDs are interferes with the recessed part of the enclosure or removing the PCB corner where the power led is (likely not an issue to do so but I am not sure I want to). The ESP32 S2 fits perfectly but I canāt get the UART1 to workā¦
Edit2: Creative and careful use of a Dremel on both the ESP32 board and the enclosure solved the problem.
This is very interesting as we want to make the board ESP32 compatible.
There are D1 ESP32 based modules that are smaller and should not have the problem that we need to adjust the enclosure, e.g.
S2 Mini
or
C3 Mini
Would you like to share the code adjustments you made?
@AirGradient - I have an ESP32 S2 Mini but I could not get UART1 to work as stated above. I did not see any errors so I did not know what else to try.
I do not have a C3 Mini and I did not find any on amazon so I canāt test that one.
My ESPHome code is for the ESP32-WROOM-32 that is a bit too large to fit (I modified both the board and the case and it fits now):
Note: Code is still work in progressā¦
substitutions:
devicename: "air-quality-sensor"
devicename_no_dashes: "air_quality_sensor"
friendly_devicename: "Air Quality Sensor"
device_description: "Air Gradient Air Quality Sensor"
update_interval_s: "2s"
update_interval_wifi: "120s"
esphome:
name: "${devicename}"
comment: "${device_description}"
# Automatically add the mac address to the name
# so you can use a single firmware for all devices
# name_add_mac_suffix: true
esp32:
board: nodemcu-32s
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret api_pwd
ota:
password: !secret ota_pwd
wifi:
networks:
- ssid: !secret iot_wifi_ssid
password: !secret iot_wifi_password
reboot_timeout: 15min
#Faster than DHCP. Also use if can't reach because of name change
manual_ip:
static_ip: 192.168.3.212
gateway: 192.168.3.1
subnet: 255.255.255.0
dns1: 192.168.1.25
dns2: 192.168.1.26
#Manually override what address to use to connect to the ESP.
#Defaults to auto-generated value. Example, if you have changed your
#static IP and want to flash OTA to the previously configured IP address.
# use_address: 192.168.3.212
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${devicename}"
password: !secret iot_wifi_password
web_server:
port: 80
include_internal: false
switch:
- platform: restart
name: "${friendly_devicename}: Restart"
- platform: template
name: "Calibrate CO2 Sensor"
id : "calibrate_co2_sensor"
disabled_by_default: true
turn_on_action:
- senseair.background_calibration: co2_sensor
- logger.log: "CO2 Sensor Calibration Triggered! Must be done OUTDOORS!"
- platform: template
name: "CO2 Sensor Calibration Result"
id : co2_sensor_calibration_result
disabled_by_default: true
turn_on_action:
- senseair.background_calibration_result: co2_sensor
button:
- platform: safe_mode
name: "${friendly_devicename}: Restart (Safe Mode)"
captive_portal:
# Sync time with Home Assistant
time:
- platform: homeassistant
id: ha_time
text_sensor:
- platform: wifi_info
ip_address:
name: "${friendly_devicename}: IP"
icon: "mdi:ip-outline"
update_interval: ${update_interval_wifi}
ssid:
name: "${friendly_devicename}: SSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
bssid:
name: "${friendly_devicename}: BSSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
mac_address:
name: "${friendly_devicename}: MAC"
icon: "mdi:network-outline"
scan_results:
name: "${friendly_devicename}: Wifi Scan"
icon: "mdi:wifi-refresh"
disabled_by_default: true
#https://esphome.io/guides/automations.html?highlight=restore_value#bonus-2-global-variables
globals: ##to set default reboot behavior
# Wifi variables
- id: wifi_connection
type: bool
restore_value: no
initial_value: "false"
i2c:
sda: 21 #D2
scl: 22 #D1
uart:
- rx_pin: 18 #D5
tx_pin: 19 #D6
baud_rate: 9600
id: uart1
- rx_pin: 16 #D4
tx_pin: 17 #D3
baud_rate: 9600
id: uart2
font:
# gfonts://family[@weight]
- file: "gfonts://Roboto"
id: roboto
size: 12
- file: "gfonts://Roboto"
id: roboto_symbols
size: 12
glyphs: [
"\U000000B5", #µ
"\U00000067" #g
]
- file: "gfonts://Roboto"
id: roboto_medium
size: 16
- file: "gfonts://Roboto"
id: roboto_large
size: 32
- file: "fonts/materialdesignicons-webfont.ttf"
id: wifi_icon_font
size: 12
glyphs: [
"\U000F05A9", #wifi
"\U000F05AA" #no wifi
]
- file: "fonts/materialdesignicons-webfont.ttf"
id: face_icon_font
size: 48
glyphs: [
"\U000F01F5", #mdi-emoticon-happy-outline
"\U000F01F6", #mdi-emoticon-neutral-outline
"\U000F01F8" #mdi-emoticon-sad-outline
]
# https://www.co2meter.com/blogs/news/co2-levels-at-home
# ~400 ppm background (normal) outdoor air levels
# 400- 1,000 ppm typical levels found in occupied spaces with good air exchange
# 1,000 ā 2,000 ppm levels associated with complaints of drowsiness and poor air
# 2,000 ā 5,000 ppm levels associated with headaches, sleepiness, and stagnant, stale, stuffy air,
# poor concentration, loss of attention, increased heart rate and slight nausea may also be present
# >5,000 ppm Exposure may lead to serious oxygen deprivation symptoms
display:
- platform: ssd1306_i2c
id: device_display
model: "SH1106 128x64"
address: 0x3C
rotation: 180
flip_x: false
flip_y: false
offset_y: 0
offset_x: 0
external_vcc: true
update_interval: 1s
pages:
- id: page1
lambda: |-
it.rectangle(0, 0, 128, 64);
it.printf( 6, 8, id(roboto_medium), "CO2 ");
it.printf( 92, 8, id(roboto_medium), TextAlign::TOP_RIGHT, "%5.0f", id(co2).state);
it.printf(120, 11, id(roboto), TextAlign::TOP_RIGHT, "ppm");
it.line( 0, 32, 128, 32);
it.line(64, 32, 64, 128);
it.printf( 4, 34, id(roboto), "C");
it.printf( 68, 34, id(roboto), "RH");
it.printf( 54, 40, id(roboto_medium), TextAlign::TOP_RIGHT, "%3.1f°", id(temp).state);
it.printf(120, 40, id(roboto_medium), TextAlign::TOP_RIGHT, "%2.0f%%", id(humidity).state);
- id: page2
lambda: |-
it.rectangle(0, 0, 128, 64);
it.printf(4, 4, id(roboto_medium), "PM 1: ");
it.printf(105, 4, id(roboto_medium), TextAlign::TOP_RIGHT, "%4.0f", id(pm1_0).state);
it.printf(124, 7, id(roboto_symbols), TextAlign::TOP_RIGHT, "µg");
it.printf(4, 23, id(roboto_medium), "PM 2.5: ");
it.printf(105, 23, id(roboto_medium), TextAlign::TOP_RIGHT, "%4.0f", id(pm2_5).state);
it.printf(124, 26, id(roboto_symbols), TextAlign::TOP_RIGHT, "µg");
it.printf(4, 42, id(roboto_medium), "PM 10: ");
it.printf(105, 42, id(roboto_medium), TextAlign::TOP_RIGHT, "%4.0f", id(pm10_0).state);
it.printf(124, 45, id(roboto_symbols), TextAlign::TOP_RIGHT, "µg");
- id: page3
lambda: |-
it.rectangle(0, 0, 128, 64);
if (id(co2).state <= 1000.0 && id(pm2_5).state < 35 ) {
it.printf(8, 8, id(face_icon_font), "%s", "\U000F01F5"); //mdi-emoticon-happy-outline
it.printf(76, 14, id(roboto_medium), "ALL");
it.printf(68, 34, id(roboto_medium), "GOOD");
} else if (id(co2).state > 1000.0 && id(co2).state < 2000.0 || id(pm2_5).state >= 35 && id(pm2_5).state <= 50) {
it.printf(8, 8, id(face_icon_font), "%s", "\U000F01F6"); //mdi-emoticon-neutral-outline
it.printf(74, 14, id(roboto_medium), "NOT");
it.printf(68, 34, id(roboto_medium), "GOOD");
} else {
it.printf(8, 8, id(face_icon_font), "%s", "\U000F01F8"); //mdi-emoticon-sad-outline
it.printf(72, 14, id(roboto_medium), "NOT");
it.printf(68, 34, id(roboto_medium), "SAFE");
}
interval:
- interval: 10s
then:
- display.page.show_next: device_display
- component.update: device_display
- interval: 20s
then:
if:
condition:
wifi.connected:
then:
- globals.set:
id: wifi_connection
value: 'true'
else:
- globals.set:
id: wifi_connection
value: 'false'
sensor:
- platform: wifi_signal
name: "${friendly_devicename}: WiFi Signal"
update_interval: ${update_interval_wifi}
device_class: signal_strength
- platform: sht3xd
temperature:
id: temp
name: ${friendly_devicename} Temperature
humidity:
id: humidity
name: ${friendly_devicename} Humidity
address: 0x44
update_interval: 10s
- platform: pmsx003
type: PMSX003
uart_id: uart1
pm_1_0:
id: pm1_0
name: "${friendly_devicename}: Particulate <1.0µm"
pm_2_5:
id: pm2_5
name: "${friendly_devicename}: Particulate <2.5µm"
pm_10_0:
id: pm10_0
name: "${friendly_devicename}: Particulate <10.0µm"
- platform: senseair
id: co2_sensor
uart_id: uart2
co2:
id: co2
name: "${friendly_devicename} CO2"
update_interval: 60s
I certainly want to come back and try out your esphome code!