この家には壁掛け時計も置時計も無いな。一つ欲しいな。出来れば気温や湿度やCO2濃度なんかも見られたら良いな。
This is a documentation of my personal project to build a table clock with environmental sensors using a HMI display panel.
- It displays the current time and date, keeping the time synchronized using SNTP over Wi-Fi.
- It displays readings from environmental sensors, and optionally issues alerts when necessary.
- It displays a one-week weather forecast using OpenWeather® One Call API 4.0. (API key required)
- It stores three months' worth of environmental data as a data logger and provides it in JSON format via an HTTP server.
Table of Contents
- 1. Hadrware
- 2. Tools Required for Development
- 3. Firmware
- 4. Developed by / License for the Created Data
- 5. Acknowledgments
- WaveShare ESP32-P4-WIFI6-Touch-LCD-7B HMI display panel (7inch 1024 × 600)
- BME680 I2C temperature, humidity, barometric pressure, and gas sensor module
- SCD41 I2C CO2 sensor module
Installation of temperature sensors requires careful consideration of ventilation. If there is insufficient airflow, heat will build up and the measured temperature will gradually rise. It should be placed away from the processor; ideally, it would be best to install it outside the enclosure.
Since the steady-state current of the HMI panel is around 300 mA, battery power is not a practical option for a stationary device (IMO).
この制作で使用したHMIディスプレイパネル製品は認証取得済みのモジュールを搭載しています。
- モジュール: ESP32-C6-MINI-1
- 認可番号: 007-AN0136
- Visual Studio Code
- ESP-IDF Extension for VS Code
- FontForge
- QCAD
- OpenSCAD
- 3D printers
- solder and soldering irons
- wire strippers, wire cutters, pliers, files, screwdrivers, etc.
As I was getting started with firmware development, I learned that LVGL offers a Win32 port. So, I decided to divide the application into modules, prototype most of the implementation to run on Windows, and replace only the platform-dependent modules with implementations using ESP-IDF/FreeRTOS functions.

