#include #include #include #include #include #include #include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "freertos/semphr.h" #include "esp_chip_info.h" #include "esp_flash.h" #include "esp_system.h" #include "esp_timer.h" #include "esp_spiffs.h" #include "esp_sleep.h" #include "driver/gpio.h" #include "driver/rtc_io.h" #define PRINT_DEBUG false #define WIPE_MEMORY false #define DHT11_GPIO 0 #define BLINK_GPIO 8 #define DATA_BUFFER_SIZE 100 typedef struct { char timestamp[64]; float humidity; float temperature; } SensorData; // RTC buffer to store time RTC_DATA_ATTR time_t rtc_unix_time = 0; RTC_DATA_ATTR bool rtc_time_initialized = false; // RTC buffer to store measurements RTC_DATA_ATTR SensorData rtc_measurement_buffer[DATA_BUFFER_SIZE]; RTC_DATA_ATTR int rtc_buffer_index = 0; // ############### Base Setup ############### void set_system_time() { if (!rtc_time_initialized) { // First boot: use compile time struct tm tm = {0}; char month[4]; int day, year, hour, min, sec; sscanf(__DATE__, "%3s %d %d", month, &day, &year); sscanf(__TIME__, "%d:%d:%d", &hour, &min, &sec); const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; for (int i = 0; i < 12; i++) { if (strcmp(month, months[i]) == 0) { tm.tm_mon = i; break; } } tm.tm_mday = day; tm.tm_year = year - 1900; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; tm.tm_isdst = -1; rtc_unix_time = mktime(&tm); rtc_time_initialized = true; } // Set system time from RTC struct timeval tv = { .tv_sec = rtc_unix_time, .tv_usec = 0 }; settimeofday(&tv, NULL); } void get_current_time(char* time_string, size_t size) { time_t now; struct tm timeinfo; time(&now); localtime_r(&now, &timeinfo); strftime(time_string, size, "%Y-%m-%d %H:%M:%S", &timeinfo); } void print_chip_info() { esp_chip_info_t chip_info; uint32_t flash_size; esp_chip_info(&chip_info); printf("This is %s chip with %d CPU core(s), %s%s%s%s, ", CONFIG_IDF_TARGET, chip_info.cores, (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "", (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "", (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "", (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : ""); unsigned major_rev = chip_info.revision / 100; unsigned minor_rev = chip_info.revision % 100; printf("silicon revision v%d.%d, ", major_rev, minor_rev); if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) { printf("Get flash size failed"); return; } printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size()); } // ############### Status indicator ############### void init_led() { gpio_reset_pin(BLINK_GPIO); gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(BLINK_GPIO, 1); } void led_flicker(int times, bool fast) { for(u_int8_t i = 0; i < times; i++) { gpio_set_level(BLINK_GPIO, 0); vTaskDelay(pdMS_TO_TICKS(fast ? 250 : 500)); gpio_set_level(BLINK_GPIO, 1); vTaskDelay(pdMS_TO_TICKS(fast ? 250 : 500)); } } // ############### Measurement ############### void read_dht11(float *humidity, float *temperature) { uint8_t data[5] = {0}; gpio_set_direction(DHT11_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(DHT11_GPIO, 0); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(DHT11_GPIO, 1); gpio_set_direction(DHT11_GPIO, GPIO_MODE_INPUT); while (gpio_get_level(DHT11_GPIO) == 1) vTaskDelay(pdMS_TO_TICKS(0.1)); while (gpio_get_level(DHT11_GPIO) == 0) vTaskDelay(pdMS_TO_TICKS(0.1)); for (int i = 0; i < 40; i++) { while (gpio_get_level(DHT11_GPIO) == 1) vTaskDelay(pdMS_TO_TICKS(0.1)); while (gpio_get_level(DHT11_GPIO) == 0) vTaskDelay(pdMS_TO_TICKS(0.1)); uint32_t pulse_start = esp_timer_get_time(); while (gpio_get_level(DHT11_GPIO) == 1) vTaskDelay(pdMS_TO_TICKS(0.1)); uint32_t pulse_end = esp_timer_get_time(); uint8_t bit = (pulse_end - pulse_start) > 50 ? 1 : 0; data[i / 8] <<= 1; data[i / 8] |= bit; } uint8_t humidity_int = data[0]; uint8_t humidity_dec = data[1]; int8_t temperature_int = (data[2]); uint8_t temperature_dec = data[3]; uint8_t checksum = data[4]; uint8_t sum = humidity_int + humidity_dec + temperature_int + temperature_dec; if (sum != checksum) { #if PRINT_DEBUG printf("Checksum failed! Retrying...\n"); #endif vTaskDelay(pdMS_TO_TICKS(1000)); read_dht11(humidity, temperature); return; } *humidity = (float)humidity_int + (float)humidity_dec / 10.0f; *temperature = (float)temperature_int + (float)temperature_dec / 10.0f; } void make_measurement() { float humidity, temperature; read_dht11(&humidity, &temperature); char time_string[64] = {0}; get_current_time(time_string, sizeof(time_string)); #if PRINT_DEBUG printf("Measurement: [%s, %.1f%%, %.1f°C]\n", time_string, humidity, temperature); #endif // Store in RTC buffer if (rtc_buffer_index < DATA_BUFFER_SIZE) { strncpy(rtc_measurement_buffer[rtc_buffer_index].timestamp, time_string, sizeof(time_string)); rtc_measurement_buffer[rtc_buffer_index].humidity = humidity; rtc_measurement_buffer[rtc_buffer_index].temperature = temperature; rtc_buffer_index++; } } // ############### IO operations ############### void init_filesystem() { esp_vfs_spiffs_conf_t conf = { .base_path = "/spiffs", .partition_label = "storage", .max_files = 5, .format_if_mount_failed = true }; esp_err_t ret = esp_vfs_spiffs_register(&conf); if (ret != ESP_OK) { #if PRINT_DEBUG printf("Failed to initialize SPIFFS (%s)\n", esp_err_to_name(ret)); #endif return; } #if PRINT_DEBUG printf("SPIFFS initialized successfully\n"); #endif } void save_buffer_to_persistent(){ FILE *f = fopen("/spiffs/measurements.csv", "a"); if (f == NULL) { printf("Failed to open CSV file!\n"); return; } // Write header if file is empty fseek(f, 0, SEEK_END); if (ftell(f) == 0) { fprintf(f, "Timestamp,Humidity,Temperature\n"); } // Write all buffered measurements for (int i = 0; i < rtc_buffer_index; i++) { fprintf(f, "%s,%.1f,%.1f\n", rtc_measurement_buffer[i].timestamp, rtc_measurement_buffer[i].humidity, rtc_measurement_buffer[i].temperature); } fclose(f); rtc_buffer_index = 0; // Reset buffer #if PRINT_DEBUG printf("CSV file updated with %d measurements!\n", DATA_BUFFER_SIZE); #endif } void clear_file(){ led_flicker(10, false); FILE *f = fopen("/spiffs/measurements.csv", "w"); if (f) { fprintf(f, "Timestamp,Humidity,Temperature\n"); fclose(f); } } void readout_persistent_as_csv(){ FILE *f = fopen("/spiffs/measurements.csv", "r"); if (f) { char line[256]; while (fgets(line, sizeof(line), f)) { printf("%s", line); } fclose(f); } } // ############### Buffer operations ############### void readout_buffer_as_csv(){ for (int i = 0; i < rtc_buffer_index; i++) { printf("%s,%.1f,%.1f\n", rtc_measurement_buffer[i].timestamp, rtc_measurement_buffer[i].humidity, rtc_measurement_buffer[i].temperature); } } // ############### Main thread ############### const uint16_t sleep_time_seconds = 15 * 60; // Measurement frequency void app_main() { setlocale(LC_ALL, "C"); set_system_time(); init_led(); init_filesystem(); led_flicker(3, true); #if PRINT_DEBUG printf("DHT11 temperature and humidity sensor\n"); char start_time[64] = {0}; get_current_time(start_time, sizeof(start_time)); printf("Program started at: %s\n", start_time); print_chip_info(); #endif make_measurement(); if (rtc_buffer_index >= DATA_BUFFER_SIZE) { save_buffer_to_persistent(); } readout_persistent_as_csv(); readout_buffer_as_csv(); #if WIPE_MEMORY clear_file(); #endif rtc_unix_time += sleep_time_seconds; esp_sleep_enable_timer_wakeup(sleep_time_seconds * 1000000); esp_deep_sleep_start(); }