Browse Source

Merge branch 'dev' of https://github.com/xoseperez/espurna into CCT_Support

rfm69
Niklas Wagner 6 years ago
parent
commit
ec9a9942f7
18 changed files with 4017 additions and 3403 deletions
  1. +1
    -0
      code/espurna/config/all.h
  2. +17
    -0
      code/espurna/config/debug.h
  3. +0
    -16
      code/espurna/config/dependencies.h
  4. +33
    -1
      code/espurna/config/general.h
  5. +24
    -5
      code/espurna/config/hardware.h
  6. +7
    -5
      code/espurna/config/progmem.h
  7. +35
    -2
      code/espurna/config/sensors.h
  8. +4
    -1
      code/espurna/config/types.h
  9. BIN
      code/espurna/data/index.html.gz
  10. +3
    -1
      code/espurna/ir.ino
  11. +122
    -46
      code/espurna/rfbridge.ino
  12. +10
    -0
      code/espurna/sensor.ino
  13. +207
    -34
      code/espurna/sensors/PMSX003Sensor.h
  14. +233
    -0
      code/espurna/sensors/SenseAirSensor.h
  15. +3278
    -3278
      code/espurna/static/index.html.gz.h
  16. +9
    -6
      code/html/custom.js
  17. +9
    -7
      code/html/index.html
  18. +25
    -1
      code/platformio.ini

+ 1
- 0
code/espurna/config/all.h View File

@ -33,6 +33,7 @@
#include "sensors.h"
#include "progmem.h"
#include "dependencies.h"
#include "debug.h"
#ifdef USE_CORE_VERSION_H
#include "core_version.h"


+ 17
- 0
code/espurna/config/debug.h View File

@ -0,0 +1,17 @@
#pragma once
// -----------------------------------------------------------------------------
// Debug
// -----------------------------------------------------------------------------
#define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT || DEBUG_WEB_SUPPORT
#if DEBUG_SUPPORT
#define DEBUG_MSG(...) debugSend(__VA_ARGS__)
#define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__)
#endif
#ifndef DEBUG_MSG
#define DEBUG_MSG(...)
#define DEBUG_MSG_P(...)
#endif

+ 0
- 16
code/espurna/config/dependencies.h View File

@ -48,19 +48,3 @@
#undef NTP_SUPPORT
#define NTP_SUPPORT 1 // Scheduler needs NTP
#endif
// -----------------------------------------------------------------------------
// Debug
// -----------------------------------------------------------------------------
#define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT || DEBUG_WEB_SUPPORT
#if DEBUG_SUPPORT
#define DEBUG_MSG(...) debugSend(__VA_ARGS__)
#define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__)
#endif
#ifndef DEBUG_MSG
#define DEBUG_MSG(...)
#define DEBUG_MSG_P(...)
#endif

+ 33
- 1
code/espurna/config/general.h View File

@ -128,7 +128,7 @@
#define SYSTEM_CHECK_ENABLED 1 // Enable crash check by default
#endif
#ifndef SYSTEM_CHECK_MAX
#ifndef SYSTEM_CHECK_TIME
#define SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis
#endif
@ -1144,6 +1144,38 @@
#endif
//Remote Buttons SET 3 (samsung AA59-00608A 8 Toggle Buttons for generic 8CH module)
#if IR_BUTTON_SET == 3
/*
+------+------+------+
| 1 | 2 | 3 |
+------+------+------+
| 4 | 5 | 6 |
+------+------+------+
| 7 | 8 | 9 |
+------+------+------+
| | 0 | |
+------+------+------+
*/
#define IR_BUTTON_COUNT 10
const unsigned long IR_BUTTON[IR_BUTTON_COUNT][3] PROGMEM = {
{ 0xE0E020DF, IR_BUTTON_MODE_TOGGLE, 0 }, // Toggle Relay #0
{ 0xE0E0A05F, IR_BUTTON_MODE_TOGGLE, 1 }, // Toggle Relay #1
{ 0xE0E0609F, IR_BUTTON_MODE_TOGGLE, 2 }, // Toggle Relay #2
{ 0xE0E010EF, IR_BUTTON_MODE_TOGGLE, 3 }, // Toggle Relay #3
{ 0xE0E0906F, IR_BUTTON_MODE_TOGGLE, 4 }, // Toggle Relay #4
{ 0xE0E050AF, IR_BUTTON_MODE_TOGGLE, 5 }, // Toggle Relay #5
{ 0xE0E030CF, IR_BUTTON_MODE_TOGGLE, 6 }, // Toggle Relay #6
{ 0xE0E0B04F, IR_BUTTON_MODE_TOGGLE, 7 } // Toggle Relay #7
//{ 0xE0E0708F, IR_BUTTON_MODE_TOGGLE, 8 } //Extra Button
//{ 0xE0E08877, IR_BUTTON_MODE_TOGGLE, 9 } //Extra Button
};
#endif
#endif // IR_SUPPORT
//--------------------------------------------------------------------------------


