FloKra 2fa26e7d15 documentation update, schematic/layout as image 3 years ago
Firmware_Arduino_ATmega328p fb4468bf16 initial check-in 3 years ago
Schematics 2fa26e7d15 documentation update, schematic/layout as image 3 years ago
README.md 2fa26e7d15 documentation update, schematic/layout as image 3 years ago



Arduino based hardware for capturing impulses from S0 impulse meters.

The main reason for starting this project was, that counting S0 impulses directly with the Raspberry Pi GPIOs has proven me to be unreliable. I did this for a longer time and got intermittent impulses recorded regularly, so that the counters on the actual meters differed from the recorded values more and more over time. At least I made this experience using RasPi GPIO and a python program for reading them. On the other hand, on every downtime of the Raspberry, there are no data recorded at all, so that - again - the actual counters differ with the records with time.


compiled using Arduino 1.8

  • 2 independent configurable S0 inputs/counters

  • direct implementation of UNITS- and IMPULSES- (between full unit) counters, so that the device can keep track of the actual meters for a long time without the need of an up and running host system, as long as there is electric power available. This was the main reason for me to start this project.

  • configurable impulses per unit and unit counter per input (currently supports 10/100/1000 imp/unit)

  • store current unit readings to EEPROM on every change

    • EEPROM endurance should not be a problem in most cases here. EEPROM has guaranteed 100.000 write cycles per cell:
    • i.E. electric energy meter counting 5000 kWh p.A. => EEPROM lasts 20 years
    • gas meter, 500-1000 m³ p.A. => EEPROM lasts 100 years
    • a unit rollover means i.E. every 10/100/1000 impulses, so this value is not written that often
    • protection against write failures - every value is written 3 times on different EEPROM addresses, therefore it is possible to restore a valid value even if there is a data inconsistency
  • store impulses between units to EEPROM in configurable intervals

    • EEPROM endurance

    • as these values are written far more often we need some kind of wear leveling here

    • implemented as ring buffer which stores the value and a write counter, every time to another EEPROM cell, until the reserved memory area is full, then starts again at the beginning.

    EEPROM area is currently hard coded and set to:

    • counter 1: 512 bytes or 128 values

    • counter 2: 128 bytes or 32 values

      one dataset is 2 bytes data + 2 bytes write counter

    • protection against write failures: write counter is written after the data, on restore a valid write counter must be exactly 1 value bigger than the one before, so in every case a not too old value should be restorable

    • save on no-impulse-timeout (i.E. for gas counters which count several impulses and then be idle for a longer time when the heating has been switched off by the thermostat)

    • save on fixed interval

  • store all current unsaved data on power loss, implemented using:

    • capacitor buffered power supply with relatively high voltage
    • power loss detection using an ADC input
  • measuring of duration in between impulses (which can then be used to calculate power/momentary usage)

  • data output via UART on every impulse and on no-impulse/fixed timeout

  • configuration via UART commands

  • intended to be used aside a Raspberry Pi or similar, logging data to an InfluxDB server with python program s0meters.py

  • software function to trigger MCU reset (via GPIO output connected to /RES input) in order to be able to update/flash the MCU without on-site intervention although the normal Arduino flash/reset method doesn´t work by design. This output is normally configured as input and has a pullup resistor (on the Pro Mini board), so that it does not prevent from normal flashing using a USB-UART Arduino interface. Of course this function saves all data to EEPROM before actually resetting the MCU.

  • WDT enabled, this possibly requires flashing another boot loader depending on the Arduino board used (Pro Minis usually come with a different boot loader than Unos, which does not support WDT - Optiboot works well - I use a custom Optiboot version with baud rate 57600 as is the default for early Pro Mini boards)


