+// Radio Transmitting Clock
+//
+// Circuit consists of an Arduino Nano with a DS13xx, NRF24L01+ board
+// and I2C for connecting the LED display device.
+// We have the I2C interface connected to a pair of mosfets to
+// brodge to 3V3 signalling and a 78L33 device is putting 3V3 on the breadboard
+// main power bus. The NRF24 board is 3V3 only.
+// Added an LED with 330R to pin 6 for heartbeat.
+#define F_CPU 20000000UL
+#include <Arduino.h>
+#include <Wire.h>
+#include <MilliTimer.h>
+#include <SPI.h>
+#include <RHDatagram.h>
+#include <RH_NRF24.h>
+#include <LEDPulse.h>
+#include <RTClib.h>
+#include <OneWire.h>
+#include <DallasTemperature.h>
+#include <DisplayI2C.h>
+#include <LiquidCrystal_I2C.h>
+
+static void I2C_Init();
+static void LCD_Init();
+static void RTC_Init();
+static void Radio_Init();
+static void DS18B20_Init();
+static void LedDisplay_Init();
+static void printAddress(DeviceAddress address);
+
+#define RADIO_CE_PIN 8
+#define RADIO_CHANNEL 85
+const uint8_t SENSOR_HUB_ADDRESS = 1;
+
+#define LCD_ADDR 0x20
+#define LCD_EN_BIT 4
+#define LCD_RW_BIT 5
+#define LCD_RS_BIT 6
+#define LCD_D4_BIT 0
+#define LCD_BL_BIT 7
+LiquidCrystal_I2C LCD(LCD_ADDR, LCD_EN_BIT, LCD_RW_BIT, LCD_RS_BIT,
+ LCD_D4_BIT, LCD_D4_BIT+1, LCD_D4_BIT+2, LCD_D4_BIT+3);
+
+OneWire W1Bus(4); // OneWire bus connected to pin 4.
+DallasTemperature w1sensors(&W1Bus);
+DeviceAddress w1address;
+RH_NRF24 nrf24;
+RHDatagram Radio(nrf24, SENSOR_HUB_ADDRESS);
+DisplayI2C LedDisplay(0x22, 1);
+RTC_DS1307 RTC;
+MilliTimer HeartbeatTimer, DS18B20Timer, OutputTimer, TimingTimer;
+LEDPulse Heartbeat(6);
+DateTime CurrentTime;
+float CurrentTemperature = 0.0f;
+float SlaveTemperature = 0.0f;
+uint8_t SlaveAddress = 0;
+bool UseRadio = false;
+bool UseDS18B20 = false;
+bool UseRTC = false;
+bool UseLCDDisplay = false;
+bool UseLedDisplay = false;
+bool LedDisplayUpdate = false;
+
+enum OutputState_t {Output_None, Output_A, Output_B, Output_C,
+ Output_D, Output_E, Output_F, Output_G,
+ Output_H, Output_I, Output_Temperature,
+ Output_RequestNext, Output_LCD};
+OutputState_t OutputState = Output_None;
+
+void setup()
+{
+ Wire.begin();
+ Serial.begin(57600);
+ delay(400);
+ Serial.println(F("# NRF24 Clock Receiver"));
+ I2C_Init();
+ LCD_Init();
+ RTC_Init();
+ Radio_Init();
+ DS18B20_Init();
+ LedDisplay_Init();
+ pinMode(7, OUTPUT); digitalWrite(7, LOW);
+}
+
+void loop()
+{
+ bool new_date = false;
+ if (LedDisplayUpdate)
+ {
+ LedDisplay.Update();
+ LedDisplayUpdate = false;
+ }
+ if (HeartbeatTimer.poll(10))
+ {
+ Heartbeat.Next();
+ }
+ if (TimingTimer.poll(10))
+ {
+ PORTD ^= _BV(7);
+ }
+ if (OutputState != Output_None)
+ {
+ Output();
+ }
+ if (UseDS18B20 && DS18B20Timer.poll(1500))
+ {
+ // read the temperature result from our chosen sensor.
+ CurrentTemperature = w1sensors.getTempC(w1address);
+ // trigger another conversion.
+ //w1sensors.requestTemperatures();
+ }
+ if (UseRadio && Radio.available())
+ {
+ uint8_t buffer[RH_NRF24_MAX_MESSAGE_LEN];
+ uint8_t len = sizeof(buffer), client_addr, target_addr, id = 0, flags = 0;
+ if (Radio.recvfrom(buffer, &len, &client_addr, &target_addr, &id, &flags))
+ {
+ SlaveAddress = client_addr;
+ SlaveTemperature = *(float *)buffer;
+ }
+ }
+ if (OutputTimer.poll(1000))
+ {
+ OutputState = Output_A;
+ }
+}
+
+static void Output()
+{
+ switch (OutputState)
+ {
+ case Output_A:
+ {
+ if (UseLedDisplay)
+ {
+ if (UseDS18B20)
+ {
+ LedDisplay.SetValue(CurrentTemperature * 100);
+ }
+ }
+ OutputState = Output_B;
+ break;
+ }
+ case Output_B:
+ {
+ if (CurrentTime.unixtime() == 0)
+ {
+ Serial.print(millis()/1000, DEC);
+ OutputState = Output_Temperature;
+ }
+ else
+ OutputState = Output_C;
+ break;
+ }
+ case Output_C:
+ {
+ Serial.print(CurrentTime.unixtime(), DEC);
+ Serial.print(' ');
+ OutputState = Output_D;
+ break;
+ }
+ case Output_D:
+ {
+ Serial.print(CurrentTime.day(), DEC);
+ Serial.print('/');
+ OutputState = Output_E;
+ break;
+ }
+ case Output_E:
+ {
+ Serial.print(CurrentTime.month(), DEC);
+ Serial.print('/');
+ OutputState = Output_F;
+ break;
+ }
+ case Output_F:
+ {
+ Serial.print(CurrentTime.year(), DEC);
+ Serial.print(' ');
+ OutputState = Output_G;
+ break;
+ }
+ case Output_G:
+ {
+ if (CurrentTime.hour() < 10) Serial.print('0');
+ Serial.print(CurrentTime.hour(), DEC);
+ Serial.print(':');
+ OutputState = Output_H;
+ break;
+ }
+ case Output_H:
+ {
+ if (CurrentTime.minute() < 10) Serial.print('0');
+ Serial.print(CurrentTime.minute(), DEC);
+ Serial.print(':');
+ OutputState = Output_I;
+ break;
+ }
+ case Output_I:
+ {
+ if (CurrentTime.second() < 10) Serial.print('0');
+ Serial.print(CurrentTime.second(), DEC);
+ OutputState = Output_Temperature;
+ break;
+ }
+ case Output_Temperature:
+ {
+ Serial.print(' ');
+ Serial.print(CurrentTemperature);
+ if (SlaveTemperature != 0.0f)
+ {
+ Serial.print(F(" ["));
+ Serial.print(SlaveAddress, HEX);
+ Serial.print(' ');
+ Serial.print(SlaveTemperature);
+ Serial.print(']');
+ }
+ Serial.println();
+ OutputState = Output_RequestNext;
+ break;
+ }
+ case Output_RequestNext:
+ {
+ // trigger another conversion.
+ w1sensors.requestTemperatures();
+ OutputState = Output_LCD;
+ break;
+ }
+ case Output_LCD:
+ {
+ if (UseLCDDisplay)
+ {
+ LCD.clear();
+ LCD.print(CurrentTemperature);
+ if (UseRTC)
+ {
+ LCD.setCursor(0, 1);
+ DateTime now = RTC.now();
+ PrintTimeOnStream(&LCD, now);
+ }
+ }
+ OutputState = Output_None;
+ break;
+ }
+ }
+}
+
+static void Radio_Init()
+{
+ // defaults to channel 2, 2Mbps and 0dBm
+ if (Radio.init())
+ {
+ nrf24.setChannel(RADIO_CHANNEL);
+ Radio.setThisAddress(SENSOR_HUB_ADDRESS);
+ UseRadio = true;
+ Serial.println(F("Radio initialized"));
+ }
+ else
+ {
+ Serial.println(F("Radio initialization failed"));
+ }
+}
+
+static void DS18B20_Init()
+{
+ Serial.println(F("Initializing OneWire bus..."));
+ w1sensors.begin();
+ w1sensors.setResolution(12);
+ w1sensors.setWaitForConversion(false);
+ int count = w1sensors.getDeviceCount();
+ for (int n = 0; n < count; ++n)
+ {
+ if (w1sensors.getAddress(w1address, n))
+ {
+ Serial.print(n);
+ Serial.print(F(": "));
+ printAddress(&Serial, w1address);
+ Serial.println();
+ UseDS18B20 = true;
+ w1sensors.requestTemperatures();
+ }
+ }
+}
+
+static void printAddress(Print *stream, DeviceAddress address)
+{
+ for (int n = 0; n < 8; ++n)
+ {
+ if (address[n] < 16)
+ stream->print('0');
+ stream->print(address[n], HEX);
+ }
+}
+
+// print the time (HH:MM:SS) on a stream.
+static void PrintTimeOnStream(Print *stream, DateTime now)
+{
+ if(now.hour() < 10) stream->print('0');
+ stream->print(now.hour());
+ stream->print(':');
+ if(now.minute() < 10) stream->print('0');
+ stream->print(now.minute());
+ stream->print(':');
+ if(now.second() < 10) stream->print('0');
+ stream->print(now.second());
+}
+
+static void I2C_Init()
+{
+ for (uint8_t addr = 0; addr < 127; ++addr)
+ {
+ Wire.beginTransmission(addr);
+ uint8_t err = Wire.endTransmission();
+ if (err == 0)
+ {
+ Serial.print(F("# Found I2C device at 0x"));
+ if (addr < 16) Serial.print('0');
+ Serial.print(addr, HEX);
+ if (addr == 0x20) { Serial.print(F(" (LCD)")); UseLCDDisplay = true; }
+ if (addr == 0x22) { Serial.print(F(" (LED)")); UseLedDisplay = true; }
+ if (addr == 0x57) { Serial.print(F(" (EEPROM)")); }
+ if (addr == 0x68) { Serial.print(F(" (RTC)")); UseRTC = true;}
+ Serial.println();
+ }
+ else if (err == 4)
+ {
+ Serial.print(F("# Unknown error at address 0x"));
+ if (addr < 16) Serial.print('0');
+ Serial.print(addr, HEX);
+ }
+ }
+}
+
+static void LCD_Init()
+{
+ if (UseLCDDisplay)
+ {
+ LCD.begin(16, 2);
+ LCD.clear();
+ LCD.print(F("Radio Clock Recvr"));
+ LCD.setCursor(0,1);
+ LCD.print(F("v1.0"));
+ }
+}
+
+static void RTC_Init()
+{
+ if (UseRTC)
+ {
+ RTC.begin();
+ //RTC.adjust(DateTime(__DATE__, __TIME__));
+ if (!RTC.isrunning())
+ {
+ Serial.println(F("WARNING: RTC uninitialized"));
+ }
+ }
+}
+
+static void LedDisplay_Init()
+{
+ if (UseLedDisplay)
+ {
+ DisplayI2C::SetupTimer2();
+ LedDisplay.Setup();
+ }
+}
+
+ISR(TIMER2_OVF_vect)
+{
+ TCNT2 = DisplayI2C::TimerInterval; // reset the timer
+ LedDisplayUpdate = true; // set flag for loop function
+}