Browse Source

Basic sensor scafolding, DHT 90% migrated

fastled
Xose Pérez 7 years ago
parent
commit
2299ce9bfd
18 changed files with 751 additions and 53 deletions
  1. +19
    -3
      code/espurna/config/general.h
  2. +7
    -0
      code/espurna/config/prototypes.h
  3. +2
    -3
      code/espurna/config/sensors.h
  4. +4
    -4
      code/espurna/dht.ino
  5. +4
    -2
      code/espurna/espurna.ino
  6. +6
    -0
      code/espurna/influxdb.ino
  7. +49
    -0
      code/espurna/libs/AggregatorBase.h
  8. +3
    -29
      code/espurna/libs/AggregatorMedian.h
  9. +41
    -0
      code/espurna/libs/AggregatorMovingAverage.h
  10. +5
    -5
      code/espurna/power.ino
  11. +1
    -3
      code/espurna/relay.ino
  12. +290
    -0
      code/espurna/sensor.ino
  13. +70
    -0
      code/espurna/sensors/SensorBase.h
  14. +194
    -0
      code/espurna/sensors/SensorDHT.h
  15. +2
    -2
      code/espurna/ws.ino
  16. +3
    -0
      code/html/custom.css
  17. +36
    -0
      code/html/custom.js
  18. +15
    -2
      code/html/index.html

+ 19
- 3
code/espurna/config/general.h View File

