Protoype 1 can read data and retrieve it by pressing button once and reset storage by pressing twice.
This commit is contained in:
420
main/main.c
420
main/main.c
@@ -1,30 +1,122 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <locale.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
#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 "driver/gpio.h"
|
||||
|
||||
// Define the GPIO pin for the DHT11 data line
|
||||
#define DHT11_GPIO 0
|
||||
#define PRINT_DEBUG false
|
||||
|
||||
void read_dht11(uint8_t *humidity_int, uint8_t *humidity_dec,
|
||||
int8_t *temperature_int, uint8_t *temperature_dec,
|
||||
uint8_t *checksum) {
|
||||
#define DHT11_GPIO 0
|
||||
#define BUTTON_PIN 9 // BOOT button on ESP32-C3 SuperMini
|
||||
|
||||
#define DATA_BUFFER_SIZE 100
|
||||
#define MAX_MEASUREMENTS_PER_DAY 96 // 96 * 15 minutes = 24 hours
|
||||
static int measurement_count = 0;
|
||||
QueueHandle_t sensor_data_queue;
|
||||
SemaphoreHandle_t xMutex;
|
||||
|
||||
typedef struct {
|
||||
char timestamp[64];
|
||||
float humidity;
|
||||
float temperature;
|
||||
} SensorData;
|
||||
|
||||
TaskHandle_t file_handler_task_handle;
|
||||
|
||||
// ############### Base Setup ###############
|
||||
|
||||
void set_system_time_from_compiler() {
|
||||
// Parse __DATE__: "Mmm dd yyyy"
|
||||
char date[] = __DATE__;
|
||||
char time[] = __TIME__;
|
||||
|
||||
struct tm tm = {0};
|
||||
char month[4];
|
||||
int day, year;
|
||||
int hour, min, sec;
|
||||
|
||||
// Parse date
|
||||
sscanf(date, "%3s %d %d", month, &day, &year);
|
||||
|
||||
// Parse time
|
||||
sscanf(time, "%d:%d:%d", &hour, &min, &sec);
|
||||
|
||||
// Convert month abbreviation to number
|
||||
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; // Years since 1900
|
||||
tm.tm_hour = hour;
|
||||
tm.tm_min = min;
|
||||
tm.tm_sec = sec;
|
||||
tm.tm_isdst = -1; // Let system determine daylight saving time
|
||||
|
||||
// Set system time
|
||||
struct timeval tv = { .tv_sec = mktime(&tm), .tv_usec = 0 };
|
||||
settimeofday(&tv, NULL);
|
||||
}
|
||||
|
||||
void print_chip_info(){
|
||||
/* Print chip information */
|
||||
#if PRINT_DEBUG
|
||||
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());
|
||||
#endif
|
||||
}
|
||||
|
||||
// ############### Measurement task ###############
|
||||
|
||||
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 read_dht11(float *humidity, float *temperature) {
|
||||
uint8_t data[5] = {0};
|
||||
|
||||
// Send start signal: pull the line low for 20ms
|
||||
@@ -70,121 +162,235 @@ void read_dht11(uint8_t *humidity_int, uint8_t *humidity_dec,
|
||||
}
|
||||
|
||||
// Parse the data
|
||||
*humidity_int = data[0];
|
||||
*humidity_dec = data[1]; // Decimal part (0–9)
|
||||
*temperature_int = (data[2] & 0x7F); // Remove sign bit (0–50)
|
||||
*temperature_dec = data[3]; // Decimal part (0–9)
|
||||
*checksum = data[4];
|
||||
uint8_t humidity_int = data[0];
|
||||
uint8_t humidity_dec = data[1]; // Decimal part (0-9)
|
||||
int8_t temperature_int = (data[2]); // signed value
|
||||
uint8_t temperature_dec = data[3]; // Decimal part (0-9)
|
||||
uint8_t checksum = data[4];
|
||||
|
||||
#if PRINT_DEBUG
|
||||
printf("\nReadout values: [hi: %d, hd: %d, ti %d, td %d, c: %d]\n", humidity_int, humidity_dec, temperature_int, temperature_dec, checksum);
|
||||
#endif
|
||||
|
||||
// Validate checksum
|
||||
uint8_t sum = *humidity_int + *humidity_dec + *temperature_int + *temperature_dec;
|
||||
if (sum != *checksum) {
|
||||
printf("Checksum failed! Retrying...\n");
|
||||
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_int, humidity_dec, temperature_int, temperature_dec, checksum);
|
||||
read_dht11(humidity, temperature);
|
||||
}
|
||||
#if PRINT_DEBUG
|
||||
printf("Checksum checks out!\n");
|
||||
#endif
|
||||
|
||||
// Calculate humidity and temperature
|
||||
*humidity = (float)humidity_int + (float)humidity_dec / 10.0f;
|
||||
*temperature = (float)temperature_int + (float)temperature_dec / 10.0f;
|
||||
}
|
||||
|
||||
void measure_task(void *pvParameters){
|
||||
while (1) {
|
||||
float humidity, temperature;
|
||||
read_dht11(&humidity, &temperature);
|
||||
|
||||
char time_string[64] = {0};
|
||||
get_current_time(time_string, sizeof(time_string));
|
||||
|
||||
void set_system_time_from_compiler() {
|
||||
// Parse __DATE__: "Mmm dd yyyy"
|
||||
char date[] = __DATE__;
|
||||
char time[] = __TIME__;
|
||||
#if PRINT_DEBUG
|
||||
printf("Measurment: [%s, %.1f%%, %.1f°C]\n", time_string, humidity, temperature);
|
||||
#endif
|
||||
|
||||
struct tm tm = {0};
|
||||
char month[4];
|
||||
int day, year;
|
||||
int hour, min, sec;
|
||||
SensorData data;
|
||||
strncpy(data.timestamp, time_string, sizeof(data.timestamp));
|
||||
data.humidity = humidity;
|
||||
data.temperature = temperature;
|
||||
|
||||
// Parse date
|
||||
sscanf(date, "%3s %d %d", month, &day, &year);
|
||||
|
||||
// Parse time
|
||||
sscanf(time, "%d:%d:%d", &hour, &min, &sec);
|
||||
|
||||
// Convert month abbreviation to number
|
||||
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;
|
||||
// Send data to the queue
|
||||
if (xQueueSend(sensor_data_queue, &data, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
#if PRINT_DEBUG
|
||||
printf("Failed to add measurement to queue!\n");
|
||||
#endif
|
||||
} else {
|
||||
measurement_count++;
|
||||
if (measurement_count >= MAX_MEASUREMENTS_PER_DAY) {
|
||||
// Signal the file handler to write and reset
|
||||
xTaskNotifyGive(file_handler_task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait 15 minute before next read
|
||||
vTaskDelay(pdMS_TO_TICKS(15 * 60 * 1000));
|
||||
}
|
||||
tm.tm_mday = day;
|
||||
tm.tm_year = year - 1900; // Years since 1900
|
||||
tm.tm_hour = hour;
|
||||
tm.tm_min = min;
|
||||
tm.tm_sec = sec;
|
||||
tm.tm_isdst = -1; // Let system determine daylight saving time
|
||||
|
||||
// Set system time
|
||||
struct timeval tv = { .tv_sec = mktime(&tm), .tv_usec = 0 };
|
||||
settimeofday(&tv, NULL);
|
||||
}
|
||||
|
||||
void print_current_time() {
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
char strftime_buf[64];
|
||||
// ############### Filehandling task ###############
|
||||
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
|
||||
printf("Time: %s\n", strftime_buf);
|
||||
}
|
||||
|
||||
void print_chip_info(){
|
||||
/* Print chip information */
|
||||
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");
|
||||
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
|
||||
if (ret == ESP_FAIL) {
|
||||
printf("Failed to mount or format filesystem\n");
|
||||
} else if (ret == ESP_ERR_NOT_FOUND) {
|
||||
printf("Failed to find SPIFFS partition\n");
|
||||
} else {
|
||||
printf("Failed to initialize SPIFFS (%s)\n", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
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());
|
||||
#if PRINT_DEBUG
|
||||
printf("SPIFFS initialized successfully\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void file_handler_task(void *pvParameters) {
|
||||
while (1) {
|
||||
// Wait for notification from measurement task
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
SensorData data;
|
||||
FILE *f = NULL;
|
||||
|
||||
xSemaphoreTake(xMutex, portMAX_DELAY);
|
||||
f = fopen("/spiffs/measurements.csv", "a");
|
||||
if (f == NULL) {
|
||||
#if PRINT_DEBUG
|
||||
printf("Failed to open CSV file!\n");
|
||||
#endif
|
||||
xSemaphoreGive(xMutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write header if file is empty
|
||||
fseek(f, 0, SEEK_END);
|
||||
if (ftell(f) == 0) {
|
||||
fprintf(f, "Timestamp,Humidity,Temperature\n");
|
||||
}
|
||||
|
||||
// Read all data from the queue and write to CSV
|
||||
while (xQueueReceive(sensor_data_queue, &data, 0) == pdPASS) {
|
||||
fprintf(f, "%s,%.1f,%.1f\n", data.timestamp, data.humidity, data.temperature);
|
||||
#if PRINT_DEBUG
|
||||
printf("%s,%.1f,%.1f\n", data.timestamp, data.humidity, data.temperature);
|
||||
#endif
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
measurement_count = 0;
|
||||
#if PRINT_DEBUG
|
||||
printf("CSV file updated!\n");
|
||||
#endif
|
||||
xSemaphoreGive(xMutex);
|
||||
}
|
||||
}
|
||||
|
||||
// ############### Button task ###############
|
||||
|
||||
void button_task(void *pvParameters) {
|
||||
// Configure BOOT button (GPIO9, active low with internal pullup)
|
||||
gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(BUTTON_PIN, GPIO_PULLUP_ONLY);
|
||||
|
||||
uint32_t last_press_tick = 0;
|
||||
bool waiting_for_double = false;
|
||||
|
||||
while (1) {
|
||||
if (gpio_get_level(BUTTON_PIN) == 0) { // Button pressed (active low)
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // Debounce
|
||||
if (gpio_get_level(BUTTON_PIN) == 0) {
|
||||
uint32_t press_start = xTaskGetTickCount();
|
||||
|
||||
// Wait for release
|
||||
while (gpio_get_level(BUTTON_PIN) == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // Debounce release
|
||||
uint32_t press_duration = xTaskGetTickCount() - press_start;
|
||||
|
||||
// Only count short presses (< 500ms)
|
||||
if (press_duration < pdMS_TO_TICKS(500)) {
|
||||
if (waiting_for_double) {
|
||||
// ===== DOUBLE PRESS: Reset CSV =====
|
||||
xSemaphoreTake(xMutex, portMAX_DELAY);
|
||||
FILE *f = fopen("/spiffs/measurements.csv", "w");
|
||||
if (f) {
|
||||
fprintf(f, "Timestamp,Humidity,Temperature\n");
|
||||
fclose(f);
|
||||
}
|
||||
xSemaphoreGive(xMutex);
|
||||
waiting_for_double = false;
|
||||
} else {
|
||||
// ===== FIRST PRESS: Wait for possible double press =====
|
||||
waiting_for_double = true;
|
||||
last_press_tick = press_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if double-press window expired
|
||||
if (waiting_for_double && (xTaskGetTickCount() - last_press_tick) > pdMS_TO_TICKS(500)) {
|
||||
xTaskNotifyGive(file_handler_task_handle);
|
||||
// ===== SINGLE PRESS: Print CSV contents (via USB CDC) =====
|
||||
xSemaphoreTake(xMutex, portMAX_DELAY);
|
||||
FILE *f = fopen("/spiffs/measurements.csv", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
printf("%s", line); // Goes to USB CDC (/dev/ttyACM0)
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
xSemaphoreGive(xMutex);
|
||||
waiting_for_double = false;
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
// ############### Shared buffer ###############
|
||||
|
||||
void init_shared_buffer() {
|
||||
sensor_data_queue = xQueueCreate(DATA_BUFFER_SIZE, sizeof(SensorData));
|
||||
xMutex = xSemaphoreCreateMutex();
|
||||
if (sensor_data_queue == NULL || xMutex == NULL) {
|
||||
#if PRINT_DEBUG
|
||||
printf("Failed to create queue or mutex!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ############### Main thread ###############
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Setup
|
||||
setlocale(LC_ALL, "C"); // Use ASCII for text
|
||||
set_system_time_from_compiler();
|
||||
printf("DHT11 temperature and humidity sensor read and send out\n");
|
||||
init_shared_buffer();
|
||||
init_filesystem();
|
||||
|
||||
// Intro
|
||||
#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();
|
||||
|
||||
while (1) {
|
||||
uint8_t humidity_int, humidity_dec, checksum;
|
||||
int8_t temperature_int;
|
||||
uint8_t temperature_dec;
|
||||
|
||||
read_dht11(&humidity_int, &humidity_dec, &temperature_int, &temperature_dec, &checksum);
|
||||
|
||||
// Calculate humidity and temperature
|
||||
float humidity = (float)humidity_int + (float)humidity_dec / 10.0f;
|
||||
float temperature = (float)temperature_int + (float)temperature_dec / 10.0f;
|
||||
|
||||
// Print results
|
||||
print_current_time();
|
||||
printf("Humidity: %.1f%%\n", humidity);
|
||||
printf("Temperature: %.1f°C\n", temperature);
|
||||
printf("Checksum valid: %s\n", (checksum == (humidity_int + humidity_dec + temperature_int + temperature_dec)) ? "Yes" : "No");
|
||||
|
||||
// Wait 1 minute before next read
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
}
|
||||
}
|
||||
printf("\n\n=============\n\n");
|
||||
#endif
|
||||
|
||||
// Run tasks
|
||||
xTaskCreate(measure_task, "MeasureTask", 4096, NULL, 3, NULL);
|
||||
xTaskCreate(file_handler_task, "FileHandlerTask", 4096, NULL, 4, &file_handler_task_handle);
|
||||
xTaskCreate(button_task, "ButtonTask", 2048, NULL, 2, NULL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user