/* lp_BLE_microswitch (c)2024 Forward Computing and Control Pty. Ltd. This code is not warranted to be fit for any purpose. You may only use it at your own risk. This code may be freely used for both private and commercial use Provide this copyright is maintained. */ const char LOCAL_NAME[] = "Front_Bedroom_Win_Rhs"; // Bedroom 1 window 1, (max 24char) const unsigned MAX_LOCAL_NAME_LEN = 24; // + ,O or ,C for <=26char total // location is local_name followed by , followed by O for open or C for closed const int microswitch_COM_pin = 6; // P0.06 const int microswitch_NO_pin = 7; // // P0.07 lp_timer microswitchCheck_Timer; // check for change in microswitch const unsigned long MICRO_SWITCH_CHECK_ms = 1000; // 1sec adds ~0.33uA to supply current so for 10sec adv every 100sec => ~5years battery life // if microswitch change detected the change is advertised immediately. // ~830mV avg across 2500ohm resistor => 332uA to check the microswitch at 1ms intervals no BLE // ~204mV avg across 300ohm resistor => 666uA to check the microswitch at 0.5ms intervals no BLE // implies at 1sec checks 666uA * 0.5ms/1000ms => 0.33uA to read micro switch every sec. no BLE #include "SafeString.h" #include "BLEPeripheral.h" // Supply Capacitor Selection // ----------------------------- // For Advertising pulses (~12mA with no DC/DC converter fitted to the module) // No additional supply capacitors (just the 4.7uF on the module) => 0.785V peak with a 300 ohms source resistor and 0.665V with 150 ohms source resistor // The Nordic reference schematics for nRF52832 modules all include a 4.7uF supply capacitor which should already be included on the module // Other options with additioal supply capacitors added // 1 x 22uF 16V ceramic supply capacitor => 0.430V peak with 300 ohm source resistor and 0.400V with 150 ohm source resistor // 2 x 22uF 16V ceramic supply capacitor => 0.300V peak with 300 ohms sourc resistor and 0.285V with 150 ohm source resistor // Typical coin cell source resistance (internal resistance) is initally 10ohm rising to ~140ohms towards end life, for pulse loads, or higher for continual low currents (3uA) // see the charts in https://data.energizer.com/pdfs/lithiumcoin_appman.pdf and https://data.energizer.com/pdfs/cr2032_eu.pdf // so with no additional supply capacitors almost all the capacity of the CR2032 coil cell can be used down to 2.5V (2.5 - 0.75 = 1.75V > nRF52832 drop out voltage) // // Current consumption for this sketch checking microswitch every second for a change in state adds ~0.33uA avg for microswitch check each sec // see BLE_ADVERT_MS and BLE_NO_ADVERT_MS settings below // ----------------------------------- // Crystal LowFreq clock: no advertising but still running 6.64mV => ~2.66uA // advertising ~70mV => ~28uA // Battery life for 10sec advert every 100sec // Crystal LowFreq clock: 10sec advert in 100sec => 28*10/100 + 2.66*(100-10)/100 = ~5.2uA + ~0.33uA(microswitch check 1/sec) => ~5.53uA for uC using Crystal // Using a CR2032 235mAh to 2V https://data.energizer.com/pdfs/cr2032_eu.pdf using no extra supply capacitors to handle the Tx current pulses // Crystal LowFreq clock: CR2032 gives 235e-3/5.53e-6 hrs = 42495hrs => 1771 days => ~5years (~4years 10months) (e.g. GT832E_01 module) BLEPeripheral ble; // BLE_SCAN_DATA_MAX_VALUE_LENGTH == 29 in BLEDeviceLimits.h // to this is added length and type for total of 31 == max advertisize packet size // here we also set flags for not connectable which uses 3 bytes length(1), type(1), flags(1) // so MAX_SCAN_DATA_LEN reduced to 26 const size_t MAX_DATA_LEN = 26; // this can be split between say device NAME and Manufacturing Data // but that uses another two bytes for the length and type bytes for the Manufacturing Data // // For simplicity and readability here all the data is added to the NAME as ascii date comma separated // If using NAME and Manufacturing Data then sending data as binary in Manufacturing Data allows for more data. // // Note could also just have Manfacturing Data with out a device NAME but easier to read data in the name with scanning apps // uncomment this to enable Serial output uses a lot of supply current //#define DEBUG cSF(sfData, MAX_DATA_LEN); // Local name and formatted measurements SafeString will limit data to this length. Excess data is dropped NOT truncated see SafeString docs/tutorials unsigned short ADVERT_INTERVAL = 750; // default is 500, 750ms gives 13 advert packets in the 10 secs lp_timer BLE_Timer; // the advert timer // 10sec advert on 90sec advert off const unsigned long BLE_ADVERT_MS = 10ul * 1000; // advertise for 10sec at 750ms intervals i.e. ~13 advert packets const unsigned long BLE_NO_ADVERT_MS = 90ul * 1000; // go silent for 90sec // 10sec advert on 20sec advert off //const unsigned long BLE_ADVERT_MS = 20ul * 1000; // advertise for 10sec at 750ms intervals i.e. ~13 advert packets //const unsigned long BLE_NO_ADVERT_MS = 20ul * 1000; // go silent for 20sec bool microSwitchOpen = false; void readMicroSwitch() { // all timer handlers run on loop thread so will not be interrupted by other timer handlers pinMode(microswitch_NO_pin, OUTPUT); // 11k PULLUP RESISTOR digitalWrite(microswitch_NO_pin, LOW); // will pull down COM pin of microswitch is closed microSwitchOpen = (digitalRead(microswitch_COM_pin) == HIGH); pinMode(microswitch_NO_pin, INPUT_PULLUP); // 11k PULLUP RESISTOR } // this method set the advertized NAME to include the data comma delimited void setAdvertData() { readMicroSwitch(); sfData.clear(); sfData.readFrom(LOCAL_NAME, MAX_LOCAL_NAME_LEN); // limit to 24 chars sfData += ','; if (microSwitchOpen) { sfData += 'O'; } else { sfData += 'C'; } ble.setAdvertisedName(sfData.c_str()); // #ifdef DEBUG Serial.print("set data to "); Serial.println(sfData); #endif } void handleMicroSwitchCheck() { bool currentSwitchOpen = microSwitchOpen; readMicroSwitch(); if (currentSwitchOpen != microSwitchOpen) { // advertise now BLE_Timer.stop(); ble.end(); // will stop advert if running restartAdvert(); // with new state } } void setup() { #ifdef DEBUG Serial.setPins(30, 29); // Rx 30 == D0, Tx 29 == D1 Serial.begin(115200); Serial.println(); for (int i = 10; i > 0; i--) { Serial.print(' '); Serial.print(i); delay(500); } Serial.println(); #endif pinMode(microswitch_COM_pin, INPUT_PULLUP); // 11k PULLUP RESISTOR pinMode(microswitch_NO_pin, INPUT_PULLUP); // 11k PULLUP RESISTOR // set advertised name ble.setConnectable(false); ble.setTxPower(+4); ble.setAdvertisingInterval(ADVERT_INTERVAL); setAdvertData(); // sets name and measurement data ble.begin(); #ifdef DEBUG Serial.println(" BLE started"); #endif BLE_Timer.startDelay(BLE_ADVERT_MS * 3, stopAdvert); // initially advertise for 30sec to make it easier to check the unit is running microswitchCheck_Timer.startTimer(MICRO_SWITCH_CHECK_ms, handleMicroSwitchCheck); // check for change in microswitch #ifdef DEBUG Serial.println("setup() finished"); #endif } void stopAdvert() { ble.end(); #ifdef DEBUG Serial.print("stop ble "); Serial.println(millis()); #endif BLE_Timer.startDelay(BLE_NO_ADVERT_MS, restartAdvert); //wait 90sec before restarting } void restartAdvert() { setAdvertData(); // pick up latest values ble.begin(); #ifdef DEBUG Serial.print("start ble ");; Serial.println(millis()); #endif BLE_Timer.startDelay(BLE_ADVERT_MS, stopAdvert); // stop in 10sec } void loop() { sleep(); // just sleep here waiting for the timer to trigger }