@ -146,8 +146,6 @@
#define CUSTOM_RESET_MAX 10 #define CUSTOM_RESET_MAX 10
#include <pgmspace.h>
PROGMEM const char custom_reset_hardware[] = "Hardware button"; PROGMEM const char custom_reset_hardware[] = "Hardware button";
PROGMEM const char custom_reset_web[] = "Reboot from web interface"; PROGMEM const char custom_reset_web[] = "Reboot from web interface";
PROGMEM const char custom_reset_terminal[] = "Reboot from terminal"; PROGMEM const char custom_reset_terminal[] = "Reboot from terminal";
@ -246,7 +244,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define TMP_CELSIUS 0 #define TMP_CELSIUS 0
#define TMP_FAHRENHEIT 1 #define TMP_FAHRENHEIT 1
#define TMP_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// LED // LED
@ -749,6 +746,25 @@ PROGMEM const char* const custom_reset_string[] = {
#define RF_SEND_DELAY 250 // Interval between sendings in ms #define RF_SEND_DELAY 250 // Interval between sendings in ms
#define RF_RECEIVE_DELAY 500 // Interval between recieving in ms (avoid debouncing) #define RF_RECEIVE_DELAY 500 // Interval between recieving in ms (avoid debouncing)
// -----------------------------------------------------------------------------
// SENSORS
// -----------------------------------------------------------------------------
#define SENSOR_READ_INTERVAL 6000 // Read data from sensors every 6 seconds
#define SENSOR_REPORT_EVERY 10 // Report every this many readings
#define SENSOR_USE_INDEX 0 // Use the index in topic (i.e. temperature/0)
// even if just one sensor (0 for backwards compatibility)
#define SENSOR_TEMPERATURE_DECIMALS 2
#define SENSOR_HUMIDITY_DECIMALS 0
#define SENSOR_UNKNOWN_TOPIC "unknown"
#define SENSOR_TEMPERATURE_TOPIC "temperature"
#define SENSOR_HUMIDITY_TOPIC "humidity"
#define SENSOR_TEMPERATURE_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT)
#define SENSOR_TEMPERATURE_CORRECTION 0.0 // Offset correction
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// IR // IR
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 7
- 0
code/espurna/config/prototypes.h View File

@ -1,6 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <functional> #include <functional>
#include <pgmspace.h>
extern "C" { extern "C" {
#include "user_interface.h" #include "user_interface.h"
@ -60,6 +61,7 @@ template<typename T> void domoticzSend(const char * key, T nvalue, const char *
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
template<typename T> bool idbSend(const char * topic, T payload); template<typename T> bool idbSend(const char * topic, T payload);
template<typename T> bool idbSend(const char * topic, unsigned char id, T payload);
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -69,6 +71,11 @@ template<typename T> bool idbSend(const char * topic, T payload);
#include <my92xx.h> #include <my92xx.h>
#endif #endif
// -----------------------------------------------------------------------------
// Sensors
// -----------------------------------------------------------------------------
#include "sensors/SensorBase.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Utils // Utils
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 2
- 3
code/espurna/config/sensors.h View File

@ -27,7 +27,6 @@
#define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report #define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report
#endif #endif
#define TEMPERATURE_CORRECTION 0.0 // This is both for DHT and DS18B20
#define TEMPERATURE_DECIMALS 1 // Decimals for temperature values #define TEMPERATURE_DECIMALS 1 // Decimals for temperature values
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@ -36,11 +35,11 @@
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
#ifndef DHT_SUPPORT #ifndef DHT_SUPPORT
#define DHT_SUPPORT 0
#define DHT_SUPPORT 1
#endif #endif
#ifndef DHT_PIN #ifndef DHT_PIN
#define DHT_PIN 14
#define DHT_PIN 13
#endif #endif
#ifndef DHT_TYPE #ifndef DHT_TYPE


+ 4
- 4
code/espurna/dht.ino View File

@ -139,7 +139,7 @@ void _dhtWebSocketOnSend(JsonObject& root) {
root["dhtTmp"] = getDHTTemperature(); root["dhtTmp"] = getDHTTemperature();
root["dhtHum"] = getDHTHumidity(); root["dhtHum"] = getDHTHumidity();
} }
root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt();
root["tmpUnits"] = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -152,12 +152,12 @@ bool getDHTIsConnected() {
double getDHTTemperature(bool celsius) { double getDHTTemperature(bool celsius) {
double value = celsius ? _dhtTemperature : _dhtTemperature * 1.8 + 32; double value = celsius ? _dhtTemperature : _dhtTemperature * 1.8 + 32;
double correction = getSetting("tmpCorrection", TEMPERATURE_CORRECTION).toFloat();
double correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
return roundTo(value + correction, TEMPERATURE_DECIMALS); return roundTo(value + correction, TEMPERATURE_DECIMALS);
} }
double getDHTTemperature() { double getDHTTemperature() {
bool celsius = getSetting("tmpUnits", TMP_UNITS).toInt() == TMP_CELSIUS;
bool celsius = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt() == TMP_CELSIUS;
return getDHTTemperature(celsius); return getDHTTemperature(celsius);
} }
@ -206,7 +206,7 @@ void dhtLoop() {
_dhtIsConnected = true; _dhtIsConnected = true;
// Get values // Get values
bool celsius = getSetting("tmpUnits", TMP_UNITS).toInt() == TMP_CELSIUS;
bool celsius = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt() == TMP_CELSIUS;
double t = getDHTTemperature(celsius); double t = getDHTTemperature(celsius);
unsigned int h = getDHTHumidity(); unsigned int h = getDHTHumidity();


+ 4
- 2
code/espurna/espurna.ino View File

@ -324,7 +324,8 @@ void setup() {
counterSetup(); counterSetup();
#endif #endif
#if DHT_SUPPORT #if DHT_SUPPORT
dhtSetup();
//dhtSetup();
sensorSetup();
#endif #endif
#if RF_SUPPORT #if RF_SUPPORT
rfSetup(); rfSetup();
@ -394,7 +395,8 @@ void loop() {
counterLoop(); counterLoop();
#endif #endif
#if DHT_SUPPORT #if DHT_SUPPORT
dhtLoop();
//dhtLoop();
sensorLoop();
#endif #endif
#if RF_SUPPORT #if RF_SUPPORT
rfLoop(); rfLoop();


+ 6
- 0
code/espurna/influxdb.ino View File

@ -76,6 +76,12 @@ template<typename T> bool idbSend(const char * topic, T payload) {
} }
template<typename T> bool idbSend(const char * topic, unsigned char id, T payload) {
char measurement[64];
snprintf_P(measurement, sizeof(measurement), PSTR("%s,id=%d"), topic, id);
return idbSend(topic, payload);
}
bool idbEnabled() { bool idbEnabled() {
return _idb_enabled; return _idb_enabled;
} }


+ 49
- 0
code/espurna/libs/AggregatorBase.h View File

@ -0,0 +1,49 @@
// -----------------------------------------------------------------------------
// Aggregator base class
// -----------------------------------------------------------------------------
#pragma once
#include <vector>
class AggregatorBase {
public:
AggregatorBase() {
_data = new std::vector<double>();
}
~AggregatorBase() {
if (_data) delete _data;
}
virtual void add(double value) {
_data->push_back(value);
}
virtual unsigned char count() {
return _data->size();
}
virtual void reset() {
_data->clear();
}
virtual double max() {
double max = 0;
for (unsigned char i = 1; i < _data->size(); i++) {
if (max < _data->at(i)) max = _data->at(i);
}
return max;
}
virtual double result() {
return 0;
}
protected:
std::vector<double> *_data;
};

code/espurna/libs/MedianFilter.h → code/espurna/libs/AggregatorMedian.h View File

@ -4,25 +4,11 @@
#pragma once #pragma once
class MedianFilter {
#include "AggregatorBase.h"
public:
MedianFilter() {
_data = new std::vector<double>();
}
~MedianFilter() {
if (_data) delete _data;
}
virtual void add(double value) {
_data->push_back(value);
}
class AggregatorMedian : public AggregatorBase {
virtual unsigned char count() {
return _data->size();
}
public:
virtual void reset() { virtual void reset() {
double last = _data->empty() ? 0 : _data->back(); double last = _data->empty() ? 0 : _data->back();
@ -30,14 +16,6 @@ class MedianFilter {
add(last); add(last);
} }
virtual double max() {
double max = 0;
for (unsigned char i = 1; i < _data->size(); i++) {
if (max < _data->at(i)) max = _data->at(i);
}
return max;
}
virtual double result() { virtual double result() {
double sum = 0; double sum = 0;
@ -70,8 +48,4 @@ class MedianFilter {
} }
private:
std::vector<double> *_data;
}; };

+ 41
- 0
code/espurna/libs/AggregatorMovingAverage.h View File

@ -0,0 +1,41 @@
// -----------------------------------------------------------------------------
// Aggregator Moving Average
// -----------------------------------------------------------------------------
#pragma once
#include <vector>
#include "AggregatorBase.h"
class AggregatorMovingAverage : public AggregatorBase {
public:
AggregatorMovingAverage(unsigned char size) {
_size = size;
for (unsigned char i=0; i<size; i++) {
_data->push_back(0);
}
}
virtual void add(double value) {
_sum = _sum + value - _data->at(_pointer);
_data->at(_pointer) = value;
_pointer = (_pointer + 1) % _size;
}
virtual void reset() {
// Nothing to do
}
virtual double result() {
return _sum;
}
protected:
unsigned char _size = 0;
unsigned char _pointer = 0;
double _sum = 0;
};

+ 5
- 5
code/espurna/power.ino View File

@ -12,7 +12,7 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
// MODULE GLOBALS AND CACHE // MODULE GLOBALS AND CACHE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#include "libs/MedianFilter.h"
#include "libs/AggregatorMedian.h"
#include <Hash.h> #include <Hash.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -27,15 +27,15 @@ double _power_current = 0;
double _power_voltage = 0; double _power_voltage = 0;
double _power_apparent = 0; double _power_apparent = 0;
double _power_energy = 0; double _power_energy = 0;
MedianFilter _filter_current = MedianFilter();
AggregatorMedian _filter_current = AggregatorMedian();
#if POWER_HAS_ACTIVE #if POWER_HAS_ACTIVE
double _power_active = 0; double _power_active = 0;
double _power_reactive = 0; double _power_reactive = 0;
double _power_factor = 0; double _power_factor = 0;
MedianFilter _filter_voltage = MedianFilter();
MedianFilter _filter_active = MedianFilter();
MedianFilter _filter_apparent = MedianFilter();
AggregatorMedian _filter_voltage = AggregatorMedian();
AggregatorMedian _filter_active = AggregatorMedian();
AggregatorMedian _filter_apparent = AggregatorMedian();
#endif #endif
#if POWER_HAS_ENERGY #if POWER_HAS_ENERGY


+ 1
- 3
code/espurna/relay.ino View File

@ -618,9 +618,7 @@ void relaySetupMQTT() {
#if INFLUXDB_SUPPORT #if INFLUXDB_SUPPORT
void relayInfluxDB(unsigned char id) { void relayInfluxDB(unsigned char id) {
if (id >= _relays.size()) return; if (id >= _relays.size()) return;
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%s,id=%d"), MQTT_TOPIC_RELAY, id);
idbSend(buffer, relayStatus(id) ? "1" : "0");
idbSend(MQTT_TOPIC_RELAY, id, relayStatus(id) ? "1" : "0");
} }
#endif #endif


+ 290
- 0
code/espurna/sensor.ino View File

@ -0,0 +1,290 @@
/*
SENSOR MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <vector>
#include "libs/AggregatorMedian.h"
#include "libs/AggregatorMovingAverage.h"
#include "sensors/SensorBase.h"
typedef struct {
SensorBase * sensor;
unsigned char local; // Local index in its provider
magnitude_t type; // Type of measurement
unsigned char global; // Global index in its type
double current; // Current (last) value, unfiltered
double filtered; // Filtered (averaged) value
AggregatorBase * filter; // Filter object
} sensor_magnitude_t;
std::vector<SensorBase *> _sensors;
std::vector<sensor_magnitude_t> _magnitudes;
unsigned char _counts[MAGNITUDE_MAX];
bool _sensor_realtime = API_REAL_TIME_VALUES;
unsigned char _sensor_temperature_units = SENSOR_TEMPERATURE_UNITS;
double _sensor_temperature_correction = SENSOR_TEMPERATURE_CORRECTION;
#if DHT_SUPPORT
#include "sensors/SensorDHT.h"
#endif
// -----------------------------------------------------------------------------
// Private
// -----------------------------------------------------------------------------
String _sensorTopic(magnitude_t type) {
if (type == MAGNITUDE_TEMPERATURE) {
return String(SENSOR_TEMPERATURE_TOPIC);
} else if (type == MAGNITUDE_HUMIDITY) {
return String(SENSOR_HUMIDITY_TOPIC);
}
return String(SENSOR_UNKNOWN_TOPIC);
}
unsigned char _sensorDecimals(magnitude_t type) {
if (type == MAGNITUDE_TEMPERATURE) {
return SENSOR_TEMPERATURE_DECIMALS;
} else if (type == MAGNITUDE_HUMIDITY) {
return SENSOR_HUMIDITY_DECIMALS;
}
return 0;
}
String _sensorUnits(magnitude_t type) {
if (type == MAGNITUDE_TEMPERATURE) {
if (_sensor_temperature_units == TMP_CELSIUS) {
return String("C");
} else {
return String("F");
}
} else if (type == MAGNITUDE_HUMIDITY) {
return String("%");
}
return String();
}
double _sensorProcess(magnitude_t type, double value) {
if (type == MAGNITUDE_TEMPERATURE) {
if (_sensor_temperature_units == TMP_FAHRENHEIT) value = value * 1.8 + 32;
value = value + _sensor_temperature_correction;
}
return roundTo(value, _sensorDecimals(type));
}
void _sensorConfigure() {
_sensor_realtime = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
_sensor_temperature_units = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
_sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
}
#if WEB_SUPPORT
void _sensorWebSocketOnSend(JsonObject& root) {
bool hasTemperature = false;
JsonArray& sensors = root.createNestedArray("sensors");
for (unsigned char i=0; i<_magnitudes.size(); i++) {
sensor_magnitude_t magnitude = _magnitudes[i];
JsonObject& sensor = sensors.createNestedObject();
sensor["type"] = int(magnitude.type);
sensor["value"] = magnitude.current;
sensor["units"] = _sensorUnits(magnitude.type);
sensor["description"] = magnitude.sensor->slot(magnitude.local);
if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true;
}
//root["apiRealTime"] = _sensor_realtime;
root["tmpUnits"] = _sensor_temperature_units;
root["tmpCorrection"] = _sensor_temperature_correction;
if (hasTemperature) root["temperatureVisible"] = 1;
}
void _sensorAPISetup() {
for (unsigned char magnitude_id=0; magnitude_id<_magnitudes.size(); magnitude_id++) {
sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
String topic = _sensorTopic(magnitude.type);
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) topic = topic + "/" + String(magnitude.global);
apiRegister(topic.c_str(), topic.c_str(), [magnitude_id](char * buffer, size_t len) {
sensor_magnitude_t magnitude = _magnitudes[magnitude_id];
unsigned char decimals = _sensorDecimals(magnitude.type);
double value = _sensor_realtime ? magnitude.current : magnitude.filtered;
dtostrf(value, 1-len, decimals, buffer);
});
}
}
#endif
// -----------------------------------------------------------------------------
// Values
// -----------------------------------------------------------------------------
void sensorSetup() {
// Load sensors
#if DHT_SUPPORT
{
_sensors.push_back(new SensorDHT(DHT_PIN, DHT_TYPE));
#if DHT_PULLUP
pinMode(DHT_PIN, INPUT_PULLUP);
#endif
}
#endif
// Read magnitudes
for (unsigned char i=0; i<_sensors.size(); i++) {
SensorBase * sensor = _sensors[i];
DEBUG_MSG("[SENSOR] %s\n", sensor->name().c_str());
for (unsigned char k=0; k<sensor->count(); k++) {
magnitude_t type = sensor->type(k);
sensor_magnitude_t new_magnitude;
new_magnitude.sensor = sensor;
new_magnitude.local = k;
new_magnitude.type = type;
new_magnitude.global = _counts[type];
new_magnitude.current = 0;
new_magnitude.filtered = 0;
if (type == MAGNITUDE_EVENTS) {
new_magnitude.filter = new AggregatorMovingAverage(SENSOR_REPORT_EVERY);
} else {
new_magnitude.filter = new AggregatorMedian();
}
_magnitudes.push_back(new_magnitude);
DEBUG_MSG("[SENSOR] -> %s:%d\n", _sensorTopic(type).c_str(), _counts[type]);
_counts[type] = _counts[type] + 1;
}
}
#if WEB_SUPPORT
// Websockets
wsOnSendRegister(_sensorWebSocketOnSend);
wsOnAfterParseRegister(_sensorConfigure);
// API
_sensorAPISetup();
#endif
}
void sensorLoop() {
static unsigned long last_update = 0;
static unsigned long report_count = 0;
// Check if we should read new data
if ((millis() - last_update > SENSOR_READ_INTERVAL) || (last_update == 0)) {
last_update = millis();
report_count = (report_count + 1) % SENSOR_REPORT_EVERY;
double value;
char buffer[64];
// Pre-read hook
for (unsigned char i=0; i<_sensors.size(); i++) {
_sensors[i]->pre();
if (!_sensors[i]->status()) {
DEBUG_MSG("[SENSOR] Error reading data from %s (error: %d)\n",
_sensors[i]->name().c_str(),
_sensors[i]->error()
);
}
}
// Get readings
for (unsigned char i=0; i<_magnitudes.size(); i++) {
sensor_magnitude_t magnitude = _magnitudes[i];
if (magnitude.sensor->status()) {
unsigned char decimals = _sensorDecimals(magnitude.type);
value = magnitude.sensor->value(magnitude.local);
magnitude.filter->add(value);
value = _sensorProcess(magnitude.type, value);
_magnitudes[i].current = value;
// Debug
/*
{
dtostrf(value, 1-sizeof(buffer), decimals, buffer);
DEBUG_MSG("[SENSOR] %s - %s: %s%s\n",
magnitude.sensor->name().c_str(),
_sensorTopic(magnitude.type).c_str(),
buffer,
_sensorUnits(magnitude.type).c_str()
);
}
*/
if (report_count == 0) {
double value = magnitude.filter->result();
value = _sensorProcess(magnitude.type, value);
_magnitudes[i].filtered = value;
magnitude.filter->reset();
dtostrf(value, 1-sizeof(buffer), decimals, buffer);
#if MQTT_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
mqttSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
mqttSend(_sensorTopic(magnitude.type).c_str(), buffer);
}
#endif
#if INFLUXDB_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
idbSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
idbSend(_sensorTopic(magnitude.type).c_str(), buffer);
}
#endif
#if DOMOTICZ_SUPPORT
// TODO
#endif
}
}
}
// Post-read hook
for (unsigned char i=0; i<_sensors.size(); i++) {
_sensors[i]->post();
}
#if WEB_SUPPORT
wsSend(_sensorWebSocketOnSend);
#endif
}
}

+ 70
- 0
code/espurna/sensors/SensorBase.h View File

@ -0,0 +1,70 @@
// -----------------------------------------------------------------------------
// Median Filter
// -----------------------------------------------------------------------------
#pragma once
typedef enum magnitude_t {
MAGNITUDE_NONE = 0,
MAGNITUDE_TEMPERATURE,
MAGNITUDE_HUMIDITY,
MAGNITUDE_PRESSURE,
MAGNITUDE_ACTIVE_POWER,
MAGNITUDE_APPARENT_POWER,
MAGNITUDE_REACTIVE_POWER,
MAGNITUDE_VOLTAGE_POWER,
MAGNITUDE_CURRENT_POWER,
MAGNITUDE_ENERGY_POWER,
MAGNITUDE_POWER_FACTOR,
MAGNITUDE_ANALOG,
MAGNITUDE_EVENTS,
MAGNITUDE_MAX,
} magnitude_t;
class SensorBase {
public:
SensorBase() {
}
~SensorBase() {
}
// Pre-read hook (usually to populate registers with up-to-date data)
virtual void pre();
// Post-read hook (usually to reset things)
virtual void post();
// Return sensor status (true for ready)
virtual bool status();
// Return sensor last internal error
virtual int error();
// Number of available slots
virtual unsigned char count();
// Descriptive name of the sensor
virtual String name();
// Descriptive name of the slot # index
virtual String slot(unsigned char index);
// Type for slot # index
virtual magnitude_t type(unsigned char index);
// Current value for slot # index
virtual double value(unsigned char index);
private:
};

+ 194
- 0
code/espurna/sensors/SensorDHT.h View File

@ -0,0 +1,194 @@
// -----------------------------------------------------------------------------
// DHT Sensor
// -----------------------------------------------------------------------------
#pragma once
#include "Arduino.h"
#include "SensorBase.h"
#define DHT_MAX_DATA 5
#define DHT_MAX_ERRORS 5
#define DHT_MIN_INTERVAL 2000
#define DHT_OK 0
#define DHT_CHECKSUM_ERROR -1
#define DHT_TIMEOUT_ERROR -2
#define DHT11 11
#define DHT22 22
#define DHT21 21
#define AM2301 21
class SensorDHT : public SensorBase {
public:
SensorDHT(unsigned char gpio, unsigned char type): SensorBase() {
_gpio = gpio;
_type = type;
}
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
if ((_last_ok > 0) && (millis() - _last_ok < DHT_MIN_INTERVAL)) {
_error = 0;
return;
}
unsigned long low = 0;
unsigned long high = 0;
unsigned char dhtData[DHT_MAX_DATA] = {0};
unsigned char byteInx = 0;
unsigned char bitInx = 7;
// Send start signal to DHT sensor
if (++_errors > DHT_MAX_ERRORS) {
_errors = 0;
digitalWrite(_gpio, HIGH);
delay(250);
}
pinMode(_gpio, OUTPUT);
noInterrupts();
digitalWrite(_gpio, LOW);
delayMicroseconds(500);
digitalWrite(_gpio, HIGH);
delayMicroseconds(40);
pinMode(_gpio, INPUT_PULLUP);
// No errors, read the 40 data bits
for( int k = 0; k < 41; k++ ) {
// Starts new data transmission with >50us low signal
low = _signal(56, LOW);
if (low == 0) {
_error = DHT_TIMEOUT_ERROR;
return;
}
// Check to see if after >70us rx data is a 0 or a 1
high = _signal(75, HIGH);
if (high == 0) {
_error = DHT_TIMEOUT_ERROR;
return;
}
// Skip the first bit
if (k == 0) continue;
// add the current read to the output data
// since all dhtData array where set to 0 at the start,
// only look for "1" (>28us us)
if (high > low) dhtData[byteInx] |= (1 << bitInx);
// index to next byte
if (bitInx == 0) {
bitInx = 7;
++byteInx;
} else {
--bitInx;
}
}
interrupts();
// Verify checksum
if (dhtData[4] != ((dhtData[0] + dhtData[1] + dhtData[2] + dhtData[3]) & 0xFF)) {
_error = DHT_CHECKSUM_ERROR;
return;
}
// Get humidity from Data[0] and Data[1]
if (_type == DHT11) {
_humidity = dhtData[0];
} else {
_humidity = dhtData[0] * 256 + dhtData[1];
_humidity /= 10;
}
// Get temp from Data[2] and Data[3]
if (_type == DHT11) {
_temperature = dhtData[2];
} else {
_temperature = (dhtData[2] & 0x7F) * 256 + dhtData[3];
_temperature /= 10;
if (dhtData[2] & 0x80) _temperature *= -1;
}
_last_ok = millis();
_errors = 0;
_error = 0;
}
// Post-read hook (usually to reset things)
void post() {
}
// Return sensor status (true for ready)
bool status() {
return (_last_ok > 0) & (_error == 0);
}
// Return sensor last internal error
int error() {
return _error;
}
// Number of available slots
unsigned char count() {
return 2;
}
// Descriptive name of the sensor
String name() {
char buffer[64];
snprintf(buffer, sizeof(buffer), "DHT%d @ GPIO%d", _type, _gpio);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
return name();
}
// Type for slot # index
magnitude_t type(unsigned char index) {
if (index == 0) return MAGNITUDE_TEMPERATURE;
if (index == 1) return MAGNITUDE_HUMIDITY;
return MAGNITUDE_NONE;
}
// Current value for slot # index
double value(unsigned char index) {
if (index == 0) return _temperature;
if (index == 1) return _humidity;
return 0;
}
private:
unsigned long _signal(int usTimeOut, bool state) {
unsigned long uSec = 1;
while (digitalRead(_gpio) == state) {
if (++uSec > usTimeOut) return 0;
delayMicroseconds(1);
}
return uSec;
}
unsigned char _gpio;
unsigned char _type;
int _error;
unsigned long _last_ok = 0;
unsigned char _errors = 0;
double _temperature;
unsigned int _humidity;
};

+ 2
- 2
code/espurna/ws.ino View File

@ -339,8 +339,8 @@ void _wsStart(uint32_t client_id) {
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt(); root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEB_PORT).toInt(); root["webPort"] = getSetting("webPort", WEB_PORT).toInt();
root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt();
root["tmpCorrection"] = getSetting("tmpCorrection", TEMPERATURE_CORRECTION).toFloat();
root["tmpUnits"] = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
root["tmpCorrection"] = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat();
// Callbacks // Callbacks
for (unsigned char i = 0; i < _ws_on_send_callbacks.size(); i++) { for (unsigned char i = 0; i < _ws_on_send_callbacks.size(); i++) {


+ 3
- 0
code/html/custom.css View File

@ -93,6 +93,9 @@ div.hint {
font-size: 80%; font-size: 80%;
color: #ccc; color: #ccc;
} }
div.hint.inline {
margin-top: 6px;
}
.break { .break {
margin-top: 5px; margin-top: 5px;
} }


+ 36
- 0
code/html/custom.js View File

@ -27,6 +27,12 @@ function initMessages() {
messages[10] = "Session expired, please reload page..."; messages[10] = "Session expired, please reload page...";
} }
function sensorType(type) {
if (type == 1) return "Temperature";
if (type == 2) return "Humidity";
return null;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Utils // Utils
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -422,6 +428,24 @@ function addRelayGroup() {
} }
function initSensors(data) {
// check if already initialized
var done = $("#sensors > div").length;
if (done > 0) return;
// add templates
var template = $("#sensorTemplate").children();
for (var i=0; i<data.length; i++) {
var line = $(template).clone();
$("label", line).html(sensorType(data[i].type));
$("div.hint", line).html(data[i].description);
$("input", line).attr("data", i);
line.appendTo("#sensors");
}
}
function initColorRGB() { function initColorRGB() {
// check if already initialized // check if already initialized
@ -557,6 +581,8 @@ function rfbSend() {
function processData(data) { function processData(data) {
console.log(data);
// title // title
if ("app_name" in data) { if ("app_name" in data) {
var title = data.app_name; var title = data.app_name;
@ -669,6 +695,16 @@ function processData(data) {
return; return;
} }
// Sensors
if (key == "sensors") {
initSensors(data[key]);
for (var i=0; i<data[key].length; i++) {
var element = $("input[data=" + i + "]");
if (element.length) element.val(data[key][i].value + data[key][i].units);
}
return;
}
// Wifi // Wifi
if (key == "wifi") { if (key == "wifi") {


+ 15
- 2
code/html/index.html View File

@ -168,6 +168,9 @@
<div id="channels"> <div id="channels">
</div> </div>
<div id="sensors">
</div>
<div class="pure-g module module-analog"> <div class="pure-g module module-analog">
<label class="pure-u-1 pure-u-sm-1-4">Analog</label> <label class="pure-u-1 pure-u-sm-1-4">Analog</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="analogValue" readonly /> <input class="pure-u-1 pure-u-sm-3-4" type="text" name="analogValue" readonly />
@ -365,13 +368,13 @@
<input class="pure-u-1 pure-u-md-1-4" name="haPrefix" type="text" tabindex="15" /> <input class="pure-u-1 pure-u-md-1-4" name="haPrefix" type="text" tabindex="15" />
</div> </div>
<div class="pure-g module module-ds module-dht">
<div class="pure-g module module-temperature">
<label class="pure-u-1 pure-u-sm-1-4">Temperature units</label> <label class="pure-u-1 pure-u-sm-1-4">Temperature units</label>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="16" value="0"> Celsius (&deg;C)</input></div> <div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="16" value="0"> Celsius (&deg;C)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="17" value="1"> Fahrenheit (&deg;F)</input></div> <div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="17" value="1"> Fahrenheit (&deg;F)</input></div>
</div> </div>
<div class="pure-g module module-ds module-dht">
<div class="pure-g module module-temperature">
<label class="pure-u-1 pure-u-md-1-4">Temperature correction</label> <label class="pure-u-1 pure-u-md-1-4">Temperature correction</label>
<input name="tmpCorrection" class="pure-u-1 pure-u-md-1-4" type="number" action="reboot" min="-100" step="0.1" max="100" tabindex="18" /> <input name="tmpCorrection" class="pure-u-1 pure-u-md-1-4" type="number" action="reboot" min="-100" step="0.1" max="100" tabindex="18" />
<div class="pure-u-0 pure-u-md-1-2">&nbsp;</div> <div class="pure-u-0 pure-u-md-1-2">&nbsp;</div>
@ -1146,6 +1149,16 @@
</div> </div>
</div> </div>
<div id="sensorTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4"></label>
<div class="pure-u-1 pure-u-sm-1-4">
<input class="pure-u-1 pure-u-sm-23-24" type="text" name="sensor" data="256" readonly />
</div>
<div class="pure-u-1 pure-u-sm-1-2 hint inline"></div>
</div>
</div>
<iframe id="downloader"></iframe> <iframe id="downloader"></iframe>
<input id="uploader" type="file" /> <input id="uploader" type="file" />


Loading…
Cancel
Save