analogsensors, outputs PWM, DAC, digital

This commit is contained in:
MichaelDvP
2022-02-16 18:35:25 +01:00
parent 7f5e0f7244
commit 073493cba2
11 changed files with 433 additions and 123 deletions

View File

@@ -59,7 +59,8 @@ import {
DeviceValue,
DeviceValueUOM,
DeviceValueUOM_s,
AnalogTypes,
AnalogType,
AnalogTypeNames,
Sensor,
Analog
} from './types';
@@ -715,7 +716,7 @@ const DashboardData: FC = () => {
))}
</ValidatedTextField>
</Grid>
{analog.t === 3 && (
{analog.t >= AnalogType.COUNTER && analog.t <= AnalogType.RATE && (
<>
<Grid item>
<ValidatedTextField name="u" label="UoM" value={analog.u} select onChange={updateValue(setAnalog)}>
@@ -726,22 +727,37 @@ const DashboardData: FC = () => {
))}
</ValidatedTextField>
</Grid>
<Grid item></Grid>
<Grid item>
<ValidatedTextField
name="o"
label="Offset"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', max: '3300', step: '1' }}
InputProps={{
endAdornment: <InputAdornment position="end">mV</InputAdornment>
}}
/>
</Grid>
{analog.t === AnalogType.ADC && (
<Grid item>
<ValidatedTextField
name="o"
label="Offset"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', max: '3300', step: '1' }}
InputProps={{
startAdornment: <InputAdornment position="start">mV</InputAdornment>
}}
/>
</Grid>
)}
{analog.t === AnalogType.COUNTER && (
<Grid item>
<ValidatedTextField
name="o"
label="Start Value"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', step: '1' }}
/>
</Grid>
)}
<Grid item>
<ValidatedTextField
name="f"
@@ -756,6 +772,73 @@ const DashboardData: FC = () => {
</Grid>
</>
)}
{analog.t === AnalogType.DIGITAL_OUT && (analog.i === 25 || analog.i === 26) && (
<>
<Grid item>
<ValidatedTextField
name="o"
label="DAC Value"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', max: '255', step: '1' }}
/>
</Grid>
</>
)}
{analog.t === AnalogType.DIGITAL_OUT && analog.i !== 25 && analog.i !== 26 && (
<>
<Grid item>
<ValidatedTextField
name="o"
label="Value"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', max: '1', step: '1' }}
/>
</Grid>
</>
)}
{analog.t >= AnalogType.PWM_0 && (
<>
<Grid item>
<ValidatedTextField
name="f"
label="Frequency"
value={numberValue(analog.f)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '1', max: '5000', step: '1' }}
InputProps={{
startAdornment: <InputAdornment position="start">Hz</InputAdornment>
}}
/>
</Grid>
<Grid item>
<ValidatedTextField
name="o"
label="Dutycycle"
value={numberValue(analog.o)}
sx={{ width: '20ch' }}
type="number"
variant="outlined"
onChange={updateValue(setAnalog)}
inputProps={{ min: '0', max: '100', step: '0.1' }}
InputProps={{
startAdornment: <InputAdornment position="start">%</InputAdornment>
}}
/>
</Grid>
</>
)}
</Grid>
<Box color="warning.main" mt={2}>
<Typography variant="body2">Warning: be careful when assigning a GPIO!</Typography>

View File

