mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-14 07:49:01 +00:00
Support for pulse meter
This commit is contained in:
parent
e7ca408baa
commit
87ddf00afa
24
lib/SvelteUi/app/dist/index.js
vendored
24
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -368,30 +368,37 @@
|
||||
Communication<br/>
|
||||
<select name="ma" bind:value={configuration.m.a} class="in-s">
|
||||
<option value={0}>Passive (Push)</option>
|
||||
<option value={9}>Kamstrup (Pull)</option>
|
||||
<option value={2}>Pulse</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="my-1">
|
||||
<span class="float-right">Buffer size</span>
|
||||
<span>Serial conf.</span>
|
||||
<label class="mt-2 ml-3 whitespace-nowrap"><input name="mi" value="true" bind:checked={configuration.m.i} type="checkbox" class="rounded mb-1"/> inverted</label>
|
||||
<div class="flex w-full">
|
||||
<select name="mb" bind:value={configuration.m.b} class="in-f tr w-1/2">
|
||||
<option value={0} disabled={configuration.m.b != 0}>Autodetect</option>
|
||||
{#each [24,48,96,192,384,576,1152] as b}
|
||||
<option value={b*100}>{b*100}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<select name="mp" bind:value={configuration.m.p} class="in-m" disabled={configuration.m.b == 0}>
|
||||
<option value={0} disabled={configuration.m.b != 0}>-</option>
|
||||
<option value={2}>7N1</option>
|
||||
<option value={3}>8N1</option>
|
||||
<option value={10}>7E1</option>
|
||||
<option value={11}>8E1</option>
|
||||
</select>
|
||||
<input name="ms" type="number" bind:value={configuration.m.s} min={64} max={sysinfo.chip == 'esp8266' ? configuration.i.h.p == 3 || configuration.i.h.p == 113 ? 512 : 128 : 4096} step={64} class="in-l tr w-1/2">
|
||||
{#if configuration.m.a === 2}
|
||||
<div class="my-1">
|
||||
<span>Pulses per kWh</span>
|
||||
<input name="mb" bind:value={configuration.m.b} class="in-s tr" type="number" min={1} max={3600}/>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="my-1">
|
||||
<span class="float-right">Buffer size</span>
|
||||
<span>Serial conf.</span>
|
||||
<label class="mt-2 ml-3 whitespace-nowrap"><input name="mi" value="true" bind:checked={configuration.m.i} type="checkbox" class="rounded mb-1"/> inverted</label>
|
||||
<div class="flex w-full">
|
||||
<select name="mb" bind:value={configuration.m.b} class="in-f tr w-1/2">
|
||||
<option value={0} disabled={configuration.m.b != 0}>Autodetect</option>
|
||||
{#each [24,48,96,192,384,576,1152] as b}
|
||||
<option value={b*100}>{b*100}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<select name="mp" bind:value={configuration.m.p} class="in-m" disabled={configuration.m.b == 0}>
|
||||
<option value={0} disabled={configuration.m.b != 0}>-</option>
|
||||
<option value={2}>7N1</option>
|
||||
<option value={3}>8N1</option>
|
||||
<option value={10}>7E1</option>
|
||||
<option value={11}>8E1</option>
|
||||
</select>
|
||||
<input name="ms" type="number" bind:value={configuration.m.s} min={64} max={sysinfo.chip == 'esp8266' ? configuration.i.h.p == 3 || configuration.i.h.p == 113 ? 512 : 128 : 4096} step={64} class="in-l tr w-1/2">
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="my-1">
|
||||
Voltage<br/>
|
||||
<select name="md" bind:value={configuration.m.d} class="in-s">
|
||||
|
||||
@ -17,23 +17,23 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.21.87",
|
||||
"/energyprice.json": "http://192.168.21.87",
|
||||
"/dayplot.json": "http://192.168.21.87",
|
||||
"/monthplot.json": "http://192.168.21.87",
|
||||
"/temperature.json": "http://192.168.21.87",
|
||||
"/sysinfo.json": "http://192.168.21.87",
|
||||
"/configuration.json": "http://192.168.21.87",
|
||||
"/tariff.json": "http://192.168.21.87",
|
||||
"/realtime.json": "http://192.168.21.87",
|
||||
"/priceconfig.json": "http://192.168.21.87",
|
||||
"/save": "http://192.168.21.87",
|
||||
"/reboot": "http://192.168.21.87",
|
||||
"/configfile": "http://192.168.21.87",
|
||||
"/upgrade": "http://192.168.21.87",
|
||||
"/mqtt-ca": "http://192.168.21.87",
|
||||
"/mqtt-cert": "http://192.168.21.87",
|
||||
"/mqtt-key": "http://192.168.21.87",
|
||||
"/data.json": "http://192.168.233.110",
|
||||
"/energyprice.json": "http://192.168.233.110",
|
||||
"/dayplot.json": "http://192.168.233.110",
|
||||
"/monthplot.json": "http://192.168.233.110",
|
||||
"/temperature.json": "http://192.168.233.110",
|
||||
"/sysinfo.json": "http://192.168.233.110",
|
||||
"/configuration.json": "http://192.168.233.110",
|
||||
"/tariff.json": "http://192.168.233.110",
|
||||
"/realtime.json": "http://192.168.233.110",
|
||||
"/priceconfig.json": "http://192.168.233.110",
|
||||
"/save": "http://192.168.233.110",
|
||||
"/reboot": "http://192.168.233.110",
|
||||
"/configfile": "http://192.168.233.110",
|
||||
"/upgrade": "http://192.168.233.110",
|
||||
"/mqtt-ca": "http://192.168.233.110",
|
||||
"/mqtt-cert": "http://192.168.233.110",
|
||||
"/mqtt-key": "http://192.168.233.110",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -30,11 +30,12 @@ ADC_MODE(ADC_VCC);
|
||||
#define WDT_TIMEOUT 60
|
||||
|
||||
#define METER_SOURCE_NONE 0
|
||||
#define METER_SOURCE_SERIAL 1
|
||||
#define METER_SOURCE_GPIO 1
|
||||
#define METER_SOURCE_MQTT 2
|
||||
#define METER_SOURCE_ESPNOW 3
|
||||
|
||||
#define METER_PARSER_PASSIVE 0
|
||||
#define METER_PARSER_PULSE 2
|
||||
#define METER_PARSER_KAMSTRUP 9
|
||||
|
||||
#define METER_ERROR_NO_DATA 90
|
||||
@ -79,6 +80,7 @@ ADC_MODE(ADC_VCC);
|
||||
#include "MeterCommunicator.h"
|
||||
#include "PassiveMeterCommunicator.h"
|
||||
#include "KamstrupPullCommunicator.h"
|
||||
#include "PulseMeterCommunicator.h"
|
||||
|
||||
#include "Uptime.h"
|
||||
|
||||
@ -172,6 +174,7 @@ RealtimePlot rtp;
|
||||
MeterCommunicator* mc = NULL;
|
||||
PassiveMeterCommunicator* passiveMc = NULL;
|
||||
KamstrupPullCommunicator* kamstrupMc = NULL;
|
||||
PulseMeterCommunicator* pulseMc = NULL;
|
||||
|
||||
bool networkConnected = false;
|
||||
bool setupMode = false;
|
||||
@ -193,6 +196,9 @@ bool handleVoltageCheck();
|
||||
bool readHanPort();
|
||||
void errorBlink();
|
||||
|
||||
uint8_t pulses = 0;
|
||||
void onPulse();
|
||||
|
||||
#if defined(ESP32)
|
||||
uint8_t dnsState = 0;
|
||||
ip_addr_t dns0;
|
||||
@ -682,9 +688,13 @@ void loop() {
|
||||
|
||||
if(config.isMeterChanged()) {
|
||||
config.getMeterConfig(meterConfig);
|
||||
if(meterConfig.source = METER_SOURCE_SERIAL) {
|
||||
if(meterConfig.source = METER_SOURCE_GPIO) {
|
||||
switch(meterConfig.parser) {
|
||||
case METER_PARSER_PASSIVE:
|
||||
if(pulseMc != NULL) {
|
||||
delete pulseMc;
|
||||
pulseMc = NULL;
|
||||
}
|
||||
if(kamstrupMc != NULL) {
|
||||
delete(kamstrupMc);
|
||||
kamstrupMc = NULL;
|
||||
@ -697,6 +707,10 @@ void loop() {
|
||||
mc = passiveMc;
|
||||
break;
|
||||
case METER_PARSER_KAMSTRUP:
|
||||
if(pulseMc != NULL) {
|
||||
delete pulseMc;
|
||||
pulseMc = NULL;
|
||||
}
|
||||
if(passiveMc != NULL) {
|
||||
delete(passiveMc);
|
||||
passiveMc = NULL;
|
||||
@ -708,6 +722,23 @@ void loop() {
|
||||
hwSerial = kamstrupMc->getHwSerial();
|
||||
mc = kamstrupMc;
|
||||
break;
|
||||
case METER_PARSER_PULSE:
|
||||
if(kamstrupMc != NULL) {
|
||||
delete(kamstrupMc);
|
||||
kamstrupMc = NULL;
|
||||
}
|
||||
if(passiveMc != NULL) {
|
||||
delete(passiveMc);
|
||||
passiveMc = NULL;
|
||||
}
|
||||
if(pulseMc == NULL) {
|
||||
pulseMc = new PulseMeterCommunicator(&Debug);
|
||||
}
|
||||
pulseMc->configure(meterConfig, tz);
|
||||
attachInterrupt(digitalPinToInterrupt(meterConfig.rxPin), onPulse, meterConfig.rxPinPullup ? RISING : FALLING);
|
||||
|
||||
mc = pulseMc;
|
||||
break;
|
||||
default:
|
||||
debugE_P(PSTR("Unknown meter parser selected: %d"), meterConfig.parser);
|
||||
}
|
||||
@ -1092,6 +1123,10 @@ void toggleSetupMode() {
|
||||
|
||||
bool readHanPort() {
|
||||
if(mc == NULL) return false;
|
||||
if(pulseMc != NULL) {
|
||||
pulseMc->onPulse(pulses);
|
||||
pulses = 0;
|
||||
}
|
||||
if(!mc->loop()) {
|
||||
meterState.setLastError(mc->getLastError());
|
||||
return false;
|
||||
@ -1818,3 +1853,7 @@ void configFileParse() {
|
||||
config.save();
|
||||
LittleFS.end();
|
||||
}
|
||||
|
||||
void IRAM_ATTR onPulse() {
|
||||
pulses++;
|
||||
}
|
||||
16
src/ImpulseAmsData.cpp
Normal file
16
src/ImpulseAmsData.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "ImpulseAmsData.h"
|
||||
#include "Uptime.h"
|
||||
|
||||
ImpulseAmsData::ImpulseAmsData(AmsData& state, uint16_t pulsePerKwh, uint8_t pulses) {
|
||||
listType = 1;
|
||||
if(pulses > 0) {
|
||||
lastUpdateMillis = millis64();
|
||||
uint64_t lastStateMillis = state.getLastUpdateMillis();
|
||||
if(lastStateMillis > 0) {
|
||||
uint64_t ms = (lastUpdateMillis - lastStateMillis) / pulses;
|
||||
activeImportPower = (1000.0 / pulsePerKwh) / (((float) ms) / 3600000.0);
|
||||
}
|
||||
} else {
|
||||
lastUpdateMillis = state.getLastUpdateMillis();
|
||||
}
|
||||
}
|
||||
11
src/ImpulseAmsData.h
Normal file
11
src/ImpulseAmsData.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _IMPULSEAMSDATA_H
|
||||
#define _IMPULSEAMSDATA_H
|
||||
|
||||
#include "AmsData.h"
|
||||
|
||||
class ImpulseAmsData : public AmsData {
|
||||
public:
|
||||
ImpulseAmsData(AmsData &state, uint16_t pulsePerKwh, uint8_t pulses);
|
||||
};
|
||||
|
||||
#endif
|
||||
78
src/PulseMeterCommunicator.cpp
Normal file
78
src/PulseMeterCommunicator.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "PulseMeterCommunicator.h"
|
||||
#include "Uptime.h"
|
||||
|
||||
PulseMeterCommunicator::PulseMeterCommunicator(RemoteDebug* debugger) {
|
||||
this->debugger = debugger;
|
||||
}
|
||||
|
||||
void PulseMeterCommunicator::configure(MeterConfig& meterConfig, Timezone* tz) {
|
||||
this->meterConfig = meterConfig;
|
||||
this->configChanged = false;
|
||||
this->tz = tz;
|
||||
setupGpio();
|
||||
}
|
||||
|
||||
bool PulseMeterCommunicator::loop() {
|
||||
return updated || !initialized;
|
||||
}
|
||||
|
||||
AmsData* PulseMeterCommunicator::getData(AmsData& meterState) {
|
||||
if(!initialized) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Initializing pulse meter state\n"));
|
||||
state.apply(meterState);
|
||||
initialized = true;
|
||||
return NULL;
|
||||
}
|
||||
updated = false;
|
||||
|
||||
AmsData* ret = new AmsData();
|
||||
ret->apply(state);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("Returning AMS data, list type: %d\n"), ret->getListType());
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PulseMeterCommunicator::getLastError() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PulseMeterCommunicator::isConfigChanged() {
|
||||
return this->configChanged;
|
||||
}
|
||||
|
||||
void PulseMeterCommunicator::getCurrentConfig(MeterConfig& meterConfig) {
|
||||
meterConfig = this->meterConfig;
|
||||
}
|
||||
|
||||
void PulseMeterCommunicator::setupGpio() {
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Setting up Pulse Meter GPIO, rx: %d, tx: %d\n"), meterConfig.rxPin, meterConfig.txPin);
|
||||
if(meterConfig.rxPin != NOT_A_PIN) {
|
||||
pinMode(meterConfig.rxPin, meterConfig.rxPinPullup ? INPUT_PULLUP : INPUT_PULLDOWN);
|
||||
}
|
||||
if(meterConfig.txPin != NOT_A_PIN) {
|
||||
pinMode(meterConfig.txPin, OUTPUT);
|
||||
digitalWrite(meterConfig.txPin, HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
void PulseMeterCommunicator::onPulse(uint8_t pulses) {
|
||||
uint64_t now = millis64();
|
||||
if(initialized && pulses == 0) {
|
||||
if(now - lastUpdate > 10000) {
|
||||
ImpulseAmsData update(state, meterConfig.baud, pulses);
|
||||
state.apply(update);
|
||||
updated = true;
|
||||
lastUpdate = now;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("PULSE\n"));
|
||||
if(!initialized) {
|
||||
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("Pulse communicator not initialized\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
ImpulseAmsData update(state, meterConfig.baud, pulses);
|
||||
state.apply(update);
|
||||
updated = true;
|
||||
lastUpdate = now;
|
||||
}
|
||||
41
src/PulseMeterCommunicator.h
Normal file
41
src/PulseMeterCommunicator.h
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @copyright Utilitech AS 2024
|
||||
* License: Fair Source
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PULSEMETERCOMMUNICATOR_H
|
||||
#define _PULSEMETERCOMMUNICATOR_H
|
||||
|
||||
#include "MeterCommunicator.h"
|
||||
#include "RemoteDebug.h"
|
||||
#include "AmsConfiguration.h"
|
||||
#include "Timezone.h"
|
||||
#include "ImpulseAmsData.h"
|
||||
|
||||
class PulseMeterCommunicator : public MeterCommunicator {
|
||||
public:
|
||||
PulseMeterCommunicator(RemoteDebug* debugger);
|
||||
void configure(MeterConfig&, Timezone*);
|
||||
bool loop();
|
||||
AmsData* getData(AmsData& meterState);
|
||||
int getLastError();
|
||||
bool isConfigChanged();
|
||||
void getCurrentConfig(MeterConfig& meterConfig);
|
||||
|
||||
void onPulse(uint8_t pulses);
|
||||
|
||||
protected:
|
||||
RemoteDebug* debugger = NULL;
|
||||
MeterConfig meterConfig;
|
||||
bool configChanged = false;
|
||||
Timezone* tz;
|
||||
bool updated = false;
|
||||
bool initialized = false;
|
||||
AmsData state;
|
||||
uint64_t lastUpdate = 0;
|
||||
|
||||
void setupGpio();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user