Support multiple temperature sensors

This commit is contained in:
Gunnar Skjold 2020-08-05 19:55:16 +02:00
parent 6479fd6a63
commit e121ec75d8
10 changed files with 325 additions and 30 deletions

View File

@ -675,8 +675,7 @@ bool AmsConfiguration::load() {
bool success = false;
EEPROM.begin(EEPROM_SIZE);
int cs = EEPROM.read(address);
address++;
int cs = EEPROM.read(address++);
switch(cs) {
case 81: // v1.2
success = loadConfig81(address);
@ -686,11 +685,13 @@ bool AmsConfiguration::load() {
break;
case 83: // v1.4
EEPROM.get(address, config);
loadTempSensors();
success = true;
break;
}
EEPROM.end();
if(config.apPin >= 0)
pinMode(config.apPin, INPUT_PULLUP);
meterChanged = true;
@ -698,6 +699,35 @@ bool AmsConfiguration::load() {
return success;
}
void AmsConfiguration::loadTempSensors() {
int address = EEPROM_TEMP_CONFIG_ADDRESS;
int c = 0;
int storedCount = EEPROM.read(address++);
if(storedCount > 0 && storedCount <= 32) {
for(int i = 0; i < storedCount; i++) {
TempSensorConfig* tsc = new TempSensorConfig();
EEPROM.get(address, *tsc);
if(tsc->address[0] != 0xFF) {
tempSensors[c++] = tsc;
}
address += sizeof(*tsc);
}
}
tempSensorCount = c;
}
void AmsConfiguration::saveTempSensors() {
int address = EEPROM_TEMP_CONFIG_ADDRESS;
EEPROM.put(address++, tempSensorCount);
for(int i = 0; i < tempSensorCount; i++) {
TempSensorConfig* tsc = tempSensors[i];
if(tsc->address[0] != 0xFF) {
EEPROM.put(address, *tsc);
address += sizeof(*tsc);
}
}
}
bool AmsConfiguration::loadConfig82(int address) {
ConfigObject82 config82;
EEPROM.get(address, config82);
@ -855,6 +885,7 @@ bool AmsConfiguration::save() {
EEPROM.put(address, EEPROM_CHECK_SUM);
address++;
EEPROM.put(address, config);
saveTempSensors();
bool success = EEPROM.commit();
EEPROM.end();
@ -862,6 +893,42 @@ bool AmsConfiguration::save() {
return success;
}
uint8_t AmsConfiguration::getTempSensorCount() {
return tempSensorCount;
}
TempSensorConfig* AmsConfiguration::getTempSensorConfig(uint8_t i) {
return tempSensors[i];
}
void AmsConfiguration::updateTempSensorConfig(uint8_t address[8], const char name[32], bool common) {
bool found = false;
for(int x = 0; x < tempSensorCount; x++) {
TempSensorConfig *data = tempSensors[x];
if(isSensorAddressEqual(data->address, address)) {
found = true;
strcpy(data->name, name);
data->common = common;
}
}
if(!found) {
TempSensorConfig *data = new TempSensorConfig();
memcpy(data->address, address, 8);
strcpy(data->name, name);
data->common = common;
tempSensors[tempSensorCount] = data;
tempSensorCount++;
}
}
bool AmsConfiguration::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) {
for(int i = 0; i < 8; i++) {
if(a[i] != b[i]) return false;
}
return true;
}
int AmsConfiguration::readString(int pAddress, char* pString[]) {
int address = 0;
byte length = EEPROM.read(pAddress + address);
@ -969,6 +1036,8 @@ void AmsConfiguration::print(Print* debugger)
debugger->printf("NTP server: %s\r\n", this->getNtpServer());
debugger->printf("NTP DHCP: %s\r\n", this->isNtpDhcp() ? "Yes" : "No");
}
debugger->printf("Temp sensor count: %i\r\n", this->getTempSensorCount());
debugger->println("-----------------------------------------------");
}

View File

@ -67,8 +67,6 @@ struct ConfigObject {
char ntpServer[64];
uint8_t tempAnalogSensorPin;
int8_t tempSensorInternal; // -128 = disabled, -1 = analog, 0-127 = digital sensor index
uint8_t tempSensorCount;
};
struct ConfigObject82 {
@ -124,6 +122,12 @@ struct ConfigObject82 {
uint16_t domoCL1IDX;
};
struct TempSensorConfig {
uint8_t address[8];
char name[16];
bool common;
};
class AmsConfiguration {
public:
bool hasConfig();
@ -285,6 +289,12 @@ public:
bool isNtpChanged();
void ackNtpChange();
uint8_t getTempSensorCount();
TempSensorConfig* getTempSensorConfig(uint8_t i);
void updateTempSensorConfig(uint8_t address[8], const char name[32], bool common);
bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]);
void clear();
protected:
@ -350,15 +360,20 @@ private:
360, // Summertime offset (*10)
"pool.ntp.org", // NTP server
0xFF, // Analog temp sensor
0xFF, // Internal temp sensor
0, // Temp sensor count
// 900 bytes
// 894 bytes
};
bool wifiChanged, mqttChanged, meterChanged = true, domoChanged, ntpChanged;
const int EEPROM_SIZE = 904; // Config size + 4 bytes for config version
uint8_t tempSensorCount = 0;
TempSensorConfig* tempSensors[32];
const int EEPROM_SIZE = 1024 * 3;
const int EEPROM_CHECK_SUM = 83; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0;
const int EEPROM_TEMP_CONFIG_ADDRESS = 2048;
void loadTempSensors();
void saveTempSensors();
bool loadConfig81(int address);
bool loadConfig82(int address);

View File

@ -156,6 +156,14 @@ void setup() {
#endif
}
uint8_t c = config.getTempSensorCount();
for(int i = 0; i < c; i++) {
TempSensorConfig* tsc = config.getTempSensorConfig(i);
hw.confTempSensor(tsc->address, tsc->name, tsc->common);
Debug.print("Sensor name: ");
Debug.println(tsc->name);
}
double vcc = hw.getVcc();
if (Debug.isActive(RemoteDebug::INFO)) {
@ -269,7 +277,6 @@ bool longPressActive = false;
bool wifiConnected = false;
unsigned long lastTemperatureRead = 0;
double temperature = -127;
unsigned long lastRead = 0;
unsigned long lastSuccessfulRead = 0;
@ -306,8 +313,8 @@ void loop() {
}
}
if(now - lastTemperatureRead > 5000) {
temperature = hw.getTemperature();
if(now - lastTemperatureRead > 10000) {
hw.updateTemperatures();
lastTemperatureRead = now;
}
@ -554,7 +561,7 @@ void readHanPort() {
if(strlen(config.getMqttHost()) > 0 && strlen(config.getMqttPublishTopic()) > 0) {
if(config.getMqttPayloadFormat() == 0) {
StaticJsonDocument<512> json;
hanToJson(json, data, hw, temperature, config.getMqttClientId());
hanToJson(json, data, hw, hw.getTemperature(), config.getMqttClientId());
if (Debug.isActive(RemoteDebug::INFO)) {
debugI("Sending data to MQTT");
if (Debug.isActive(RemoteDebug::DEBUG)) {
@ -1012,7 +1019,7 @@ void sendSystemStatusToMqtt() {
mqtt.publish(String(config.getMqttPublishTopic()) + "/vcc", String(vcc, 2));
}
mqtt.publish(String(config.getMqttPublishTopic()) + "/rssi", String(hw.getWifiRssi()));
if(temperature != DEVICE_DISCONNECTED_C) {
mqtt.publish(String(config.getMqttPublishTopic()) + "/temperature", String(temperature, 2));
if(hw.getTemperature() != DEVICE_DISCONNECTED_C) {
mqtt.publish(String(config.getMqttPublishTopic()) + "/temperature", String(hw.getTemperature(), 2));
}
}

View File

@ -3,8 +3,8 @@
void HwTools::setTempSensorPin(int tempSensorPin) {
if(tempSensorPin != this->tempSensorPin) {
this->tempSensorInit = false;
if(tempSensor)
delete tempSensor;
if(sensorApi)
delete sensorApi;
if(oneWire)
delete oneWire;
if(tempSensorPin > 0 && tempSensorPin < 40) {
@ -50,27 +50,99 @@ double HwTools::getVcc() {
return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0);
}
double HwTools::getTemperature() {
void HwTools::confTempSensor(uint8_t address[8], const char name[32], bool common) {
bool found = false;
for(int x = 0; x < sensorCount; x++) {
TempSensorData *data = tempSensors[x];
if(isSensorAddressEqual(data->address, address)) {
found = true;
strcpy(data->name, name);
data->common = common;
}
}
if(!found) {
TempSensorData *data = new TempSensorData();
memcpy(data->address, address, 8);
strcpy(data->name, name);
data->common = common;
data->lastRead = DEVICE_DISCONNECTED_C;
data->lastValidRead = DEVICE_DISCONNECTED_C;
tempSensors[sensorCount] = data;
sensorCount++;
}
}
uint8_t HwTools::getTempSensorCount() {
return sensorCount;
}
TempSensorData* HwTools::getTempSensorData(uint8_t i) {
return tempSensors[i];
}
bool HwTools::updateTemperatures() {
if(tempSensorPin != 0xFF) {
if(!tempSensorInit) {
oneWire = new OneWire(tempSensorPin);
tempSensor = new DallasTemperature(this->oneWire);
tempSensor->begin();
sensorApi = new DallasTemperature(this->oneWire);
sensorApi->begin();
delay(50);
tempSensor->requestTemperatures();
hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C;
tempSensorInit = true;
}
if(hasTempSensor) {
tempSensor->requestTemperatures();
return tempSensor->getTempCByIndex(0);
} else {
return DEVICE_DISCONNECTED_C;
DeviceAddress addr;
sensorApi->requestTemperatures();
int c = sensorApi->getDeviceCount();
for(int i = 0; i < c; i++) {
bool found = false;
sensorApi->getAddress(addr, i);
float t = sensorApi->getTempC(addr);
for(int x = 0; x < sensorCount; x++) {
TempSensorData *data = tempSensors[x];
if(isSensorAddressEqual(data->address, addr)) {
found = true;
data->lastRead = t;
if(t > -85) {
data->lastValidRead = t;
}
}
}
if(!found) {
TempSensorData *data = new TempSensorData();
memcpy(data->address, addr, 8);
data->common = true;
data->lastRead = t;
if(t > -85) {
data->lastValidRead = t;
}
tempSensors[sensorCount] = data;
sensorCount++;
}
}
return true;
}
return false;
}
bool HwTools::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) {
for(int i = 0; i < 8; i++) {
if(a[i] != b[i]) return false;
}
return true;
}
double HwTools::getTemperature() {
uint8_t c = 0;
double ret = 0;
for(int x = 0; x < sensorCount; x++) {
TempSensorData data = *tempSensors[x];
if(data.common && data.lastValidRead > -85) {
ret += data.lastValidRead;
c++;
}
}
return DEVICE_DISCONNECTED_C;
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
}
int HwTools::getWifiRssi() {

View File

@ -18,6 +18,14 @@
#define LED_BLUE 3
#define LED_YELLOW 4
struct TempSensorData {
uint8_t address[8];
char name[32];
bool common;
float lastRead;
float lastValidRead;
};
class HwTools {
public:
void setTempSensorPin(int tempSensorPin);
@ -25,7 +33,12 @@ public:
void setVccOffset(double vccOffset);
void setVccMultiplier(double vccMultiplier);
double getVcc();
void confTempSensor(uint8_t address[8], const char name[32], bool common);
uint8_t getTempSensorCount();
TempSensorData* getTempSensorData(uint8_t i);
bool updateTemperatures();
double getTemperature();
double getTemperature(uint8_t address[8]);
int getWifiRssi();
void setLed(uint8_t ledPin, bool ledInverted);
void setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted);
@ -41,11 +54,15 @@ private:
bool ledInverted, ledRgbInverted;
double vccOffset = 0.0;
double vccMultiplier = 1.0;
bool tempSensorInit, hasTempSensor;
bool tempSensorInit;
OneWire *oneWire;
DallasTemperature *tempSensor;
DallasTemperature *sensorApi;
uint8_t sensorCount = 0;
TempSensorData *tempSensors[32];
bool writeLedPin(uint8_t color, uint8_t state);
bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]);
};
#endif

View File

@ -22,6 +22,9 @@
#include "root/upload_html.h"
#include "root/delete_html.h"
#include "root/reset_html.h"
#include "root/temperature_head_html.h"
#include "root/temperature_row_html.h"
#include "root/temperature_foot_html.h"
#include "base64.h"
@ -37,6 +40,7 @@ void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
server.on("/", HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on("/", HTTP_POST, std::bind(&AmsWebServer::handleSetup, this));
server.on("/application.js", HTTP_GET, std::bind(&AmsWebServer::applicationJs, this));
server.on("/temperature", HTTP_GET, std::bind(&AmsWebServer::temperature, this));
server.on("/config-meter", HTTP_GET, std::bind(&AmsWebServer::configMeterHtml, this));
server.on("/config-wifi", HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this));
server.on("/config-mqtt", HTTP_GET, std::bind(&AmsWebServer::configMqttHtml, this));
@ -120,6 +124,36 @@ bool AmsWebServer::checkSecurity(byte level) {
return access;
}
void AmsWebServer::temperature() {
printD("Serving /temperature.html over http...");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
String html;
uint8_t c = hw->getTempSensorCount();
for(int i = 0; i < c; i++) {
TempSensorData* data = hw->getTempSensorData(i);
String row = String((const __FlashStringHelper*) TEMPERATURE_ROW_HTML);
row.replace("${index}", String(i, DEC));
row.replace("${address}", toHex(data->address));
row.replace("${name}", data->name);
row.replace("${common}", data->common ? "checked" : "");
row.replace("${value}", String(data->lastRead, 1));
html += row;
}
server.setContentLength(html.length() + HEAD_HTML_LEN + TEMPERATURE_HEAD_HTML_LEN + FOOT_HTML_LEN + TEMPERATURE_FOOT_HTML_LEN);
server.send_P(200, "text/html", HEAD_HTML);
server.sendContent_P(TEMPERATURE_HEAD_HTML);
server.sendContent(html);
server.sendContent_P(TEMPERATURE_FOOT_HTML);
server.sendContent_P(FOOT_HTML);
}
void AmsWebServer::indexHtml() {
printD("Serving /index.html over http...");
@ -269,6 +303,26 @@ void AmsWebServer::configMeterHtml() {
server.sendContent_P(FOOT_HTML);
}
String AmsWebServer::toHex(uint8_t* in) {
String hex;
for(int i = 0; i < sizeof(in)*2; i++) {
if(in[i] < 0x10) {
hex += '0';
}
hex += String(in[i], HEX);
}
hex.toUpperCase();
return hex;
}
uint8_t* AmsWebServer::fromHex(String in, uint8_t size) {
uint8_t ret[size];
for(int i = 0; i < size*2; i += 2) {
ret[i/2] = strtol(in.substring(i, i+2).c_str(), 0, 16);
}
return ret;
}
void AmsWebServer::configWifiHtml() {
printD("Serving /config-wifi.html over http...");
@ -835,6 +889,16 @@ void AmsWebServer::handleSave() {
config->setNtpServer(server.arg("ntpServer").c_str());
}
if(server.hasArg("tempConfig") && server.arg("tempConfig") == "true") {
for(int i = 0; i < 32; i++) {
if(!server.hasArg("sensor" + String(i, DEC))) break;
String address = server.arg("sensor" + String(i, DEC));
String name = server.arg("sensor" + String(i, DEC) + "name");
bool common = server.hasArg("sensor" + String(i, DEC) + "common") && server.arg("sensor" + String(i, DEC) + "common") == "true";
config->updateTempSensorConfig(fromHex(address, 8), name.c_str(), common);
}
}
printI("Saving configuration now...");
if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger);
@ -854,6 +918,11 @@ void AmsWebServer::handleSave() {
hw->setVccPin(config->getVccPin());
hw->setVccOffset(config->getVccOffset());
hw->setVccMultiplier(config->getVccMultiplier());
uint8_t c = config->getTempSensorCount();
for(int i = 0; i < c; i++) {
TempSensorConfig* tsc = config->getTempSensorConfig(i);
hw->confTempSensor(tsc->address, tsc->name, tsc->common);
}
}
} else {
printE("Error saving configuration");

View File

@ -60,6 +60,7 @@ private:
void indexHtml();
void applicationJs();
void temperature();
void configMeterHtml();
void configWifiHtml();
void configMqttHtml();
@ -103,6 +104,9 @@ private:
void notFound();
String toHex(uint8_t* in);
uint8_t* fromHex(String in, uint8_t size);
void printD(String fmt, ...);
void printI(String fmt, ...);
void printW(String fmt, ...);

11
web/temperature_foot.html Normal file
View File

@ -0,0 +1,11 @@
</div>
<hr/>
<div class="row form-group">
<div class="col-6">
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</form>

View File

@ -0,0 +1,3 @@
<form method="post" action="/save">
<input type="hidden" name="tempConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">

28
web/temperature_row.html Normal file
View File

@ -0,0 +1,28 @@
<input type="hidden" name="sensor${index}" value="${address}"/>
<div class="row mb-3">
<div class="col-xl-3 col-lg-4 col-sm-6">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Address</span>
</div>
<input name="sensor${index}address" type="text" class="form-control" value="${address}" maxlength="16" disabled/>
</div>
</div>
<div class="col-xl-4 col-lg-3 col-sm-6">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Name</span>
</div>
<input name="sensor${index}name" type="text" class="form-control" value="${name}" maxlength="32"/>
</div>
</div>
<div class="col-xl-3 col-lg-3 col-sm-6">
<div class="form-check">
<input name="sensor${index}common" class="form-check-input" type="checkbox" value="true" id="sensor${index}common" ${common}>
<label class="form-check-label" for="sensor${index}common">Include in average</label>
</div>
</div>
<div class="col-xl-2 col-lg-2 col-sm-6">
${value} °C
</div>
</div>