Python program for logging data captured by DualImpCount (Arduino based S0 impulse counter module).
Intended to run (but not limited to) on a Raspberry Pi.
I developed this plus the hardware counter module because directly logging impulse counters with the Raspberry Pi was not reliable enough for me. Missed impulses, because the Pi was not running for some time, ghost impulses because the RasPi GPIOs seems somewhat unreliable (at least when reading them using Python), even when always using optocouplers on the impulse inputs.
This example describes my setup - running on a Raspberry Pi 3/4 as user "pi".
extract s0meters folder to /home/pi/s0meters
install prerequisites:
Python 3
Python 3 modules:
influxdb
pip3 install pyserial paho-mqtt PyYAML influxdb
copy or symlink s0meters.service to /etc/systemd/system,
then reload systemctl and start service:
sudo ln -s s0meters.service /etc/systemd/system
or
sudo cp s0meters.service /etc/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable s0meters.service
edit configuration to your needs (see below)
when configuration is finished (and tested in interactive mode), start service:
sudo systemctl start s0meters.service
check if it is still running after some time:
sudo systemctl status s0meters.service
Configuration is split in 3 parts. Base configuration is in ini format - most parameters are self describing
config file: s0meters.ini
[main]
# add timestamps to console output in debug/verbose mode
consoleAddTimestamp = true
# additional YAML config files
meters_config_yml = s0meters.yml
influx_config_yml = s0meters_influxdb.yml
[hardware]
serialPort = /dev/ttyUSB0
serialBaud = 57600
serialTout = 1
[filelog]
# needed for calculating today/yesterday totals, so only disable if that is not needed
enable = True
# storage path must be existing and writable for the user that runs s0meters.py (sub directories are created as needed)
storage_path = /home/pi/s0meters
[mqtt]
enable = true
server = mqtt.lan
port = 1883
keepalive = 60
user = mqttuser
password = *******
# MQTT topics
# Note: this is only for status infos and commands, meters Out-Topics must be defined in the meters configuration!
# In-Topic for sending commands to the device
topic_cmd = Top5/ImpCount/cmd
# Out-Topic
topic_stat = Top5/ImpCount/stat
# Out-Topic for response to commands (can be the same as topic_stat)
topic_cmdresponse = Top5/ImpCount/cmdResponse
config file: meters.yml (unless differently defined in s0meters.ini)
In this YAML styled config file, all meters/counters are declared. The main identifier is the counter number used in the hardware counter module, so for "C1" the identifier is 1 and so on.
1: # meter ID as used in counter hardware (="C1")
name: "Power"
impPerUnit: 1000
unit: "kWh"
digits: 3
readingType: "Energy_Total__kWh"
momType: "power"
momUnit: "W"
momDigits: 0
momFactor: 1000
#conv_unit: "kWh"
#conv_factor: 10.73184
#conv_digits: 2
cost_unit: "EUR"
cost_per_unit: 0.330995
cost_from_conv: False
statTopic: "Powermeter"
influxInstance_energy: "energymeters"
influxInstance_mom: "energy_momentary"
influxMeasurement_energy: "energy"
influxMeasurement_mom: "energy"
influxFieldName_energy: "Energy_Total__kWh"
influxFieldName_mom: "Power__W"
influxMinWriteInterval_energy: 300
MQTTPublishJSON: False
historyPublishInterval: 300
billingStartDate: '2022-03-03'
2: # meter ID as used in counter hardware (="C2")
name: "Gas"
impPerUnit: 100
unit: "m3"
digits: 2
#readingType: ""
type: "usage"
conv_unit: "kWh"
conv_factor: 10.73184
conv_digits: 2
cost_unit: "EUR"
cost_per_unit: 0.33
cost_from_conv: True
momType: "momUsage"
momUnit: "m3/h"
momDigits: 2
momFactor: 1
momType_conv1: "momUsage__kW"
momUnit_conv1: "kW"
momDigits_conv1: 2
momFactor_conv1: 10.73184
momType_conv2: "momUsage__l_min"
momUnit_conv2: "l/min"
momDigits_conv2: 1
momFactor_conv2: 16.6667
statTopic: "Gasmeter"
influxInstance_energy: "energymeters"
influxInstance_mom: "energy_momentary"
influxMeasurement_energy: "gas"
influxMeasurement_mom: "gas"
influxFieldName_energy: "Gas_Total__m3"
##influxFieldName_energy_conv: "Gas_Total__kWh"
influxFieldName_mom: "Gas_Usage__m3_h"
#influxFieldName_mom_conv1: "Gas_Usage__kW"
#influxFieldName_mom_conv2: "Gas_Usage__l_min"
influxMinWriteInterval_energy: 300
MQTTPublishJSON: False
historyPublishInterval: 300
billingStartDate: '2022-03-02'
A-Z a-z _ and -
should be used. Used for MQTT topics Yesterday__unit
and Today__unit
momFactor: factor for calculation of momentary value
Formula for calculation:
momValue = (3600000 / dTime / impPerUnit) * momFactor
where dTime
is the measured time between impulses in milliseconds.
Normally 1, except if the unit of momentary value should be different - for example:
momType_conv1: type of converted momentary reading. used as MQTT sub-topic
momUnit_conv1: free text, currently only used in MQTT JSON output
momDigits_conv1: integer, number of digits in output of converted momentary value
momFactor_conv1: factor for calculation of converted momentary value
Formula for calculation:
momValue_conv1 = momValue * momFactor_conv1
where momValue
is the origin unconverted value.
For example:
momType_conv2: type of 2nd converted momentary reading. used as MQTT sub-topic
momUnit_conv2: free text, currently only used in MQTT JSON output
momDigits_conv2: integer, number of digits in output of 2nd converted momentary value
momFactor_conv2: factor for calculation of 2nd converted momentary value
Formula for calculation:
momValue_conv2 = momValue * momFactor_conv2
where momValue
is the origin unconverted value.
For example:
statTopic: MQTT topic prefix to publish all values of this meter i.E. if statTopic = "Powermeter", following topics will be published to:
influxInstance_energy: a valid InfluxDB instance from s0meters_influxdb.yml where energy/counter readings are written to
influxInstance_mom: a valid InfluxDB instance from s0meters_influxdb.yml where momentary values are written to
influxMeasurement_energy: InfluxDB measurement name for energy (total counter value)
influxMeasurement_mom: InfluxDB measurement name for momentary values
influxFieldName_energy: InfluxDB field name for energy (total counter value)
influxFieldName_energy_conv: InfluxDB field name for converted energy (total counter value) - CURRENTLY NOT IMPLEMENTED
influxFieldName_mom: InfluxDB measurement name for momentary value
influxFieldName_mom_conv1: InfluxDB measurement name for converted momentary value
influxFieldName_mom_conv2: InfluxDB measurement name for 2nd converted momentary value
influxMinWriteInterval_energy: minimal InfluxDB write interval for energy readings If no interval is configured, EVERY impulse received will be written to InfluxDB! There is no such option for the momentary value, as there a high resolution is desired in the logs.
MQTTPublishJSON: if set to True, a JSON object with all data is published on MQTT every time
historyPublishInterval: interval in seconds to publish history data (yesterday, this week, this month etc.)
billingStartDate: date on which the current billing period starts. Format: YYYY-MM-DD
. Used for statistics (consumption since last bill)
config file: s0meters_influx.yml (unless differently defined in s0meters.ini)
All other parameters here are self describing. Comment out/remove username/password if the server does not use it.
energymeters:
host: localhost
port: 8086
username: s0meters
password: ********
database: energymeters
defaultMeasurement: energy
energy_momentary:
host: localhost
port: 8086
username: s0meters
password: ********
database: energy_momentary
defaultMeasurement: energy
Tools for serial monitor and to reset and flash Arduino board that supports it with reset [ms]command.
controlarduino.py is used by flasharduino.sh to reset the MCU.
This alternate reset/flash method can be used for Arduino based projects with:
In this project a reset [ms] command is built into the Arduino software which resets the hardware by pulling /RESET to low using a dedicated (normally INPUT) GPIO pin. This resets the Arduino in such a way, that it will start up in bootloader mode and can be flashed using avrdude. This reset command also calls a save-all-data-to-EEPROM routine before doing the actual reset.
flasharduino.sh sends the reset command using controlarduino.py, flashes the MCU using avrdude and then starts controlarduino.py in --monitor mode.