Merge branch 'huzzah'
This commit is contained in:
commit
d96d4cc666
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Gateway & sensors
|
||||||
|
|
||||||
|
## Branches
|
||||||
|
Each sensor has a dedicated branch. E.g.:
|
||||||
|
* temp_sensor
|
||||||
|
* oil_sensor
|
||||||
|
|
||||||
|
The gateway uses `master` as the main branch. Other sensors' branches get merged once they are ready for production.
|
||||||
|
|
||||||
|
## Release flow
|
||||||
1
gateway/.gitignore
vendored
1
gateway/.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/ipch
|
.vscode/ipch
|
||||||
|
include/credentials.h
|
||||||
|
|||||||
@ -44,7 +44,7 @@ enum SensorId : unsigned short {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
#### Stairs temperature
|
#### Stairs temperature
|
||||||
##### Value and volatage
|
##### Value and voltage
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sensor": {
|
"sensor": {
|
||||||
@ -66,7 +66,7 @@ enum SensorId : unsigned short {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
#### Oil sensor
|
#### Oil sensor
|
||||||
##### Value and volatage
|
##### Value and voltage
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sensor": {
|
"sensor": {
|
||||||
|
|||||||
@ -2,26 +2,34 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <RCSwitch.h>
|
#include <RCSwitch.h>
|
||||||
|
|
||||||
|
enum ProtocolNo : unsigned int {
|
||||||
|
NO_PROTOCOL = 0,
|
||||||
|
PROTOCOL_1 = 1,
|
||||||
|
PROTOCOL_2 = 2,
|
||||||
|
PROTOCOL_13 = 13
|
||||||
|
};
|
||||||
|
|
||||||
class Protocol {
|
class Protocol {
|
||||||
protected:
|
protected:
|
||||||
unsigned int protocol;
|
ProtocolNo no;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Protocol(unsigned int protocol) {
|
Protocol(ProtocolNo protocol) : no(protocol) {}
|
||||||
this->protocol = protocol;
|
|
||||||
|
Protocol& setProtocol(unsigned int p) {
|
||||||
|
no = static_cast<ProtocolNo>(p);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
virtual ~Protocol() {}
|
|
||||||
|
|
||||||
virtual void fromJson(JsonObjectConst& rcSwitch, RCSwitch& rcDevice) {
|
virtual void fromJson(JsonObjectConst& rcSwitch, RCSwitch& rcDevice) {
|
||||||
unsigned int protocol = rcSwitch["protocol"];
|
ProtocolNo protocol = rcSwitch["protocol"];
|
||||||
rcDevice.setProtocol(protocol);
|
rcDevice.setProtocol(protocol);
|
||||||
rcDevice.send(rcSwitch["value"]);
|
rcDevice.send(rcSwitch["value"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void toJson(unsigned long value, JsonDocument& jsonDoc) {
|
virtual void toJson(unsigned long value, JsonDocument& jsonDoc) {
|
||||||
JsonObject rcSwitch = jsonDoc.createNestedObject("rcSwitch");
|
JsonObject rcSwitch = jsonDoc.createNestedObject("rcSwitch");
|
||||||
rcSwitch["protocol"] = protocol;
|
rcSwitch["protocol"] = no;
|
||||||
rcSwitch["value"] = value;
|
rcSwitch["value"] = value;
|
||||||
}
|
}
|
||||||
|
} fallbackProtocol{ NO_PROTOCOL };
|
||||||
};
|
|
||||||
|
|||||||
@ -5,25 +5,32 @@
|
|||||||
class Protocol_1 : public Protocol {
|
class Protocol_1 : public Protocol {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Protocol_1() : Protocol(1) {
|
Protocol_1() : Protocol(PROTOCOL_1) {}
|
||||||
}
|
|
||||||
|
|
||||||
void fromJson(JsonObjectConst& rcSwitch, RCSwitch& rcDevice) override {
|
void fromJson(JsonObjectConst& rcSwitch, RCSwitch& rcDevice) override {
|
||||||
unsigned int protocol = rcSwitch["protocol"];
|
ProtocolNo protocol = rcSwitch["protocol"];
|
||||||
rcDevice.setProtocol(protocol);
|
rcDevice.setProtocol(protocol);
|
||||||
char* group = rcSwitch["group"];
|
const char* group = rcSwitch["group"];
|
||||||
int channel = rcSwitch["channel"];
|
int channel = rcSwitch["channel"];
|
||||||
rcSwitch["state"] ? rcDevice.switchOn(group, channel) : rcDevice.switchOff(group, channel);
|
rcSwitch["state"] ? rcDevice.switchOn(group, channel) : rcDevice.switchOff(group, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void toJson(unsigned long value, JsonDocument& jsonDoc) override {
|
void toJson(unsigned long value, JsonDocument& jsonDoc) override {
|
||||||
JsonObject rcSwitch = jsonDoc.createNestedObject("rcSwitch");
|
JsonObject rcSwitch = jsonDoc.createNestedObject("rcSwitch");
|
||||||
rcSwitch["protocol"] = protocol;
|
rcSwitch["protocol"] = no;
|
||||||
RcDecoder decoder;
|
RcDecoder decoder;
|
||||||
decoder.decode(value);
|
decoder.decode(value);
|
||||||
rcSwitch["state"] = decoder.state;
|
rcSwitch["state"] = decoder.state;
|
||||||
rcSwitch["group"] = String(decoder.group, BIN);
|
rcSwitch["group"] = decoder.group;
|
||||||
rcSwitch["channel"] = decoder.device;
|
rcSwitch["channel"] = decoder.device;
|
||||||
rcSwitch["raw_value"] = value;
|
rcSwitch["raw_value"] = value;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
static std::string buildId(const char* group, const unsigned char channel) {
|
||||||
|
char uId[30];
|
||||||
|
sprintf(uId, "%s_%d", group, channel);
|
||||||
|
return std::string{ uId };
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} protocol1;
|
||||||
@ -5,8 +5,7 @@
|
|||||||
class Protocol_2 : public Protocol {
|
class Protocol_2 : public Protocol {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Protocol_2() : Protocol(2) {
|
Protocol_2() : Protocol(PROTOCOL_2) {}
|
||||||
}
|
|
||||||
|
|
||||||
void toJson(unsigned long value, JsonDocument& jsonDoc) override {
|
void toJson(unsigned long value, JsonDocument& jsonDoc) override {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
@ -33,4 +32,4 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
} protocol2;
|
||||||
|
|||||||
58
gateway/include/Protocol_Doorbell.h
Normal file
58
gateway/include/Protocol_Doorbell.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Protocol.h"
|
||||||
|
|
||||||
|
#define BIT_LENGTH 40
|
||||||
|
#define BIT_LENGTH_3 BIT_LENGTH*3
|
||||||
|
#define TX_DELAY 620
|
||||||
|
|
||||||
|
class Protocol_Doorbell : public Protocol {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Protocol_Doorbell() : Protocol(PROTOCOL_13) {}
|
||||||
|
|
||||||
|
void ring(const char* value) {
|
||||||
|
preamble();
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
delayMicroseconds(TX_DELAY);
|
||||||
|
code(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void transmitBit(uint8_t value) {
|
||||||
|
digitalWrite(SEND_PIN, value);
|
||||||
|
delayMicroseconds(BIT_LENGTH);
|
||||||
|
digitalWrite(SEND_PIN, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void transmitHigh() {
|
||||||
|
digitalWrite(SEND_PIN, HIGH);
|
||||||
|
delayMicroseconds(BIT_LENGTH_3);
|
||||||
|
digitalWrite(SEND_PIN, LOW);
|
||||||
|
delayMicroseconds(BIT_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void transmitLow() {
|
||||||
|
digitalWrite(SEND_PIN, HIGH);
|
||||||
|
delayMicroseconds(BIT_LENGTH);
|
||||||
|
digitalWrite(SEND_PIN, LOW);
|
||||||
|
delayMicroseconds(BIT_LENGTH_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void preamble() {
|
||||||
|
noInterrupts();
|
||||||
|
for (int i = 0; i < 370; i++) {
|
||||||
|
transmitBit(HIGH);
|
||||||
|
transmitBit(LOW);
|
||||||
|
}
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void code(const char* value) {
|
||||||
|
noInterrupts();
|
||||||
|
for (const char* p = value; *p; p++) {
|
||||||
|
*p == '1' ? transmitHigh() : transmitLow();
|
||||||
|
}
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
} doorbell;
|
||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
struct RcDecoder {
|
struct RcDecoder {
|
||||||
bool state;
|
bool state;
|
||||||
char group;
|
char group[6];
|
||||||
unsigned char device;
|
unsigned char device;
|
||||||
|
|
||||||
void decode(unsigned long value) {
|
void decode(unsigned long value) {
|
||||||
@ -16,7 +16,7 @@ struct RcDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = RC_STATE(res);
|
state = RC_STATE(res);
|
||||||
group = RC_GROUP(res);
|
sprintf(group, "%05ld", RC_GROUP(res));
|
||||||
switch (RC_DEVICE(res)) {
|
switch (RC_DEVICE(res)) {
|
||||||
case 0b10000:
|
case 0b10000:
|
||||||
device = 1;
|
device = 1;
|
||||||
|
|||||||
@ -6,9 +6,11 @@ bool buildSensorJson(unsigned long value, JsonDocument& jsonDoc) {
|
|||||||
sensor["id"] = ID(value);
|
sensor["id"] = ID(value);
|
||||||
|
|
||||||
float voltage = (float)GET_VCC(value) / 1000;
|
float voltage = (float)GET_VCC(value) / 1000;
|
||||||
|
JsonObject diagnostic = sensor.createNestedObject("diagnostic");
|
||||||
if (voltage != 0) {
|
if (voltage != 0) {
|
||||||
JsonObject diagnostic = sensor.createNestedObject("diagnostic");
|
|
||||||
diagnostic["voltage"] = voltage;
|
diagnostic["voltage"] = voltage;
|
||||||
|
} else {
|
||||||
|
diagnostic["voltage"] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (GET_TYPE(value)) {
|
switch (GET_TYPE(value)) {
|
||||||
|
|||||||
4
gateway/include/credentials.h.tpl
Normal file
4
gateway/include/credentials.h.tpl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
struct WifiCredentials {
|
||||||
|
const char* ssid;
|
||||||
|
const char* password;
|
||||||
|
} credentials[] = {"foo", "bar"};
|
||||||
136
gateway/include/devices.h
Normal file
136
gateway/include/devices.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define MAIN_DEVICE_ID "rc-gateway"
|
||||||
|
|
||||||
|
#include "ha.h"
|
||||||
|
|
||||||
|
using namespace Ha;
|
||||||
|
|
||||||
|
typedef unordered_multimap<unsigned long, Ha::Switch*> mapswitches;
|
||||||
|
|
||||||
|
mapswitches onSwitches;
|
||||||
|
mapswitches offSwitches;
|
||||||
|
unordered_map<string, Ha::Switch*> p1Switches;
|
||||||
|
|
||||||
|
auto gatewayDevice = &DeviceConfig::create(MAIN_DEVICE_ID).withName("RC Gateway").withManufacturer("Adafruit").withModel("Huzzah Esp8266");
|
||||||
|
|
||||||
|
namespace OilTank {
|
||||||
|
Sensor* buildRoomSensor(const char* id) {
|
||||||
|
DeviceConfig* device = &DeviceConfig::create(id)
|
||||||
|
.withName("Servers room")
|
||||||
|
.withManufacturer("Atmel")
|
||||||
|
.withModel("AtTiny85")
|
||||||
|
.withArea("Basement")
|
||||||
|
.withParent(gatewayDevice);
|
||||||
|
return Builder<TemperatureSensor>::instance(id)
|
||||||
|
.asDevice(device)
|
||||||
|
.withValueTemplate("{{ value_json.sensor.temperature }}")
|
||||||
|
.addDiagnostic(new VoltageSensor{id, "Battery voltage", "{{ value_json.sensor.diagnostic.voltage }}"})
|
||||||
|
.addDiagnostic(new BatterySensor{id, "Battery level", "{{ ((states('sensor.servers_room_battery_voltage')|float-2.5)|round(2)*100/2)|int }}"})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Sensor* buildTankSensor(const char* id) {
|
||||||
|
DeviceConfig* device = &DeviceConfig::create(id)
|
||||||
|
.withName("Oil tank")
|
||||||
|
.withManufacturer("Arduino")
|
||||||
|
.withModel("Pro Mini")
|
||||||
|
.withArea("Basement")
|
||||||
|
.withParent(gatewayDevice);
|
||||||
|
return Builder<Sensor>::instance(new Sensor{ "Depth", id })
|
||||||
|
.asDevice(device)
|
||||||
|
.withDeviceClass("distance")
|
||||||
|
.withUnitMseasure("cm")
|
||||||
|
.withValueTemplate("{{ value_json.sensor.value }}")
|
||||||
|
.addSecondary(
|
||||||
|
Builder<Sensor>::instance(new Sensor{ "Level", id })
|
||||||
|
.withUnitMseasure("%")
|
||||||
|
.withValueTemplate("{{ 100 - ((value_json.sensor.value-7)|float*100/110)|round(2) }}")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.addDiagnostic(new VoltageSensor{id, "Battery voltage", "{{ value_json.sensor.diagnostic.voltage }}"})
|
||||||
|
.addDiagnostic(new BatterySensor{id, "Battery level", "{{ ((states('sensor.oil_tank_battery_voltage')|float-3.6)|round(2)*100/1.6)|int }}"})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PollinSwitch : Switch {
|
||||||
|
const char* group;
|
||||||
|
unsigned char channel;
|
||||||
|
|
||||||
|
PollinSwitch(const char* name, const char* group, const unsigned char channel, const char* area = nullptr)
|
||||||
|
: Switch(nullptr, [group, channel]{
|
||||||
|
// copy id from string into a new pointer, to avoid memory leaks
|
||||||
|
string s = Protocol_1::buildId(group, channel);
|
||||||
|
char* uId = new char[s.length() + 1];
|
||||||
|
strcpy(uId, s.c_str());
|
||||||
|
return uId;
|
||||||
|
}()), group(group), channel(channel) {
|
||||||
|
mainDevice = &DeviceConfig::create(id).withName(name).withManufacturer("Pollin").withArea(area).withParent(gatewayDevice);
|
||||||
|
withStateTopic();
|
||||||
|
deviceClass = "outlet";
|
||||||
|
p1Switches.insert({ string(id), this });
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCommand(const char* msg) override {
|
||||||
|
strcmp("ON", msg) == 0 ? mySwitch.switchOn(group, channel) : mySwitch.switchOff(group, channel);
|
||||||
|
publisher(stateTopic, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EasyHomeSwitch : Switch {
|
||||||
|
unsigned long on[8] = { 4326554, 4537114, 4767530, 4972714 };
|
||||||
|
unsigned long off[8] = { 4483146, 4626810, 4661562, 4819642 };
|
||||||
|
|
||||||
|
EasyHomeSwitch(const char* name, const char* id, unsigned long on[4], unsigned long off[4], const char* area = nullptr)
|
||||||
|
: Switch(nullptr, id) {
|
||||||
|
memcpy(&this->on[4], on, 4 * sizeof(unsigned long));
|
||||||
|
memcpy(&this->off[4], off, 4 * sizeof(unsigned long));
|
||||||
|
mainDevice = &DeviceConfig::create(id).withName(name).withManufacturer("Intertek").withModel("Easy Home").withArea(area).withParent(gatewayDevice);
|
||||||
|
withStateTopic();
|
||||||
|
deviceClass = "outlet";
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
onSwitches.insert({ this->on[i], this });
|
||||||
|
offSwitches.insert({ this->off[i], this });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCommand(const char* msg) override {
|
||||||
|
mySwitch.setProtocol(4);
|
||||||
|
strcmp("ON", msg) == 0 ? mySwitch.send(on[4], 24) : mySwitch.send(off[4], 24);
|
||||||
|
publisher(stateTopic, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Command* commands[] = {
|
||||||
|
Builder<Button>::instance(new Button{"Restart", "restart",
|
||||||
|
[](const char* msg) {
|
||||||
|
if (strcmp("PRESS", msg) == 0) ESP.restart();
|
||||||
|
}
|
||||||
|
}).asDevice(gatewayDevice).build(),
|
||||||
|
Builder<Button>::instance(new Button{"Front door", "doorbell_front",
|
||||||
|
[](const char* msg) {
|
||||||
|
if (strcmp("PRESS", msg) == 0) doorbell.ring("00000000110100101000100");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.asDevice(
|
||||||
|
&DeviceConfig::create("doorbell")
|
||||||
|
.withName("Doorbell")
|
||||||
|
.withManufacturer("Thomson")
|
||||||
|
.withModel("Kinetic Halo")
|
||||||
|
.withParent(gatewayDevice)
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
new EasyHomeSwitch{"FritzBox", "easy_home_a", (unsigned long[4]) { 4483136, 4626800, 4661552, 4819632 }, (unsigned long[4]) { 4326544, 4537104, 4767520, 4972704 }, "Basement"},
|
||||||
|
new EasyHomeSwitch{"Outside", "easy_home_b", (unsigned long[4]) { 4483140, 4626804, 4661556, 4819636 }, (unsigned long[4]) { 4326548, 4537108, 4767524, 4972708 }, "Basement"},
|
||||||
|
new PollinSwitch{"Meeting sensor", "00001", 1, "Dining room"},
|
||||||
|
new PollinSwitch{"Fire Tv", "00001", 2, "Living room"},
|
||||||
|
new PollinSwitch{"Diningroom player", "00001", 3, "Dining room"},
|
||||||
|
new PollinSwitch{"Train", "11111", 4, "Playroom"}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sensor* sensors[] = {
|
||||||
|
OilTank::buildRoomSensor("4"),
|
||||||
|
OilTank::buildTankSensor("7")
|
||||||
|
};
|
||||||
96
gateway/include/huzzah.h
Normal file
96
gateway/include/huzzah.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include <TaskScheduler.h>
|
||||||
|
|
||||||
|
#define MQTT_HOST IPAddress(192, 168, 5, 11)
|
||||||
|
#define MQTT_PORT 1883
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
Scheduler ts;
|
||||||
|
|
||||||
|
#include "devices.h"
|
||||||
|
#include "mqtt.h"
|
||||||
|
#include "ota.h"
|
||||||
|
#include "wifi.h"
|
||||||
|
|
||||||
|
namespace Board {
|
||||||
|
|
||||||
|
Task tReadCommand(TASK_IMMEDIATE, TASK_FOREVER, [](){
|
||||||
|
if (serialReader.readLine(Serial) > 0) {
|
||||||
|
char* cmd = serialReader.getBuffer();
|
||||||
|
runJsonCommand(cmd);
|
||||||
|
}
|
||||||
|
}, &ts);
|
||||||
|
|
||||||
|
void turnLed(uint8_t led, bool on = true) {
|
||||||
|
on ? digitalWrite(led, LOW) : digitalWrite(led, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Serial.begin(9600, SERIAL_8N1, SERIAL_TX_ONLY);
|
||||||
|
|
||||||
|
pinMode(RED_LED, OUTPUT);
|
||||||
|
pinMode(BLUE_LED, OUTPUT);
|
||||||
|
turnLed(RED_LED, false);
|
||||||
|
turnLed(BLUE_LED);
|
||||||
|
|
||||||
|
Mqtt::setup(&ts,
|
||||||
|
[] {turnLed(BLUE_LED, false);},
|
||||||
|
[] {turnLed(BLUE_LED);}
|
||||||
|
);
|
||||||
|
Wifi::setup();
|
||||||
|
Ota::setup();
|
||||||
|
|
||||||
|
tReadCommand.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
ts.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSwitches(JsonDocument& jsonDoc) {
|
||||||
|
JsonObjectConst rcSwitch = jsonDoc["rcSwitch"];
|
||||||
|
switch ((unsigned int)rcSwitch["protocol"]) {
|
||||||
|
case 1: {
|
||||||
|
string id = Protocol_1::buildId((const char*)rcSwitch["group"], (int)rcSwitch["channel"]);
|
||||||
|
Ha::Switch* el = p1Switches[id];
|
||||||
|
if (el) el->updateState((bool)rcSwitch["state"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
unsigned long value = rcSwitch["value"];
|
||||||
|
auto range = onSwitches.equal_range(value);
|
||||||
|
for_each(range.first, range.second, [](mapswitches::value_type& x){
|
||||||
|
x.second->updateState(true);
|
||||||
|
});
|
||||||
|
range = offSwitches.equal_range(value);
|
||||||
|
for_each(range.first, range.second, [](mapswitches::value_type& x){
|
||||||
|
x.second->updateState(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSensors(JsonDocument& jsonDoc, char* message) {
|
||||||
|
JsonObjectConst json = jsonDoc["sensor"];
|
||||||
|
string id = to_string((unsigned int)json["id"]);
|
||||||
|
auto sensor = Sensor::mapSensors[id];
|
||||||
|
if (sensor) sensor->updateState(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void publishResponse(JsonDocument& jsonDoc) {
|
||||||
|
char message[255];
|
||||||
|
serializeJson(jsonDoc, message);
|
||||||
|
Mqtt::publish("homeassistant/sensor/rc-gateway/raw", message);
|
||||||
|
if (jsonDoc.containsKey("rcSwitch")) parseSwitches(jsonDoc);
|
||||||
|
if (jsonDoc.containsKey("sensor")) parseSensors(jsonDoc, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleJsonError(JsonDocument& jsonError) {
|
||||||
|
char message[255];
|
||||||
|
serializeJson(jsonError, message);
|
||||||
|
Mqtt::publish("homeassistant/sensor/rc-gateway/raw", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// {"rcSwitch":{"protocol":1,"state":false,"group":"11111","channel":4}}
|
||||||
34
gateway/include/ota.h
Normal file
34
gateway/include/ota.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
|
namespace Ota {
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Task tLoop(TASK_IMMEDIATE, TASK_FOREVER, loop, &ts, true);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
ArduinoOTA.onStart([]() {
|
||||||
|
Serial.println("Starting OTA");
|
||||||
|
Mqtt::publishCleanupConfig();
|
||||||
|
delay(2000);
|
||||||
|
Mqtt::disconnect();
|
||||||
|
});
|
||||||
|
ArduinoOTA.onEnd([]() {
|
||||||
|
Serial.println("\nOTA Finished");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
|
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||||
|
});
|
||||||
|
ArduinoOTA.onError([](ota_error_t error) {
|
||||||
|
Serial.printf("Error[%u]: ", error);
|
||||||
|
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||||
|
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||||
|
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||||
|
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
||||||
|
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
||||||
|
});
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
gateway/include/pins.h
Normal file
12
gateway/include/pins.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
#define SEND_PIN 14
|
||||||
|
#define RECEIVE_PIN 12
|
||||||
|
#define RED_LED LED_BUILTIN
|
||||||
|
#define BLUE_LED 2
|
||||||
|
#else
|
||||||
|
#define RESET_PIN 10
|
||||||
|
#define SEND_PIN 11
|
||||||
|
#define RECEIVE_PIN 2
|
||||||
|
#endif
|
||||||
32
gateway/include/pro-mini.h
Normal file
32
gateway/include/pro-mini.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
namespace Board {
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
digitalWrite(RESET_PIN, HIGH);
|
||||||
|
pinMode(RESET_PIN, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readCommand() {
|
||||||
|
if (serialReader.readLine(Serial) > 0) {
|
||||||
|
char* cmd = serialReader.getBuffer();
|
||||||
|
if (strcmp("reset", cmd) == 0) {
|
||||||
|
Serial.println("resetting...");
|
||||||
|
delay(1200);
|
||||||
|
digitalWrite(RESET_PIN, LOW);
|
||||||
|
Serial.println("resetting failed");
|
||||||
|
}
|
||||||
|
runJsonCommand(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
readCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
void publishResponse(JsonDocument& jsonDoc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleJsonError(JsonDocument& jsonError) {
|
||||||
|
}
|
||||||
|
}
|
||||||
68
gateway/include/wifi.h
Normal file
68
gateway/include/wifi.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266WiFiMulti.h>
|
||||||
|
#include <ESP8266mDNS.h>
|
||||||
|
#include "credentials.h"
|
||||||
|
|
||||||
|
namespace Wifi {
|
||||||
|
|
||||||
|
WiFiEventHandler stationConnectedHandler;
|
||||||
|
WiFiEventHandler stationDisconnectedHandler;
|
||||||
|
ESP8266WiFiMulti wifiMulti;
|
||||||
|
|
||||||
|
String currentSSID;
|
||||||
|
String currentPsk;
|
||||||
|
|
||||||
|
void printStatus();
|
||||||
|
|
||||||
|
Task tReconnect(1 * TASK_MINUTE, TASK_FOREVER, [](){
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
WiFi.forceSleepWake();
|
||||||
|
WiFi.begin(currentSSID.c_str(), currentPsk.c_str());
|
||||||
|
Serial.println("Reconnecting to WiFi netowrk...");
|
||||||
|
}
|
||||||
|
}, &ts);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
stationConnectedHandler = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP& e) {
|
||||||
|
Serial.println("Connected to network.");
|
||||||
|
printStatus();
|
||||||
|
tReconnect.cancel();
|
||||||
|
Ota::tLoop.enable();
|
||||||
|
Mqtt::tReConnect.enable();
|
||||||
|
});
|
||||||
|
|
||||||
|
stationDisconnectedHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& e) {
|
||||||
|
Serial.println("Disconnected from network.");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sizeof(credentials) / sizeof(WifiCredentials); i++) {
|
||||||
|
wifiMulti.addAP(credentials[i].ssid, credentials[i].password);
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.setHostname("rc-gateway");
|
||||||
|
Serial.println("Connecting to WiFi netowrk.");
|
||||||
|
while (wifiMulti.run() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
currentSSID = WiFi.SSID();
|
||||||
|
currentPsk = WiFi.psk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printStatus() {
|
||||||
|
// print the SSID of the network you're attached to:
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WiFi.SSID());
|
||||||
|
|
||||||
|
// print your WiFi shield's IP address:
|
||||||
|
IPAddress ip = WiFi.localIP();
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(ip);
|
||||||
|
|
||||||
|
// print the received signal strength:
|
||||||
|
long rssi = WiFi.RSSI();
|
||||||
|
Serial.print("signal strength (RSSI):");
|
||||||
|
Serial.print(rssi);
|
||||||
|
Serial.println(" dBm");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,54 +9,47 @@
|
|||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
default_envs = pro-mini
|
default_envs = huzzah
|
||||||
|
|
||||||
[env:pro-mini]
|
[env]
|
||||||
platform = atmelavr
|
|
||||||
board = pro16MHzatmega328
|
|
||||||
framework = arduino
|
|
||||||
lib_extra_dirs =
|
lib_extra_dirs =
|
||||||
../libraries
|
../libraries
|
||||||
lib_deps =
|
lib_deps =
|
||||||
sui77/rc-switch@^2.6.3
|
sui77/rc-switch@^2.6.4
|
||||||
bblanchon/ArduinoJson@6.19.4
|
bblanchon/ArduinoJson@6.21.5
|
||||||
adafruit/Adafruit Unified Sensor@^1.1.4
|
adafruit/Adafruit Unified Sensor@^1.1.4
|
||||||
adafruit/DHT sensor library@1.3.10
|
adafruit/DHT sensor library@1.3.2
|
||||||
https://git.hodos.ro/arduino/lib_serial-reader.git@^1.0.0
|
https://git.hodos.ro/arduino/lib_serial-reader.git@^1.0.0
|
||||||
build_flags = -D DHT_SENSOR=0 -D DEBUG_RAW=0
|
build_flags = -D DHT_SENSOR=0 -D DEBUG_RAW=0
|
||||||
upload_port = /dev/ttyUSB0
|
|
||||||
check_tool = cppcheck
|
check_tool = cppcheck
|
||||||
check_flags = --enable=all
|
check_flags = --enable=all
|
||||||
check_skip_packages = yes
|
check_skip_packages = yes
|
||||||
check_severity = medium, high
|
check_severity = medium, high
|
||||||
|
|
||||||
|
[env:huzzah]
|
||||||
|
platform = espressif8266
|
||||||
|
board = huzzah
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
${env.lib_deps}
|
||||||
|
https://git.hodos.ro/arduino/ha-mqtt.git@^1.0.0
|
||||||
|
upload_port = 192.168.6.161
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_flags =
|
||||||
|
--host_port=10000
|
||||||
|
|
||||||
|
[env:huzzah_dev]
|
||||||
|
extends = env:huzzah
|
||||||
|
build_flags = ${env.build_flags} -D MQTT_CLEANUP=1
|
||||||
|
|
||||||
|
[env:pro-mini]
|
||||||
|
platform = atmelavr
|
||||||
|
board = pro16MHzatmega328
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
[env:native]
|
[env:native]
|
||||||
platform = native
|
platform = native
|
||||||
test_filter = native/*
|
test_filter = native/*
|
||||||
lib_extra_dirs =
|
|
||||||
../libraries
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@6.19.4
|
bblanchon/ArduinoJson@6.21.5
|
||||||
build_flags = -std=c++11
|
debug_build_flags = -std=c++11
|
||||||
|
|
||||||
[env:embedded]
|
|
||||||
platform = atmelavr
|
|
||||||
framework = arduino
|
|
||||||
board = pro16MHzatmega328
|
|
||||||
lib_extra_dirs =
|
|
||||||
../libraries
|
|
||||||
lib_deps =
|
|
||||||
sui77/rc-switch@^2.6.3
|
|
||||||
bblanchon/ArduinoJson@6.19.4
|
|
||||||
|
|
||||||
platform_packages =
|
|
||||||
platformio/tool-simavr
|
|
||||||
test_speed = 9600
|
|
||||||
test_testing_command =
|
|
||||||
${platformio.packages_dir}/tool-simavr/bin/simavr
|
|
||||||
-m
|
|
||||||
atmega328p
|
|
||||||
-f
|
|
||||||
16000000L
|
|
||||||
${platformio.build_dir}/${this.__env__}/firmware.elf
|
|
||||||
test_filter = embedded/*
|
|
||||||
|
|||||||
@ -1,49 +1,46 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <RCSwitch.h>
|
#include <RCSwitch.h>
|
||||||
|
#include <SerialReader.h>
|
||||||
|
#include "pins.h"
|
||||||
#include "Dht.h"
|
#include "Dht.h"
|
||||||
#include "Protocol_1.h"
|
#include "Protocol_1.h"
|
||||||
#include "Protocol_2.h"
|
#include "Protocol_2.h"
|
||||||
#include "output.h"
|
#include "Protocol_Doorbell.h"
|
||||||
#include <SerialReader.h>
|
|
||||||
|
|
||||||
#define RESET_PIN 10
|
|
||||||
#define SEND_PIN 11
|
|
||||||
#define RECEIVE_PIN 2
|
|
||||||
|
|
||||||
|
|
||||||
RCSwitch mySwitch;
|
RCSwitch mySwitch;
|
||||||
SerialReader<200> serialReader;
|
SerialReader<200> serialReader;
|
||||||
|
void runJsonCommand(char* cmd);
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
#include "huzzah.h"
|
||||||
|
#else
|
||||||
|
#include "pro-mini.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
digitalWrite(RESET_PIN, HIGH);
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
|
||||||
pinMode(RESET_PIN, OUTPUT);
|
|
||||||
|
|
||||||
mySwitch.enableReceive(digitalPinToInterrupt(RECEIVE_PIN));
|
mySwitch.enableReceive(digitalPinToInterrupt(RECEIVE_PIN));
|
||||||
mySwitch.enableTransmit(SEND_PIN);
|
mySwitch.enableTransmit(SEND_PIN);
|
||||||
mySwitch.setRepeatTransmit(10);
|
mySwitch.setRepeatTransmit(10);
|
||||||
|
|
||||||
Dht::setup();
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
Dht::setup();
|
||||||
|
Board::setup();
|
||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void blink() {
|
|
||||||
digitalWrite(LED_BUILTIN, HIGH);
|
|
||||||
delay(200);
|
|
||||||
digitalWrite(LED_BUILTIN, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
Protocol* findProtocol(unsigned int protocol) {
|
Protocol* findProtocol(unsigned int protocol) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case 1:
|
case PROTOCOL_1:
|
||||||
return new Protocol_1();
|
return &protocol1;
|
||||||
case 2:
|
case PROTOCOL_2:
|
||||||
return new Protocol_2();
|
return &protocol2;
|
||||||
|
case PROTOCOL_13:
|
||||||
|
return &doorbell;
|
||||||
default:
|
default:
|
||||||
return new Protocol(protocol);
|
return &fallbackProtocol.setProtocol(protocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,12 +56,12 @@ void readRcSwitch() {
|
|||||||
mySwitch.resetAvailable();
|
mySwitch.resetAvailable();
|
||||||
|
|
||||||
StaticJsonDocument<128> jsonDoc;
|
StaticJsonDocument<128> jsonDoc;
|
||||||
Protocol* p = findProtocol(mySwitch.getReceivedProtocol());
|
auto p = findProtocol(mySwitch.getReceivedProtocol());
|
||||||
p->toJson(value, jsonDoc);
|
p->toJson(value, jsonDoc);
|
||||||
delete p;
|
|
||||||
if (!jsonDoc.isNull()) {
|
if (!jsonDoc.isNull()) {
|
||||||
serializeJson(jsonDoc, Serial);
|
serializeJson(jsonDoc, Serial);
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
Board::publishResponse(jsonDoc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -77,40 +74,28 @@ void handleJsonError(DeserializationError err, const char* cmd) {
|
|||||||
error["orig_cmd"] = cmd;
|
error["orig_cmd"] = cmd;
|
||||||
serializeJson(jsonError, Serial);
|
serializeJson(jsonError, Serial);
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
Board::handleJsonError(jsonError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runJsonCommand(char* cmd) {
|
void runJsonCommand(char* cmd) {
|
||||||
StaticJsonDocument<50> jsonDoc;
|
StaticJsonDocument<128> jsonDoc;
|
||||||
DeserializationError err = deserializeJson(jsonDoc, cmd);
|
DeserializationError err = deserializeJson(jsonDoc, cmd);
|
||||||
if (err == DeserializationError::Ok) {
|
if (err == DeserializationError::Ok) {
|
||||||
if (jsonDoc.containsKey("rcSwitch")) {
|
if (jsonDoc.containsKey("rcSwitch")) {
|
||||||
JsonObjectConst rcSwitch = jsonDoc["rcSwitch"];
|
JsonObjectConst rcSwitch = jsonDoc["rcSwitch"];
|
||||||
Protocol* p = findProtocol(rcSwitch["protocol"]);
|
auto p = findProtocol(rcSwitch["protocol"]);
|
||||||
p->fromJson(rcSwitch, mySwitch);
|
p->fromJson(rcSwitch, mySwitch);
|
||||||
delete p;
|
|
||||||
serializeJson(jsonDoc, Serial);
|
serializeJson(jsonDoc, Serial);
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
Board::publishResponse(jsonDoc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleJsonError(err, cmd);
|
handleJsonError(err, cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void readCommand() {
|
|
||||||
if (serialReader.readLine(Serial) > 0) {
|
|
||||||
char* cmd = serialReader.getBuffer();
|
|
||||||
if (strcmp("reset", cmd) == 0) {
|
|
||||||
Serial.println("resetting...");
|
|
||||||
delay(1200);
|
|
||||||
digitalWrite(RESET_PIN, LOW);
|
|
||||||
Serial.println("resetting failed");
|
|
||||||
}
|
|
||||||
runJsonCommand(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
readCommand();
|
|
||||||
readRcSwitch();
|
readRcSwitch();
|
||||||
|
Board::loop();
|
||||||
Dht::read();
|
Dht::read();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include <unity.h>
|
|
||||||
|
|
||||||
void setUp(void) {
|
|
||||||
// set stuff up here
|
|
||||||
}
|
|
||||||
|
|
||||||
void tearDown(void) {
|
|
||||||
// clean stuff up here
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
UNITY_BEGIN();
|
|
||||||
UNITY_END();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
}
|
|
||||||
@ -16,6 +16,7 @@
|
|||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
|
"*.yaml": "esphome",
|
||||||
"functional": "cpp",
|
"functional": "cpp",
|
||||||
"queue": "cpp",
|
"queue": "cpp",
|
||||||
"array": "cpp",
|
"array": "cpp",
|
||||||
@ -49,7 +50,8 @@
|
|||||||
"algorithm": "cpp",
|
"algorithm": "cpp",
|
||||||
"iterator": "cpp",
|
"iterator": "cpp",
|
||||||
"memory": "cpp",
|
"memory": "cpp",
|
||||||
"variant": "cpp"
|
"variant": "cpp",
|
||||||
|
"string_view": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user