+ 24
- 5
code/espurna/config/hardware.h View File

@ -599,16 +599,13 @@
// Info
#define MANUFACTURER "ITEAD"
#define DEVICE "SONOFF_RFBRIDGE"
#define SERIAL_BAUDRATE 19200
#define RELAY_PROVIDER RELAY_PROVIDER_RFBRIDGE
// Number of virtual switches
#ifndef DUMMY_RELAY_COUNT
#define DUMMY_RELAY_COUNT 8
#endif
// Remove UART noise on serial line
#define DEBUG_SERIAL_SUPPORT 0
// Buttons
#define BUTTON1_PIN 0
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
@ -617,6 +614,28 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
// RFB Direct hack thanks to @wildwiz
// https://github.com/xoseperez/espurna/wiki/Hardware-Itead-Sonoff-RF-Bridge---Direct-Hack
#ifndef RFB_DIRECT
#define RFB_DIRECT 0
#endif
#ifndef RFB_RX_PIN
#define RFB_RX_PIN 4 // GPIO for RX when RFB_DIRECT
#endif
#ifndef RFB_TX_PIN
#define RFB_TX_PIN 5 // GPIO for TX when RFB_DIRECT
#endif
// When using un-modified harware, ESPurna communicates with the secondary
// MCU EFM8BB1 via UART at 19200 bps so we need to change the speed of
// the port and remove UART noise on serial line
#if not RFB_DIRECT
#define SERIAL_BAUDRATE 19200
#define DEBUG_SERIAL_SUPPORT 0
#endif
#elif defined(ITEAD_SONOFF_B1)
// Info
@ -1641,7 +1660,7 @@
#define MANUFACTURER "MAXCIO"
#define DEVICE "WUS002S"
// Buttons
// Buttons
#define BUTTON1_PIN 2
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1


+ 7
- 5
code/espurna/config/progmem.h View File

