// Record values of temperature and humidty if delta >= X. Dump them over serial once the button is pressed
//
//#define REAL_HARDWARE
#include <EEPROM.h>
#include "DHT.h"
#ifndef REAL_HARDWARE
#define DT_STEP 1*1000UL // [ms] 1 sec
#define DT_MAX 20*1000UL // [ms] 20 sec - make a record even if no event
#define DHTTYPE DHT22 // DHT 22 is available in Wokwi
#define SLEEP_TIME 10 // ms
#else
#define DT_STEP 10*1000UL // [ms] 10 sec
#define DT_MAX 2*60*60*1000UL // [ms] 2 hours - make a record even if no event
#define DHTTYPE DHT11 // DHT 11
#define SLEEP_TIME 500 // [ms]
#endif
// Scale values values to increase resolution by using a single byte
#define T_GAIN 100 //
#define T_MIN -10 // [degC]
#define T_MAX 40 // [degC]
#define C_GAIN 10 // clocking
#define T_DELTA 1 // [degC]
#define H_DELTA 2.5 // [%]
typedef struct data_entry {
unsigned int time;
byte temperature;
byte humidity;
};
typedef struct data_point {
unsigned long time;
float temperature;
float humidity;
};
data_point data_last; // last recorded value
// Pinning
const byte pin_forceSave = 12; // pull to ground to force save to memory
const byte pin_dumpAll = 11; // pull to ground to dump all memory
const byte pin_dumpRecord = 10; // pull to ground to dump relevant memory
const byte pin_cyclicBuffer = 9;// pull to ground to use EEPROM as cyclic buffer (overflow to 0), else - halt
const byte pin_resetIx = 8; // pull to ground to reset index to 0
const byte pin_DHT = 2; // Digital pin connected to the DHT sensor
bool enableWriting = true;
bool forceSave_old = HIGH;
bool buttonTriggerDump_old = HIGH;
bool buttonResetIx_old = HIGH;
unsigned long time_old = 0;
int ix_buffer = 0; // current index in buffer
int ix_start = 0; // start position
unsigned int ix_eeprom; // current index in EEPROM
// Init sensor
DHT dht(pin_DHT, DHTTYPE);
// Setup
//
void setup() {
// put your setup code here, to run once:
Serial.begin(9600); // 9600 vs 152000
pinMode(LED_BUILTIN, OUTPUT);
pinMode(pin_forceSave, INPUT_PULLUP);
pinMode(pin_dumpAll, INPUT_PULLUP);
pinMode(pin_dumpRecord, INPUT_PULLUP);
pinMode(pin_resetIx, INPUT_PULLUP);
pinMode(pin_cyclicBuffer, INPUT_PULLUP);
dht.begin();
EEPROM.get(EEPROM.length()-sizeof(ix_eeprom), ix_eeprom); // read the last stored location
check_EEPROM_pointer();
// dump all memory
if ( do_data_dump( digitalRead(pin_dumpAll), HIGH, EEPROM.length()-sizeof(data_entry) ) == true )
delay(20000); // give time to save the exported to CSV file
// dump relevant memory
if ( do_data_dump( digitalRead(pin_dumpRecord), HIGH, ix_eeprom ) == true )
delay(20000); // give time to save the exported to CSV file
display_info();
do_save_a_sample( millis(), dht.readTemperature(), dht.readHumidity() );
}
// put your main code here, to run repeatedly
//
void loop() {
unsigned long time_now = millis();
// Check if it is time to evaluate again
//
if (time_now >= time_old + DT_STEP)
{
if (enableWriting)
digitalWrite(LED_BUILTIN, LOW );
else // blick to show memory full
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN) );
do_check_for_event(time_now);
time_old = time_now;
}
// Check if a button was pressed
do_force_save( digitalRead(pin_forceSave), time_now );
do_data_dump( digitalRead(pin_dumpRecord), buttonTriggerDump_old, ix_eeprom);
do_reset_eeprom_ix(digitalRead(pin_resetIx), buttonResetIx_old);
delay(SLEEP_TIME);
}
// Check if temperature, humidity, or time have changed enough
//
void do_check_for_event(unsigned long time_now) {
float t = dht.readTemperature();
float h = dht.readHumidity();
if ( ( time_now > data_last.time + DT_MAX ) ||
( abs( data_last.temperature - t) > T_DELTA ) ||
( abs( data_last.humidity - h) > H_DELTA ) )
{
do_save_a_sample( time_now, t, h );
}
}
// Forced save
//
void do_force_save ( bool forceSave, unsigned long time_now ) {
if ( forceSave == LOW && forceSave_old == HIGH ) {
float t = dht.readTemperature();
float h = dht.readHumidity();
do_save_a_sample( time_now, t, h );
}
forceSave_old = forceSave;
}
// Save a sample
//
void do_save_a_sample ( unsigned long time_now, float t, float h) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.print(F("t: "));
Serial.print(time_now);
Serial.print(F(" ADR: "));
Serial.print(ix_eeprom);
Serial.print(F(" T= "));
Serial.print(t);
Serial.print(F(" °C, "));
Serial.print(F(" H= "));
Serial.print(h);
Serial.print(F(" %, "));
data_entry data;
data.time = (int) (C_GAIN * time_now / 1000 / 60); // scaled minutes
data.temperature = (int) map(t*T_GAIN, T_MIN*T_GAIN, T_MAX*T_GAIN, 0, 250); // scale range [T_MIN,T_MAX] to [0,255]
data.humidity = (int) 2 * h; // scale by 2
Serial.print(F(" --> "));
Serial.print(data.time);
Serial.print(F(", "));
Serial.print(data.temperature);
Serial.print(F(", "));
Serial.print(data.humidity);
if (enableWriting) {
Serial.println();
EEPROM.put(ix_eeprom, data); // write the value
ix_eeprom += sizeof( data_entry ); // update pointer
check_EEPROM_pointer();
EEPROM.put(EEPROM.length()-sizeof(ix_eeprom), ix_eeprom); // save the index
}
else
Serial.println(F(" not saved"));
data_last.time = time_now;
data_last.temperature = t;
data_last.humidity = h;
digitalWrite(LED_BUILTIN, LOW);
}
// Check EEPROM address pointer
//
void check_EEPROM_pointer() {
if (ix_eeprom >= EEPROM.length()-sizeof(data_entry)) { // if we write here, we will overwrite the ix_eeprom bytes
if(digitalRead(pin_cyclicBuffer) == LOW) { // cyclic buffer enabled. Reset address to 0
enableWriting = true;
ix_eeprom = 0;
}
else { // stop recording
Serial.print(F("Recording stopped. Pull pin"));
Serial.print(pin_cyclicBuffer);
Serial.print(F(" to GND and reset to enable cyclic buffer. Or reset address to 0."));
enableWriting = false;
}
}
}
// Check if data should be sent over serial
//
bool do_data_dump(bool state_now, bool state_old, int end_index) {
buttonTriggerDump_old = state_now;
if (state_now == LOW && state_old == HIGH) {
digitalWrite(LED_BUILTIN, HIGH);
// Data stored in EEPROM
if (end_index > 0) {
Serial.println(F("t [h], t_raw [min], index [-], Temperature [°C], Humidity [%]"));
int ix = 0;
unsigned long t_offset = 0;
unsigned long t_used = 0;
data_entry data;
while ( ix < end_index ) {
EEPROM.get(ix, data);
if (data.time + t_offset <= t_used) {
t_offset = (t_used - data.time) + 1;
}
t_used = data.time + t_offset;
Serial.print( ((float) t_used) / C_GAIN / 60, 4);
Serial.print(F(", "));
Serial.print( ((float) data.time) / C_GAIN, 4);
Serial.print(F(", "));
Serial.print( ix / sizeof(data) );
Serial.print(F(", "));
Serial.print( ((float) data.temperature)*(T_MAX-T_MIN)/250 + T_MIN, 1 );
Serial.print(F(", "));
Serial.println( ((float) data.humidity) / 2, 1 );
ix += sizeof(data);
}
}
digitalWrite(LED_BUILTIN, LOW);
return true;
}
return false;
}
// Reset the EEPROM indexing to 0
//
void do_reset_eeprom_ix(bool state_now, bool state_old) {
buttonResetIx_old = state_now;
if (state_now == LOW && state_old == HIGH) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println(F("Reseting EEPROM write address to 0"));
ix_eeprom = 0;
EEPROM.put(EEPROM.length()-sizeof(ix_eeprom), ix_eeprom ); // save the index
enableWriting = true;
delay(250);
digitalWrite(LED_BUILTIN, LOW);
}
}
// Display board infos
//
void display_info() {
Serial.println(F(" "));
Serial.println(F("TEMPERATURE AND HUMIDITY EVENT LOGGER"));
Serial.print(F("Save when |dTemperature| >= "));
Serial.print( T_DELTA );
Serial.print(F(" or |dHumidity| >= "));
Serial.print( H_DELTA );
Serial.print(F(" or |dTime| >= "));
Serial.print((unsigned long) DT_MAX);
Serial.println(F(" ms"));
Serial.print(F("Pull pin "));
Serial.print(pin_forceSave);
Serial.println(F(" to GND to force save"));
Serial.print(F("Pull pin "));
Serial.print(pin_dumpRecord);
Serial.println(F(" to GND to dump EEPROM (do at reset for CSV export friendly format)"));
Serial.print(F("Pull pin "));
Serial.print(pin_dumpAll);
Serial.println(F(" to GND during reset to dump all EEPROM"));
Serial.print(F("Pull pin "));
Serial.print(pin_resetIx);
Serial.println(F(" to GND to reset EEPROM indexing to 0"));
Serial.print(F("EEPROM write address starts at "));
Serial.println(ix_eeprom);
Serial.println();
#ifndef REAL_HARDWARE
Serial.println(F("SIMULATION MODE"));
Serial.println(F("- Blue button = force save entry"));
Serial.println(F("- Green button = dump relevant memory"));
Serial.println(F("- Top slider to the left + reser = dump all memory"));
Serial.println(F("- Red button = reset index to 0"));
Serial.println(F("- Right slider to the left = cyclic buffer enabled"));
Serial.println();
#endif
}