// LIBRARIES NEEDED:
// - "DHT sensor library by Adafruit" - Needed for commands to use DHT11 temperature sensor module
// - "Adafruit Unified Sensor by Adafruit" - Needed in conjunction with above library to use DHT11 temperature sensor library
// NOTES:
// Arduino Timers run on 16 MHz clock
// Need 10s interrupt
// https://www.instructables.com/Arduino-Timer-Interrupts/
// Timer 0/2 = 8 bit counters (255), timer 1 = 16 bit (65535)
// Prescalar indicates speed of timer count => Timer speed (Hz) = (arduino clock speed) / prescalar
// Max prescalar is 1024 => 16,000,000 / 1,024 = 15,625 Hz
// Meaning that we need to utilize timer 1 for this since we are looking for 10 second interrupts. In order to get this, we need to add our own count logic
// Utilizing a 1Hz interrupt signal for this, will count when 10 interrupts occur and on the 10th interrupt will process sensor data
// DHT11 temperature sensor - https://learn.adafruit.com/dht/using-a-dhtxx-sensor-with-arduino
// https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
#include "DHT.h"
#define DHTPIN 12 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT22 // DHT 22
DHT temp_sensor(DHTPIN, DHTTYPE);
volatile bool one_second_isr_triggered = false;
unsigned long lastLoggingTime = 0; // For tracking logging interval without temp_capture_timer_count
bool temp_sensor_data_calibrated = false; // Calibration flag
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600); // Set up serial communication at 9600 bps
temp_sensor.begin(); // Initialize DHT sensor
noInterrupts(); // Stop interrupts
// set timer1 interrupt at 1Hz
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; //initialize counter value to 0
// set compare match register for 1hz increments
OCR1A = 15624; // = (16*10^6) / (1*1024) - 1 (must be <65536)
TCCR1B |= (1 << WGM12); // turn on CTC mode
TCCR1B |= (1 << CS12) | (1 << CS10); // Set CS10 and CS12 bits for 1024 prescaler (the rate at which the timer counts up)
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt for OCR1A (when timer matches value in OCR1A, TIMER1_COMPA_vect ISR will be triggered)
interrupts(); // Allow interrupts
}
// Timer 1 ISR routine - ISR triggers every second. Flag is used to increment a count in main loop to determine when 10 seconds has passed
ISR(TIMER1_COMPA_vect){
one_second_isr_triggered = true;
return;
}
// Celsius to Farenheit converter
float celsius_to_farenheit(float temp_celsius){
return ((temp_celsius * 9.0) / 5.0) + 32;
}
// Log data by printing CSV format of (time, temperature) to Serial Console. Putty is used to log data in actual CSV file
// https://www.circuitbasics.com/logging-arduino-data-to-files-on-a-computer/#:~:text=Enter%20the%20COM%20port%20(e.g.,save%20the%20file%20you%20logged.
void log_data(float temp_farenheit) {
Serial.print("Time (s): ");
Serial.print(millis() / 1000);
Serial.print(", Temperature (F): ");
Serial.println(temp_farenheit);
}
// Function that enters a loop and waits for temperature sensor data to stabilize before exiting
void handleTempSensorCalibration(){
unsigned long lastReadTime = millis();
// This is all really not needed, just printing stuff
int readingNumber = 1; // Variable to track the number of readings
noInterrupts(); // Disable interrupts to ensure atomic operation for reading and conversion
float lastTemperature = temp_sensor.readTemperature();
float lastTemperatureF = celsius_to_farenheit(lastTemperature);
interrupts(); // Re-enable interrupts after critical section
Serial.print("Initial Calibration Reading: ");
Serial.println(lastTemperatureF);
int stableReadingsCount = 0;
Serial.println("Calibrating temperature sensor...");
// bool temp_sensor_data_calibrated = false; // TODO: INTIALIZE THIS TO FALSE!!! Setting to true to skip calibration phase for now
// Stay in while loop and continuosly read temperature sensor data every couple of seconds (based on DHT11 temperature sensor datasheet)
// Could user same Timer for data capture intervals here, or just use delay since we arent processing anything else
// Implemented logic here to determine when temperature sensor data has settled
while (!temp_sensor_data_calibrated) {
if (millis() - lastReadTime >= 2000) { // 2 seconds interval
noInterrupts(); // Disable interrupts to ensure atomic operation for reading and conversion
float currentTemperature = temp_sensor.readTemperature();
float currentTemperatureF = celsius_to_farenheit(currentTemperature);
interrupts(); // Re-enable interrupts after critical section
Serial.print("Calibration Reading " + String(readingNumber) + ": "); // Number each reading
Serial.println(currentTemperatureF);
// Maybe create like a 10 element array of floats and ensure that all of the values are within +/- 1 degree of each other or something??
// Implemented something simpler
if (abs(currentTemperature - lastTemperature) <= 0.5) {
stableReadingsCount++;
// From some quick testing, looks like it takes like 4 reads or so to get some updated data so do 5
if (stableReadingsCount >= 5) {
temp_sensor_data_calibrated = true;
break; // Exit the loop once calibrated
}
} else {
stableReadingsCount = 0; // Reset if not stable
}
lastTemperature = currentTemperature;
lastReadTime = millis();
readingNumber++; // Increment the reading number for the next reading
}
}
}
// Function that enters while loop and reads temperature sensor data every 10 seconds and logs data to serial console
void handleRecordTemperature() {
// Only log if 10 seconds have passed since the last log
if (millis() - lastLoggingTime >= 10000) {
lastLoggingTime = millis(); // Update timing for the next log interval
// Serial.println("10 Seconds have passed!"); // DEBUG PRINTOUT
// Capture temp sensor data
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
// Read temperature as Celsius (the default)
noInterrupts(); // Disable interrupts to ensure atomic operation for reading and conversion
float temp_in_celsius = temp_sensor.readTemperature();
float temp_in_farenheit = celsius_to_farenheit(temp_in_celsius);
interrupts(); // Re-enable interrupts after critical section
// Read temperature as Fahrenheit (isFahrenheit = true)
// TODO: There is a command here to just read Farenheit temperature instead of doing a conversion from C to F... Think this still satisfies his requirement of capturing temp and converting it into Farenheit
// float temp_in_farenheit = temp_sensor.readTemperature(true);
// Check if read failed, if failed dont log and print error message
if (!isnan(temp_in_farenheit)){
log_data(temp_in_farenheit);
}
else {
// TODO: Should we add any other sort of error handling here besides a simple print out?
Serial.println("ERROR! Failed to read temperature sensor data!");
}
}
}
// We sequentially advance forward to every state of the FSA and then restart the loop.
// Example: https://eng.libretexts.org/Bookshelves/Electrical_Engineering/Electronics/Embedded_Controllers_Using_C_and_Arduino_(Fiore)/24%3A_Bits_and_Pieces__digitalRead()/24.2%3A_A_Practical_Example__Round-Robin_Switch
enum State {
TempSensorCalibration,
RecordTemperature
};
State currentState = TempSensorCalibration; // Define starting state
void loop() {
switch (currentState) {
case TempSensorCalibration:
Serial.println("ENTERING TempSensorCalibration STATE (#1) - Waiting for temperature sensor data to settle");
handleTempSensorCalibration();
Serial.println("Temperature sensor has been calibrated!");
currentState = RecordTemperature; // Once sensor has been calibrated, begin to record temperature forever
break;
case RecordTemperature:
static bool firstEntry = true; // Static variable to track the first entry into this state
if (firstEntry) {
Serial.println("ENTERING RecordTemperature STATE (#2) - Recording temperature value every 10 seconds");
firstEntry = false; // Ensure the message is printed only once
}
handleRecordTemperature();
break;
// Reset the firstEntry flag when leaving the RecordTemperature state
if (currentState != RecordTemperature) {
firstEntry = true; // Ready to print the message again next time the state is entered
}
// Optionally: Implement any needed logic to re-enter calibration under specific conditions
}
}