@@ -176,7 +176,9 @@ export enum DeviceValueUOM {
DBM,
FAHRENHEIT,
MV,
SQM
SQM,
M3,
L
}
export const DeviceValueUOM_s = [
@@ -199,10 +201,36 @@ export const DeviceValueUOM_s = [
'°F',
'mV',
'sqm',
"o'clock"
"m3",
"l"
];
export enum AnalogType {
NOTUSED = 0,
DIGITAL_IN,
COUNTER,
ADC,
TIMER,
RATE,
DIGITAL_OUT,
PWM_0,
PWM_1,
PWM_2
}
export const AnalogTypeNames = [
'(disabled)',
'Digital in',
'Counter',
'ADC',
'Timer',
'Rate',
'Digital out',
'PWM 0',
'PWM 1',
'PWM 2'
];
export const AnalogTypes = ['(disabled)', 'Digital in', 'Counter', 'ADC'];
type BoardProfiles = {
[name: string]: string;

View File

@@ -27,7 +27,7 @@ void AnalogSensor::start() {
reload(); // fetch the list of sensors from our customization service
if (analog_enabled_) {
analogSetAttenuation(ADC_2_5db); // for all channels
analogSetAttenuation(ADC_2_5db); // for all channels 1.5V
}
LOG_INFO(F("Starting Analog sensor service"));
@@ -40,10 +40,15 @@ void AnalogSensor::start() {
F_(info_cmd));
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(counter),
[&](const char * value, const int8_t id) { return command_counter(value, id); },
F("set counter value"),
F_(setvalue),
[&](const char * value, const int8_t id) { return command_setvalue(value, id); },
F("set io value"),
CommandFlag::ADMIN_ONLY);
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(commands),
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
F_(commands_cmd));
}
// load settings from the customization file, sorts them and initializes the GPIOs
@@ -58,11 +63,47 @@ void AnalogSensor::reload() {
// and store them locally and then activate them
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
auto sensors = settings.analogCustomizations;
sensors_.clear(); // start with an empty list
if (sensors.size() != 0) {
for (auto & sensor : sensors) {
auto it = sensors_.begin();
for (auto & sensor_ : sensors_) {
// update existing sensors
bool found = false;
for (auto & sensor : sensors) { //search customlist
if (sensor_.id() == sensor.id) {
// for output sensors set value to new start-value
if ((sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT)
&& (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) {
sensor_.set_value(sensor.offset);
}
sensor_.set_name(sensor.name);
sensor_.set_type(sensor.type);
sensor_.set_offset(sensor.offset);
sensor_.set_factor(sensor.factor);
sensor_.set_uom(sensor.uom);
sensor_.ha_registered = false;
found = true;
}
}
if (!found) {
sensors_.erase(it);
}
it++;
}
// add new sensors from list
for (auto & sensor : sensors) {
bool found = false;
for (auto & sensor_ : sensors_) {
if (sensor_.id() == sensor.id) {
found = true;
}
}
if (!found) {
sensors_.emplace_back(sensor.id, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type);
sensors_.back().ha_registered = false; // this will trigger recrate of the HA config
if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
sensors_.back().set_value(sensor.offset);
} else {
sensors_.back().set_value(0); // reset value only for new sensors
}
}
}
return true;
@@ -82,11 +123,21 @@ void AnalogSensor::reload() {
} else if (sensor.type() == AnalogType::COUNTER) {
LOG_DEBUG(F("Adding analog I/O Counter sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), INPUT_PULLUP);
sensor.set_value(0); // reset count
sensor.set_uom(0); // no uom, just for safe measures
if (sensor.id() == 25 || sensor.id() == 26) {
dacWrite(sensor.id(), 255);
}
sensor.polltime_ = 0;
sensor.poll_ = digitalRead(sensor.id());
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
LOG_DEBUG(F("Adding analog Timer/Rate sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), INPUT_PULLUP);
sensor.polltime_ = uuid::get_uptime();
sensor.last_polltime_ = uuid::get_uptime();
sensor.poll_ = digitalRead(sensor.id());
sensor.set_offset(0);
sensor.set_value(0);
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
LOG_DEBUG(F("Adding analog Read sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), INPUT_PULLUP);
@@ -95,18 +146,49 @@ void AnalogSensor::reload() {
sensor.polltime_ = 0;
sensor.poll_ = digitalRead(sensor.id());
publish_sensor(sensor);
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
LOG_DEBUG(F("Adding analog Write sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), OUTPUT);
if (sensor.id() == 25 || sensor.id() == 26) {
if (sensor.offset() > 255) {
sensor.set_offset(255);
} else if (sensor.offset() < 0) {
sensor.set_offset(0);
}
dacWrite(sensor.id(), sensor.offset());
sensor.set_value(sensor.offset());
} else {
digitalWrite(sensor.id(), sensor.offset() > 0 ? 1 : 0);
sensor.set_value(digitalRead(sensor.id()));
}
sensor.set_uom(0); // no uom, just for safe measures
publish_sensor(sensor);
} else if (sensor.type() >= AnalogType::PWM_0) {
LOG_DEBUG(F("Adding PWM output sensor on GPIO%d"), sensor.id());
uint channel = sensor.type() - AnalogType::PWM_0;
ledcSetup(channel, sensor.factor(), 13);
ledcAttachPin(sensor.id(), channel);
if (sensor.offset() > 100) {
sensor.set_offset(100);
} else if (sensor.offset() < 0) {
sensor.set_offset(0);
}
ledcWrite(channel, (uint32_t)(sensor.offset() * 8191 / 100));
sensor.set_value(sensor.offset());
sensor.set_uom(DeviceValueUOM::PERCENT);
publish_sensor(sensor);
}
}
}
// measure and moving average adc
// measure input sensors and moving average adc
void AnalogSensor::measure() {
static uint32_t measure_last_ = 0;
// measure interval 500ms for analog sensors
// measure interval 500ms for adc sensors
if (!measure_last_ || (uuid::get_uptime() - measure_last_) >= MEASURE_ANALOG_INTERVAL) {
measure_last_ = uuid::get_uptime();
// go through the list of ADC sensors
// go through the list of adc sensors
for (auto & sensor : sensors_) {
if (sensor.type() == AnalogType::ADC) {
uint16_t a = analogReadMilliVolts(sensor.id()); // e.g. ADC1_CHANNEL_0_GPIO_NUM
@@ -128,34 +210,38 @@ void AnalogSensor::measure() {
}
}
}
// poll digital io every time
// poll digital io every time with debounce
// go through the list of digital sensors
for (auto & sensor : sensors_) {
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER) {
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER
|| sensor.type() == AnalogType::RATE) {
auto old_value = sensor.value(); // remember current value before reading
auto current_reading = digitalRead(sensor.id());
if (sensor.poll_ != current_reading) { // check for pinchange
sensor.polltime_ = uuid::get_uptime();
if (sensor.poll_ != current_reading) { // check for pinchange
sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
sensor.poll_ = current_reading;
}
if (uuid::get_uptime() - sensor.polltime_ >= 15) { // debounce
// debounce and check for real pinchange
if (uuid::get_uptime() - sensor.polltime_ >= 15 && sensor.poll_ != sensor.last_reading_) {
sensor.last_reading_ = sensor.poll_;
if (sensor.type() == AnalogType::DIGITAL_IN) {
sensor.set_value(sensor.poll_);
} else if (sensor.type() == AnalogType::COUNTER) {
// capture reading and compare with the last one to see if there is high/low change
if (sensor.poll_ != sensor.last_reading_) {
sensor.last_reading_ = sensor.poll_;
if (!sensor.poll_) {
sensor.set_value(old_value + 1);
}
} else if (!sensor.poll_) { // falling edge
if (sensor.type() == AnalogType::COUNTER) {
sensor.set_value(old_value + sensor.factor());
} else if (sensor.type() == AnalogType::RATE) { // dafault uom: Hz (1/sec) with factor 1
sensor.set_value(sensor.factor() * 1000 / (sensor.polltime_ - sensor.last_polltime_));
} else if (sensor.type() == AnalogType::TIMER) { // default seconds with factor 1
sensor.set_value(sensor.factor() * (sensor.polltime_ - sensor.last_polltime_) / 1000);
}
sensor.last_polltime_ = sensor.polltime_;
}
// see if there is a change and increment # reads
if (old_value != sensor.value()) {
sensorreads_++;
changed_ = true;
publish_sensor(sensor);
}
}
// see if there is a change and increment # reads
if (old_value != sensor.value()) {
sensorreads_++;
changed_ = true;
publish_sensor(sensor);
}
}
}
@@ -170,7 +256,7 @@ void AnalogSensor::loop() {
}
// update analog information name and offset
bool AnalogSensor::update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type) {
bool AnalogSensor::update(uint8_t id, const std::string & name, float offset, float factor, uint8_t uom, int8_t type) {
boolean found_sensor = false; // see if we can find the sensor in our customization list
EMSESP::webCustomizationService.update(
@@ -240,7 +326,11 @@ bool AnalogSensor::updated_values() {
void AnalogSensor::publish_sensor(const Sensor & sensor) {
if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
if (Mqtt::publish_single2cmd()) {
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
} else {
snprintf(topic, sizeof(topic), "%s%s/%s", read_flash_string(F_(analogsensor)).c_str(), "_data", sensor.name().c_str());
}
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as floats
}
@@ -285,12 +375,16 @@ void AnalogSensor::publish_values(const bool force) {
dataSensor["name"] = sensor.name();
switch (sensor.type()) {
case AnalogType::COUNTER:
dataSensor["value"] = (uint16_t)sensor.value(); // convert to integer
break;
case AnalogType::TIMER:
case AnalogType::RATE:
case AnalogType::ADC:
case AnalogType::PWM_0:
case AnalogType::PWM_1:
case AnalogType::PWM_2:
dataSensor["value"] = (float)sensor.value(); // float
break;
case AnalogType::DIGITAL_IN:
case AnalogType::DIGITAL_OUT:
default:
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
break;
@@ -373,6 +467,15 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
dataSensor["offset"] = sensor.offset();
dataSensor["factor"] = sensor.factor();
} else if (sensor.type() == AnalogType::COUNTER) {
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
dataSensor["start_value"] = sensor.offset();
dataSensor["factor"] = sensor.factor();
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
dataSensor["factor"] = sensor.factor();
} else if (sensor.type() >= AnalogType::PWM_0) {
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
dataSensor["frequency"] = sensor.factor();
}
dataSensor["value"] = sensor.value();
} else {
@@ -384,7 +487,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
}
// this creates the sensor, initializing everything
AnalogSensor::Sensor::Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type)
AnalogSensor::Sensor::Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type)
: id_(id)
, name_(name)
, offset_(offset)
@@ -405,28 +508,70 @@ std::string AnalogSensor::Sensor::name() const {
}
// set the counter value, id is gpio-no
bool AnalogSensor::command_counter(const char * value, const int8_t id) {
int val;
if (!Helpers::value2number(value, val)) {
bool AnalogSensor::command_setvalue(const char * value, const int8_t id) {
float val;
if (!Helpers::value2float(value, val)) {
return false;
}
for (auto & sensor : sensors_) {
if (sensor.type() == AnalogType::COUNTER && sensor.id() == id) {
if (val < 0) { // negative values corrects
sensor.set_value(sensor.value() + val);
} else { // positive values are set
if (sensor.id() == id) {
if (sensor.type() == AnalogType::COUNTER) {
if (val < 0 || value[0] == '+') { // sign corrects values
sensor.set_offset(sensor.value() + val);
sensor.set_value(sensor.value() + val);
} else { // positive values are set
sensor.set_offset(val);
sensor.set_value(val);
}
publish_sensor(sensor);
return true;
} else if (sensor.type() == AnalogType::ADC) {
sensor.set_offset(val);
return true;
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
uint8_t v = val;
if ((sensor.id() == 25 || sensor.id() == 26) && v <= 255) {
sensor.set_offset(v);
sensor.set_value(v);
pinMode(sensor.id(), OUTPUT);
dacWrite(sensor.id(), sensor.offset());
publish_sensor(sensor);
return true;
} else if (v == 0 || v == 1) {
sensor.set_offset(v);
sensor.set_value(v);
pinMode(sensor.id(), OUTPUT);
digitalWrite(sensor.id(), sensor.offset() > 0 ? 1 : 0);
publish_sensor(sensor);
return true;
}
} else if (sensor.type() >= AnalogType::PWM_0) {
uint8_t channel = sensor.type() - AnalogType::PWM_0;
if (val > 100) {
val = 100;
} else if (val < 0) {
val = 0;
}
sensor.set_offset(val);
sensor.set_value(val);
ledcWrite(channel, (uint32_t)(val * 8191 / 100));
publish_sensor(sensor);
return true;
}
return true;
}
}
return false;
}
// list commands
bool AnalogSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
return Command::list(EMSdevice::DeviceType::ANALOGSENSOR, output);
}
// hard coded tests
#ifdef EMSESP_DEBUG
void AnalogSensor::test() {
// Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
// Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
sensors_.back().set_value(12.4);

View File

@@ -36,10 +36,10 @@ class AnalogSensor {
public:
class Sensor {
public:
Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
~Sensor() = default;
void set_offset(const uint16_t offset) {
void set_offset(const float offset) {
offset_ = offset;
}
@@ -68,7 +68,7 @@ class AnalogSensor {
factor_ = factor;
}
uint16_t offset() const {
float offset() const {
return offset_;
}
@@ -90,16 +90,18 @@ class AnalogSensor {
bool ha_registered = false;
uint16_t analog_ = 0; // ADC - average value
uint32_t sum_ = 0; // ADC - rolling sum
uint16_t last_reading_ = 0; // IO COUNTER & ADC - last reading
uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
int poll_ = 0;
uint16_t analog_ = 0; // ADC - average value
uint32_t sum_ = 0; // ADC - rolling sum
uint16_t last_reading_ = 0; // IO COUNTER & ADC - last reading
uint16_t count_ = 0; // counter raw counts
uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
int poll_ = 0;
uint32_t last_polltime_ = 0; // for timer
private:
uint8_t id_;
std::string name_;
uint16_t offset_;
float offset_;
float factor_;
uint8_t uom_;
float value_; // float because of the factor is a float
@@ -112,9 +114,15 @@ class AnalogSensor {
enum AnalogType : int8_t {
MARK_DELETED = -1, // mark for deletion
NOTUSED, // 0 - disabled
DIGITAL_IN, // 1
COUNTER, // 2
ADC // 3
DIGITAL_IN,
COUNTER,
ADC,
TIMER,
RATE,
DIGITAL_OUT,
PWM_0,
PWM_1,
PWM_2
};
void start();
@@ -149,7 +157,7 @@ class AnalogSensor {
return sensors_.size();
}
bool update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type);
bool update(uint8_t id, const std::string & name, float offset, float factor, uint8_t uom, int8_t type);
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id);
#ifdef EMSESP_DEBUG
@@ -163,9 +171,10 @@ class AnalogSensor {
static uuid::log::Logger logger_;
void remove_ha_topic(const uint8_t id);
bool command_counter(const char * value, const int8_t id);
bool command_setvalue(const char * value, const int8_t id);
void measure();
bool command_info(const char * value, const int8_t id, JsonObject & output);
bool command_commands(const char * value, const int8_t id, JsonObject & output);
std::vector<Sensor> sensors_; // our list of sensors

View File

@@ -66,7 +66,9 @@ class DeviceValue {
DBM, // 15
FAHRENHEIT, // 16
MV, // 17
SQM // 18
SQM, // 18 squaremeter
M3, // 19 cubic meter
L // 20
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp

View File

@@ -526,6 +526,10 @@ bool Helpers::value2float(const char * v, float & value) {
value = atof((char *)v);
return true;
}
if (v[0] == '+' && (v[1] == '.' || (v[1] >= '0' && v[1] <= '9'))) {
value = atof((char *)(v + 1));
return true;
}
return false;
}

View File

@@ -70,7 +70,7 @@ MAKE_PSTR_WORD(pin)
MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(timeout)
MAKE_PSTR_WORD(board_profile)
MAKE_PSTR_WORD(counter)
MAKE_PSTR_WORD(setvalue)
// for commands
MAKE_PSTR_WORD(call)
@@ -121,7 +121,7 @@ MAKE_STR(productid_fmt, "%s EMS ProductID")
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"))
MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"), F("timer"), F("rate"), F("digital out"), F("pwm 0"), F("pwm 1"), F("pwm 2"))
// strings
MAKE_PSTR(EMSESP, "EMS-ESP")
@@ -196,6 +196,8 @@ MAKE_PSTR(dbm, "dBm")
MAKE_PSTR(fahrenheit, "°F")
MAKE_PSTR(mv, "mV")
MAKE_PSTR(sqm, "sqm")
MAKE_PSTR(m3, "m3")
MAKE_PSTR(l, "l")
// MAKE_PSTR(times, "mal")
// MAKE_PSTR(oclock, "Uhr")
@@ -585,6 +587,9 @@ MAKE_PSTR_LIST(mode, F("mode"), F("modus"))
MAKE_PSTR_LIST(modetype, F("modetype"), F("modus Typ"))
MAKE_PSTR_LIST(fastheatup, F("fastheatup"), F("fast heatup"))
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("Tagestemperatur"))
MAKE_PSTR_LIST(daylowtemp, F("daytemp2"), F("Tagestemperatur T2"))
MAKE_PSTR_LIST(daymidtemp, F("daytemp3"), F("Tagestemperatur T3"))
MAKE_PSTR_LIST(dayhightemp, F("daytemp4"), F("Tagestemperatur T4"))
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("Heizen Temperatur"))
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("Nachttemperatur"))
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco Temperatur"))
@@ -597,7 +602,8 @@ MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("Temperaturanhebung"))
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min Flusstemperatur"))
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max Flusstemperatur"))
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("Raumeinfluss"))
MAKE_PSTR_LIST(curroominfl, F("curroominfl"), F("current room influence"))
MAKE_PSTR_LIST(roominfl_factor, F("roominflfactor"), F("Raumeinfluss Factor"))
MAKE_PSTR_LIST(curroominfl, F("curroominfl"), F("aktueller Raumeinfluss"))
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("Frostschutztemperatur"))
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("berechnete Flusstemperatur"))
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("Heizungstyp"))

View File

@@ -70,7 +70,7 @@ MAKE_PSTR_WORD(pin)
MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(timeout)
MAKE_PSTR_WORD(board_profile)
MAKE_PSTR_WORD(counter)
MAKE_PSTR_WORD(setvalue)
// for commands
MAKE_PSTR_WORD(call)
@@ -121,7 +121,7 @@ MAKE_STR(productid_fmt, "%s EMS ProductID")
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"))
MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"), F("timer"), F("rate"), F("digital out"), F("pwm 0"), F("pwm 1"), F("pwm 2"))
// strings
MAKE_PSTR(EMSESP, "EMS-ESP")
@@ -196,6 +196,8 @@ MAKE_PSTR(dbm, "dBm")
MAKE_PSTR(fahrenheit, "°F")
MAKE_PSTR(mv, "mV")
MAKE_PSTR(sqm, "sqm")
MAKE_PSTR(m3, "m3")
MAKE_PSTR(l, "l")
// MAKE_PSTR(times, "times")
// MAKE_PSTR(oclock, "o'clock")

View File

@@ -73,18 +73,18 @@ bool System::command_pin(const char * value, const int8_t id) {
} else if (Helpers::value2bool(value, v)) {
pinMode(id, OUTPUT);
digitalWrite(id, v);
LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
// LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
return true;
} else if (Helpers::value2string(value, v1)) {
if (v1 == "input" || v1 == "in" || v1 == "-1") {
pinMode(id, INPUT);
v = digitalRead(id);
LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
// LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
return true;
}
}
LOG_INFO(F("GPIO %d: invalid value"), id);
// LOG_INFO(F("GPIO %d: invalid value"), id);
#endif
return false;
@@ -162,13 +162,19 @@ bool System::command_publish(const char * value, const int8_t id) {
bool System::command_syslog_level(const char * value, const int8_t id) {
uint8_t s = 0xff;
if (Helpers::value2enum(value, s, FL_(enum_syslog_level))) {
bool changed = false;
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
settings.syslog_level = (int8_t)s - 1;
if (settings.syslog_level != (int8_t)s - 1) {
settings.syslog_level = (int8_t)s - 1;
changed = true;
}
return StateUpdateResult::CHANGED;
},
"local");
EMSESP::system_.syslog_init();
if (changed) {
EMSESP::system_.syslog_init();
}
return true;
}
return false;
@@ -176,29 +182,35 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
// watch
bool System::command_watch(const char * value, const int8_t id) {
uint8_t w = 0xff;
uint8_t w = 0xff;
uint16_t i = Helpers::hextoint(value);
if (Helpers::value2enum(value, w, FL_(enum_watch))) {
if (w == 0 || EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
EMSESP::watch_id(0);
}
EMSESP::watch(w);
if (Mqtt::publish_single()) {
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[w]).c_str());
if (Mqtt::publish_single() && w != EMSESP::watch()) {
if (Mqtt::publish_single2cmd()) {
Mqtt::publish(F("system/watch"),
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : read_flash_string(FL_(enum_watch)[w]).c_str());
} else {
Mqtt::publish(F("system_data/watch"),
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : read_flash_string(FL_(enum_watch)[w]).c_str());
}
}
EMSESP::watch(w);
return true;
}
uint16_t i = Helpers::hextoint(value);
if (i) {
} else if (i) {
if (Mqtt::publish_single() && i != EMSESP::watch_id()) {
if (Mqtt::publish_single2cmd()) {
Mqtt::publish(F("system/watch"), Helpers::hextoa(i));
} else {
Mqtt::publish(F("system_data/watch"), Helpers::hextoa(i));
}
}
EMSESP::watch_id(i);
if (EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
EMSESP::watch(EMSESP::Watch::WATCH_ON);
}
if (Mqtt::publish_single()) {
char s[10];
snprintf(s, sizeof(s), "0x%04X", i);
Mqtt::publish(F("system/watch"), s);
// Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
}
return true;
}
return false;
@@ -273,13 +285,25 @@ void System::syslog_init() {
}
if (Mqtt::publish_single()) {
Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
if (Mqtt::publish_single2cmd()) {
Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
Mqtt::publish(F("system/watch"),
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch())
: read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
} else {
Mqtt::publish(F("system/watch"), Helpers::hextoa(EMSESP::watch_id()));
}
} else {
char s[10];
snprintf(s, sizeof(s), "0x%04X", EMSESP::watch_id());
Mqtt::publish(F("system/watch"), s);
Mqtt::publish(F("system_data/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
Mqtt::publish(F("system_data/watch"),
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch())
: read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
} else {
Mqtt::publish(F("system_data/watch"), Helpers::hextoa(EMSESP::watch_id()));
}
}
}
#endif
@@ -524,7 +548,7 @@ bool System::heartbeat_json(JsonObject & output) {
output["txfails"] = EMSESP::txservice_.telegram_read_fail_count() + EMSESP::txservice_.telegram_write_fail_count();
if (Mqtt::enabled()) {
output["mqttfails"] = Mqtt::publish_fails();
output["mqttcount"] = Mqtt::publish_count();
output["mqttfails"] = Mqtt::publish_fails();
}
output["apicalls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
@@ -636,16 +660,12 @@ void System::system_check() {
// commands - takes static function pointers
void System::commands_init() {
Command::add(EMSdevice::DeviceType::SYSTEM,
F_(pin),
System::command_pin,
F("set a GPIO on/off"),
CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY); // dont create a MQTT topic for this
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set a GPIO on/off"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restart EMS-ESP"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, F("watch incoming telegrams"));
// register syslog command in syslog init
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("set syslog level"), CommandFlag::ADMIN_ONLY);
if (Mqtt::enabled()) {
@@ -822,7 +842,7 @@ void System::show_system(uuid::console::Shell & shell) {
// show Ethernet if connected
if (ethernet_connected_) {
shell.println();
shell.printfln(F(" Wired Network: connected"));
shell.printfln(F(" Ethernet Network: connected"));
shell.printfln(F(" MAC address: %s"), ETH.macAddress().c_str());
shell.printfln(F(" Hostname: %s"), ETH.getHostname());
shell.printfln(F(" IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
@@ -1021,6 +1041,13 @@ bool System::command_customizations(const char * value, const int8_t id, JsonObj
sensorJson["offset"] = sensor.offset;
sensorJson["factor"] = sensor.factor;
sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
} else if (sensor.type == AnalogSensor::AnalogType::COUNTER || sensor.type == AnalogSensor::AnalogType::TIMER
|| sensor.type == AnalogSensor::AnalogType::RATE) {
sensorJson["factor"] = sensor.factor;
sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
} else if (sensor.type >= AnalogSensor::AnalogType::PWM_0) {
sensorJson["frequency"] = sensor.factor;
sensorJson["factor"] = sensor.factor;
}
}
@@ -1154,10 +1181,14 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : EMSESP::emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
JsonObject obj = devices.createNestedObject();
obj["type"] = emsdevice->device_type_name();
obj["name"] = emsdevice->to_string();
obj["entities"] = emsdevice->count_entities();
JsonObject obj = devices.createNestedObject();
obj["type"] = emsdevice->device_type_name();
// obj["name"] = emsdevice->to_string();
obj["name"] = emsdevice->name();
obj["device id"] = Helpers::hextoa(emsdevice->device_id());
obj["product id"] = emsdevice->product_id();
obj["version"] = emsdevice->version();
obj["entities"] = emsdevice->count_entities();
char result[200];
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::RECEIVED);
if (result[0] != '\0') {

View File

@@ -44,7 +44,7 @@ class AnalogCustomization {
public:
uint8_t id;
std::string name;
uint16_t offset;
float offset;
float factor;
uint8_t uom; // 0 is none
int8_t type; // -1 is for deletion

View File

@@ -275,7 +275,7 @@ void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant &
uint8_t id = analog["id"]; // this is the unique key
std::string name = analog["name"];
float factor = analog["factor"];
int16_t offset = analog["offset"];
float offset = analog["offset"];
uint8_t uom = analog["uom"];
int8_t type = analog["type"];
ok = EMSESP::analogsensor_.update(id, name, offset, factor, uom, type);