Skip to content

Add low-power pump-handle odometer counter #6

Description

@HanClinto

Summary

Add an odometer-like pump-handle stroke counter as a second measurement channel for WaterPAL. The goal is to count rapid pump strokes from a handle-movement sensor, keep counting while the Lilygo SIM7000G / ESP32 is asleep, and read the accumulated count whenever the main micro wakes for existing reasons.

This is distinct from #2, which tracks cycle counts for pulse-output flow meters. This issue is specifically about a low-power external counter IC for pump-handle movement / predictive maintenance.

Ask

Investigate and integrate a small always-on counter circuit, likely on a breakout board or small PCB, using the ABLIC counter IC below or a suitable off-the-shelf equivalent. The Lilygo should only need to wake up, read the count over I2C, and include the count in the SMS / data payload.

Candidate IC:

Suggested breakout:

Motivation

Current WaterPAL measurements are based on the float switch on/off state and water-use duration. Handle movement sensing is the desired second measurement for Afridev-style pumps because it can reveal:

  • Pump performance trends
  • Changes in strokes-per-water-output behavior
  • Early signals for predictive maintenance
  • Usage patterns not visible from water-time alone

Known Design Notes

From the email response, the ABLIC counter IC may only need a breakout board and pull-ups:

  • Pin 1 /RST: pull up to VCC with a resistor, suggested around 30K, or connect to a Lilygo output pin for reset control.
  • Pin 2: not connected.
  • Pin 3 CLKIN: pull up to VCC with a resistor and use a reed switch / IR sensor output to pull to ground, or tie to a Lilygo input with internal pull-up enabled. External resistor gives better control over current loss while the switch is closed.
  • Pin 4 GND: connect to ground.
  • Pin 5 LOOP: can float, or connect to a Lilygo input if overflow detection is desired. Overflow is above roughly 16 million counts and may not be likely.
  • Pin 6/7 I2C: connect to Lilygo open-drain I2C pins. External pull-ups are preferred; internal pull-ups may require a slower I2C clock, probably around 10-20 kHz.
  • Pin 8 VCC: share the Lilygo 3.3V rail.
  • Pull-ups are needed on CLK, RST, SDA, and SCL.

The count-input signal can start as a simple reed switch like the current WaterPAL sensing approach, with IR sensing as a likely future option.

Desired Behavior

  • Counter IC remains awake continuously and counts pump-handle strokes while the ESP32 sleeps.
  • ESP32 reads the current count over I2C whenever it wakes for existing time-based or water-triggered wake reasons.
  • Firmware adds the new count value, or count delta, to the SMS and HTTP / database reporting package.
  • Firmware can reset the counter intentionally if the final hardware design supports reset control.
  • Counting must tolerate rapid pump strokes without relying on the ESP32 being awake for each edge.

Unknowns / Decisions Needed

  • Confirm whether the S-35770 is the right IC, or whether an off-the-shelf Adafruit / Arduino / AliExpress module already provides an always-on low-power I2C pulse counter.
  • Decide whether /RST should be hard-pulled high or controlled by a Lilygo GPIO.
  • Choose final pull-up resistor values for CLKIN, /RST, SDA, and SCL, balancing sleep current against signal reliability.
  • Decide whether CLKIN should be driven by reed switch, IR sensor module, or another handle movement sensor for the first implementation.
  • Confirm whether switch debounce / pulse conditioning is needed before CLKIN for real pump-handle motion.
  • Decide whether to wire LOOP for overflow detection or leave it unconnected.
  • Select Lilygo I2C pins and determine whether external pull-ups are required for reliable low-speed I2C.
  • Define how the backend should store the count: raw lifetime count, daily delta, per-report delta, or all of the above.
  • Define when, if ever, the hardware counter should be reset.

Acceptance Criteria

  • Hardware approach is documented with a candidate schematic / wiring plan for the counter IC or selected module.
  • Firmware can read the counter value over I2C after wake.
  • Firmware reports handle-stroke count or delta in outgoing SMS / HTTP payloads.
  • ESP32 does not need to wake for every handle stroke.
  • Sleep power impact is estimated or measured, including pull-up leakage during switch closure.
  • A bench test verifies rapid edge counting while the ESP32 remains asleep or inactive.

Source Notes

Opening ask:

The holy grail is to add a second measurement, which is the handle movement sensing. This tells us, as you know, about performance trends and can get predictive maintenance rolling.

The purpose is to only use the Lilygo to wake up, go and read a count from the chip, and then add to SMS package.

The requirement for the chip is that it needs to be awake all the time to count, and store that count, ready to hand over a number to Lilygo when requested. The power consumption is a tiny 10nA.

Response summary:

Short story: Use a TMSOP-8 breakout board. You’re only going to need pull-ups on CLK RST SDA and SCL.

