Version 1.003 Changes

This commit is contained in:
Robert Strouse 2023-01-21 14:56:56 -08:00
parent a63e881a63
commit 72d1cdcccb
15 changed files with 48597 additions and 1691 deletions

View file

@ -166,6 +166,18 @@ bool NTPSettings::apply() {
setenv("TZ", this->posixZone, 1);
return true;
}
WifiSettings::WifiSettings() {
uint32_t chipId = 0;
uint64_t mac = ESP.getEfuseMac();
for(int i=0; i<17; i=i+8) {
chipId |= ((mac >> (40 - i)) & 0xff) << i;
}
snprintf_P(this->serverId, sizeof(this->serverId), "%02X%02X%02X",
(uint16_t)((chipId >> 16) & 0xff),
(uint16_t)((chipId >> 8) & 0xff),
(uint16_t)chipId & 0xff);
}
bool WifiSettings::begin() {
this->load();
return true;

View file

@ -2,7 +2,7 @@
#ifndef configsettings_h
#define configsettings_h
#define FW_VERSION "v0.90.1"
#define FW_VERSION "v1.03.1"
enum DeviceStatus {
DS_OK = 0,
DS_ERROR = 1,
@ -35,6 +35,8 @@ class NTPSettings: BaseSettings {
};
class WifiSettings: BaseSettings {
public:
WifiSettings();
char serverId[10] = "";
char hostname[32] = "";
char ssid[32] = "";
char passphrase[32] = "";

View file

@ -26,6 +26,68 @@ bool MQTTClass::loop() {
mqttClient.loop();
return true;
}
void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
//Serial.print("MQTT Topic:");
//Serial.print(topic);
//Serial.print(" payload:");
//for(uint32_t i=0; i<length; i++)
// Serial.print((char)payload[i]);
//Serial.println();
// We need to start at the last slash in the data
uint16_t ndx = strlen(topic) - 1;
// ------------------+
// shades/1/target/set
while(ndx >= 0 && topic[ndx] != '/') ndx--; // Back off the set command
uint16_t end_command = --ndx;
// --------------+----
// shades/1/target/set
while(ndx >= 0 && topic[ndx] != '/') ndx--; // Get the start of the leaf.
// --------+----------
// shades/1/target/set
uint16_t start_command = ndx + 1;
uint16_t id_end = --ndx;
while(ndx >= 0 && topic[ndx] != '/') ndx--;
// ------+------------
// shades/1/target/set
uint16_t id_start = ndx + 1;
char shadeId[4];
char command[32];
memset(command, 0x00, sizeof(command));
memset(shadeId, 0x00, sizeof(shadeId));
for(uint16_t i = 0;id_start <= id_end; i++)
shadeId[i] = topic[id_start++];
for(uint16_t i = 0;start_command <= end_command; i++)
command[i] = topic[start_command++];
char value[10];
memset(value, 0x00, sizeof(value));
for(uint8_t i = 0; i < length; i++)
value[i] = payload[i];
Serial.print("MQTT Command:[");
Serial.print(command);
Serial.print("] shadeId:");
Serial.print(shadeId);
Serial.print(" value:");
Serial.println(value);
SomfyShade* shade = somfy.getShadeById(atoi(shadeId));
if (shade) {
int val = atoi(value);
if(strncmp(command, "target", sizeof(command)) == 0) {
if(val >= 0 && val <= 100)
shade->moveToTarget(atoi(value));
}
else if(strncmp(command, "direction", sizeof(command)) == 0) {
if(val < 0)
shade->sendCommand(somfy_commands::Up);
else if(val > 0)
shade->sendCommand(somfy_commands::Down);
else
shade->sendCommand(somfy_commands::My);
}
}
}
bool MQTTClass::connect() {
if(mqttClient.connected()) {
if(!settings.MQTT.enabled)
@ -42,6 +104,9 @@ bool MQTTClass::connect() {
Serial.print("Successfully connected MQTT client ");
Serial.println(this->clientId);
somfy.publish();
this->subscribe("shades/+/target/set");
this->subscribe("shades/+/direction/set");
mqttClient.setCallback(MQTTClass::receive);
return true;
}
else {
@ -56,10 +121,36 @@ bool MQTTClass::connect() {
}
bool MQTTClass::disconnect() {
if(mqttClient.connected()) {
this->unsubscribe("shades/+/target/set");
this->unsubscribe("shades/+/direction/set");
mqttClient.disconnect();
}
return true;
}
bool MQTTClass::unsubscribe(const char *topic) {
if(mqttClient.connected()) {
char top[64];
if(strlen(settings.MQTT.rootTopic) > 0)
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else
strlcpy(top, topic, sizeof(top));
return mqttClient.unsubscribe(top);
}
return true;
}
bool MQTTClass::subscribe(const char *topic) {
if(mqttClient.connected()) {
char top[64];
if(strlen(settings.MQTT.rootTopic) > 0)
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else
strlcpy(top, topic, sizeof(top));
Serial.print("MQTT Subscribed to:");
Serial.println(top);
return mqttClient.subscribe(top);
}
return true;
}
bool MQTTClass::publish(const char *topic, const char *payload) {
if(mqttClient.connected()) {
char top[64];

4
MQTT.h
View file

@ -20,6 +20,10 @@ class MQTTClass {
bool publish(const char *topic, int8_t val);
bool publish(const char *topic, uint32_t val);
bool publish(const char *topic, uint16_t val);
bool subscribe(const char *topic);
bool unsubscribe(const char *topic);
static void receive(const char *topic, byte *payload, uint32_t length);
};
#endif

View file

@ -45,9 +45,14 @@ void Network::loop() {
}
void Network::emitSockets() {
if(WiFi.status() == WL_CONNECTED) {
char buf[50];
sprintf(buf, "{\"ssid\":\"%s\", \"strength\":%d, \"channel\":%d}", WiFi.SSID(), WiFi.RSSI(), WiFi.channel());
sockEmit.sendToClients("wifiStrength", buf);
if(abs(abs(WiFi.RSSI()) - abs(this->lastRSSI)) > 2 || WiFi.channel() != this->lastChannel) {
char buf[50];
sprintf(buf, "{\"ssid\":\"%s\", \"strength\":%d, \"channel\":%d}", WiFi.SSID(), WiFi.RSSI(), WiFi.channel());
sockEmit.sendToClients("wifiStrength", buf);
this->lastRSSI = WiFi.RSSI();
this->lastChannel = WiFi.channel();
}
}
else
sockEmit.sendToClients("wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}");
@ -88,29 +93,32 @@ void Network::setConnected() {
SSDP.setHTTPPort(80);
SSDP.setSchemaURL(0, "upnp.xml");
SSDP.setChipId(0, this->getChipId());
SSDP.setDeviceType(0, "urn:schemas-rstrouse-org:device:SomfyServer:1");
SSDP.setDeviceType(0, "urn:schemas-rstrouse-org:device:ESPSomfyRTS:1");
SSDP.setName(0, settings.WIFI.hostname);
//SSDP.setSerialNumber(0, "C2496952-5610-47E6-A968-2FC19737A0DB");
//SSDP.setUUID(0, settings.uuid);
SSDP.setModelName(0, "Somfy Server");
SSDP.setModelName(0, "ESPSomfy RTS");
SSDP.setModelNumber(0, "SS v1");
SSDP.setModelURL(0, "https://github.com/rstrouse/ESP32-somfyServer");
SSDP.setModelURL(0, "https://github.com/rstrouse/ESPSomfy-RTS");
SSDP.setManufacturer(0, "rstrouse");
SSDP.setManufacturerURL(0, "https://github.com/rstrouse");
SSDP.setURL(0, "/");
if(MDNS.begin(settings.WIFI.hostname)) {
Serial.println(F("MDNS Responder Started"));
MDNS.addService("_http", "_tcp", 80);
MDNS.addServiceTxt("_http", "_tcp", "board", "ESP32");
//MDNS.addServiceTxt("_osc", "_udp", "board", settings.WIFI.hostname);
Serial.printf("MDNS Responder Started: serverId=%s\n", settings.WIFI.serverId);
MDNS.addService("http", "tcp", 80);
MDNS.addServiceTxt("http", "tcp", "board", "ESP32");
MDNS.addServiceTxt("http", "tcp", "model", "ESPSomfyRTS");
MDNS.addService("espsomfy_rts", "tcp", 8080);
MDNS.addServiceTxt("espsomfy_rts", "tcp", "serverId", String(settings.WIFI.serverId));
MDNS.addServiceTxt("espsomfy_rts", "tcp", "model", "ESPSomfyRTS");
MDNS.addServiceTxt("espsomfy_rts", "tcp", "version", String(settings.fwVersion));
}
if(settings.WIFI.ssdpBroadcast) {
if( SSDP.begin()) Serial.println("SSDP Client Started...");
if(SSDP.begin()) Serial.println("SSDP Client Started...");
}
else if(SSDP.isStarted) SSDP.end();
//digitalWrite(LED_BUILTIN, HIGH);
this->emitSockets();
}
bool Network::connect() {
@ -243,7 +251,7 @@ bool Network::openSoftAP() {
WiFi.disconnect(true);
WiFi.mode(WIFI_AP_STA);
delay(100);
WiFi.softAP("Somfy Controller", "");
WiFi.softAP("ESPSomfy RTS", "");
Serial.println("Initializing AP for credentials modification");
Serial.println();
Serial.print("SoftAP IP: ");
@ -251,6 +259,7 @@ bool Network::openSoftAP() {
pinMode(D0, INPUT_PULLUP);
long startTime = millis();
int c = 0;
while ((WiFi.status() != WL_CONNECTED))
{
for(int i = 0; i < 3; i++) {

View file

@ -5,6 +5,8 @@
class Network {
protected:
unsigned long lastEmit = 0;
int lastRSSI = 0;
int lastChannel = 0;
public:
String ssid;
String mac;

View file

@ -3,9 +3,12 @@
#include <WebSocketsServer.h>
#include "Sockets.h"
#include "ConfigSettings.h"
#include "Somfy.h"
extern ConfigSettings settings;
extern SomfyShadeController somfy;
WebSocketsServer sockServer = WebSocketsServer(8080);
char g_buffer[1024];
@ -62,7 +65,7 @@ void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t
// Send all the current Sensor readings to the client.
sockServer.sendTXT(num, "Connected");
settings.emitSockets();
//settings.Inputs.emitSocket(num);
somfy.emitState(num);
}
break;
case WStype_TEXT:

489
Somfy.cpp
View file

@ -22,6 +22,14 @@ extern MQTTClass mqtt;
#define RECEIVE_ATTR
#endif
int sort_asc(const void *cmp1, const void *cmp2) {
int a = *((uint8_t *)cmp1);
int b = *((uint8_t *)cmp2);
if(a == b) return 0;
else if(a < b) return -1;
return 1;
}
static int interruptPin = 0;
static uint8_t bit_length = 56;
somfy_commands translateSomfyCommand(const String& string) {
@ -141,7 +149,9 @@ void somfy_frame_t::decodeFrame(byte* frame) {
Serial.print(" DECCS:");
Serial.println(this->checksum);
*/
Serial.print("ADDR:");
Serial.print("KEY:");
Serial.print(this->encKey);
Serial.print(" ADDR:");
Serial.print(this->remoteAddress);
Serial.print(" CMD:");
Serial.print(translateSomfyCommand(this->cmd));
@ -150,7 +160,9 @@ void somfy_frame_t::decodeFrame(byte* frame) {
}
else {
Serial.print("INVALID FRAME ");
Serial.print("ADDR:");
Serial.print("KEY:");
Serial.print(this->encKey);
Serial.print(" ADDR:");
Serial.print(this->remoteAddress);
Serial.print(" CMD:");
Serial.print(translateSomfyCommand(this->cmd));
@ -180,26 +192,26 @@ void somfy_frame_t::decodeFrame(byte* frame) {
Serial.println();
}
}
void somfy_frame_t::encodeFrame(const uint32_t address, const somfy_commands cmd, const uint16_t rcode, byte* frame) {
const byte btn = static_cast<byte>(cmd);
frame[0] = 0xA7; // Encryption key. Doesn't matter much
frame[1] = btn << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = rcode >> 8; // Rolling code (big endian)
frame[3] = rcode; // Rolling code
frame[4] = address >> 16; // Remote address
frame[5] = address >> 8; // Remote address
frame[6] = address; // Remote address
byte checksum = 0;
for (byte i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
// Checksum integration
frame[1] |= checksum;
// Obfuscation: a XOR of all the bytes
for (byte i = 1; i < 7; i++) {
frame[i] ^= frame[i - 1];
}
void somfy_frame_t::encodeFrame(byte *frame) {
const byte btn = static_cast<byte>(cmd);
frame[0] = this->encKey; // Encryption key. Doesn't matter much
frame[1] = btn << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = this->rollingCode >> 8; // Rolling code (big endian)
frame[3] = this->rollingCode; // Rolling code
frame[4] = this->remoteAddress >> 16; // Remote address
frame[5] = this->remoteAddress >> 8; // Remote address
frame[6] = this->remoteAddress; // Remote address
byte checksum = 0;
for (byte i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
// Checksum integration
frame[1] |= checksum;
// Obfuscation: a XOR of all the bytes
for (byte i = 1; i < 7; i++) {
frame[i] ^= frame[i - 1];
}
}
void somfy_frame_t::print() {
Serial.println("----------- Receiving -------------");
@ -223,7 +235,7 @@ void SomfyShadeController::end() { this->transceiver.disableReceive(); }
SomfyShadeController::SomfyShadeController() {
memset(this->m_shadeIds, 255, sizeof(this->m_shadeIds));
uint64_t mac = ESP.getEfuseMac();
this->startingAddress = mac & 0xFFFF00;
this->startingAddress = mac & 0x0FFFFF;
}
SomfyShade *SomfyShadeController::findShadeByRemoteAddress(uint32_t address) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
@ -239,18 +251,32 @@ SomfyShade *SomfyShadeController::findShadeByRemoteAddress(uint32_t address) {
}
bool SomfyShadeController::begin() {
// Load up all the configuration data.
Serial.printf("sizeof(SomfyShade) = %d\n", sizeof(SomfyShade));
pref.begin("Shades");
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
pref.end();
this->transceiver.begin();
sortArray(this->m_shadeIds, sizeof(this->m_shadeIds));
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
if(i != 0) Serial.print(",");
Serial.print(this->m_shadeIds[i]);
}
Serial.println();
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
if(i != 0) Serial.print(",");
Serial.print(this->m_shadeIds[i]);
}
Serial.println();
uint8_t id = 0;
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
if(this->m_shadeIds[i] == id) this->m_shadeIds[i] = 255;
id = this->m_shadeIds[i];
SomfyShade *shade = &this->shades[i];
shade->setShadeId(id);
if(id == 255) continue;
if(id == 255) {
continue;
}
shade->load();
}
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
@ -355,6 +381,18 @@ void SomfyShade::checkMovement() {
}
}
this->position = floor(this->currentPos * 100);
if(this->seekingPos && this->position >= this->target) {
Serial.print("Stopping Shade:");
Serial.print(this->name);
Serial.print(" at ");
Serial.print(this->position);
Serial.print("% target ");
Serial.print(this->target);
Serial.println("%");
this->sendCommand(somfy_commands::My);
this->seekingPos = false;
}
}
}
else if(this->direction < 0) {
@ -383,6 +421,17 @@ void SomfyShade::checkMovement() {
}
}
this->position = floor(this->currentPos * 100);
if(this->seekingPos && this->position <= this->target) {
Serial.print("Stopping Shade:");
Serial.print(this->name);
Serial.print(" at ");
Serial.print(this->position);
Serial.print("% target ");
Serial.print(this->target);
Serial.println("%");
this->sendCommand(somfy_commands::My);
this->seekingPos = false;
}
}
if(currDir != this->direction || currPos != this->position) {
// We need to emit on the socket that our state has changed.
@ -402,6 +451,7 @@ void SomfyShade::checkMovement() {
void SomfyShade::load() {
char shadeKey[15];
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
memset(linkedAddresses, 0x00, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
// Now load up each of the shades into memory.
Serial.print("key:");
@ -414,6 +464,7 @@ void SomfyShade::load() {
this->setRemoteAddress(pref.getULong("remoteAddress", 0));
this->currentPos = pref.getFloat("currentPos", 0);
this->position = (uint8_t)floor(this->currentPos * 100);
this->target = this->position;
pref.getBytes("linkedAddr", linkedAddresses, sizeof(linkedAddresses));
pref.end();
Serial.print("shadeId:");
@ -424,7 +475,6 @@ void SomfyShade::load() {
Serial.print(this->getRemoteAddress());
Serial.print(" position:");
Serial.println(this->position);
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
SomfyLinkedRemote &lremote = this->linkedRemotes[j];
lremote.setRemoteAddress(linkedAddresses[j]);
@ -434,12 +484,6 @@ void SomfyShade::load() {
}
}
void SomfyShade::emitConfig() {
DynamicJsonDocument doc(256);
JsonObject obj = doc.to<JsonObject>();
this->toJSON(obj);
sockEmit.sendToClients("somfyShade", obj);
}
void SomfyShade::publish() {
if(mqtt.connected()) {
char topic[32];
@ -459,12 +503,14 @@ void SomfyShade::publish() {
mqtt.publish(topic, this->lastRollingCode);
}
}
void SomfyShade::emitState() {
char buf[200];
void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); }
void SomfyShade::emitState(uint8_t num, const char *evt) {
char buf[220];
char shadeKey[15];
snprintf(shadeKey, sizeof(shadeKey), "Shade_%u", this->shadeId);
sprintf(buf, "{\"shadeId\":%d, \"remoteAddress\":%d, \"name\":\"%s\", \"direction\":%d, \"position\":%d}", this->shadeId, this->getRemoteAddress(), this->name, this->direction, this->position);
sockEmit.sendToClients("somfyShadeState", buf);
sprintf(buf, "{\"shadeId\":%d, \"remoteAddress\":%d, \"name\":\"%s\", \"direction\":%d, \"position\":%d, \"target\":%d}", this->shadeId, this->getRemoteAddress(), this->name, this->direction, this->position, this->target);
if(num >= 255) sockEmit.sendToClients(evt, buf);
else sockEmit.sendToClient(num, evt, buf);
if(mqtt.connected()) {
char topic[32];
snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId);
@ -477,7 +523,7 @@ void SomfyShade::emitState() {
mqtt.publish(topic, this->lastRollingCode);
}
}
void SomfyShade::processFrame(somfy_frame_t &frame) {
void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
bool hasRemote = this->getRemoteAddress() == frame.remoteAddress;
if(!hasRemote) {
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
@ -490,14 +536,18 @@ void SomfyShade::processFrame(somfy_frame_t &frame) {
}
if(!hasRemote) return;
int8_t dir = 0;
// If the frame came from the radio it cannot be seeing a position. This means that the target will be set.
if(!internal) this->seekingPos = false;
// At this point we are not processing the combo buttons
// will need to see what the shade does when you press both.
switch(frame.cmd) {
case somfy_commands::Up:
dir = -1;
if(!internal) this->target = 0;
break;
case somfy_commands::Down:
dir = 1;
if(!internal) this->target = 100;
break;
default:
dir = 0;
@ -512,6 +562,7 @@ void SomfyShade::setMovement(int8_t dir) {
this->startPos = this->currentPos;
this->moveStart = 0;
this->direction = dir;
this->emitState();
}
else if(this->direction != dir) {
this->moveStart = millis();
@ -523,6 +574,39 @@ void SomfyShade::setMovement(int8_t dir) {
this->emitState();
}
}
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
if(cmd == somfy_commands::Up) {
this->target = 0;
this->seekingPos = false;
}
else if(cmd == somfy_commands::Down) {
this->target = 100;
this->seekingPos = false;
}
else if(cmd == somfy_commands::My) {
this->target = this->position;
this->seekingPos = false;
}
SomfyRemote::sendCommand(cmd, repeat);
}
void SomfyShade::moveToTarget(uint8_t target) {
int8_t newDir = 0;
somfy_commands cmd = somfy_commands::My;
if(target < this->position)
cmd = somfy_commands::Up;
else if(target > this->position)
cmd = somfy_commands::Down;
Serial.print("Moving to ");
Serial.print(target);
Serial.print("% from ");
Serial.print(this->position);
Serial.print("% using ");
Serial.println(translateSomfyCommand(cmd));
this->target = target;
this->seekingPos = true;
SomfyRemote::sendCommand(cmd);
}
bool SomfyShade::save() {
char shadeKey[15];
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
@ -564,10 +648,10 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
return true;
}
bool SomfyShade::toJSON(JsonObject &obj) {
Serial.print("Serializing Shade:");
Serial.print(this->getShadeId());
Serial.print(" ");
Serial.println(this->name);
//Serial.print("Serializing Shade:");
//Serial.print(this->getShadeId());
//Serial.print(" ");
//Serial.println(this->name);
obj["shadeId"] = this->getShadeId();
obj["name"] = this->name;
obj["remoteAddress"] = this->m_remoteAddress;
@ -577,6 +661,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
obj["remotePrefId"] = this->getRemotePrefId();
obj["lastRollingCode"] = this->lastRollingCode;
obj["position"] = this->position;
obj["target"] = this->target;
SomfyRemote::toJSON(obj);
JsonArray arr = obj.createNestedArray("linkedRemotes");
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
@ -596,9 +681,16 @@ bool SomfyRemote::toJSON(JsonObject &obj) {
}
void SomfyRemote::setRemoteAddress(uint32_t address) { this->m_remoteAddress = address; snprintf(this->m_remotePrefId, sizeof(this->m_remotePrefId), "_%lu", (unsigned long)this->m_remoteAddress); }
uint32_t SomfyRemote::getRemoteAddress() { return this->m_remoteAddress; }
void SomfyShadeController::processFrame(somfy_frame_t &frame) {
void SomfyShadeController::processFrame(somfy_frame_t &frame, bool internal) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++)
this->shades[i].processFrame(frame);
this->shades[i].processFrame(frame, internal);
}
void SomfyShadeController::emitState(uint8_t num) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade *shade = &this->shades[i];
if(shade->getShadeId() == 255) continue;
shade->emitState(num);
}
}
void SomfyShadeController::publish() {
StaticJsonDocument<128> doc;
@ -612,15 +704,26 @@ void SomfyShadeController::publish() {
mqtt.publish("shades", arr);
}
uint8_t SomfyShadeController::getNextShadeId() {
uint8_t maxId = 0;
uint8_t lastId = 0;
sortArray(this->m_shadeIds, sizeof(this->m_shadeIds));
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
uint8_t id = this->m_shadeIds[i];
if(id >= 255) continue;
maxId = max(lastId, id);
uint8_t nextId = 0;
// There is no shortcut for this since the deletion of
// a shade in the middle makes all of this very difficult.
for(uint8_t i = 1; i < SOMFY_MAX_SHADES - 1; i++) {
bool id_exists = false;
for(uint8_t j = 0; j < SOMFY_MAX_SHADES; j++) {
SomfyShade *shade = &this->shades[j];
if(shade->getShadeId() == i) {
id_exists = true;
break;
}
}
if(!id_exists) {
Serial.print("Got next Shade Id:");
Serial.print(i);
return i;
}
}
return maxId + 1;
return 255;
}
uint8_t SomfyShadeController::shadeCount() {
uint8_t count = 0;
@ -629,164 +732,103 @@ uint8_t SomfyShadeController::shadeCount() {
}
return count;
}
uint32_t SomfyShadeController::getNextRemoteAddress(uint8_t shadeId) {
uint32_t address = this->startingAddress + shadeId;
uint8_t i = 0;
while(i < SOMFY_MAX_SHADES) {
if(this->shades[i].getShadeId() != 255) {
if(this->shades[i].getRemoteAddress() == address) {
address++;
i = 0; // Start over we cannot share addresses.
}
else i++;
}
else i++;
}
return address;
}
SomfyShade *SomfyShadeController::addShade(JsonObject &obj) {
SomfyShade *shade = this->addShade();
if(shade) {
shade->fromJSON(obj);
shade->save();
shade->emitState("shadeAdded");
}
return shade;
}
SomfyShade *SomfyShadeController::addShade() {
uint8_t shadeId = getNextShadeId();
uint8_t shadeId = this->getNextShadeId();
SomfyShade *shade = nullptr;
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
if(this->shades[i].getShadeId() == 255)
if(this->shades[i].getShadeId() == 255) {
shade = &this->shades[i];
break;
}
}
if(shade) {
shade->setShadeId(shadeId);
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
if(this->m_shadeIds[i] == 255) this->m_shadeIds[i] = shadeId;
this->m_shadeIds[i] = this->shades[i].getShadeId();
}
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
uint8_t id = 0;
// Eliminate the duplicates.
// This little diddy is about a bug I had previously that left duplicates in the
// sorted array. So we will walk the sorted array until we hit a duplicate where the previous
// value == the current value. Set it to 255 then sort the array again.
// 1,1,2,2,3,3,255...
bool hadDups = false;
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
if(id == this->m_shadeIds[i]) this->m_shadeIds[i] = 255;
if(this->m_shadeIds[i] == 255) break;
if(id == this->m_shadeIds[i]) {
id = this->m_shadeIds[i];
this->m_shadeIds[i] = 255;
hadDups = true;
}
else {
id = this->m_shadeIds[i];
}
}
sortArray(this->m_shadeIds, sizeof(this->m_shadeIds));
if(hadDups) sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
pref.begin("Shades");
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
pref.end();
}
return shade;
}
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
somfy_frame_t frame;
frame.rollingCode = this->getNextRollingCode();
frame.remoteAddress = this->getRemoteAddress();
frame.cmd = cmd;
somfy.sendFrame(frame, repeat);
somfy.processFrame(frame, true);
}
void SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat) {
somfy.transceiver.beginTransmit();
uint16_t rcode = this->getNextRollingCode();
Serial.println("------------- Sending -------------");
//Serial.println("----------- Sending Raw -------------");
Serial.print("CMD:");
Serial.print(translateSomfyCommand(cmd));
Serial.print(translateSomfyCommand(frame.cmd));
Serial.print(" ADDR:");
Serial.print(this->getRemoteAddress());
Serial.print(frame.remoteAddress);
Serial.print(" RCODE:");
Serial.println(rcode);
Serial.print(frame.rollingCode);
Serial.print(" REPEAT:");
Serial.println(repeat);
byte frame[7];
this->encodeFrame(frame, cmd, rcode);
this->sendFrame(frame, 2);
byte frm[10];
frame.encodeFrame(frm);
this->transceiver.sendFrame(frm, 2);
for(uint8_t i = 0; i < repeat; i++) {
sendFrame(frame, 7);
this->transceiver.sendFrame(frm, 7);
}
somfy.transceiver.endTransmit();
somfy_frame_t rx;
this->decodeFrame(frame, &rx);
//rx.print();
somfy.processFrame(rx);
}
void SomfyRemote::decodeFrame(byte *frame, somfy_frame_t *rx) {
byte decoded[7];
decoded[0] = frame[0];
for (byte i = 1; i < 7; i++) {
decoded[i] = frame[i] ^ frame[i-1];
}
byte checksum = 0;
// We only want the upper nibble for the command byte.
for (byte i = 0; i < 7; i++) {
if(i == 1) checksum = checksum ^ (decoded[i] >> 4);
else checksum = checksum ^ decoded[i] ^ (decoded[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
rx->checksum = decoded[1] & 0b1111;
rx->encKey = decoded[0];
rx->cmd = (somfy_commands)(decoded[1] >> 4);
rx->rollingCode = decoded[3] + (decoded[2] << 8);
rx->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
rx->valid = rx->checksum == checksum;
Serial.println(" KEY 1 2 3 4 5 6 ");
Serial.println("--------------------------------");
Serial.print("ENC ");
for(byte i = 0; i < 7; i++) {
if(frame[i] < 10)
Serial.print(" ");
else if(frame[i] < 100)
Serial.print(" ");
Serial.print(frame[i]);
Serial.print(" ");
}
Serial.println();
Serial.print("DEC ");
for(byte i = 0; i < 7; i++) {
if(decoded[i] < 10)
Serial.print(" ");
else if(decoded[i] < 100)
Serial.print(" ");
Serial.print(decoded[i]);
Serial.print(" ");
}
Serial.println();
Serial.print("VALID:");
Serial.print(rx->valid ? "true" : "false");
Serial.print(" ENCCS:");
Serial.print(checksum);
Serial.print(" DECCS:");
Serial.println(rx->checksum);
}
void SomfyRemote::encodeFrame(byte *frame, somfy_commands cmd, uint16_t rcode) {
const byte btn = static_cast<byte>(cmd);
const uint32_t address = this->getRemoteAddress();
frame[0] = 0xA7; // Encryption key. Doesn't matter much
frame[1] = btn << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = rcode >> 8; // Rolling code (big endian)
frame[3] = rcode; // Rolling code
frame[4] = address >> 16; // Remote address
frame[5] = address >> 8; // Remote address
frame[6] = address; // Remote address
byte checksum = 0;
for (byte i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
// Checksum integration
frame[1] |= checksum;
// Obfuscation: a XOR of all the bytes
for (byte i = 1; i < 7; i++) {
frame[i] ^= frame[i - 1];
}
}
void SomfyRemote::sendFrame(byte *frame, byte sync) {
if (sync == 2) { // Only with the first frame.
// Wake-up pulse & Silence
this->sendHigh(9415);
this->sendLow(9565);
delay(80);
}
// Hardware sync: two sync for the first frame, seven for the following ones.
for (int i = 0; i < sync; i++) {
this->sendHigh(4 * SYMBOL);
this->sendLow(4 * SYMBOL);
}
// Software sync
this->sendHigh(4550);
this->sendLow(SYMBOL);
// Data: bits are sent one by one, starting with the MSB.
for (byte i = 0; i < bit_length; i++) {
if (((frame[i / 8] >> (7 - (i % 8))) & 1) == 1) {
this->sendLow(SYMBOL);
this->sendHigh(SYMBOL);
} else {
this->sendHigh(SYMBOL);
this->sendLow(SYMBOL);
}
}
// Inter-frame silence
this->sendLow(415);
delay(30);
}
void SomfyRemote::sendHigh(uint16_t durationInMicroseconds) {
digitalWrite(somfy.transceiver.config.TXPin, HIGH);
delayMicroseconds(durationInMicroseconds);
}
void SomfyRemote::sendLow(uint16_t durationInMicroseconds) {
digitalWrite(somfy.transceiver.config.TXPin, LOW);
delayMicroseconds(durationInMicroseconds);
this->transceiver.endTransmit();
}
bool SomfyShadeController::deleteShade(uint8_t shadeId) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
if(this->shades[i].getShadeId() == shadeId) {
shades[i].emitState("shadeRemoved");
this->shades[i].setShadeId(255);
}
}
@ -795,7 +837,8 @@ bool SomfyShadeController::deleteShade(uint8_t shadeId) {
this->m_shadeIds[i] = 255;
}
}
sortArray(this->m_shadeIds, sizeof(this->m_shadeIds));
//qsort(this->m_shadeIds, sizeof(this->m_shadeIds)/sizeof(this->m_shadeIds[0]), sizeof(this->m_shadeIds[0]), sort_asc);
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
pref.begin("Shades");
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
pref.end();
@ -819,7 +862,6 @@ uint16_t SomfyRemote::setRollingCode(uint16_t code) {
return code;
}
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
Serial.println("Setting Controller Json");
doc["maxShades"] = SOMFY_MAX_SHADES;
doc["maxLinkedRemotes"] = SOMFY_MAX_LINKED_REMOTES;
doc["startingAddress"] = this->startingAddress;
@ -836,13 +878,25 @@ bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
return true;
}
bool SomfyShadeController::toJSON(JsonObject &obj) {
Serial.print("Setting Transceiver Json ");
obj["maxShades"] = SOMFY_MAX_SHADES;
obj["maxLinkedRemotes"] = SOMFY_MAX_LINKED_REMOTES;
obj["startingAddress"] = this->startingAddress;
JsonObject oradio = obj.createNestedObject("transceiver");
this->transceiver.toJSON(oradio);
JsonArray arr = obj.createNestedArray("shades");
this->toJSON(arr);
/*
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade &shade = this->shades[i];
if(shade.getShadeId() != 255) {
JsonObject oshade = arr.createNestedObject();
shade.toJSON(oshade);
}
}
*/
return true;
}
bool SomfyShadeController::toJSON(JsonArray &arr) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade &shade = this->shades[i];
if(shade.getShadeId() != 255) {
@ -852,6 +906,7 @@ bool SomfyShadeController::toJSON(JsonObject &obj) {
}
return true;
}
void SomfyShadeController::loop() {
this->transceiver.loop();
for(uint8_t i; i < SOMFY_MAX_SHADES; i++) {
@ -894,6 +949,51 @@ static struct somfy_rx_t
} somfy_rx;
uint8_t receive_buffer[10]; // 80 bits
bool packet_received = false;
void Transceiver::sendFrame(byte *frame, uint8_t sync) {
uint32_t pin = 1 << this->config.TXPin;
if (sync == 2) { // Only with the first frame.
// Wake-up pulse & Silence
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
delayMicroseconds(9415);
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(9565);
delay(80);
}
// Hardware sync: two sync for the first frame, seven for the following ones.
for (int i = 0; i < sync; i++) {
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
delayMicroseconds(4 * SYMBOL);
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(4 * SYMBOL);
}
// Software sync
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
delayMicroseconds(4450);
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(SYMBOL);
// Data: bits are sent one by one, starting with the MSB.
// TODO: Handle the 80-bit send protocol
for (byte i = 0; i < bit_length; i++) {
if (((frame[i / 8] >> (7 - (i % 8))) & 1) == 1) {
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(SYMBOL);
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
delayMicroseconds(SYMBOL);
//this->sendLow(SYMBOL);
//this->sendHigh(SYMBOL);
} else {
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
delayMicroseconds(SYMBOL);
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(SYMBOL);
//this->sendHigh(SYMBOL);
//this->sendLow(SYMBOL);
}
}
// Inter-frame silence
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
delayMicroseconds(30415);
}
void RECEIVE_ATTR Transceiver::handleReceive() {
static unsigned long last_time = 0;
@ -909,19 +1009,13 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
case waiting_synchro:
if (duration > tempo_synchro_hw_min && duration < tempo_synchro_hw_max) {
// We have found a hardware sync bit. There should be at least 4 of these.
// The original code sets gpio 2 on and off but only when in debug mode. I suspect this was to flash an LED
// to see the tempo.
//SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
++somfy_rx.cpt_synchro_hw;
//CLR_TP1
}
else if (duration > tempo_synchro_sw_min && duration < tempo_synchro_sw_max && somfy_rx.cpt_synchro_hw >= 4) {
// If we have a full hardware sync then we should look for the software sync. If we have a software sync
// bit and enough hardware sync bits then we should start receiving data.
//SET_TP1 //WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
memset(&somfy_rx, 0x00, sizeof(somfy_rx));
somfy_rx.status = receiving_data;
}
@ -933,14 +1027,12 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
case receiving_data:
// We should be receiving data at this point.
if (duration > tempo_symbol_min && duration < tempo_symbol_max && !somfy_rx.waiting_half_symbol) {
//SET_TP1
somfy_rx.previous_bit = 1 - somfy_rx.previous_bit;
// Bits come in high order bit first.
somfy_rx.payload[somfy_rx.cpt_bits / 8] += somfy_rx.previous_bit << (7 - somfy_rx.cpt_bits % 8);
++somfy_rx.cpt_bits;
}
else if (duration > tempo_half_symbol_min && duration < tempo_half_symbol_max) {
//SET_TP1 WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
if (somfy_rx.waiting_half_symbol) {
somfy_rx.waiting_half_symbol = false;
somfy_rx.payload[somfy_rx.cpt_bits / 8] += somfy_rx.previous_bit << (7 - somfy_rx.cpt_bits % 8);
@ -955,11 +1047,9 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
somfy_rx.cpt_synchro_hw = 0;
somfy_rx.status = waiting_synchro;
}
//CLR_TP1
break;
default:
break;
}
if (somfy_rx.status == receiving_data && somfy_rx.cpt_bits == bit_length) {
// The original code posted the task so that it would pick it up immediately.
@ -991,8 +1081,6 @@ void Transceiver::enableReceive(void) {
interruptPin = digitalPinToInterrupt(this->config.RXPin);
ELECHOUSE_cc1101.SetRx();
attachInterrupt(interruptPin, handleReceive, CHANGE);
Serial.print("Hooked interrupt #");
Serial.println(interruptPin);
}
void Transceiver::disableReceive(void) { detachInterrupt(interruptPin); }
bool Transceiver::toJSON(JsonObject& obj) {
@ -1172,13 +1260,19 @@ void transceiver_config_t::apply() {
bit_length = this->type;
Serial.print("Applying radio settings ");
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
ELECHOUSE_cc1101.setGDO(this->RXPin, this->TXPin);
Serial.println("Set GDO");
ELECHOUSE_cc1101.setSpiPin(this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
Serial.println("Set SPI");
ELECHOUSE_cc1101.Init();
Serial.println("Initialized");
ELECHOUSE_cc1101.setMHZ(this->frequency); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet.
Serial.println("Set Frequency");
ELECHOUSE_cc1101.setRxBW(this->rxBandwidth); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz.
Serial.println("Set RxBW");
ELECHOUSE_cc1101.setPA(this->txPower); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max!
//ELECHOUSE_cc1101.setCCMode(this->internalCCMode); // set config for internal transmission mode.
ELECHOUSE_cc1101.setCCMode(this->internalCCMode); // set config for internal transmission mode.
ELECHOUSE_cc1101.setModulation(this->modulationMode); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
/*
ELECHOUSE_cc1101.setDeviation(this->deviation); // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz.
@ -1219,15 +1313,16 @@ bool Transceiver::begin() {
void Transceiver::loop() {
if (this->receive()) {
this->clearReceived();
somfy.processFrame(this->frame);
somfy.processFrame(this->frame, false);
char buf[128];
sprintf(buf, "{\"address\":%d, \"rcode\":%d, \"command\":\"%s\"}", this->frame.remoteAddress, this->frame.rollingCode, translateSomfyCommand(this->frame.cmd));
sprintf(buf, "{\"encKey\":%d, \"address\":%d, \"rcode\":%d, \"command\":\"%s\"}", this->frame.encKey, this->frame.remoteAddress, this->frame.rollingCode, translateSomfyCommand(this->frame.cmd));
sockEmit.sendToClients("remoteFrame", buf);
}
}
somfy_frame_t& Transceiver::lastFrame() { return this->frame; }
void Transceiver::beginTransmit() {
this->disableReceive();
pinMode(this->config.TXPin, OUTPUT);
ELECHOUSE_cc1101.SetTx();
}
void Transceiver::endTransmit() {

45
Somfy.h
View file

@ -1,8 +1,8 @@
#ifndef SOMFY_H
#define SOMFY_H
#define SOMFY_MAX_SHADES 5
#define SOMFY_MAX_LINKED_REMOTES 2
#define SOMFY_MAX_SHADES 32
#define SOMFY_MAX_LINKED_REMOTES 5
enum class somfy_commands : byte {
My = 0x1,
@ -25,11 +25,11 @@ typedef struct somfy_frame_t {
somfy_commands cmd;
uint32_t remoteAddress = 0;
uint16_t rollingCode = 0;
uint8_t encKey = 0;
uint8_t encKey = 0xA7;
uint8_t checksum = 0;
bool valid = false;
void print();
void encodeFrame(const uint32_t address, const somfy_commands cmd, const uint16_t rcode, byte* frame);
void encodeFrame(byte *frame);
void decodeFrame(byte* frame);
};
@ -40,11 +40,6 @@ class SomfyRemote {
protected:
char m_remotePrefId[10] = "";
uint32_t m_remoteAddress = 0;
void encodeFrame(byte *frame, somfy_commands cmd, uint16_t rcode);
void decodeFrame(byte *frame, somfy_frame_t *decoded);
void sendFrame(byte *frame, byte sync);
void sendHigh(uint16_t durationInMicroseconds);
void sendLow(uint16_t durationInMicroseconds);
public:
char *getRemotePrefId() {return m_remotePrefId;}
virtual bool toJSON(JsonObject &obj);
@ -53,7 +48,7 @@ class SomfyRemote {
virtual uint16_t getNextRollingCode();
virtual uint16_t setRollingCode(uint16_t code);
uint16_t lastRollingCode = 0;
void sendCommand(somfy_commands, uint8_t repeat = 4);
virtual void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
};
class SomfyLinkedRemote : public SomfyRemote {
public:
@ -64,6 +59,7 @@ class SomfyShade : public SomfyRemote {
uint8_t shadeId = 255;
uint64_t moveStart = 0;
float startPos = 0.0;
bool seekingPos = false;
public:
void load();
float currentPos = 0.0;
@ -75,19 +71,22 @@ class SomfyShade : public SomfyRemote {
bool paired = false;
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj) override;
char name[20] = "";
char name[21] = "";
void setShadeId(uint8_t id) { shadeId = id; }
uint8_t getShadeId() { return shadeId; }
uint16_t upTime = 10000;
uint16_t downTime = 1000;
bool save();
void checkMovement();
void processFrame(somfy_frame_t &frame);
void processFrame(somfy_frame_t &frame, bool internal = false);
void setMovement(int8_t dir);
void setTarget(uint8_t target);
void moveToTarget(uint8_t target);
void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
bool unlinkRemote(uint32_t remoteAddress);
void emitState();
void emitConfig();
void emitState(const char *evt = "shadeState");
void emitState(uint8_t num, const char *evt = "shadeState");
void publish();
};
@ -95,8 +94,8 @@ typedef struct transceiver_config_t {
bool printBuffer = false;
uint8_t type = 56; // 56 or 80 bit protocol.
uint8_t SCKPin = 18;
uint8_t TXPin = 6;
uint8_t RXPin = 4;
uint8_t TXPin = 12;
uint8_t RXPin = 13;
uint8_t MOSIPin = 23;
uint8_t MISOPin = 19;
uint8_t CSNPin = 5;
@ -108,8 +107,8 @@ typedef struct transceiver_config_t {
float channelSpacing = 199.95; // Channel spacing in multiplied by the channel number and added to the base frequency in kHz. 25.39 to 405.45. Default 199.95
float rxBandwidth = 812.5; // Receive bandwidth in kHz. Value from 58.03 to 812.50. Default is 99.97kHz.
float dataRate = 99.97; // The data rate in kBaud. 0.02 to 1621.83 Default is 99.97.
int8_t txPower = 12; // Transmission power {-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12}. Default is 12.
uint8_t syncMode = 2; // 0=No preamble/sync,
int8_t txPower = 10; // Transmission power {-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12}. Default is 12.
uint8_t syncMode = 0; // 0=No preamble/sync,
// 1=16 sync word bits detected,
// 2=16/16 sync words bits detected.
// 3=30/32 sync word bits detected,
@ -177,29 +176,35 @@ class Transceiver {
void enableReceive();
void disableReceive();
somfy_frame_t& lastFrame();
void sendFrame(byte *frame, uint8_t sync);
void beginTransmit();
void endTransmit();
};
class SomfyShadeController {
protected:
uint8_t m_shadeIds[SOMFY_MAX_SHADES];
uint8_t getNextShadeId();
public:
uint32_t startingAddress;
uint8_t getNextShadeId();
uint32_t getNextRemoteAddress(uint8_t shadeId);
SomfyShadeController();
Transceiver transceiver;
SomfyShade *addShade();
SomfyShade *addShade(JsonObject &obj);
bool deleteShade(uint8_t shadeId);
bool begin();
void loop();
void end();
SomfyShade shades[SOMFY_MAX_SHADES];
bool toJSON(DynamicJsonDocument &doc);
bool toJSON(JsonArray &arr);
bool toJSON(JsonObject &obj);
uint8_t shadeCount();
SomfyShade * getShadeById(uint8_t shadeId);
SomfyShade * findShadeByRemoteAddress(uint32_t address);
void processFrame(somfy_frame_t &frame);
void sendFrame(somfy_frame_t &frame, uint8_t repeats = 0);
void processFrame(somfy_frame_t &frame, bool internal = false);
void emitState(uint8_t num = 255);
void publish();
};

1521
Web.cpp

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,23 @@
}
</style>
<script type="text/javascript">
let runRepair = false;
function runRemoteRepair() {
let obj = { address: parseInt(document.getElementById('fldRepairAddress').value, 10), command: 'Down', rcode: parseInt(document.getElementById('fldRepairRCode').value, 10), repeats: 1 };
if (runRepair) {
// Call the service.
console.log(obj);
putJSON('/sendRemoteCommand', obj, (err, ret) => {
console.log(ret);
document.getElementById('fldRepairRCode').value = obj.rcode + 1;
setTimeout(() => { runRemoteRepair() }, 1000);
});
}
}
function stopRemoteRepair() {
runRepair = false;
}
let baseUrl = ''; //'http://192.168.1.204'; This is the current server.
Number.prototype.round = function (dec) { return Number(Math.round(this + 'e' + dec) + 'e-' + dec); };
@ -378,6 +395,16 @@
</script>
</head>
<body>
<div syle="white-space:nowrap;">
<label for="repairAddress">Repair Address</label>
<input id="fldRepairAddress" name="repairAddress" type="number" style="margin-right: 10px; text-align: right; display: inline-block; width: 127px;" value="4624451" />
<label for="repairRCode">Rolling Code</label>
<input id="fldRepairRCode" name="repairRCode" type="number" style="margin-right:10px;text-align:right;display:inline-block;width:127px;" value="1641" />
</div>
<div class="button-container">
<button id="btnRepairRemote" style="width:127px" onclick="runRepair = true; runRemoteRepair();">Repair Remote</button>
<button id="btnStopRepairRemote" style="width:127px" onclick="stopRemoteRepair();">Stop Repair</button>
</div>
<div id="divFields"></div>
<div class="button-container">
<button id="btnSaveSetting" type="button" style="display:inline-block;width:44%" onclick="resetDefaults();">

File diff suppressed because it is too large Load diff

View file

@ -512,3 +512,42 @@ div.waitoverlay > .lds-roller {
display: block;
overflow: hidden;
}
.somfyShadeCtl {
height:60px;
border-bottom:dotted 2px gainsboro;
position:relative;
}
.shade-positioner {
position:absolute;
width:100%;
background-color:gainsboro;
color:gray;
height:60px;
top:0px;
padding-left:7px;
padding-right:7px;
}
.shade-positioner .shade-name {
display:block;
font-size:22px;
width:100%;
margin-top:-1px;
}
.shade-positioner input[type=range] {
width:100%;
margin-top:0px;
margin-bottom:0px;
}
.shade-positioner label {
display:block;
font-size:1em;
margin-top:-3px;
margin-left:27px;
}
.shade-positioner label > span:last-child {
float: right;
margin-right: 7px;
}
.shade-positioner label .shade-target {
display:inline-block;
}

14
debug.cfg Normal file
View file

@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for ESP32-WROVER-KIT board.
#
# For example, OpenOCD can be started for ESP32 debugging on
#
# openocd -f board/esp32-wrover-kit-3.3v.cfg
#
# Source the JTAG interface configuration file
source [find interface/ftdi/esp32_devkitj_v1.cfg]
set ESP32_FLASH_VOLTAGE 3.3
# Source the ESP32 configuration file
source [find target/esp32.cfg]

46087
esp32.svd Normal file

File diff suppressed because it is too large Load diff