#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include "esp_log.h"
using namespace ArduinoJson;
#define DHTPIN 4 // Pin pour le DHT22
#define PIRPIN 2 // Pin pour le PIR
#define LDRPIN 34 // Pin pour le LDR (capteur de luminosité)
#define LEDPIN_R 25 // Pin pour la LED rouge (RGB)
#define LEDPIN_G 27 // Pin pour la LED verte (RGB)
#define LEDPIN_B 26 // Pin pour la LED bleue (RGB)
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Initialiser l'écran LCD
LiquidCrystal_I2C lcd(0x27, 16, 2); // Adresse I2C et dimensions
char topics[3][20];
class state_t {
public:
// FIXME: how to give an ID to the esp32 ?
uint32_t gid = 0;
uint32_t id = 0;
uint8_t led_wattage = 50;
bool led_broken = false;
bool led_intensity_is_manual = false;
float led_intensity_value = 0;
uint8_t led_color[3] = {255, 255, 255}; // rgb
uint32_t movement_last_detection = 0;
uint32_t movement_time_on_after_detection_ms = 1000;
bool movement_activation_enabled = true;
uint32_t light_sensor_value = 0;
uint32_t light_sensor_activation_value = 100;
bool light_sensor_activation_enabled = true;
bool camera_on = false;
float temp = 0;
float humidity = 0;
bool display_on = false;
bool publish_changes = true;
void to_json_string(char *out, size_t out_size)
{
JsonDocument doc;
doc["gid"] = this->gid;
doc["id"] = this->id;
doc["fail"] = this->led_broken;
doc["grad"] = this->led_intensity_value;
doc["mov"] = ((millis() - this->movement_last_detection) < 10);
doc["lum"] = this->light_sensor_value;
doc["temp"] = this->temp;
doc["hum"] = this->humidity;
serializeJson(doc, out, out_size);
}
};
static state_t current_state;
static state_t next_state;
static bool made_publishable_changes = true;
// this function will be called by the mqtt pubsubclient callback
static void callback_helper(const char *request_str)
{
JsonDocument doc;
deserializeJson(doc, request_str);
JsonObject obj = doc.as<JsonObject>();
next_state.led_intensity_is_manual = (
strcmp(obj["gradMode"], "manual") == 0
);
if( ! next_state.led_intensity_is_manual )
next_state.led_intensity_value = obj["grad"];
const char *col = obj["col"];
if( strcmp(col, "white") == 0 )
{
next_state.led_color[0] = 255;
next_state.led_color[1] = 255;
next_state.led_color[2] = 255;
}
else if( strcmp(col, "yellow") == 0 )
{
next_state.led_color[0] = 255;
next_state.led_color[1] = 255;
next_state.led_color[2] = 0;
}
else // red
{
next_state.led_color[0] = 255;
next_state.led_color[1] = 0;
next_state.led_color[2] = 0;
}
next_state.camera_on = obj["cam"];
next_state.display_on = obj["aff"];
}
static void update_next_state_sensor_values(void)
{
// read values from the sensors
// and change next_state
// Lire l'humidité et la température du DHT22
next_state.humidity = dht.readHumidity();
next_state.temp = dht.readTemperature();
// Lire la valeur du LDR (lumière ambiante)
// Normaliser la valeur du LDR pour qu'elle soit entre 0 et 255
// Calculer l'intensité de la LED en fonction de la luminosité ambiante
// Plus la lumière est faible, plus la LED est intense
int ldrValue = analogRead(LDRPIN);
next_state.led_intensity_value = 255 - map(ldrValue, 0, 4095, 0, 255);
}
// make changes in next_state
static void update_next_state(void)
{
update_next_state_sensor_values();
bool bad_brightness = (
next_state.light_sensor_value <
next_state.light_sensor_activation_value
);
// FIXME: compute the intensity correctly and check the decision logique
if( next_state.led_broken )
next_state.led_intensity_value = 0;
else
if( ! next_state.led_intensity_is_manual )
{
if( bad_brightness )
{
if( next_state.camera_on )
next_state.led_intensity_value = 1;
else
next_state.led_intensity_value = 0.5;
}
}
}
// may send intructions to hardware to mactch next_state
// may set made_publishable_changes to true
static void update_hardware(void)
{
if(
current_state.led_intensity_value != next_state.led_intensity_value ||
current_state.led_color != next_state.led_color
) {
made_publishable_changes = true;
// TODO: send instruction to led
}
if( current_state.camera_on != next_state.camera_on )
{
made_publishable_changes = true;
// TODO: send instruction to camera
}
// if sensors values changed
if(
current_state.temp != next_state.temp ||
current_state.humidity != next_state.humidity
)
made_publishable_changes = true;
}
/////////////////////////////////////////////////
class stat_point_t {
public:
float time_led_on_ms = 0;
uint8_t led_broken = 0;
float power_Wh = 0;
float light_intensity = 0;
float temp = 0;
float humidity = 0;
// used to compute the averages
uint16_t total_time_ms = 0;
};
uint32_t last_stat_point_creation_time;
uint32_t last_stat_point_last_time;
std::vector<stat_point_t> stats;
void update_stats(void)
{
uint32_t current_time = millis();
if( stats.size() == 0 )
{
stats.push_back(stat_point_t());
last_stat_point_creation_time = current_time;
last_stat_point_last_time = current_time;
}
uint32_t dt = current_time - last_stat_point_last_time;
last_stat_point_last_time = current_time;
stats.back().total_time_ms += dt;
// stat_point_t::time_led_on_ms
if( current_state.led_intensity_value > 0 )
stats.back().time_led_on_ms += dt;
// stat_point_t::led_broken
if( current_state.led_broken )
stats.back().led_broken = 1;
// stat_point_t::power_Wh FIXME: compute this correctly, use wattage
stats.back().power_Wh +=
current_state.led_intensity_value * current_state.led_wattage * dt / 1000 / 3600;
// stat_point_t::light_intensity
stats.back().light_intensity = current_state.led_broken * dt;
// stat_point_t::temp
stats.back().temp = current_state.temp * dt;
// stat_point_t::humidity
stats.back().humidity = current_state.humidity * dt;
if( current_time - last_stat_point_creation_time >= 60*1000 )
{
if( stats.size() == 60 )
{
// TODO: send stats_to_json to server
stats.clear();
}
else
{
stats.push_back(stat_point_t());
last_stat_point_creation_time = current_time;
last_stat_point_last_time = current_time;
}
}
}
void stats_to_json(char *out, size_t out_size)
{
JsonDocument doc;
doc["gid"] = current_state.gid;
doc["id"] = current_state.id;
JsonArray time_led_on_ms = doc["time_led_on_ms"].to<JsonArray>();
JsonArray led_broken = doc["led_broken"].to<JsonArray>();
JsonArray power_Wh = doc["power_Wh"].to<JsonArray>();
JsonArray light_intensity = doc["light_intensity"].to<JsonArray>();
JsonArray temp = doc["temp"].to<JsonArray>();
JsonArray humidity = doc["humidity"].to<JsonArray>();
for( uint32_t idx = 0 ; idx < stats.size() ; idx++ )
{
time_led_on_ms[idx] = stats[idx].time_led_on_ms;
led_broken[idx] = stats[idx].led_broken;
power_Wh[idx] = stats[idx].power_Wh;
light_intensity[idx] = stats[idx].light_intensity / stats[idx].total_time_ms;
temp[idx] = stats[idx].temp / stats[idx].total_time_ms;
humidity[idx] = stats[idx].humidity / stats[idx].total_time_ms;
}
serializeJson(doc, out, out_size);
}
/////////////////////////////////////////////////
void loop(void)
{
// TODO: call mqtt pubsubclient loop
update_stats();
update_next_state();
update_hardware();
if( made_publishable_changes &&
( current_state.publish_changes || next_state.publish_changes )
)
{
// TODO: send next_state.to_json_string to server
}
current_state = next_state;
made_publishable_changes = false;
delay(1000);
}
void setup(void)
{
sprintf(topics[0], "all");
sprintf(topics[1], "group%d", current_state.gid);
sprintf(topics[2], "group%d/lamp%d", current_state.gid, current_state.id);
// Initialiser les pins
pinMode(PIRPIN, INPUT);
pinMode(LDRPIN, INPUT);
pinMode(LEDPIN_R, OUTPUT);
pinMode(LEDPIN_G, OUTPUT);
pinMode(LEDPIN_B, OUTPUT);
// Démarrer la communication série
Serial.begin(115200);
// Démarrer le capteur DHT
dht.begin();
// Démarrer l'écran LCD et initialiser I2C
Wire.begin(21, 22); // Broches SDA et SCL pour ESP32
lcd.begin(16, 2);
lcd.setBacklight(1); // Rétroéclairage activé
lcd.print("Initialisation...");
delay(2000); // Attendre 2 secondes
// TODO: do the needed setup steps
}
/////////////////////////////////////////////////
/////////////////////////////////////////////////