fig. prototype running on Windows
Platform: This application used ESP-IDF v5.5.4 as the base framework and LVGL v9.5 for the GUI, along with the WaveShare's board support package (BSP).
I2C port: I found the drivers for the SCD4x and the BME680 on the Espressif Component Registry. These drivers are implemented using the i2cdev library; however, since the BSP and the i2cdev conflict during I2C initialization, so I decided to assign these sensors to a port other than the one assigned to the BSP.
Assets: Since rendering the clock requires a font with a larger point size, a TTF file itself is embedded as a asset. Also, several images, sounds, and texts are embedded too. So, I choosed the FrogFS to allow load these assets as read-only files.
Taking these factors into account, the following sdkconfig options are required:
- CONFIG_I2CDEV_DEFAULT_SDA_PIN=52
- CONFIG_I2CDEV_DEFAULT_SCL_PIN=51
- CONFIG_LV_USE_LODEPNG=y
- CONFIG_LV_USE_TINY_TTF=y
- CONFIG_LV_TINY_TTF_FILE_SUPPORT=y
- CONFIG_LV_GRADIENT_MAX_STOPS=4
- CONFIG_LV_FONT_MONTSERRAT_14=y
- CONFIG_LV_FONT_MONTSERRAT_18=y
- CONFIG_LV_FONT_MONTSERRAT_36=y
- CONFIG_LV_USE_LOG=y
- CONFIG_LV_FS_DEFAULT_DRIVER_LETTER=88 # 'X' : not required but recommended
- CONFIG_LV_USE_FS_FROGFS=y
- CONFIG_LV_FS_FROGFS_LETTER=87 # 'W'
- CONFIG_LV_BUILD_DEMOS=n
- CONFIG_LV_BUILD_EXAMPLES=n
- CONFIG_LV_USE_SYSMON=n
If the chip revision is less than 3, the following options are also required:
- CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y
The command esptool chip_id can be used to check the actual chip version.
When encountering the linker error “Linker error: --enable-non-contiguous-regions discards section...”, the following option may be required:
- CONFIG_COMPILER_OPTIMIZATION_SIZE=y
The following options are effective for utilizing PSRAM and preserving limited internal memory:
- CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
- CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
- CONFIG_SPIRAM_RODATA=y
- CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
- CONFIG_SPIRAM_XIP_FROM_PSRAM=y
The following options are required but should be set by default:
- CONFIG_BSP_I2C_NUM=1
- CONFIG_BSP_I2S_NUM=1
- CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
- CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
The “factory” partition is likely to overflow with the default partition table, so a custom partition table is required.
To complete the configuration of the ESP-IDF project, it is sufficient to follow the steps below within the IDE:
- Select "ESP-IDF v5.5.4"
- Select "esp32-p4" → "ESP32-P4 chip (via builtin USB-JTAG)"
In addition to the standard LVGL fonts, I have provided versions of main fonts with character sets expanded to be equivalent to Latin-1. I have also provided semi-bold styles. To do this, I used the online font converter.
(Here, XX represents the font size)
Name: lv_font_montserrat_latin1x_XX
Size: XX
Bpp: 4bit-per-pixel
Fallback: none
Output format: C file
TTF/WOFF font: Montserrat-Medium.ttf
Range: 0x20-0xFF,0x2022
TTF/WOFF font: TTF/WOFF font: FontAwesome5-Solid+Brands+Regular.woff
Range: 61441,61448,61451,61452,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650
These conversion parameters basically follow the LVGL script. The script is located in the LVGL folder managed_components/lvgl__lvgl/scripts/built_in_font, which is fetched after the project is configured.
I have also added the Monserrat-Medium.ttf file itself to the assets so that the font can be displayed in a larger point size. To reduce the final image size, I cut down the character set of the font to the equivalent of Latin-1.
While using Kconfig.projbuild makes it easy to switch between IDF configurations, it cannot be used for prototype code on Windows. Therefore, I used a more primitive manual method.
If using a different HMI display panel, the screen resolution can be selected.
- Edit
main/gui/GuiConfig.hto enable the correct preprocessor macro:- SCREENRES_1024x600
- SCREENRES_800x600
- Edit
main/CMakeLists.txtto ensure the appropriate fonts are compiled:lv_font_montserrat_latin1x_14.candlv_font_montserrat_semibold_latin1x_14.cfor 1024x600lv_font_montserrat_latin1x_12.candlv_font_montserrat_semibold_latin1x_12.cfor 800x480
- Open the IDF configration menu and ensure that the required fonts are checked:
- CONFIG_LV_FONT_MONTSERRAT_XX, here XX={ 14, 18, 32 } for 1024x600
- CONFIG_LV_FONT_MONTSERRAT_XX, here XX={ 12, 14, 28 } for 800x480
Although it is not mandatory, enabling HTTPS is also an option. Thanks to the ESP-IDF, no additional implementation is required to add HTTPS support, but it's a little troublesome to obtain valid server certificates.
- Locate server certificates at
main/certs:prvtkey.pemservercert.pem
- Edit
main/CMakeLists.txtto enable the embedding line:- EMBED_TXTFILES "certs/servercert.pem" "certs/prvtkey.pem"
- Edit
main/modules/HttpServerCore.config.hto enable the preprocessor macro:- HTTPSERVER_SUPPORT_HTTPS
Since I intend to use this project only for personal use at home, I’m not too concerned about security; however, if it were to be released as a product, at least NVS encryption would be desirable. The Wi-Fi accespoint/passphrase and the API key are stored there.
The DataLogger collects a history of measurements from environmental sensors and weather data.
- It records the measurements from the sensors every 10 minutes, and the weather data is recorded every hour.
- It stores three months' worth of data in the internal volatile RAM.
- A micro SD card can be used for persistence (optional). The estimated amount of data written to the SD card is approximately 30MB/day, or 11GB/year. This amount of writing is excessive for the internal flash partition.
- All measured values are in metric units, i.e., °C, hPa, ppm, meters/second, etc.
The HistoryServer provides data collected by the DataLogger in JSON format via a web interface.
- Thanks to the mDNS server, it can be accessed by its server name.
- Allows clients to specify a time range for queries.
Example of request (HTTP/1.1 GET):
http://sensorclock.local/sensor?from=1777474800&to=1777561200
http://sensorclock.local/weather?from=1777474800&to=1777561200
- from: The UNIX timestamp at the start of the time range (optional)
- to: The UNIX timestamp at the end of the time range (optional)
Example of response:
{
"status": "200 OK",
"path": "/sensor",
"from": "1777474800",
"to": "1777561200",
"length": "8",
"data": [
{
"dt": "1777555800",
"temp": "25.9",
"humidity": "44",
"pressure": "1013",
"co2": "691",
"iaq": "98"
},
...
]
}
{
"status": "200 OK",
"path": "/weather",
"from": "1777474800",
"to": "1777561200",
"length": "8",
"data": [
{
"dt": "1777528800",
"temp": "23.2",
"pressure": "1006",
"humidity": "36",
"clouds": "69",
"wind_speed": "0.45",
"wind_deg": "329",
"weather_id": "803",
"weather_icon": "04n"
},
...
]
}
A sample client application is also available: SensorClockViewer
The CAD data for the enclosure and the source code for the firmware are developed by yu2924 and released under CC0 1.0 Universal.
- raspi-bme680-iaq by thstielow Thanks for the great implementation.
- svgSimpleImages by aikige Thanks for the helpful images.