Schematics (KiCad) here: Schematics I also created a PCB layout, despite I only built a prototype on breadboard, as it speeds up this process by far. It can be found in schematics folder.

  • based on Arduino Pro Mini (or clone, with ATmega 328p MCU)
  • power supply using a 12VDC wall plug
  • big buffer capacitor only for the MCU itself, right before it´s diode decoupled 5VDC regulator,
  • resistor network from 12VDC supply to ADC input to detect power loss early enough
  • 2 S0 impulse inputs using optocouplers, active LOW (compatible with S0 outputs, which are usually open collector outputs)
  • connection to host over RS232, so NOT USING USB POWER SUPPLY
  • RS232 connection without DTS signal, so that the MCU cannot be surprise-reset by the host on (re-)connect
  • reset circuit (connection from a GPIO out to /RES input)

UART output

If configuration JSON = 1 (default), the module outputs a JSON formatted dataset on UART on every incoming impulse and on no-impulse-timeout:



  • CNUM = 1, 2...
  • READING= actual current reading with decimals, i.E. 1234.123
  • DTIME= time in ms since the last impulse (not present if requested by command 'get readings', = 0 if sent on no-impulse-timeout)

UART commands

default UART baud rate: 57600, 8 data bits, no parity, 1 stop bit

Impulse inputs configuration

configure impulse counter inputs:

set conf c1 impPerUnit=1000
set conf c1 noImpTout=60
set conf c1 saveInt=15

or as a batch job:

set conf c1 impPerUnit=1000;noImpTout=60;saveInt=15
set conf c2 impPerUnit=100;noImpTout=60;saveIint=15


  • noImpTout in seconds
  • saveInt in minutes

  • c1/c2 is the input/counter number

Set counter readings

used for initial setting and value corrections


set reading c1 1023.423


  • cN: counter number (c1, c2...)
  • UNITCOUNTER: counter in the main unit (digits before comma)
  • IMPULSES: optional, impulses since last unit counter rollover (or comma places on the actual meter)

  • if a valid value is given, EEPROM area belonging to counter number is formatted and new value stored instantly

Set power-good

Sets ADC value for "power good". POWER GOOD - monitors voltage in front of the onboard voltage regulator powering the AVR/Arduino in order to save all data to EEPROM before power is really gone. To make this possible, supply voltage is much higher than needed for an AVR/Arduino (9-12V) connected to a big (2200uF) capacitor over a decoupling diode (voltage drop 0.6-0.7V) and regulated to Vcc=5V by the Arduino onboard regulator. Vin is measured through a voltage divider of 56k/24k (after the diode). A value of 420 seems to be the minimum at ~7.5V supply voltage (in front of the decoupling diode). Below this the ADC value increases again due to voltage regulator drop out and therefore sinking Vref, so a reasonable safety margin is mandatory here. As we use a 12V supply we set this to 500 for now (which is about ~= 9.0V)

First check current ADC value using:

get pwrgood

Then set new value with a safety margin - do not use a too low value!

set pwrgood 500

commands overview

command description
get eeprom print entire EEPROM content to UART (formatted)
save data save all unsaved current data
debug [1|0] switch on/off debug output
format [all|c1|c2] format EEPROM area (all, or only counter1/2)
get readings print current readings to UART as done on every impulse
get imp cN get current impulses count of counter N
get pwrgood print current power supply ADC value and configured "power good" value
set pwrgood VALUE set "power good" to an ADC value.
If power supply falls below this value, all data will be saved to EEPROM once
and further saving will be stopped until power is "good" again.
set json 0|1 enable/disable readings output in JSON format (1=default)
get conf print current counters configuration to UART
get debounce print current input debounce values (in ms)
set debounce VALUE[,VALUE2] set input debounce
VALUE = debounce time in ms
VALUE2 = debounce recovery time in ms (optional, comma separated)
set conf cN counter configuration for counter N, as described above
save conf to be called after all configuration changes to store them to EEPROM
set reading cN sets the current reading of counter N to value given as described above
comma separator is '.'
get wcount print write counters of all counters
reset DELAY resets the MCU via GPIO output connected to /RESET input
used for firmware flashing.
[delay] is optional, can be used to fine tune successful flashing with avrdude.