Intro:
When I get in to work in the morning and open up, I’ve noticed that all the rooms are different temperatures. So like a cat, curiosity got the better of me and I built a set of wireless temperature sensors.
Thankfully, the foundation of this project was covered by the previous weather station projects, so most of the mistakes had already been learned.
Hardware & Setup.
The setup consists of a base station and three remote sensors. For consistency, all of the temperature sensors are DS18B20 sensors (two in the waterproof-probe format and two in the TO-92 package).
All of the data is sent via nRF24L01 wireless transceivers. The remote sensors had the small PCB version and the base station had the +PA+LNA version.
The external sensor was powered via 10,000mAh lipo with suitable charging board. (Literally robbed from the weather station and butchered to fit this experiment).
The base station was also equipped with a temperature sensor and built an Arduino Uno fitted with a datalogging shield which saved the data to a .csv file on an SD card.
A 20×4 I2C LCD display also showed the real-time data from each of the sensors and also indicated if any of the remote sensors had gone offline.
Code
In the most simple terms, the transmitters take a measurement, send the data, go to sleep and repeat ad infinitum.
Each transmitter sends a single integer which acts as an ID, and a floating point number with the temperature data.
#include <LowPower.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
RF24 radio(7, 8); // CE, CSN
const byte address[] = "00001";
struct Data_Package {
int roomNo; //0 = main office, 1 = kitchen, 2 = office, 3 = outside
float temp;
};
Data_Package data = {2, 0.00}; //2 = office transmitter
//******************************************
void setup() {
Serial.begin(9600);
sensors.begin();
sensors.setResolution(12);
radio.begin();
if (!radio.begin()) {
Serial.println(F("radio hardware not responding!"));
while (1);
}
radio.setAutoAck(false);
radio.setChannel(115);
radio.setPALevel(RF24_PA_LOW);
radio.openWritingPipe(address);
radio.stopListening();
}
//******************************************
void loop() {
sendMeasure();
goToSleep(4); //pass number of 8 second sleeps , 4 * 8 = 32s refresh
}
//******************************************
void goToSleep(int _i) {
for (int i = 0; i < _i; i++) {
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
delay(500);
}
//******************************************
void sendMeasure() {
sensors.requestTemperatures();
data.temp = sensors.getTempCByIndex(0);
radio.write(&data, sizeof(Data_Package));
//Serial.println(data.temp);
delay(1);
}
The base station listens for transmissions and when one arrives, it sorts the data according to the ID attached to the data packet.
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>
#include "RTClib.h"
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
RF24 radio(7, 8); // CE, CSN
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS1307 rtc;
File dataFile;
const byte address[] = "00001";
const int chipSelect = 10;
struct Data_Package {
int roomNo; //0 = main office, 1 = kitchen, 2 = ncb office, 3 = outside
float temp;
};
Data_Package data;
unsigned long tempTimer = 0;
unsigned long recieveTimer[4] = {0, 0, 0, 0};
byte degreeSymbol[8] =
{
0b00000,
0b00100,
0b01010,
0b00100,
0b00000,
0b00000,
0b00000,
0b00000
};
//***************************************
void setup() {
Serial.begin(9600);
sensors.begin();
sensors.setResolution(12);
lcd.init();
lcd.backlight();
lcd.createChar(0, degreeSymbol);
radio.begin();
if (!radio.begin()) {
lcd.setCursor(0, 0);
lcd.print("Radio error");
while (1);
}
if (!SD.begin(chipSelect)) {
lcd.setCursor(0, 0);
lcd.print("SD card failed");
while (1) ;
}
dataFile = SD.open("datalog.txt", FILE_WRITE);
if (! dataFile) {
lcd.setCursor(0, 0);
lcd.print("Can't write data");
while (1) ;
}
if (! rtc.begin()) {
lcd.setCursor(0, 0);
lcd.print("RTC failed");
while (1);
}
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); //uncomment to set date and time
radio.setAutoAck(false);
radio.setChannel(115);
radio.setPALevel(RF24_PA_LOW);
radio.openReadingPipe(1, address);
radio.startListening();
writeHeader();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Base Stn");
lcd.setCursor(0, 1);
lcd.print("Kitchen");
lcd.setCursor(0, 2);
lcd.print("Office");
lcd.setCursor(0, 3);
lcd.print("Outside");
getBaseTemp();
}
//***************************************
void loop() {
if (radio.available()) {
radio.read(&data, sizeof(Data_Package));
recieveTimer[data.roomNo] = millis();
refreshDisplay(data.roomNo, data.temp);
writeToSD(data.roomNo, data.temp);
}
if ((tempTimer + 32000L) < millis()) { //30second refresh
tempTimer = millis();
getBaseTemp();
}
for (int i = 1; i < 4; i++) {
if ((recieveTimer[i] + 60000L) < millis()) {
lcd.setCursor(19, i);
lcd.print("?");
}
}
delay(20);
}
//***************************************
void refreshDisplay(int room_no, float temp_ ) {
switch (room_no) {
case 1:
lcd.setCursor(9, room_no);
lcd.print(temp_);
lcd.write(0);
lcd.print("C");
lcd.setCursor(19, room_no);
lcd.print(" ");
break;
case 2:
lcd.setCursor(9, room_no);
lcd.print(temp_);
lcd.write(0);
lcd.print("C");
lcd.setCursor(19, room_no);
lcd.print(" ");
break;
case 3:
lcd.setCursor(9, room_no);
lcd.print(temp_);
lcd.write(0);
lcd.print("C");
lcd.setCursor(19, room_no);
lcd.print(" ");
break;
}
}
//***************************************
void writeToSD(int room_no, float temp_) {
DateTime now = rtc.now();
String dataString = "";
dataString += String(now.year(), DEC);
dataString += String('/');
dataString += String(now.month(), DEC);
dataString += String('/');
dataString += String(now.day(), DEC);
dataString += String(" ");
dataString += String(now.hour(), DEC);
dataString += String(':');
dataString += String(now.minute(), DEC);
dataString += String(':');
dataString += String(now.second(), DEC);
dataString += String(", ");
switch (room_no) {
case 1: //kitchen
dataString += String(" ,");
dataString += String(temp_);
dataString += String(", ,");
break;
case 2: //ncb office
dataString += String(" , ,");
dataString += String(temp_);
dataString += String(",");
break;
case 3: //outside
dataString += String(" , , ,");
dataString += String(temp_);
break;
}
dataFile.println(dataString);
dataFile.flush();
}
//***************************************
void writeHeader() {
//create column headers
String dataString = "";
dataString += String("Date & Time , Base Temp (deg.C) , Kitchen Temp (deg.C) , Office Temp (deg.C) , Outside Temp (deg.C)");
dataFile.println(dataString);
dataFile.flush();
}
//*********************************
void getBaseTemp() {
sensors.requestTemperatures();
float baseTemp = sensors.getTempCByIndex(0);
lcd.setCursor(9, 0);
lcd.print(baseTemp);
lcd.write(0);
lcd.print("C");
DateTime now = rtc.now();
String dataString = "";
dataString += String(now.year(), DEC);
dataString += String('/');
dataString += String(now.month(), DEC);
dataString += String('/');
dataString += String(now.day(), DEC);
dataString += String(" ");
dataString += String(now.hour(), DEC);
dataString += String(':');
dataString += String(now.minute(), DEC);
dataString += String(':');
dataString += String(now.second(), DEC);
dataString += String(", ");
dataString += String(baseTemp);
dataString += String(", , ,");
dataFile.println(dataString);
dataFile.flush();
}
Judge the code all you want, but it compiles and more impressively: it works! The only real ‘bug’ I’ve seen is that if the outside temperature falls from a two digit number to a single digit number then the Celsius symbol is repeated.
Results
The experiment was run over three days before I became too impatient and had to see the data.
Overall, I’m happy with how this went. I’m even happier that I was able to harvest all of the parts from various other projects. The results are interesting – it’s interesting to see how being in the office makes it warmer.
In a future version, I would like to introduce an LDR to the indicator light of the boiler to see how the external temperature effects the duty cycle of the heating system. When will this happen? Probably never.
Last updated: 19/12/2022