@ -60,6 +60,7 @@ PROGMEM const char magnitude_co2_topic[] = "co2";
PROGMEM const char magnitude_lux_topic[] = "lux";
PROGMEM const char magnitude_uv_topic[] = "uv";
PROGMEM const char magnitude_distance_topic[] = "distance";
PROGMEM const char magnitude_hcho_topic[] = "hcho";
PROGMEM const char* const magnitude_topics[] = {
magnitude_unknown_topic, magnitude_temperature_topic, magnitude_humidity_topic,
@ -69,12 +70,12 @@ PROGMEM const char* const magnitude_topics[] = {
magnitude_analog_topic, magnitude_digital_topic, magnitude_events_topic,
magnitude_pm1dot0_topic, magnitude_pm2dot5_topic, magnitude_pm10_topic,
magnitude_co2_topic, magnitude_lux_topic, magnitude_uv_topic,
magnitude_distance_topic
magnitude_distance_topic, magnitude_hcho_topic
};
PROGMEM const char magnitude_empty[] = "";
PROGMEM const char magnitude_celsius[] = "C";
PROGMEM const char magnitude_fahrenheit[] = "F";
PROGMEM const char magnitude_celsius[] = "°C";
PROGMEM const char magnitude_fahrenheit[] = "°F";
PROGMEM const char magnitude_percentage[] = "%";
PROGMEM const char magnitude_hectopascals[] = "hPa";
PROGMEM const char magnitude_amperes[] = "A";
@ -83,11 +84,12 @@ PROGMEM const char magnitude_watts[] = "W";
PROGMEM const char magnitude_kw[] = "kW";
PROGMEM const char magnitude_joules[] = "J";
PROGMEM const char magnitude_kwh[] = "kWh";
PROGMEM const char magnitude_ugm3[] = "µg/m3";
PROGMEM const char magnitude_ugm3[] = "µg/m³";
PROGMEM const char magnitude_ppm[] = "ppm";
PROGMEM const char magnitude_lux[] = "lux";
PROGMEM const char magnitude_uv[] = "uv";
PROGMEM const char magnitude_distance[] = "m";
PROGMEM const char magnitude_mgm3[] = "mg/m³";
PROGMEM const char* const magnitude_units[] = {
magnitude_empty, magnitude_celsius, magnitude_percentage,
@ -97,7 +99,7 @@ PROGMEM const char* const magnitude_units[] = {
magnitude_empty, magnitude_empty, magnitude_empty,
magnitude_ugm3, magnitude_ugm3, magnitude_ugm3,
magnitude_ppm, magnitude_lux, magnitude_uv,
magnitude_distance
magnitude_distance, magnitude_mgm3
};


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

@ -374,7 +374,24 @@
#endif
//------------------------------------------------------------------------------
// Particle Monitor based on Plantower PMSX003
// SenseAir CO2 sensor
// Enable support by passing SENSEAIR_SUPPORT=1 build flag
//------------------------------------------------------------------------------
#ifndef SENSEAIR_SUPPORT
#define SENSEAIR_SUPPORT 0
#endif
#ifndef SENSEAIR_RX_PIN
#define SENSEAIR_RX_PIN 0
#endif
#ifndef SENSEAIR_TX_PIN
#define SENSEAIR_TX_PIN 2
#endif
//------------------------------------------------------------------------------
// Particle Monitor based on Plantower PMS
// Enable support by passing PMSX003_SUPPORT=1 build flag
//------------------------------------------------------------------------------
@ -382,6 +399,17 @@
#define PMSX003_SUPPORT 0
#endif
#ifndef PMS_TYPE
#define PMS_TYPE PMS_TYPE_X003
#endif
// You can enable smart sleep (read 6-times then sleep on 24-reading-cycles) to extend PMS sensor's life.
// Otherwise the default lifetime of PMS sensor is about 8000-hours/1-years.
// The PMS's fan will stop working on sleeping cycle, and will wake up on reading cycle.
#ifndef PMS_SMART_SLEEP
#define PMS_SMART_SLEEP 0
#endif
#ifndef PMS_RX_PIN
#define PMS_RX_PIN 13
#endif
@ -503,6 +531,7 @@
HCSR04_SUPPORT || \
HLW8012_SUPPORT || \
MHZ19_SUPPORT || \
SENSEAIR_SUPPORT || \
PMSX003_SUPPORT || \
PZEM004T_SUPPORT || \
SHT3X_I2C_SUPPORT || \
@ -624,9 +653,13 @@
#include "../sensors/MHZ19Sensor.h"
#endif
#if SENSEAIR_SUPPORT
#include <SoftwareSerial.h>
#include "../sensors/SenseAirSensor.h"
#endif
#if PMSX003_SUPPORT
#include <SoftwareSerial.h>
#include <PMS.h>
#include "../sensors/PMSX003Sensor.h"
#endif


+ 4
- 1
code/espurna/config/types.h View File

@ -159,6 +159,7 @@
#define IR_BUTTON_MODE_BRIGHTER 3
#define IR_BUTTON_MODE_STATE 4
#define IR_BUTTON_MODE_EFFECT 5
#define IR_BUTTON_MODE_TOGGLE 6
#define LIGHT_EFFECT_SOLID 0
#define LIGHT_EFFECT_FLASH 1
@ -249,6 +250,7 @@
#define SENSOR_CSE7766_ID 0x21
#define SENSOR_TMP3X_ID 0x22
#define SENSOR_HCSR04_ID 0x23
#define SENSOR_SENSEAIR_ID 0x24
//--------------------------------------------------------------------------------
// Magnitudes
@ -276,5 +278,6 @@
#define MAGNITUDE_LUX 19
#define MAGNITUDE_UV 20
#define MAGNITUDE_DISTANCE 21
#define MAGNITUDE_HCHO 22
#define MAGNITUDE_MAX 22
#define MAGNITUDE_MAX 23

BIN
code/espurna/data/index.html.gz View File


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

@ -42,7 +42,9 @@ void _irProcessCode(unsigned long code) {
if (button_mode == IR_BUTTON_MODE_STATE) {
relayStatus(0, button_value);
}
if (button_mode == IR_BUTTON_MODE_TOGGLE) {
relayToggle(button_value);
}
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (button_mode == IR_BUTTON_MODE_BRIGHTER) {


+ 122
- 46
code/espurna/rfbridge.ino View File

@ -11,6 +11,10 @@ Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
#include <queue>
#include <Ticker.h>
#if RFB_DIRECT
#include <RCSwitch.h>
#endif
// -----------------------------------------------------------------------------
// DEFINITIONS
// -----------------------------------------------------------------------------
@ -51,6 +55,11 @@ static std::queue<rfb_message_t> _rfb_message_queue;
Ticker _rfb_ticker;
bool _rfb_ticker_active = false;
#if RFB_DIRECT
RCSwitch * _rfModem;
bool _learning = false;
#endif
// -----------------------------------------------------------------------------
// PRIVATES
// -----------------------------------------------------------------------------
@ -105,24 +114,30 @@ void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject&
}
void _rfbAck() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_ACK);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#if not RFB_DIRECT
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_ACK);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#endif
}
void _rfbLearn() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_LEARN);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#if RFB_DIRECT
DEBUG_MSG_P(PSTR("[RFBRIDGE] Entering LEARN mode\n"));
_learning = true;
#else
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_LEARN);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#endif
#if WEB_SUPPORT
char buffer[100];
@ -139,13 +154,25 @@ void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
}
void _rfbSend(byte * message) {
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_RFOUT);
_rfbSendRaw(message);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#if RFB_DIRECT
unsigned int protocol = message[1];
unsigned int bitlength = message[4];
unsigned long rf_code =
(message[5] << 24) |
(message[6] << 16) |
(message[7] << 8) |
(message[8] << 0) ;
_rfModem->setProtocol(protocol);
_rfModem->send(rf_code, bitlength);
#else
Serial.println();
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_RFOUT);
_rfbSendRaw(message);
Serial.write(RF_CODE_STOP);
Serial.flush();
Serial.println();
#endif
}
void _rfbSend() {
@ -301,33 +328,74 @@ void _rfbDecode() {
}
void _rfbReceive() {
static bool receiving = false;
while (Serial.available()) {
yield();
byte c = Serial.read();
//DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
if (receiving) {
if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
#if RFB_DIRECT
static long learn_start = 0;
if (!_learning && learn_start) {
learn_start = 0;
}
if (_learning) {
if (!learn_start) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] arming learn timeout\n"));
learn_start = millis();
}
if (learn_start > 0 && millis() - learn_start > RF_LEARN_TIMEOUT) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] learn timeout triggered\n"));
memset(_uartbuf, 0, sizeof(_uartbuf));
_uartbuf[0] = RF_CODE_LEARN_KO;
_rfbDecode();
receiving = false;
} else if (_uartpos <= RF_MESSAGE_SIZE) {
_uartbuf[_uartpos++] = c;
} else {
// wrong message, should have received a RF_CODE_STOP
receiving = false;
_learning = false;
}
} else if (c == RF_CODE_START) {
_uartpos = 0;
receiving = true;
}
}
if (_rfModem->available()) {
static unsigned long last = 0;
if (millis() - last > RF_DEBOUNCE) {
last = millis();
unsigned long rf_code = _rfModem->getReceivedValue();
if ( rf_code > 0) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Received code: %08X\n"), rf_code);
memset(_uartbuf, 0, sizeof(_uartbuf));
unsigned char *msgbuf = _uartbuf + 1;
_uartbuf[0] = _learning ? RF_CODE_LEARN_OK: RF_CODE_RFIN;
msgbuf[0] = 0xC0;
msgbuf[1] = _rfModem->getReceivedProtocol();
msgbuf[4] = _rfModem->getReceivedBitlength();
msgbuf[5] = rf_code >> 24;
msgbuf[6] = rf_code >> 16;
msgbuf[7] = rf_code >> 8;
msgbuf[8] = rf_code >> 0;
_rfbDecode();
_learning = false;
}
}
_rfModem->resetAvailable();
}
#else
static bool receiving = false;
while (Serial.available()) {
yield();
byte c = Serial.read();
//DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
if (receiving) {
if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
_rfbDecode();
receiving = false;
} else if (_uartpos <= RF_MESSAGE_SIZE) {
_uartbuf[_uartpos++] = c;
} else {
// wrong message, should have received a RF_CODE_STOP
receiving = false;
}
} else if (c == RF_CODE_START) {
_uartpos = 0;
receiving = true;
}
}
#endif
}
bool _rfbCompare(const char * code1, const char * code2) {
@ -346,9 +414,9 @@ void _rfbMqttCallback(unsigned int type, const char * topic, const char * payloa
snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
mqttSubscribe(buffer);
mqttSubscribe(MQTT_TOPIC_RFOUT);
#if RF_RAW_SUPPORT
mqttSubscribe(MQTT_TOPIC_RFRAW);
#endif
#if RF_RAW_SUPPORT
mqttSubscribe(MQTT_TOPIC_RFRAW);
#endif
}
if (type == MQTT_MESSAGE_EVENT) {
@ -522,6 +590,14 @@ void rfbSetup() {
wsOnActionRegister(_rfbWebSocketOnAction);
#endif
#if RFB_DIRECT
_rfModem = new RCSwitch();
_rfModem->enableReceive(RFB_RX_PIN);
_rfModem->enableTransmit(RFB_TX_PIN);
DEBUG_MSG_P(PSTR("[RFBRIDGE] RF receiver on GPIO %u\n"), RFB_RX_PIN);
DEBUG_MSG_P(PSTR("[RFBRIDGE] RF transmitter on GPIO %u\n"), RFB_TX_PIN);
#endif
// Register loop
espurnaRegisterLoop(rfbLoop);


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

@ -487,11 +487,21 @@ void _sensorLoad() {
}
#endif
#if SENSEAIR_SUPPORT
{
SenseAirSensor * sensor = new SenseAirSensor();
sensor->setRX(SENSEAIR_RX_PIN);
sensor->setTX(SENSEAIR_TX_PIN);
_sensors.push_back(sensor);
}
#endif
#if PMSX003_SUPPORT
{
PMSX003Sensor * sensor = new PMSX003Sensor();
sensor->setRX(PMS_RX_PIN);
sensor->setTX(PMS_TX_PIN);
sensor->setType(PMS_TYPE);
_sensors.push_back(sensor);
}
#endif


+ 207
- 34
code/espurna/sensors/PMSX003Sensor.h View File

@ -1,7 +1,8 @@
// -----------------------------------------------------------------------------
// PMSX003 Dust Sensor
// PMS Dust Sensor
// Uses SoftwareSerial library
// Contribution by Òscar Rovira López
// Refine to support PMS5003T/PMS5003ST by Yonsm Guo
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && PMSX003_SUPPORT
@ -11,10 +12,141 @@
#include "Arduino.h"
#include "BaseSensor.h"
#include <PMS.h>
#include <SoftwareSerial.h>
class PMSX003Sensor : public BaseSensor {
// Type of sensor
#define PMS_TYPE_X003 0
#define PMS_TYPE_X003_9 1
#define PMS_TYPE_5003T 2
#define PMS_TYPE_5003ST 3
// Sensor type specified data
#define PMS_SLOT_MAX 4
#define PMS_DATA_MAX 17
const static struct {
const char *name;
unsigned char data_count;
unsigned char slot_count;
unsigned char slot_types[PMS_SLOT_MAX];
} pms_specs[] = {
{"PMSX003", 13, 3, {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10}},
{"PMSX003_9", 9, 3, {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10}},
{"PMS5003T", 13, 3, {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY}},
{"PMS5003ST", 17, 4, {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY, MAGNITUDE_HCHO}}
};
// [MAGIC][LEN][DATA9|13|17][SUM]
#define PMS_PACKET_SIZE(data_count) ((data_count + 3) * 2)
#define PMS_PAYLOAD_SIZE(data_count) ((data_count + 1) * 2)
// PMS sensor utils
// Command functions copied from: https://github.com/fu-hsi/PMS/blob/master/src/PMS.cpp
// Reading function is rewrited to support flexible reading for PMS5003T/PMS5003ST
class PMSX003 {
protected:
SoftwareSerial *_serial = NULL; // Should initialized by child class
public:
// Standby mode. For low power consumption and prolong the life of the sensor.
inline void sleep() {
uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 };
_serial->write(command, sizeof(command));
}
// Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance.
inline void wakeUp() {
uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 };
_serial->write(command, sizeof(command));
}
// Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically.
inline void activeMode() {
uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
_serial->write(command, sizeof(command));
}
// Passive mode. In this mode, sensor would send serial data to the host only for request.
inline void passiveMode() {
uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 };
_serial->write(command, sizeof(command));
}
// Request read, ONLY needed in Passive Mode!!
inline void requestRead() {
uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 };
_serial->write(command, sizeof(command));
}
// Read sensor's data
bool readData(uint16_t data[], unsigned char data_count) {
do {
int avail = _serial->available();
#if SENSOR_DEBUG
//debugSend("[SENSOR] PMS: Packet available = %d\n", avail);
#endif
if (avail < PMS_PACKET_SIZE(data_count)) {
break;
}
if (_serial->read() == 0x42 && _serial->read() == 0x4D) {
uint16_t sum = 0x42 + 0x4D;
uint16_t size = read16(sum);
if (size != PMS_PAYLOAD_SIZE(data_count)) {
#if SENSOR_DEBUG
debugSend(("[SENSOR] PMS: Payload size: %d != %d.\n"), size, PMS_PAYLOAD_SIZE(data_count));
#endif
break;
}
for (int i = 0; i < data_count; i++) {
data[i] = read16(sum);
#if SENSOR_DEBUG
//debugSend(("[SENSOR] PMS: data[%d] = %d\n"), i, data[i]);
#endif
}
uint16_t checksum = read16();
if (sum == checksum) {
return true;
} else {
#if SENSOR_DEBUG
debugSend(("[SENSOR] PMS checksum: %04X != %04X\n"), sum, checksum);
#endif
}
break;
}
} while (true);
return false;
}
private:
// Read 16-bit
inline uint16_t read16() {
return ((uint16_t) _serial->read()) << 8 | _serial->read();
}
// Read 16-bit and calculate checksum
uint16_t read16(uint16_t &checksum) {
uint8_t high = _serial->read();
uint8_t low = _serial->read();
checksum += high;
checksum += low;
return ((uint16_t) high) << 8 | low;
}
};
class PMSX003Sensor : public BaseSensor, PMSX003 {
public:
@ -23,13 +155,12 @@ class PMSX003Sensor : public BaseSensor {
// ---------------------------------------------------------------------
PMSX003Sensor(): BaseSensor() {
_count = 3;
_count = pms_specs[_type].slot_count;
_sensor_id = SENSOR_PMSX003_ID;
}
~PMSX003Sensor() {
if (_serial) delete _serial;
if (_pms) delete _pms;
}
void setRX(unsigned char pin_rx) {
@ -44,6 +175,12 @@ class PMSX003Sensor : public BaseSensor {
_dirty = true;
}
// Should call setType after constrcutor immediately to enable corresponding slot count
void setType(unsigned char type) {
_type = type;
_count = pms_specs[_type].slot_count;
}
// ---------------------------------------------------------------------
unsigned char getRX() {
@ -54,6 +191,10 @@ class PMSX003Sensor : public BaseSensor {
return _pin_tx;
}
unsigned char getType() {
return _type;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@ -64,13 +205,11 @@ class PMSX003Sensor : public BaseSensor {
if (!_dirty) return;
if (_serial) delete _serial;
if (_pms) delete _pms;
_serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 32);
_serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64);
_serial->enableIntTx(false);
_serial->begin(9600);
_pms = new PMS(* _serial);
_pms->passiveMode();
passiveMode();
_startTime = millis();
_ready = true;
@ -81,16 +220,14 @@ class PMSX003Sensor : public BaseSensor {
// Descriptive name of the sensor
String description() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
snprintf(buffer, sizeof(buffer), "%s @ SwSerial(%u,%u)", pms_specs[_type].name, _pin_rx, _pin_tx);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
char buffer[36] = {0};
if (index == 0) snprintf(buffer, sizeof(buffer), "PM1.0 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
if (index == 1) snprintf(buffer, sizeof(buffer), "PM2.5 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
if (index == 2) snprintf(buffer, sizeof(buffer), "PM10 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
snprintf(buffer, sizeof(buffer), "%d @ %s @ SwSerial(%u,%u)", int(index + 1), pms_specs[_type].name, _pin_rx, _pin_tx);
return String(buffer);
}
@ -103,10 +240,7 @@ class PMSX003Sensor : public BaseSensor {
// Type for slot # index
unsigned char type(unsigned char index) {
if (index == 0) return MAGNITUDE_PM1dot0;
if (index == 1) return MAGNITUDE_PM2dot5;
if (index == 2) return MAGNITUDE_PM10;
return MAGNITUDE_NONE;
return pms_specs[_type].slot_types[index];
}
void pre() {
@ -118,35 +252,74 @@ class PMSX003Sensor : public BaseSensor {
_error = SENSOR_ERROR_OK;
if(_pms->read(_data)) {
_pm1dot0 = _data.PM_AE_UG_1_0;
_pm2dot5 = _data.PM_AE_UG_2_5;
_pm10 = _data.PM_AE_UG_10_0;
#if PMS_SMART_SLEEP
unsigned int readCycle;
if (_readCount++ > 30) {
readCycle = _readCount % 30;
if (readCycle == 0) {
#if SENSOR_DEBUG
debugSend("[SENSOR] %s: Wake up: %d\n", pms_specs[_type].name, _readCount);
#endif
wakeUp();
return;
} else if (readCycle == 1) {
requestRead();
} else if (readCycle > 6) {
return;
}
} else {
readCycle = -1;
}
#endif
uint16_t data[PMS_DATA_MAX];
if (readData(data, pms_specs[_type].data_count)) {
if (_type == PMS_TYPE_5003ST) {
_slot_values[0] = data[4];
_slot_values[1] = (double)data[13] / 10;
_slot_values[2] = (double)data[14] / 10;
_slot_values[3] = (double)data[12] / 1000;
} else if (_type == PMS_TYPE_5003T) {
_slot_values[0] = data[4];
_slot_values[1] = (double)data[10] / 10;
_slot_values[2] = (double)data[11] / 10;
} else {
_slot_values[0] = data[3];
_slot_values[1] = data[4];
_slot_values[2] = data[5];
}
}
_pms->requestRead();
#if PMS_SMART_SLEEP
if (readCycle == 6) {
sleep();
#if SENSOR_DEBUG
debugSend("[SENSOR] %s: Enter sleep mode: %d\n", pms_specs[_type].name, _readCount);
#endif
return;
}
#endif
requestRead();
}
// Current value for slot # index
double value(unsigned char index) {
if(index == 0) return _pm1dot0;
if(index == 1) return _pm2dot5;
if(index == 2) return _pm10;
return 0;
return _slot_values[index];
}
protected:
unsigned int _pm1dot0;
unsigned int _pm2dot5;
unsigned int _pm10;
unsigned int _pin_rx;
unsigned int _pin_tx;
unsigned long _startTime;
SoftwareSerial * _serial = NULL;
PMS * _pms = NULL;
PMS::DATA _data;
unsigned char _type = PMS_TYPE_X003;
double _slot_values[PMS_SLOT_MAX] = {0};
#if PMS_SMART_SLEEP
unsigned int _readCount = 0;
#endif
};
#endif // SENSOR_SUPPORT && PMSX003_SUPPORT
#endif // SENSOR_SUPPORT && PMS_SUPPORT

+ 233
- 0
code/espurna/sensors/SenseAirSensor.h View File

@ -0,0 +1,233 @@
// -----------------------------------------------------------------------------
// SenseAir S8 CO2 Sensor
// Uses SoftwareSerial library
// Contribution by Yonsm Guo
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && SENSEAIR_SUPPORT
#pragma once
#include "Arduino.h"
#include "BaseSensor.h"
#include <SoftwareSerial.h>
// SenseAir sensor utils
class SenseAir
{
protected:
SoftwareSerial *_serial; // Should initialized by child class
public:
int sendCommand(byte command[]) {
byte recv_buf[7] = {0xff};
byte data_buf[2] = {0xff};
long value = -1;
_serial->write(command, 8); //Send the byte array
delay(50);
// Read answer from sensor
int ByteCounter = 0;
while(_serial->available()) {
recv_buf[ByteCounter] = _serial->read();
ByteCounter++;
}
data_buf[0] = recv_buf[3];
data_buf[1] = recv_buf[4];
value = (data_buf[0] << 8) | (data_buf[1]);
return value;
}
int readCo2(void) {
int co2 = 0;
byte frame[8] = {0};
buildFrame(0xFE, 0x04, 0x03, 1, frame);
co2 = sendCommand(frame);
return co2;
}
private:
// Compute the MODBUS RTU CRC
static unsigned int modRTU_CRC(byte buf[], int len, byte checkSum[2]) {
unsigned int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
checkSum[1] = (byte)((crc >> 8) & 0xFF);
checkSum[0] = (byte)(crc & 0xFF);
return crc;
}
static int getBitOfInt(int reg, int pos) {
// Create a mask
int mask = 0x01 << pos;
// Mask the status register
int masked_register = mask & reg;
// Shift the result of masked register back to position 0
int result = masked_register >> pos;
return result;
}
static void buildFrame(byte slaveAddress,
byte functionCode,
short startAddress,
short numberOfRegisters,
byte frame[8]) {
frame[0] = slaveAddress;
frame[1] = functionCode;
frame[2] = (byte)(startAddress >> 8);
frame[3] = (byte)(startAddress);
frame[4] = (byte)(numberOfRegisters >> 8);
frame[5] = (byte)(numberOfRegisters);
// CRC-calculation
byte checkSum[2] = {0};
modRTU_CRC(frame, 6, checkSum);
frame[6] = checkSum[0];
frame[7] = checkSum[1];
}
};
//
class SenseAirSensor : public BaseSensor, SenseAir {
public:
// ---------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------
SenseAirSensor(): BaseSensor() {
_count = 1;
_co2 = 0;
_lastCo2 = 0;
_serial = NULL;
_sensor_id = SENSOR_SENSEAIR_ID;
}
~SenseAirSensor() {
if (_serial) delete _serial;
_serial = NULL;
}
void setRX(unsigned char pin_rx) {
if (_pin_rx == pin_rx) return;
_pin_rx = pin_rx;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
_dirty = true;
}
// ---------------------------------------------------------------------
unsigned char getRX() {
return _pin_rx;
}
unsigned char getTX() {
return _pin_tx;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
// Initialization method, must be idempotent
void begin() {
if (!_dirty) return;
if (_serial) delete _serial;
_serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64);
_serial->enableIntTx(false);
_serial->begin(9600);
_serial->enableRx(true);
_startTime = 0;
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "SenseAir S8 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
return description();
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) {
char buffer[6];
snprintf(buffer, sizeof(buffer), "%u:%u", _pin_rx, _pin_tx);
return String(buffer);
}
// Type for slot # index
unsigned char type(unsigned char index) {
return MAGNITUDE_CO2;
}
void pre() {
if (millis() - _startTime < 20000) {
_error = SENSOR_ERROR_WARM_UP;
return;
}
_error = SENSOR_ERROR_OK;
unsigned int co2 = readCo2();
if (co2 >= 5000 || co2 < 100)
{
_co2 = _lastCo2;
}
else
{
_co2 = (co2 > _lastCo2 + 2000) ? _lastCo2 : co2;
_lastCo2 = co2;
}
}
// Current value for slot # index
double value(unsigned char index) {
return _co2;
}
protected:
unsigned int _pin_rx;
unsigned int _pin_tx;
unsigned long _startTime;
unsigned int _co2;
unsigned int _lastCo2;
};
#endif // SENSOR_SUPPORT && SENSEAIR_SUPPORT

+ 3278
- 3278
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 9
- 6
code/html/custom.js View File

@ -40,7 +40,7 @@ function sensorName(id) {
"HLW8012", "V9261F", "ECH1560", "Analog", "Digital",
"Events", "PMSX003", "BMX280", "MHZ19", "SI7021",
"SHT3X I2C", "BH1750", "PZEM004T", "AM2320 I2C", "GUVAS12SD",
"TMP3X", "HC-SR04"
"TMP3X", "HC-SR04", "SenseAir"
];
if (1 <= id && id <= names.length) {
return names[id - 1];
@ -54,7 +54,7 @@ function magnitudeType(type) {
"Current", "Voltage", "Active Power", "Apparent Power",
"Reactive Power", "Power Factor", "Energy", "Energy (delta)",
"Analog", "Digital", "Events",
"PM1.0", "PM2.5", "PM10", "CO2", "Lux", "UV", "Distance"
"PM1.0", "PM2.5", "PM10", "CO2", "Lux", "UV", "Distance" , "HCHO"
];
if (1 <= type && type <= types.length) {
return types[type - 1];
@ -89,14 +89,17 @@ $.fn.enterKey = function (fnc) {
};
function keepTime() {
$("span[name='ago']").html(ago);
ago++;
if (0 === now) { return; }
var date = new Date(now * 1000);
var text = date.toISOString().substring(0, 19).replace("T", " ");
$("input[name='now']").val(text);
$("span[name='now']").html(text);
$("span[name='ago']").html(ago);
now++;
ago++;
}
// http://www.the-art-of-web.com/javascript/validate-password/
@ -1207,13 +1210,12 @@ function processData(data) {
var position = key.indexOf("Visible");
if (position > 0 && position === key.length - 7) {
var module = key.slice(0,-7);
$(".module-" + module).show();
$(".module-" + module).css("display", "inherit");
return;
}
if ("now" === key) {
now = value;
ago = 0;
return;
}
@ -1229,6 +1231,7 @@ function processData(data) {
value = value ? "SYNC'D" : "NOT SYNC'D";
}
if ("uptime" === key) {
ago = 0;
var uptime = parseInt(value, 10);
var seconds = uptime % 60; uptime = parseInt(uptime / 60, 10);
var minutes = uptime % 60; uptime = parseInt(uptime / 60, 10);


+ 9
- 7
code/html/index.html View File

@ -211,8 +211,10 @@
<div class="pure-u-1-2">Firmware version</div>
<div class="pure-u-11-24"><span class="right" name="app_version"></span></div>
<!--
<div class="pure-u-1-2">Firmware revision</div>
<div class="pure-u-11-24"><span class="right" name="app_revision"></span></div>
-->
<div class="pure-u-1-2">Firmware build date</div>
<div class="pure-u-11-24"><span class="right" name="app_build"></span></div>
@ -249,16 +251,16 @@
<div class="pure-u-11-24"><span class="right" name="loadaverage"></span><span>%</span></div>
<div class="pure-u-1-2">VCC</div>
<div class="pure-u-11-24"><span class="right" name="vcc"></span><span>mV</span></div>
<div class="pure-u-11-24"><span class="right" name="vcc">? </span><span>mV</span></div>
<div class="pure-u-1-2">MQTT Status</div>
<div class="pure-u-11-24"><span class="right" name="mqttStatus">NOT AVAILABLE</span></div>
<div class="pure-u-1-2 module module-mqtt">MQTT Status</div>
<div class="pure-u-11-24 module module-mqtt"><span class="right" name="mqttStatus"></span></div>
<div class="pure-u-1-2">NTP Status</div>
<div class="pure-u-11-24"><span class="right" name="ntpStatus">NOT AVAILABLE</span></div>
<div class="pure-u-1-2 module module-ntp">NTP Status</div>
<div class="pure-u-11-24 module module-ntp"><span class="right" name="ntpStatus"></span></div>
<div class="pure-u-1-2">Current time</div>
<div class="pure-u-11-24"><span class="right" name="now"></span></div>
<div class="pure-u-1-2 module module-ntp">Current time</div>
<div class="pure-u-11-24 module module-ntp"><span class="right" name="now"></span></div>
<div class="pure-u-1-2">Uptime</div>
<div class="pure-u-11-24"><span class="right" name="uptime"></span></div>


+ 25
- 1
code/platformio.ini View File

@ -70,7 +70,6 @@ lib_deps =
https://bitbucket.org/xoseperez/nofuss.git#0.2.5
https://github.com/xoseperez/NtpClient.git#0016a59
OneWire
PMS Library
PZEM004T
PubSubClient
rc-switch
@ -719,6 +718,31 @@ upload_flags = ${common.upload_flags}
monitor_baud = 19200
extra_scripts = ${common.extra_scripts}
[env:itead-sonoff-rfbridge-direct]
platform = ${common.platform}
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DITEAD_SONOFF_RFBRIDGE -DRFB_DIRECT
monitor_baud = 19200
extra_scripts = ${common.extra_scripts}
[env:itead-sonoff-rfbridge-direct-ota]
platform = ${common.platform}
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DITEAD_SONOFF_RFBRIDGE -DRFB_DIRECT
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
monitor_baud = 19200
extra_scripts = ${common.extra_scripts}
# ------------------------------------------------------------------------------
[env:itead-slampher]


Loading…
Cancel
Save