#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
+#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
-#include <LiquidCrystal_I2C.h>\r
-#include <RHDatagram.h>\r
-#include <RH_NRF24.h>\r
-#include "BLE24.h"\r
-\r
-static void I2C_Init();\r
-static void LCD_Init();\r
-static void RTC_Init();\r
-static void Radio_Init();\r
-static void DS18B20_Init();\r
-static void PrintAddress(Print *stream, DeviceAddress address);\r
-\r
-#define RADIO_CE_PIN 8\r
-#define RADIO_CHANNEL 85\r
+#include <LiquidCrystal_I2C.h>
+#include <RHDatagram.h>
+#include <RH_NRF24.h>
+#include "BLE24.h"
+
+static void I2C_Init();
+static void LCD_Init();
+static void RTC_Init();
+static void Radio_Init();
+static void DS18B20_Init();
+static void EEPROM_Init();
+static void PrintAddress(Print *stream, DeviceAddress address);
+
+#define RADIO_CE_PIN 8
#define PIN_LED 6
#define PIN_ONEWIRE 4
-\r
-#define SENSOR_HUB_ADDRESS 1\r
-#define DEVICE_ADDRESS 2\r
-\r
-#define LCD_ADDR 0x20\r
-#define LCD_EN_BIT 4\r
-#define LCD_RW_BIT 5\r
-#define LCD_RS_BIT 6\r
-#define LCD_D4_BIT 0\r
-#define LCD_BL_BIT 7\r
-\r
-LiquidCrystal_I2C lcd(LCD_ADDR, LCD_EN_BIT, LCD_RW_BIT, LCD_RS_BIT,\r
+
+const int DEFAULT_RADIO_CHANNEL = 85;
+const int DEFAULT_RADIO_DEV_ADDRESS = 3;
+const int DEFAULT_RADIO_HUB_ADDRESS = 1;
+const uint8_t EEPROM_INITIALIZED_MAGIC = 0xA5;
+
+EEMEM uint8_t ee_initialized = EEPROM_INITIALIZED_MAGIC;
+EEMEM int ee_nrf_channel = DEFAULT_RADIO_CHANNEL;
+EEMEM int ee_nrf_dev_address = DEFAULT_RADIO_DEV_ADDRESS;
+EEMEM int ee_nrf_hub_address = DEFAULT_RADIO_HUB_ADDRESS;
+EEMEM char ee_serial[4] = {0xde, 0xad, 0xbe, 0xef};
+
+#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);
#define SENSOR_RESOLUTION 12
OneWire w1bus(PIN_ONEWIRE);
DallasTemperature w1sensors(&w1bus);
DeviceAddress w1sensorAddress;
-RH_NRF24 nrf24(PIN_CSN, PIN_CE);\r
-RHDatagram Radio(nrf24, SENSOR_HUB_ADDRESS);
+RH_NRF24 nrf24(PIN_CSN, PIN_CE);
+RHDatagram Radio(nrf24);
CBTLE BTLE;
bool UseLCDDisplay = false;
bool UseOneWire = false;
bool UseRadio = false;
float currentTemperature = 20.0f;
uint8_t state = 0;
+int nrf24_radio_channel, nrf24_radio_dev_address, nrf24_radio_hub_address;
unsigned long btle_time = 0;
-static void Radio_Init()\r
-{\r
- if (Radio.init())\r
- {\r
- Radio.setThisAddress(DEVICE_ADDRESS);\r
- if (nrf24.setChannel(RADIO_CHANNEL))\r
- {\r
- UseRadio = true;\r
- Serial.println(F("Radio initialized"));\r
- }\r
- else\r
- {\r
- Serial.println(F("Radio: failed to select channel"));\r
- }\r
- }\r
- else\r
- {\r
- Serial.println(F("Radio initialization failed"));\r
- }\r
-}\r
-\r
-static void Radio_End()\r
-{\r
- \r
-}\r
-\r
-static void Radio_Send(uint8_t *data, uint8_t len)\r
-{\r
- if (UseRadio)\r
- {\r
- nrf24.setModeTx();\r
- if (!Radio.sendto(data, len, SENSOR_HUB_ADDRESS))\r
- {\r
- Serial.println(F("Radio: failed to send"));\r
- }\r
- //if (!nrf24.sleep())\r
- // Serial.println(F("Radio: failed to enter sleep mode"));\r
- }\r
-}\r
-\r
-// Find any 1-wire sensors and use the first one. Trigger an async conversion.\r
-static void OneWire_Init()\r
-{\r
- Serial.println(F("Initializing OneWire bus..."));\r
- w1sensors.begin();\r
- w1sensors.setResolution(12);\r
- w1sensors.setWaitForConversion(false);\r
- int count = w1sensors.getDeviceCount();\r
- for (int n = 0; n < count; ++n)\r
- {\r
- if (w1sensors.getAddress(w1sensorAddress, n))\r
- {\r
- Serial.print(n);\r
- Serial.print(F(": "));\r
- PrintAddress(&Serial, w1sensorAddress);\r
- Serial.println();\r
- UseOneWire = true;\r
- w1sensors.requestTemperatures();\r
- }\r
- }\r
-}\r
-\r
-static void PrintAddress(Print *stream, DeviceAddress address)\r
-{\r
- for (int n = 0; n < 8; ++n)\r
- {\r
- if (address[n] < 16)\r
- stream->print('0');\r
- stream->print(address[n], HEX);\r
- }\r
-}\r
-\r
-static void I2C_Init()\r
-{\r
- for (uint8_t addr = 0; addr < 127; ++addr)\r
- {\r
- Wire.beginTransmission(addr);\r
- uint8_t err = Wire.endTransmission();\r
- if (err == 0)\r
- {\r
- Serial.print(F("# Found I2C device at 0x"));\r
- if (addr < 16) Serial.print('0');\r
- Serial.print(addr, HEX);\r
- if (addr == 0x20) { Serial.print(F(" (LCD)")); UseLCDDisplay = true; }\r
- if (addr == 0x22) { Serial.print(F(" (LED)")); /*UseLedDisplay = true;*/ }\r
- if (addr == 0x57) { Serial.print(F(" (EEPROM)")); }\r
- if (addr == 0x68) { Serial.print(F(" (RTC)")); /* UseRTC = true; */}\r
- Serial.println();\r
- }\r
- else if (err == 4)\r
- {\r
- Serial.print(F("# Unknown error at address 0x"));\r
- if (addr < 16) Serial.print('0');\r
- Serial.print(addr, HEX);\r
- }\r
- }\r
-}\r
-\r
-static void LCD_Init()\r
-{\r
- if (UseLCDDisplay)\r
- {\r
- lcd.begin(16, 2);\r
- lcd.clear();\r
- lcd.print(F("BTLE Beacon"));\r
- }\r
-}\r
-\r
-volatile int analogValue;\r
-static void Analog_Init()\r
-{\r
- analogValue = 0;\r
- // REFS[1:0]: 0b00: AREF; 0b01: AVCC; 0b11: internal 1V1\r
- // ADLAR = 0: do not enable left adjust\r
- // MUX[3:0]: using pin A1 (0b0001)\r
- ADMUX = _BV(MUX0) | _BV(REFS0);// Internal 5V\r
- // ADEN: enable the ADC\r
- // ADATE: enable auto trigger\r
- // ADIE: adc interrupt enabled\r
- // ADPS[2:0]: adc clock prescale 16MHz/128 so 125kHz\r
- ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE)\r
- | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);\r
- // ADTS[2:0]: AutoTrigger Source: use free running mode\r
- ADCSRB = 0;\r
- // Disable the digital functions for pin A2\r
- DIDR0 |= _BV(ADC2D);\r
- // ADSC: start the freerunning conversion\r
- // if not freerunning we could alternate between a set of sources\r
- // like AD8 which is the internal temperature.\r
- ADCSRA |= _BV(ADSC);\r
-}\r
-\r
-static int GetAnalogValue() { return analogValue; }\r
-static int GetAnalogMillivolts() { return (analogValue * 5120) / 1024; }\r
-\r
-static long GetHumidity()\r
-{\r
- // Vref is externally provided and set to 5ishV.\r
- long millivolts = (analogValue * 5120) / 1024;\r
- // HU10 range is 1V == 0% to 3V == 100%.\r
- long rh = ((millivolts - 1000) * 100000) / 2000;\r
- return rh; /* value is percentage * 1000 */\r
+static void EEPROM_Init()
+{
+ uint8_t initialized = eeprom_read_byte(&ee_initialized);
+ if (initialized)
+ {
+ EEPROM.get((intptr_t)&ee_nrf_channel, nrf24_radio_channel);
+ EEPROM.get((intptr_t)&ee_nrf_dev_address, nrf24_radio_dev_address);
+ EEPROM.get((intptr_t)&ee_nrf_hub_address, nrf24_radio_hub_address);
+ }
+ else
+ {
+ nrf24_radio_channel = DEFAULT_RADIO_CHANNEL;
+ nrf24_radio_dev_address = DEFAULT_RADIO_DEV_ADDRESS;
+ nrf24_radio_hub_address = DEFAULT_RADIO_HUB_ADDRESS;
+ }
+}
+
+static void Radio_Init()
+{
+ if (Radio.init())
+ {
+ Radio.setThisAddress(nrf24_radio_dev_address);
+ if (nrf24.setChannel(nrf24_radio_channel))
+ {
+ UseRadio = true;
+ Serial.println(F("Radio initialized"));
+ }
+ else
+ {
+ Serial.println(F("Radio: failed to select channel"));
+ }
+ }
+ else
+ {
+ Serial.println(F("Radio initialization failed"));
+ }
+}
+
+static void Radio_End()
+{
+}
+
+static void Radio_Send(uint8_t *data, uint8_t len)
+{
+ if (UseRadio)
+ {
+ nrf24.setModeTx();
+ if (!Radio.sendto(data, len, nrf24_radio_hub_address))
+ {
+ Serial.println(F("Radio: failed to send"));
+ }
+ }
+}
+
+// Find any 1-wire sensors and use the first one. Trigger an async conversion.
+static void OneWire_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(w1sensorAddress, n))
+ {
+ Serial.print(n);
+ Serial.print(F(": "));
+ PrintAddress(&Serial, w1sensorAddress);
+ Serial.println();
+ UseOneWire = 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);
+ }
+}
+
+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("BTLE Beacon"));
+ lcd.setCursor(0, 1);
+ lcd.print(F("chan:"));
+ lcd.print(nrf24_radio_channel);
+ lcd.print(F(" id:"));
+ lcd.print(nrf24_radio_dev_address);
+ }
+}
+
+volatile int analogValue;
+static void Analog_Init()
+{
+ analogValue = 0;
+ // REFS[1:0]: 0b00: AREF; 0b01: AVCC; 0b11: internal 1V1
+ // ADLAR = 0: do not enable left adjust
+ // MUX[3:0]: using pin A1 (0b0001)
+ ADMUX = _BV(MUX0) | _BV(REFS0);// Internal 5V
+ // ADEN: enable the ADC
+ // ADATE: enable auto trigger
+ // ADIE: adc interrupt enabled
+ // ADPS[2:0]: adc clock prescale 16MHz/128 so 125kHz
+ ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE)
+ | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
+ // ADTS[2:0]: AutoTrigger Source: use free running mode
+ ADCSRB = 0;
+ // Disable the digital functions for pin A2
+ DIDR0 |= _BV(ADC2D);
+ // ADSC: start the freerunning conversion
+ // if not freerunning we could alternate between a set of sources
+ // like AD8 which is the internal temperature.
+ ADCSRA |= _BV(ADSC);
+}
+
+static int GetAnalogValue() { return analogValue; }
+static int GetAnalogMillivolts() { return (analogValue * 5120) / 1024; }
+
+static long GetHumidity()
+{
+ // Vref is externally provided and set to 5ishV.
+ long millivolts = (analogValue * 5120) / 1024;
+ // HU10 range is 1V == 0% to 3V == 100%.
+ long rh = ((millivolts - 1000) * 100000) / 2000;
+ return rh; /* value is percentage * 1000 */
+}
+
+ISR(ADC_vect)
+{
+ // NOTE: MUST read the low byte first, reading ADCH resets.
+ analogValue = ADCL | (ADCH << 8);
+ // If not in free-running mode then start another conversion
+ // ADCSRA |= _BV(ADSC);
}
-\r
-ISR(ADC_vect)\r
-{\r
- // NOTE: MUST read the low byte first, reading ADCH resets.\r
- analogValue = ADCL | (ADCH << 8);\r
- // If not in free-running mode then start another conversion\r
- // ADCSRA |= _BV(ADSC);\r
-}\r
static void UpdateDisplay()
{
lcd.home();
- lcd.print(currentTemperature);\r
- lcd.print((char)0xdf);\r
+ lcd.print(currentTemperature);
+ lcd.print((char)0xdf);
lcd.print("C ");
lcd.print(static_cast<float>(GetHumidity())/1000.0f);
lcd.print(" RH");
- lcd.setCursor(0, 1);\r
- lcd.print("BTLE Beacon: ");\r
+ lcd.setCursor(0, 1);
+ lcd.print("BTLE Beacon: ");
lcd.print(btle_time);
lcd.print("ms");
}
void setup()
{
+ EEPROM_Init();
Analog_Init();
Wire.begin();
SPI.begin();
void loop()
{
unsigned int channel_index;
- uint8_t btle_packet[BTLE_BUFFER_SIZE];\r
+ uint8_t btle_packet[BTLE_BUFFER_SIZE];
unsigned int btle_packet_len;
unsigned long start = millis();
btle_packet_len = BTLE.mkpacket(btle_packet, sizeof(btle_packet), currentTemperature);
- for (unsigned int channel_index = 0; channel_index < BTLE_NUM_CHANNELS; ++channel_index)\r
- {\r
- BTLE.announce(channel_index, btle_packet, btle_packet_len);\r
- }\r
- btle_time = millis() - start;\r
+ for (unsigned int channel_index = 0; channel_index < BTLE_NUM_CHANNELS; ++channel_index)
+ {
+ BTLE.announce(channel_index, btle_packet, btle_packet_len);
+ }
+ btle_time = millis() - start;
if (UseOneWire)
{