// lp_BLE_RainGauge.ino // !!! Note carefully, need to comment out #define LP_TIMER_US in utility/lp_timer_speed.h // to give 1hr timer // Using pfod_lp_nrf52_2042 addon for Generic nRF52832 bare modules // Using Arduino V1.8.19 IDE /* Code generated by pfodDesignerV3 V3.0.4250 */ /* (c)2025 Forward Computing and Control Pty. Ltd. NSW Australia, www.forward.com.au This code is not warranted to be fit for any purpose. You may only use it at your own risk. This generated code may be freely used for both private and commercial use provided this copyright is maintained. */ const static float mmPerPulse = 0.2794; uint32_t currentRainGaugePulses = 0; // cumulative lp_timer hr1Timer; // five min timer unsigned long HR_1_Timer_ms = 60ul * 60 * 1000; static const size_t number24hrIntervals = 24; static const size_t number7dayIntervals = 7; static const size_t number30dayIntervals = 30; // circular buffers, startingIdx... is the oldest data point // and the idx previous to startingIdx.. is the newest static uint32_t array24hr[number24hrIntervals]; static uint32_t array7day[number7dayIntervals]; static uint32_t array30day[number30dayIntervals]; static uint32_t pulses24hr = 0; // pulses 24hrs ago static uint32_t pulses7day = 0; // pulses 7days ago static uint32_t pulses30day = 0; // pulses 30 day ago static int startingIdx24hr = 0; static int startingIdx7day = 0; static int startingIdx30day = 0; int comparatorPin = A1; // P0.03 // This sketch needs the pfod_lp_nrf52_2022 add on to be installed and compile using Arduino V1.8.19 IDE // see https://www.forward.com.au/pfod/BLE/LowPower_2022/index.html #include #include // download the pfodParser library V3.63.1+ from http://www.forward.com.au/pfod/pfodParserLibraries/index.html #include // install pfodParser from the Arduino Library Manager // OR download the libraries from http://www.forward.com.au/pfod/pfodParserLibraries/index.html // this coder needs long ms timers #ifdef LP_TIMER_US #error Comment out #define LP_TIMER_US in the file ..Arduino15\packages\sandeepmistry\hardware\nRF5\0.7.0\cores\nRF5\utility\lp_timer_speed.h #endif pfodParser parser; // create a parser to handle the pfod messages lp_BLESerial bleSerial; // create a BLE serial connection lp_timer comparitorDelay; unsigned long COMPARITOR_DELAY_ms = 1000; // wait 1sec before accepting another pulse // this limits max rain fall to ~16mm/min (1m/hr) int debugPin = 10; bool debugPinOn = false; void initializeDebugPin() { pinMode(debugPin, OUTPUT); digitalWrite(debugPin, HIGH); debugPinOn = true; } lp_timer debugTimer; // for timed debug pulses void handleDebugTimer() { toggleDebugPin(); // just change state on timeout } void DebugPinOn() { digitalWrite(debugPin, HIGH); debugPinOn = true; } void DebugPinOff() { digitalWrite(debugPin, LOW); debugPinOn = false; } void toggleDebugPin() { if (debugPinOn) { DebugPinOff(); } else { DebugPinOn(); } } void pulseDebugPin(uint32_t us) { toggleDebugPin(); delayMicroseconds(us); toggleDebugPin(); } void initializeArrays() { for (int i = 0; i < number24hrIntervals; i++) { array24hr[i] = 0; } for (int i = 0; i < number7dayIntervals; i++) { array7day[i] = 0; } for (int i = 0; i < number30dayIntervals; i++) { array30day[i] = 0; } } // partials set to false once array is filled with measurements bool partial24hr = true; bool partial7day = true; bool partial30day = true; static void next24hrIdx() { startingIdx24hr++; if (startingIdx24hr >= number24hrIntervals) { startingIdx24hr = 0; partial24hr = false; } } static void next7dayIdx() { startingIdx7day++; if (startingIdx7day >= number7dayIntervals) { startingIdx7day = 0; partial7day = false; } } static void next30dayIdx() { startingIdx30day++; if (startingIdx30day >= number30dayIntervals) { startingIdx30day = 0; partial30day = false; } } // update 24hr and 7 and 30 day arrays // startingIdx... is the oldest point in the array // when a new data point is available, // save the oldest point to pulses.. for later diff with current pulses // save the new data in that point and increment to the next oldest point // in the array. // so at the end of this the head of the array (the newest point) // is the idx previous to startingIdx... and startingIdx... is the // the next oldest data point to be updated next. void handle_1hr_timer() { pulses24hr = array24hr[startingIdx24hr]; array24hr[startingIdx24hr] = currentRainGaugePulses; next24hrIdx(); if (startingIdx24hr == 0) { // wrapped around one 24hr period pulses7day = array7day[startingIdx7day]; array7day[startingIdx7day] = currentRainGaugePulses; next7dayIdx(); pulses30day = array30day[startingIdx30day]; array30day[startingIdx30day] = currentRainGaugePulses; next30dayIdx(); } } bool ignorePulses = false; void handleComparitorDelay() { ignorePulses = false; } // This it always called with LOW on lp_comparator_start // but we are looking for High from Rain Gauge switch filter circuit void handlePinLevelChange(int pinState) { if (pinState == HIGH) { // DebugPinOn(); if (!ignorePulses) { currentRainGaugePulses++; } ignorePulses = true; // ignore further pulses for 1sec comparitorDelay.startDelay(COMPARITOR_DELAY_ms, handleComparitorDelay); } else { // DebugPinOff();// ignore Low states } } // the setup routine runs once on reset: void setup() { // initializeDebugPin(); initializeArrays(); hr1Timer.startTimer(HR_1_Timer_ms, handle_1hr_timer); // start repeating timer // set advertised name bleSerial.setName("Rain Gauge"); // <<<<<<<< set your device name here // begin initialization bleSerial.begin(); parser.connect(&bleSerial); lp_comparator_start(comparatorPin, REF_4_16Vdd, handlePinLevelChange); // always triggers pin LOW first, then if pin HIGH, will trigger HIGH } // the loop routine runs over and over again forever: void loop() { sleep(); // wait here for a trigger // check ble serial when triggered by anything uint8_t cmd = parser.parse(); // parse incoming data from connection // parser returns non-zero when a pfod command is fully parsed if (cmd != 0) { // have parsed a complete msg { to } uint8_t* pfodFirstArg = parser.getFirstArg(); // may point to \0 if no arguments in this msg. pfod_MAYBE_UNUSED(pfodFirstArg); // may not be used, just suppress warning long pfodLongRtn; // used for parsing long return arguments, if any pfod_MAYBE_UNUSED(pfodLongRtn); // may not be used, just suppress warning if ('.' == cmd) { // pfodApp has connected and sent {.} , it is asking for the main menu if (!parser.isRefresh()) { sendMainMenu(); // send back the menu designed } else { sendMainMenuUpdate(); // menu is cached just send update } // handle {@} request } else if ('@' == cmd) { // pfodApp requested 'current' time parser.print("{@`0}"); // not used here as no plot } else if ('!' == cmd) { // CloseConnection command closeConnection(parser.getPfodAppStream()); } else { // unknown command parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect. } } } void closeConnection(Stream *io) { // add any special code here to force connection to be dropped ((lp_BLESerial*)io)->close(); } void sendMainMenu() { // !! Remember to change the parser version string // every time you edit this method parser.print(F("{,")); // start a Menu screen pfod message // send menu background, format, prompt, refresh and version parser.print(F("<+4>~Rain Gauge`0")); // don't refresh parser.sendVersion(); // send the menu version // send menu items parser.print(F("|!A~")); parser.print("<+2>24hr rain fall\n<+4>"); // parser.print("10.33"); // testing parser.print(mmPerPulse * (currentRainGaugePulses - pulses24hr), 2); parser.print(" mm"); if (partial24hr) { parser.print(" (partial)"); } parser.print("\n"); parser.print("\n"); parser.print("\n"); parser.print("<+2>7 day rain fall\n<+4>"); //parser.print("15.01"); // testing parser.print(mmPerPulse * (currentRainGaugePulses - pulses7day), 2); parser.print(" mm"); if (partial7day) { parser.print(" (partial)"); } parser.print("\n"); parser.print("\n"); parser.print("\n"); parser.print("<+2>30 day rain fall\n<+4>"); // parser.print("15.01"); testing parser.print(mmPerPulse * (currentRainGaugePulses - pulses30day), 2); parser.print(" mm"); if (partial30day) { parser.print(" (partial)"); } parser.print("\n"); parser.print("\n"); parser.print("\n"); parser.print("Accumulated Pulses\n"); parser.print("since power on\n"); parser.print(currentRainGaugePulses); parser.print(F("}")); // close pfod message } void sendMainMenuUpdate() { sendMainMenu(); // ============ end of menu =========== }