Activity

  1. HanClinto commented on May 19, 2026

    @HanClinto
    OwnerAuthor

    Sourcing note for the counter IC:

    Best current bare-IC source looks like DigiKey for S-35770E01I-K8T2U:

    • DigiKey product page: https://www.digikey.com/en/products/detail/ablic-inc/S-35770E01I-K8T2U/9974496
    • DigiKey part numbers: 1662-3409-1-ND cut tape, 1662-3409-6-ND Digi-Reel, 1662-3409-2-ND tape and reel
    • Manufacturer: ABLIC Inc.
    • Description: Counter IC Binary Counter 1 Element 24 Bit 8-TMSOP
    • Confirmed stock seen: 1,036 units
    • Small-quantity price seen: about $6.29 each, with lower pricing at 10/25/100 qty

    Mouser also appears to list the same MPN, but DigiKey's page was directly accessible and showed stock/price clearly.

    For a quicker bench prototype, MikroE's Counter 2 Click board is also worth considering because it is based on the S-35770 and already provides a small module:

    Recommendation: order a handful of bare ICs from DigiKey plus one Counter 2 Click module if we want to validate firmware/I2C behavior before spinning or hand-wiring a tiny breakout.

  2. HanClinto commented on May 19, 2026

    @HanClinto
    OwnerAuthor

    Circuit/BOM note:

    For a first custom breakout around S-35770E01I-K8T2U, the minimal circuit is small, but the input conditioning matters because a reed switch can bounce and the counter will be awake even when the ESP32 is asleep.

    Likely required parts:

    • S-35770E01I-K8T2U counter IC.
    • TMSOP-8 breakout or small PCB footprint.
    • 0.1 uF ceramic decoupling capacitor from VCC to GND placed close to the IC.
    • I2C pull-ups on SDA and SCL to 3.3 V. Start with 4.7k-10k for normal I2C; higher values may work at slower bus speeds if lowering current is important.
    • /RST pull-up to 3.3 V, suggested starting range 30k-100k. Optionally also route /RST to an ESP32 GPIO for firmware-controlled counter reset.
    • CLKIN pull-up to 3.3 V for a passive reed-switch input. Start around 100k if low current while the switch is closed matters; adjust after testing edge reliability.
    • Connector pads/header for sensor input, 3.3 V, GND, SDA, SCL, and optional reset/loop lines.

    Strongly recommended / design-dependent parts:

    • Input debounce or conditioning for reed switch / mechanical contact input. Without this, one pump stroke may count as several edges. Options include:
      • RC filter on CLKIN using the pull-up plus a small capacitor to GND, validated on a scope.
      • A low-power Schmitt-trigger buffer after the RC filter if the S-35770 input dislikes slow edges.
      • A dedicated debounce IC only if the current budget allows it.
    • Small series resistor on the external CLKIN line, roughly 100 ohm-1k, for input protection/noise damping.
    • ESD/TVS protection if the sensor wire is long, exposed, or routed outside the enclosure.
    • Optional LOOP connection to an ESP32 input for overflow detection. Otherwise leave it unconnected as suggested.
    • Test pads for SDA, SCL, CLKIN, RST, VCC, and GND.

    Open design cautions:

    • If the sensor is an IR module instead of a reed switch, it likely consumes far more than the 10 nA counter IC and may dominate the sleep-power budget unless duty-cycled or otherwise designed for always-on use.
    • The CLKIN pull-up value is a tradeoff: lower resistance gives sharper edges/noise immunity, higher resistance reduces closed-switch current.
    • Debounce should be bench-tested with real pump-handle motion or a representative switch, because the counter will count bounce pulses while the ESP32 is asleep.
  3. HanClinto commented on May 19, 2026

    @HanClinto
    OwnerAuthor

    Draft S-35770 Counter Breakout Schematic

    This is a first-pass schematic-style wiring sketch for a bare S-35770E01I-K8T2U breakout connected to the Lilygo / ESP32 at 3.3 V.

                             +3.3 V
                               |
                               +-------------------------------+
                               |                               |
                             C1 0.1 uF                        |
                               |                               |
                              GND                              |
                                                               |
                        U1: ABLIC S-35770E01I-K8T2U           |
                     +--------------------------------+        |
                     |                                |        |
            +3.3 V --| 8 VCC                     GND 4|--------+---- GND
                     |                                |
                     |                                |
            +3.3 V --/\/\/--+---------------- /RST 1  |
              R3 30k-100k   |                         |
                            o optional ESP32 reset GPIO
                     |                                |
                     |                                |
            +3.3 V --/\/\/--+---------------- CLKIN 3 |
              R4 about 100k |                         |
                            |                         |
                            +----/\/\/----o sensor input
                            |   R5 100R-1k   reed switch / open-drain pulse
                            |                closes or pulls low to GND
                          ----- C2 optional
                          ----- debounce cap
                            |
                           GND
                     |                                |
            ESP32 SDA o----+---------------- SDA 6/7  |
                           |                          |
            +3.3 V --/\/\/--                          |
              R1 4.7k-10k                             |
                                                      |
            ESP32 SCL o----+---------------- SCL 6/7  |
                           |                          |
            +3.3 V --/\/\/--                          |
              R2 4.7k-10k                             |
                                                      |
            optional ESP32 input <----------- LOOP 5  |
                                                      |
            NC / leave open -------------------- NC 2 |
                     |                                |
                     +--------------------------------+
    
    Optional front-end for long or noisy sensor leads:
    
       sensor input o----/\/\/----+-----> CLKIN node above
                     R5 100R-1k   |
                                  +---- TVS/ESD clamp ---- GND
                                  |
                                ----- optional C2
                                ----- debounce cap
                                  |
                                 GND
    
    If the RC edge is too slow or bounce is still visible:
    
       sensor / RC node ----> low-power Schmitt trigger ----> U1 CLKIN
    

    Pin / Net Table

    S-35770 pin Net Draft connection
    1 /RST Reset Pull up to 3.3 V with 30k-100k; optionally route to ESP32 GPIO for firmware reset.
    2 NC Not connected Leave open.
    3 CLKIN Stroke input Pull up to 3.3 V; sensor pulls low through optional series/protection/debounce network.
    4 GND Ground Common ground with Lilygo / ESP32.
    5 LOOP Overflow flag Leave open, or route to ESP32 input/test pad if overflow detection is desired.
    6/7 SDA/SCL I2C Connect to ESP32 I2C pins with pull-ups to 3.3 V. Confirm exact pin order from datasheet footprint before layout.
    8 VCC Supply 3.3 V rail, with 0.1 uF ceramic cap to GND close to the IC.

    Draft BOM

    Ref Part Starting value / note
    U1 ABLIC counter IC S-35770E01I-K8T2U, 8-TMSOP.
    C1 Decoupling capacitor 0.1 uF ceramic, close to U1.
    R1 SDA pull-up 4.7k-10k to 3.3 V.
    R2 SCL pull-up 4.7k-10k to 3.3 V.
    R3 /RST pull-up 30k-100k to 3.3 V.
    R4 CLKIN pull-up Start around 100k; tune for edge reliability vs. closed-switch current.
    R5 CLKIN series resistor 100 ohm-1k, optional but useful for damping/protection.
    C2 Debounce capacitor Optional; size by bench test with the real switch and stroke speed.
    U2 Schmitt trigger buffer Optional low-power part if RC debounce creates slow edges.
    D1 ESD / TVS protection Optional, recommended for long or exposed sensor wires.
    J1 Lilygo connector 3.3 V, GND, SDA, SCL, optional RST, optional LOOP.
    J2 Sensor connector Sensor input and GND; optionally 3.3 V if using an active sensor.

    Layout / Test Notes

    • Keep C1 very close to U1 VCC/GND.
    • Put test pads on SDA, SCL, CLKIN, /RST, VCC, and GND.
    • If the handle sensor is a passive reed switch, debounce must be validated so one stroke does not become several counts.
    • If the handle sensor is an IR module, budget its always-on current separately; it may dominate sleep power.
    • Confirm the exact SDA/SCL pin numbering from the datasheet/package before PCB layout.
  4. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Planning update from the Counter 2 Click / IR handle-stroke discussion:

    Desired event model

    The external counter should behave as a handle-stroke odometer that keeps counting while the ESP32 sleeps:

    1. User starts pumping. The IR/reflection/break-beam sensor produces one pulse per handle stroke, and the counter increments without waking the ESP32.
    2. Float switch rises when water starts flowing. ESP32 wakes, logs start time, reads the current handle-stroke odometer, and records how many strokes it took to reach flow.
    3. ESP32 sleeps again while water continues flowing. Handle strokes keep accumulating in the external counter.
    4. Float switch drops when flow ends. ESP32 wakes, logs stop time, reads the odometer again, computes flow duration, computes handle strokes during flow, and derives strokes/min during the water-flow interval.
    5. ESP32 reports raw/delta stroke counts plus derived metrics, then sleeps until the next cycle.

    This accepts that a few failed/play strokes before water starts may inflate “strokes to water” occasionally. Reporting/backend should treat those as field noise rather than assume every priming count is perfectly meaningful.

    Counter 2 Click findings

    MikroE’s open-source Counter 2 Click driver confirms this is an I2C-type Click board based on S-35770E01I-K8T2U:

    • I2C address: 0x32.
    • Counter read: 24-bit value over I2C.
    • Overflow/rollover threshold: 16,777,216 counts.
    • mikroBUS pins used by the driver: SCL, SDA, RST, and INT as LOOP.
    • The driver does not map SPI or UART pins for this Click.
    • The driver does not use a mikroBUS pin for the pulse input, so the IR/reed/break-beam signal should feed the counter board/chip input directly, not an ESP32 interrupt pin.

    Source: MikroElektronika/mikrosdk_click_v2, clicks/counter2/lib_counter2/include/counter2.h and src/counter2.c.

    WaterPAL / Lilygo IO audit from current firmware

    Current firmware pin allocation:

    • GPIO 34: float switch wake input.
    • GPIO 32: DHT sensor.
    • GPIO 4: SIM7000 power control.
    • GPIO 25: SIM7000 DTR.
    • GPIO 26/27: SIM7000 UART via Serial1 (PIN_RX/PIN_TX).
    • GPIO 21/22: listed as default Wire SDA/SCL in config comments; no current firmware use of Wire found.
    • GPIO 33 and 35 are listed/commented as open in the config comments.

    Answer to the first IO question: yes, we appear to have the needed IO for Counter 2 Click.

    Recommended first wiring:

    • Counter 2 SDA -> ESP32 GPIO 21.
    • Counter 2 SCL -> ESP32 GPIO 22.
    • Counter 2 3.3V and GND -> Lilygo 3.3V/GND.
    • Counter pulse input -> IR sensor output/open-drain output directly into the counter board/chip input, with appropriate pull-up/conditioning.
    • Optional Counter 2 RST -> an available ESP32 output such as GPIO 33, or leave under board/pull-up control if the board supports that and use software reset over I2C when sufficient.
    • Optional Counter 2 LOOP/mikroBUS INT -> an available ESP32 input such as GPIO 35 if we want overflow tracking. Overflow is probably rare, but this gives a clean path to lifetime odometer accounting.

    Serial-line concern: Counter 2 Click does not need mikroBUS UART. The WaterPAL modem already occupies GPIO 26/27 on Serial1, while USB debug uses the primary Serial. Since this Click only needs I2C plus optional reset/loop GPIO, it should not conflict with the USB debugger or modem serial lines. If we build a full mikroBUS adapter later, leave mikroBUS TX/RX unconnected or route them only to clearly unused pins, not 26/27.

    Planning implications

    For the first bench prototype, I would not wire a full mikroBUS socket one-to-one. A small adapter harness is enough: 3.3V, GND, SDA, SCL, optional RST, optional LOOP, and the separate sensor input to the counter board. This keeps us off the SIM7000 UART pins and avoids unnecessary SPI/UART routing.

    Firmware work needed next:

    • Add a waterpal_counter module using Arduino Wire on GPIO 21/22.
    • Read the 24-bit count from I2C address 0x32 on every wake before/after float-edge handling.
    • Persist last counter readings in RTC memory:
      • count at flow start
      • count at flow end / last seen count
      • optional last LOOP state
    • On float rising/start-flow wake: save flow_start_time_s and flow_start_stroke_count.
    • On float falling/end-flow wake: compute flow_duration_s, flow_stroke_delta, and strokes_per_min = delta * 60 / duration.
    • Add daily/reporting fields for at least raw lifetime/daily stroke delta; optionally add per-flow metrics once backend schema is ready.
    • Decide whether to ever reset the hardware counter. My leaning is to avoid routine resets and treat it as a monotonic odometer, handling 24-bit wrap in firmware.

    Hardware validation needed:

    • Confirm the physical Counter 2 Click pulse input connector/pad and whether the board already includes CLKIN pull-up/debounce.
    • Measure IR module current. Commodity reflective obstacle modules can draw far more than the counter IC, so always-on IR sensing may dominate sleep power.
    • Bench-test the chosen sensor arrangement for one clean pulse per handle stroke. If the output chatters, add pulse shaping/debounce before the counter input.
    • Verify Counter 2 readout from ESP32 at 3.3V using 100 kHz I2C first; lower I2C speed only if pull-up/current choices require it.
  5. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Click Snap follow-up:

    Counter 2 Click advertises a snap-off counting module with access to the relevant signal pins. Based on MikroE's driver pin map, WaterPAL does not appear to need anything from the full-size mikroBUS carrier except prototype convenience:

    • Required host signals are 3.3V, GND, SDA, SCL.
    • Optional host signals are RST and LOOP/INT.
    • The handle sensor/pulse line feeds the counter input directly.
    • SPI and UART pins on the full mikroBUS footprint are unused for this Click.

    Recommendation:

    • For bench bring-up, keep the full Counter 2 Click intact. The larger board is useful as a labeled, mechanically stable breakout and makes probing/wiring easier.
    • For an installed WaterPAL prototype or final-ish build, prefer the smaller Click Snap module if inspection confirms it retains the counter IC, decoupling, pull-ups/required passives, CLKIN/pulse input access, RST, LOOP, I2C, power, and ground.
    • The larger carrier does not appear electrically necessary for WaterPAL unless it contains support circuitry not present on the snapped module, such as pull-ups, level shifting, input conditioning, or a more convenient sensor connector. That should be verified by schematic/continuity check once the board is in hand.

    Practical path: buy/use the full board for first firmware validation, but plan the harness/PCB around the Snap module pinout so we are not forced to carry the full mikroBUS footprint into the enclosure.

  6. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Click Snap follow-up:

    Counter 2 Click advertises a snap-off counting module with access to the relevant signal pins. Based on MikroE's driver pin map, WaterPAL does not appear to need anything from the full-size mikroBUS carrier except prototype convenience:

    • Required host signals are 3.3V, GND, SDA, SCL.
    • Optional host signals are RST and LOOP/INT.
    • The handle sensor/pulse line feeds the counter input directly.
    • SPI and UART pins on the full mikroBUS footprint are unused for this Click.

    Recommendation:

    • For bench bring-up, keep the full Counter 2 Click intact. The larger board is useful as a labeled, mechanically stable breakout and makes probing/wiring easier.
    • For an installed WaterPAL prototype or final-ish build, prefer the smaller Click Snap module if inspection confirms it retains the counter IC, decoupling, pull-ups/required passives, CLKIN/pulse input access, RST, LOOP, I2C, power, and ground.
    • The larger carrier does not appear electrically necessary for WaterPAL unless it contains support circuitry not present on the snapped module, such as pull-ups, level shifting, input conditioning, or a more convenient sensor connector. That should be verified by schematic/continuity check once the board is in hand.

    Practical path: buy/use the full board for first firmware validation, but plan the harness/PCB around the Snap module pinout so we are not forced to carry the full mikroBUS footprint into the enclosure.

  7. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Click Snap pin-label caution from board photo:

    The underside photo shows the Click Snap section exposing what appear to be GND, VCC, SDA, SCL, NC, NC, LP, and RST.

    Important interpretation: LP is probably the S-35770 LOOP signal, not the pulse/count input. This matches MikroE's driver, which maps LP/LOOP to mikroBUS INT and treats it as the rollover/loop output. It should be useful for detecting 24-bit counter rollover, but it is not the normal handle-stroke pulse input.

    That means the remaining hardware question is: where is CLKIN exposed for the external handle sensor?

    From the photo alone, the Snap header gives us nearly all host/readout signals, but I do not see a clearly labeled CLK, CNT, IN, or CLKIN pin on the Snap section. Before committing to the snapped-off module, verify continuity/schematic for S-35770 pin 3 (CLKIN) to one of:

    • a Snap header pin, possibly one of the NC-labeled pads if the silkscreen is generic/ambiguous;
    • a full-size mikroBUS pin such as CS, if MikroE used it as an unmanaged pulse input;
    • a separate pad, button, terminal, or test point on the board.

    Updated recommendation: the Snap module still looks excellent for the ESP32 side (VCC, GND, I2C, optional RST, optional LOOP), but it only gives us everything we need if CLKIN is accessible for the IR/reed/break-beam sensor. If CLKIN is only available on the full carrier or an inconvenient pad, we should either keep the full board for prototypes or solder directly to the counter input/test pad.

  8. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Click Snap top-side photo follow-up / correction:

    The top-side photo appears to answer the earlier CLKIN question. The Snap section has a clearly labeled IN pad near the top-left of the small module. That is very likely the external pulse/count input to the S-35770 (CLKIN).

    So the snapped-off module appears to expose the complete set of signals WaterPAL needs:

    • GND
    • VCC
    • SDA
    • SCL
    • RST
    • LP / LOOP rollover output
    • IN pulse input for the handle sensor

    The full carrier board still appears useful for bench work because it provides the mikroBUS headers, larger solder/probe points, voltage-selection convenience, and visible indicators such as PWR/LOOP. It may also include carrier-side parts for LEDs, level/voltage selection, or interface convenience.

    For WaterPAL deployment, the carrier-side features are probably not useful and may be undesirable:

    • WaterPAL is a 3.3 V system, so 5 V/mikroBUS voltage-selection support is not needed once wiring is fixed.
    • Power/status LEDs are not helpful in an installed low-power device and can waste current if left populated/powered.
    • The full mikroBUS footprint carries unused SPI/UART/extra pins and adds enclosure volume.
    • The Snap module already appears to include the counter IC, local passives, I2C, reset, loop, and pulse input.

    Updated recommendation: use the full board intact for first bench validation, then use the Click Snap section for any installed prototype/final harness, assuming a quick continuity/schematic check confirms IN is S-35770 CLKIN and the Snap section retains the needed pull-ups/decoupling. If ultra-low sleep current matters, also check whether any onboard indicator LEDs remain connected on the Snap section and depopulate/cut them if needed.

  9. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Draft implementation plan for Counter 2 Click / Click Snap handle-stroke counting

    Executive pin summary

    Recommended installed wiring, assuming the Click Snap section is used and IN confirms as S-35770 CLKIN:

    Counter 2 Click Snap pin WaterPAL / Lilygo ESP32 connection Required? Purpose / notes
    GND ESP32 / WaterPAL ground Yes Common ground.
    VCC 3.3 V rail Yes Use 3.3 V, not 5 V, for ESP32-compatible I2C and sensor logic.
    SDA GPIO 21 (Wire SDA) Yes I2C data, address 0x32. Add/keep suitable pull-up to 3.3 V.
    SCL GPIO 22 (Wire SCL) Yes I2C clock. Add/keep suitable pull-up to 3.3 V.
    IN Handle sensor pulse output Yes Counter pulse input. Sensor should produce one clean active edge per handle stroke. Confirm continuity to S-35770 CLKIN.
    RST GPIO 33, or pull-up-only/no-connect if board handles reset Optional but recommended Allows ESP32 hardware reset of the counter during test/commissioning. Avoid routine resets if treating count as odometer.
    LP / LOOP GPIO 35 input, or no-connect Optional Rollover/loop output for detecting 24-bit wrap. Useful for long-term odometer accounting.

    Pins to avoid / not needed:

    • Do not use GPIO 26/27; those are SIM7000 Serial1.
    • Do not use USB debug serial for this feature.
    • Do not route unused mikroBUS UART/SPI pins unless a future carrier board needs them.
    • Existing GPIO 34 float switch and GPIO 32 DHT remain unchanged.

    Hardware assumptions to verify

    Before finalizing the harness/PCB:

    • Confirm IN on the Snap board connects to S-35770 CLKIN.
    • Confirm the Snap section includes required decoupling and pull-ups, or add them externally.
    • Confirm whether RST already has a pull-up. If not, add one or drive it from ESP32 with a safe default high state.
    • Confirm I2C pull-ups are present and to 3.3 V. If not, add external pull-ups, likely starting around 4.7k-10k for bench validation.
    • Measure sleep current with the Snap section installed. Remove/cut any indicator LED path that remains powered in deployment.
    • Measure the chosen IR sensor module current separately. Commodity reflective modules may dominate the power budget if always on.
    • Scope or logic-analyzer the sensor output during representative handle motion. Add RC/Schmitt/debounce/pulse shaping if one stroke produces multiple count edges.

    Firmware changes

    1. Add counter configuration

    In waterpal_config.h, add feature flags and pin constants:

    • WATERPAL_USE_HANDLE_COUNTER
    • WATERPAL_COUNTER_I2C_ADDRESS = 0x32
    • WATERPAL_COUNTER_SDA_PIN = 21
    • WATERPAL_COUNTER_SCL_PIN = 22
    • WATERPAL_COUNTER_RST_PIN = 33 if wired
    • WATERPAL_COUNTER_LOOP_PIN = 35 if wired
    • optional WATERPAL_COUNTER_I2C_SPEED_HZ, starting at 100000

    2. Add a small counter module

    Create waterpal_counter.h / implementation following current header-style firmware patterns.

    Minimum API:

    • counter_setup() initializes Wire and optional RST/LOOP pins.
    • counter_read_raw(uint32_t *count) reads the 24-bit counter value from I2C address 0x32.
    • counter_read_loop() returns optional LP/LOOP pin state if wired.
    • counter_delta_24bit(previous, current) computes deltas across 24-bit wrap.
    • Optional counter_reset() for bench/commissioning only.

    Implementation notes from MikroE driver:

    • Read current counter by reading 3 bytes from device address 0x32.
    • Treat the raw counter as 24-bit, range 0 to 16,777,215.
    • If LOOP is not wired, handle single-wrap deltas with modulo math and assume we read often enough to avoid multiple wraps between reads.
    • If LOOP is wired, use it to detect rollover state changes for long-term accounting.

    3. Add RTC-persisted state

    Add RTC_DATA_ATTR fields near the existing water-usage state:

    • last_handle_counter_raw
    • total_handle_strokes
    • daily_handle_strokes
    • flow_start_handle_counter_raw
    • flow_start_handle_total
    • last_flow_handle_strokes
    • last_flow_strokes_per_min
    • strokes_to_water_flow
    • optional last_counter_loop_state
    • optional counter_read_fail_count

    Prefer monotonic lifetime/daily totals in firmware rather than resetting the hardware counter routinely.

    4. Read counter on every wake

    In setup(), after basic serial/debug setup and before doLogWaterInput() computes edge behavior:

    • initialize I2C/counter module;
    • read current counter;
    • compute delta from last_handle_counter_raw;
    • update total_handle_strokes and daily_handle_strokes;
    • update last_handle_counter_raw only after a successful read;
    • log failures but do not block water-time logging if the counter is missing.

    5. Integrate with float switch edge handling

    In doLogWaterInput():

    • On water-flow start edge, save flow_start_time_s, flow_start_handle_counter_raw, and/or flow_start_handle_total.
    • Compute strokes_to_water_flow as the delta between the last no-flow baseline and flow-start total. If no baseline exists, mark unknown/zero.
    • On water-flow end edge, compute:
      • flow_duration_s
      • last_flow_handle_strokes
      • last_flow_strokes_per_min = strokes * 60 / duration
    • Guard against zero/negative duration and invalid counter reads.

    Terminology note: existing code uses WATERPAL_FLOAT_SWITCH_INVERT to define which logical state means water is flowing. Keep the new stroke logic tied to that same water-on/water-off abstraction, not hardcoded rising/falling language.

    6. Reporting changes

    Add fields in phases to avoid breaking receivers:

    Phase A, lowest risk:

    • include daily_handle_strokes in HTTP reporting and/or the existing SMS placeholder currently labeled Flutter Count (TODO);
    • preserve existing packet version until receiver compatibility is checked, or bump packet version if adding fields to SMS.

    Phase B:

    • report last_flow_handle_strokes, last_flow_strokes_per_min, and strokes_to_water_flow.
    • decide backend storage names and units:
      • dailyHandleStrokes
      • lastFlowHandleStrokes
      • lastFlowStrokesPerMinute
      • strokesToWaterFlow
      • optional totalHandleStrokes

    Phase C:

    • add backend/schema/app updates so these fields are visible and robust to noisy priming/play strokes.

    7. Sleep behavior

    No new ESP32 wake source is needed for handle strokes. The counter remains awake and counts while the ESP32 sleeps. Existing wake sources remain:

    • float switch edge on GPIO 34;
    • timer wake for scheduled reads/SMS.

    Do not wake the ESP32 per handle stroke.

    Suggested implementation order

    1. Bench-wire full Counter 2 Click first: 3.3 V, GND, SDA 21, SCL 22, IN from a button/signal generator/sensor output, optional RST 33, optional LP 35.
    2. Add waterpal_counter module with a simple serial debug read of raw count.
    3. Verify raw count increments while ESP32 code is idle/asleep.
    4. Add RTC delta accounting and wrap-safe delta math.
    5. Integrate flow-start/flow-end metrics into doLogWaterInput().
    6. Add daily stroke count to existing SMS/HTTP report path.
    7. Validate with bench test sequences:
      • strokes before flow start;
      • flow start, strokes during flow, flow end;
      • no counter connected;
      • counter wrap simulation;
      • sleep current measurement with counter and sensor.
    8. Move from full board to Click Snap harness once IN/pull-ups/current are verified.

    Open decisions

    • Should RST be wired to GPIO 33, or should reset be hard-pulled and only software/reset-command behavior be used?
    • Is LP/LOOP worth wiring to GPIO 35 for long-term odometer rollover tracking?
    • Which sensor is first: reflective IR module, break-beam IR, reed switch, Hall sensor, or another low-power option?
    • What is the acceptable current draw for always-on handle sensing?
    • Should the first public report contain only daily stroke delta, or also per-flow derived metrics?
  10. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Reporting-plan refinement from bucket/session discussion:

    For the first handle-counter implementation, keep reporting aggregate rather than bucket/session-level. The useful near-term daily fields should be:

    • total_daily_handle_strokes: all handle strokes counted during the report period, including dry/priming strokes and strokes while water is flowing.
    • total_flowing_water_strokes: only handle strokes counted while the float switch says water is actively flowing.
    • average_strokes_to_water_flow_from_dry: average number of dry/priming strokes needed before water starts flowing, but only for starts that follow a long enough inactive/drain period.

    Add a configuration parameter:

    • WATERPAL_MIN_DRY_DRAIN_TIME_S

    Recommended starting default: 240 seconds, or perhaps 60 seconds if field testing shows the pump drains/loses prime quickly. This should be configurable because the right value may vary by pump, plumbing, float-switch geometry, and user behavior.

    Reasoning:

    If users swap buckets/workers and water stops flowing for only a second or two, the pump may still be primed. The next flow-start event should not be treated as a true dry start, and should not feed the dry-start average. Only if the float switch has been inactive for at least WATERPAL_MIN_DRY_DRAIN_TIME_S should the next flow-start event update dry-start totals.

    Suggested aggregate counters for firmware:

    • daily_handle_strokes_total
    • daily_flowing_handle_strokes_total
    • daily_dry_start_strokes_total
    • daily_dry_start_count
    • derived at report time: average_strokes_to_water_flow_from_dry = daily_dry_start_strokes_total / daily_dry_start_count

    This gives us the predictive-maintenance signals we need soon without creating lots of per-bucket/per-session data. Rich bucket/session-level reporting is tracked separately in #7.

  11. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Naming/reporting refinement:

    Use the shorter derived field name:

    • average_dry_start_strokes

    But prefer reporting the raw aggregate pieces as well, so clients/backend can calculate or recalculate the average:

    • dry_start_count: number of qualifying dry starts during the report period.
    • dry_start_strokes_total: total priming/dry strokes across those qualifying dry starts.
    • derived: average_dry_start_strokes = dry_start_strokes_total / dry_start_count when dry_start_count > 0.

    This is more flexible than only reporting the average. It lets the receiver handle divide-by-zero, confidence/weighting, weekly/monthly aggregation, and future changes to display logic without losing information.

    Suggested near-term daily aggregate report fields are now:

    • total_daily_handle_strokes
    • total_flowing_water_strokes
    • dry_start_count
    • dry_start_strokes_total
    • optional/derived average_dry_start_strokes

    The dry-start qualification should still use WATERPAL_MIN_DRY_DRAIN_TIME_S so quick bucket swaps or worker handoffs do not count as true dry starts.

  12. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Field naming convention update:

    Use feature-first names so related fields group together alphabetically and stay easy to scan in schemas, spreadsheets, logs, and autocomplete.

    For dry-start reporting, use:

    • dry_start_count
    • dry_start_strokes_total
    • dry_start_strokes_avg

    dry_start_strokes_avg is a derived field:

    • dry_start_strokes_avg = dry_start_strokes_total / dry_start_count when dry_start_count > 0.

    This supersedes the earlier suggested name average_dry_start_strokes.

    Apply the same style to future handle-stroke fields where practical: feature/context first, measurement/aspect later.

  13. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Implementation note on reset and overflow behavior:

    The firmware should follow the existing reporting convention for handle aggregate fields: after a successful regular report, reset the report-period handle totals to zero.

    The important guardrail is that reset should only clear report-period aggregates, not the running state needed to survive mid-bucket reports. Preserve:

    • last raw 24-bit counter reading;
    • whether the previous water state was flowing;
    • active dry-start candidate stroke count, if currently waiting for water to start;
    • last water-flow end time / drain-threshold state.

    That way, if a scheduled report fires while water is flowing, future handle strokes in the same bucket/session are counted after the report without losing the raw-counter baseline. If a report fires during dry priming before water starts, the in-progress dry-start candidate can still be completed when flow begins.

    Overflow behavior for the first pass:

    • The S-35770 raw counter is 24-bit and wraps at 16,777,216.
    • Firmware delta math handles one 24-bit wrap between ESP32 reads.
    • Multiple wraps between reads are not expected for pump-handle rates; if that ever becomes a concern, wire/use LP/LOOP to track rollover explicitly.
    • Report-period aggregate fields use wider firmware counters before being clamped to 32-bit report values.
  14. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Metric update from implementation pass:

    Dry-start fields now use singular stroke in the metric name:

    • dry_start_count
    • dry_start_stroke_total
    • dry_start_stroke_avg
    • dry_start_stroke_max

    New tier-one metrics added:

    • dry_start_stroke_max: max qualifying dry-start stroke count in the report period.
    • handle_strokes_flowing_per_min: handle_strokes_flowing_total * 60 / dailyWaterUsageTime when water usage time is greater than zero.

    SMS decision:

    The regular compact R packet can include all handle aggregate fields for now because the packet is numeric/comma-delimited and still appears comfortably under the 160-character SMS target with normal values. A test string with large-but-plausible values was about 116 characters. Truly worst-case 32-bit values could exceed one SMS, but those are not realistic daily pump values. If field testing shows SMS splitting or delivery trouble, we can demote lower-priority handle fields back to HTTP-only or define a second packet type.

    Regular SMS now includes:

    • handle_strokes_total
    • handle_strokes_flowing_total
    • handle_strokes_flowing_per_min
    • dry_start_count
    • dry_start_stroke_total
    • dry_start_stroke_avg
    • dry_start_stroke_max
  15. HanClinto commented on Jun 2, 2026

    @HanClinto
    OwnerAuthor

    Implementation summary for commit 0eb7287 (Add handle stroke counter reporting):

    Hardware / pin choices

    The first firmware implementation assumes the Counter 2 Click / Click Snap module is wired as planned:

    • VCC -> 3.3 V
    • GND -> ground
    • SDA -> GPIO 21
    • SCL -> GPIO 22
    • IN -> handle sensor pulse output
    • optional RST -> GPIO 33
    • optional LP / LOOP -> GPIO 35

    waterpal_config.h now contains these defaults plus WATERPAL_MIN_DRY_DRAIN_TIME_S, currently 240 seconds.

    Firmware structure

    Handle-stroke logic is in its own module, waterpal_handle_counter.h, rather than in the DHT/extra-sensors file. That module owns:

    • I2C setup and raw 24-bit counter reads from address 0x32.
    • 24-bit wrap-safe delta math for one wrap between wakeups.
    • RTC-persisted report-period aggregate counters.
    • Dry-start qualification using the drain-time threshold.
    • Dry-start total/avg/max tracking.

    Wake / float-switch integration

    On every wake, WaterPAL reads the handle counter before logging the float-switch edge. This is intentional: on an edge wake, the new handle strokes since the previous wake belong to the previous water state.

    Strokes are attributed to:

    • total handle strokes, always;
    • flowing handle strokes, only when the previous water state was flowing;
    • the active dry-start candidate, only while the pump is not flowing and the candidate is valid.

    When flow starts, firmware decides whether the dry-start candidate qualifies based on WATERPAL_MIN_DRY_DRAIN_TIME_S. Short bucket swaps / worker handoffs should not pollute the dry-start average.

    Reporting/reset behavior

    The implementation follows the existing WaterPAL reporting convention: after a successful regular SMS report, report-period handle aggregates reset to zero.

    The reset does not clear the raw counter baseline or active dry-start candidate state, so mid-bucket and mid-priming reports should continue gracefully.

    Reported fields

    The default regular CSV/SMS packet, GPRS query string, and Design Outreach JSON now include:

    • handle_strokes_total
    • handle_strokes_flowing_total
    • handle_strokes_flowing_per_min
    • dry_start_count
    • dry_start_stroke_total
    • dry_start_stroke_avg
    • dry_start_stroke_max

    handle_strokes_flowing_per_min is calculated as handle_strokes_flowing_total * 60 / total_water_usage_time_s when water usage time is nonzero.

    The default CSV is still expected to fit inside normal SMS size for realistic daily pump values. A large-but-plausible sample was about 116 characters. If field data shows packet growth or SMS splitting problems, lower-priority handle fields can be moved to HTTP-only or a second packet type.

    Overflow behavior

    The raw S-35770 counter wraps at 16,777,216. Firmware handles one wrap between reads. Multiple wraps between reads are not expected for pump-handle rates; if that ever becomes a concern, LP / LOOP should be wired and used for explicit rollover tracking.

    Validation

    Compiled successfully with the Arduino IDE bundled CLI:

    arduino-cli compile --fqbn esp32:esp32:esp32 firmware/WaterPAL

    Result: 422,678 bytes program storage (32%) and 26,292 bytes dynamic memory (8%).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions