стабилизация кода
This commit is contained in:
39
Devices/main-module/src/Config.h
Normal file
39
Devices/main-module/src/Config.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "HalConfigBase.h"
|
||||
|
||||
class Config : public HalConfigBase
|
||||
{
|
||||
private:
|
||||
public:
|
||||
int cnt = 123;
|
||||
|
||||
inline String getFileName()
|
||||
{
|
||||
return F("/config.json");
|
||||
}
|
||||
|
||||
String convertToString()
|
||||
{
|
||||
String config = "";
|
||||
StaticJsonDocument<1024> doc;
|
||||
//JsonObject json = doc.createNestedObject();
|
||||
_toJson(doc);
|
||||
doc["cnt"] = cnt;
|
||||
serializeJson(doc, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
bool convertFromString(String json)
|
||||
{
|
||||
StaticJsonDocument<1024> doc;
|
||||
DeserializationError error = deserializeJson(doc, json);
|
||||
if (error)
|
||||
return false;
|
||||
cnt = doc[0]["cnt"];
|
||||
_fromJson(doc);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
1299
Devices/main-module/src/FastBot.h
Normal file
1299
Devices/main-module/src/FastBot.h
Normal file
File diff suppressed because it is too large
Load Diff
46
Devices/main-module/src/HalConfigBase.h
Normal file
46
Devices/main-module/src/HalConfigBase.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
class HalConfigBase
|
||||
{
|
||||
public:
|
||||
String version;
|
||||
String ssid;
|
||||
String pass;
|
||||
String ssidAP;
|
||||
String passAP;
|
||||
String updateUrl;
|
||||
String token;
|
||||
String chatId;
|
||||
|
||||
// void ToJson(JsonObject &json);
|
||||
// void FromJson(JsonObject &json);
|
||||
protected:
|
||||
void _toJson(JsonDocument &json)
|
||||
{
|
||||
json["version"] = version;
|
||||
json["ssid"] = ssid;
|
||||
json["pass"] = pass;
|
||||
json["ssidAP"] = ssidAP;
|
||||
json["passAP"] = passAP;
|
||||
json["updateUrl"] = updateUrl;
|
||||
json["token"] = token;
|
||||
json["chatId"] = chatId;
|
||||
}
|
||||
|
||||
void _fromJson(JsonDocument &json)
|
||||
{
|
||||
version = json[0]["version"].as<String>();
|
||||
ssid = json[0]["ssid"].as<String>();
|
||||
pass = json[0]["pass"].as<String>();
|
||||
ssidAP = json[0]["ssidAP"].as<String>();
|
||||
passAP = json[0]["passAP"].as<String>();
|
||||
updateUrl = json[0]["updateUrl"].as<String>();
|
||||
token = json[0]["token"].as<String>();
|
||||
chatId = json[0]["chatId"].as<String>();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
200
Devices/main-module/src/HalRelays.h
Normal file
200
Devices/main-module/src/HalRelays.h
Normal file
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "pca9554.h"
|
||||
#include "config.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#define i2c_address 0x20
|
||||
|
||||
#define CHANNELS 8
|
||||
#define SWITCH_DELAY 500
|
||||
|
||||
struct RelItem
|
||||
{
|
||||
unsigned long Code = 0;
|
||||
bool State = false;
|
||||
u8 Mode = 0;
|
||||
unsigned long Delay = 0;
|
||||
};
|
||||
|
||||
class HalRelays
|
||||
{
|
||||
private:
|
||||
PCA9554 io;
|
||||
|
||||
inline String _configFileName() { return F("/relay.json"); }
|
||||
|
||||
bool (*_saveFunction)(String, String){};
|
||||
String (*_loadFunction)(String){};
|
||||
|
||||
String _convertToString()
|
||||
{
|
||||
String config = "";
|
||||
StaticJsonDocument<512> doc;
|
||||
for (byte i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
JsonObject jo = doc.createNestedObject();
|
||||
jo["Code"] = Channels[i].Code;
|
||||
jo["Mode"] = Channels[i].Mode;
|
||||
jo["State"] = Channels[i].State;
|
||||
}
|
||||
serializeJson(doc, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
bool _convertFromString(String config_str)
|
||||
{
|
||||
// StaticJsonDocument<512> doc;
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, config_str);
|
||||
if (error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (byte i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
Channels[i].Code = doc[i]["Code"];
|
||||
Channels[i].Mode = doc[i]["Mode"];
|
||||
Channels[i].State = doc[i]["State"];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _switch(u8 number)
|
||||
{
|
||||
if (millis() - Channels[number].Delay > SWITCH_DELAY)
|
||||
{
|
||||
Serial.print("RELAY [" + (String)number + "] set ");
|
||||
// bool value = io.digitalRead(number);
|
||||
if (Channels[number].State == true)
|
||||
{
|
||||
off(number);
|
||||
Channels[number].State = false;
|
||||
Serial.println("OFF");
|
||||
}
|
||||
else
|
||||
{
|
||||
on(number);
|
||||
Channels[number].State = true;
|
||||
Serial.println("ON");
|
||||
}
|
||||
Channels[number].Delay = millis();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
RelItem Channels[CHANNELS];
|
||||
|
||||
HalRelays(byte sda, byte scl, bool (*saveFunction)(String, String), String (*loadFunction)(String))
|
||||
{
|
||||
io = PCA9554(i2c_address, sda, scl);
|
||||
_saveFunction = saveFunction;
|
||||
_loadFunction = loadFunction;
|
||||
}
|
||||
|
||||
bool cmd(unsigned long code)
|
||||
{
|
||||
for (byte i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
if (code == Channels[i].Code)
|
||||
{
|
||||
_switch(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Serial.println("Code [" + (String)code + "] not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
void switchState(u8 number)
|
||||
{
|
||||
_switch(number);
|
||||
}
|
||||
|
||||
void printChannels()
|
||||
{
|
||||
Serial.println("CHANNELS");
|
||||
for (byte i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
Serial.println((String)i + " :: " + (String)Channels[i].Code);
|
||||
}
|
||||
}
|
||||
|
||||
String printChannelsToString()
|
||||
{
|
||||
String str = "CHANNELS";
|
||||
for (byte i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
str += ((String)i + " :: " + (String)Channels[i].Code);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
String config_str = _convertToString();
|
||||
if (!_saveFunction(config_str, _configFileName()))
|
||||
{
|
||||
Serial.println(F("RELAYS SAVE :: ERROR"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("RELAYS SAVE :: OK"));
|
||||
printChannels();
|
||||
}
|
||||
|
||||
bool load()
|
||||
{
|
||||
String configStr = _loadFunction(_configFileName());
|
||||
Serial.println("DBG");
|
||||
Serial.println(configStr);
|
||||
Serial.println("DBG");
|
||||
if (!_convertFromString(configStr))
|
||||
{
|
||||
Serial.println(F("RELAYS LOAD :: ERROR"));
|
||||
return false;
|
||||
}
|
||||
Serial.println(F("RELAYS LOAD :: OK"));
|
||||
printChannels();
|
||||
return true;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
// io.portMode(ALLINPUT);
|
||||
// offAll();
|
||||
io.portMode(ALLOUTPUT);
|
||||
offAll();
|
||||
|
||||
if (!load())
|
||||
{
|
||||
Serial.println(F("Relay ERROR: config not load."));
|
||||
}
|
||||
}
|
||||
|
||||
void on(u8 number)
|
||||
{
|
||||
io.digitalWrite(number, HIGH);
|
||||
}
|
||||
|
||||
void onAll()
|
||||
{
|
||||
io.digitalWritePort(0xFF);
|
||||
// for (u8 i = 0; i < 8; i++)
|
||||
// io.digitalWrite(i, HIGH);
|
||||
}
|
||||
|
||||
void off(u8 number)
|
||||
{
|
||||
io.digitalWrite(number, LOW);
|
||||
}
|
||||
|
||||
void offAll()
|
||||
{
|
||||
io.digitalWritePort(0x0);
|
||||
// for (u8 i = 0; i < 8; i++)
|
||||
// io.digitalWrite(i, LOW);
|
||||
}
|
||||
};
|
||||
79
Devices/main-module/src/HalTimer.h
Normal file
79
Devices/main-module/src/HalTimer.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
class HalTimer
|
||||
{
|
||||
private:
|
||||
unsigned long _period;
|
||||
unsigned long _last_millis;
|
||||
bool _init_first;
|
||||
void (*_function)(){};
|
||||
bool _isRun = false;
|
||||
|
||||
public:
|
||||
HalTimer()
|
||||
{
|
||||
}
|
||||
|
||||
HalTimer(unsigned long period)
|
||||
{
|
||||
_period = period;
|
||||
}
|
||||
|
||||
HalTimer(unsigned long period, void (*function)())
|
||||
{
|
||||
_period = period;
|
||||
_function = function;
|
||||
_init_first = true;
|
||||
_last_millis = millis();
|
||||
}
|
||||
|
||||
HalTimer(unsigned long period, void (*function)(), bool autostart)
|
||||
{
|
||||
_period = period;
|
||||
_function = function;
|
||||
_init_first = true;
|
||||
_last_millis = millis();
|
||||
if (autostart)
|
||||
start();
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
_isRun = true;
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
_isRun = false;
|
||||
}
|
||||
|
||||
void tick()
|
||||
{
|
||||
if (!_isRun)
|
||||
return;
|
||||
|
||||
if (millis() - _last_millis > _period)
|
||||
{
|
||||
if (_init_first)
|
||||
_last_millis = millis();
|
||||
_function();
|
||||
if (!_init_first)
|
||||
_last_millis = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void begin(unsigned long period, void (*function)(), bool init_first)
|
||||
{
|
||||
_period = period;
|
||||
begin(function, init_first);
|
||||
}
|
||||
|
||||
void begin(void (*function)(), bool init_first)
|
||||
{
|
||||
_function = function;
|
||||
_init_first = init_first;
|
||||
_last_millis = millis();
|
||||
}
|
||||
};
|
||||
701
Devices/main-module/src/RCSwitch.cpp
Normal file
701
Devices/main-module/src/RCSwitch.cpp
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
RCSwitch - Arduino libary for remote control outlet switches
|
||||
Copyright (c) 2011 Suat Özgür. All right reserved.
|
||||
|
||||
Contributors:
|
||||
- Andre Koehler / info(at)tomate-online(dot)de
|
||||
- Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
|
||||
- Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46
|
||||
- Dominik Fischer / dom_fischer(at)web(dot)de
|
||||
- Frank Oltmanns / <first name>.<last name>(at)gmail(dot)com
|
||||
- Andreas Steinel / A.<lastname>(at)gmail(dot)com
|
||||
- Max Horn / max(at)quendi(dot)de
|
||||
- Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
|
||||
- Johann Richard / <first name>.<last name>(at)gmail(dot)com
|
||||
- Vlad Gheorghe / <first name>.<last name>(at)gmail(dot)com https://github.com/vgheo
|
||||
|
||||
Project home: https://github.com/sui77/rc-switch/
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "RCSwitch.h"
|
||||
|
||||
#ifdef RaspberryPi
|
||||
// PROGMEM and _P functions are for AVR based microprocessors,
|
||||
// so we must normalize these for the ARM processor:
|
||||
#define PROGMEM
|
||||
#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
// interrupt handler and related code must be in RAM on ESP8266,
|
||||
// according to issue #46.
|
||||
#define RECEIVE_ATTR ICACHE_RAM_ATTR
|
||||
#else
|
||||
#define RECEIVE_ATTR
|
||||
#endif
|
||||
|
||||
|
||||
/* Format for protocol definitions:
|
||||
* {pulselength, Sync bit, "0" bit, "1" bit}
|
||||
*
|
||||
* pulselength: pulse length in microseconds, e.g. 350
|
||||
* Sync bit: {1, 31} means 1 high pulse and 31 low pulses
|
||||
* (perceived as a 31*pulselength long pulse, total length of sync bit is
|
||||
* 32*pulselength microseconds), i.e:
|
||||
* _
|
||||
* | |_______________________________ (don't count the vertical bars)
|
||||
* "0" bit: waveform for a data bit of value "0", {1, 3} means 1 high pulse
|
||||
* and 3 low pulses, total length (1+3)*pulselength, i.e:
|
||||
* _
|
||||
* | |___
|
||||
* "1" bit: waveform for a data bit of value "1", e.g. {3,1}:
|
||||
* ___
|
||||
* | |_
|
||||
*
|
||||
* These are combined to form Tri-State bits when sending or receiving codes.
|
||||
*/
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
static const RCSwitch::Protocol proto[] = {
|
||||
#else
|
||||
static const RCSwitch::Protocol PROGMEM proto[] = {
|
||||
#endif
|
||||
{ 350, { 1, 31 }, { 1, 3 }, { 3, 1 }, false }, // protocol 1
|
||||
{ 650, { 1, 10 }, { 1, 2 }, { 2, 1 }, false }, // protocol 2
|
||||
{ 100, { 30, 71 }, { 4, 11 }, { 9, 6 }, false }, // protocol 3
|
||||
{ 380, { 1, 6 }, { 1, 3 }, { 3, 1 }, false }, // protocol 4
|
||||
{ 500, { 6, 14 }, { 1, 2 }, { 2, 1 }, false }, // protocol 5
|
||||
{ 450, { 23, 1 }, { 1, 2 }, { 2, 1 }, true }, // protocol 6 (HT6P20B)
|
||||
{ 150, { 2, 62 }, { 1, 6 }, { 6, 1 }, false } // protocol 7 (HS2303-PT, i. e. used in AUKEY Remote)
|
||||
};
|
||||
|
||||
enum {
|
||||
numProto = sizeof(proto) / sizeof(proto[0])
|
||||
};
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
volatile unsigned long RCSwitch::nReceivedValue = 0;
|
||||
volatile unsigned int RCSwitch::nReceivedBitlength = 0;
|
||||
volatile unsigned int RCSwitch::nReceivedDelay = 0;
|
||||
volatile unsigned int RCSwitch::nReceivedProtocol = 0;
|
||||
int RCSwitch::nReceiveTolerance = 60;
|
||||
const unsigned int RCSwitch::nSeparationLimit = 4300;
|
||||
// separationLimit: minimum microseconds between received codes, closer codes are ignored.
|
||||
// according to discussion on issue #14 it might be more suitable to set the separation
|
||||
// limit to the same time as the 'low' part of the sync signal for the current protocol.
|
||||
unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES];
|
||||
#endif
|
||||
|
||||
RCSwitch::RCSwitch() {
|
||||
this->nTransmitterPin = -1;
|
||||
this->setRepeatTransmit(10);
|
||||
this->setProtocol(1);
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
this->nReceiverInterrupt = -1;
|
||||
this->setReceiveTolerance(60);
|
||||
RCSwitch::nReceivedValue = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the protocol to send.
|
||||
*/
|
||||
void RCSwitch::setProtocol(Protocol protocol) {
|
||||
this->protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the protocol to send, from a list of predefined protocols
|
||||
*/
|
||||
void RCSwitch::setProtocol(int nProtocol) {
|
||||
if (nProtocol < 1 || nProtocol > numProto) {
|
||||
nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ???
|
||||
}
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
this->protocol = proto[nProtocol-1];
|
||||
#else
|
||||
memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the protocol to send with pulse length in microseconds.
|
||||
*/
|
||||
void RCSwitch::setProtocol(int nProtocol, int nPulseLength) {
|
||||
setProtocol(nProtocol);
|
||||
this->setPulseLength(nPulseLength);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets pulse length in microseconds
|
||||
*/
|
||||
void RCSwitch::setPulseLength(int nPulseLength) {
|
||||
this->protocol.pulseLength = nPulseLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Repeat Transmits
|
||||
*/
|
||||
void RCSwitch::setRepeatTransmit(int nRepeatTransmit) {
|
||||
this->nRepeatTransmit = nRepeatTransmit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Receiving Tolerance
|
||||
*/
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
void RCSwitch::setReceiveTolerance(int nPercent) {
|
||||
RCSwitch::nReceiveTolerance = nPercent;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Enable transmissions
|
||||
*
|
||||
* @param nTransmitterPin Arduino Pin to which the sender is connected to
|
||||
*/
|
||||
void RCSwitch::enableTransmit(int nTransmitterPin) {
|
||||
this->nTransmitterPin = nTransmitterPin;
|
||||
pinMode(this->nTransmitterPin, OUTPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable transmissions
|
||||
*/
|
||||
void RCSwitch::disableTransmit() {
|
||||
this->nTransmitterPin = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch on (Type D REV)
|
||||
*
|
||||
* @param sGroup Code of the switch group (A,B,C,D)
|
||||
* @param nDevice Number of the switch itself (1..3)
|
||||
*/
|
||||
void RCSwitch::switchOn(char sGroup, int nDevice) {
|
||||
this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch off (Type D REV)
|
||||
*
|
||||
* @param sGroup Code of the switch group (A,B,C,D)
|
||||
* @param nDevice Number of the switch itself (1..3)
|
||||
*/
|
||||
void RCSwitch::switchOff(char sGroup, int nDevice) {
|
||||
this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch on (Type C Intertechno)
|
||||
*
|
||||
* @param sFamily Familycode (a..f)
|
||||
* @param nGroup Number of group (1..4)
|
||||
* @param nDevice Number of device (1..4)
|
||||
*/
|
||||
void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) {
|
||||
this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch off (Type C Intertechno)
|
||||
*
|
||||
* @param sFamily Familycode (a..f)
|
||||
* @param nGroup Number of group (1..4)
|
||||
* @param nDevice Number of device (1..4)
|
||||
*/
|
||||
void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) {
|
||||
this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch on (Type B with two rotary/sliding switches)
|
||||
*
|
||||
* @param nAddressCode Number of the switch group (1..4)
|
||||
* @param nChannelCode Number of the switch itself (1..4)
|
||||
*/
|
||||
void RCSwitch::switchOn(int nAddressCode, int nChannelCode) {
|
||||
this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch off (Type B with two rotary/sliding switches)
|
||||
*
|
||||
* @param nAddressCode Number of the switch group (1..4)
|
||||
* @param nChannelCode Number of the switch itself (1..4)
|
||||
*/
|
||||
void RCSwitch::switchOff(int nAddressCode, int nChannelCode) {
|
||||
this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead!
|
||||
* Switch a remote switch on (Type A with 10 pole DIP switches)
|
||||
*
|
||||
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
* @param nChannelCode Number of the switch itself (1..5)
|
||||
*/
|
||||
void RCSwitch::switchOn(const char* sGroup, int nChannel) {
|
||||
const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
|
||||
this->switchOn(sGroup, code[nChannel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead!
|
||||
* Switch a remote switch off (Type A with 10 pole DIP switches)
|
||||
*
|
||||
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
* @param nChannelCode Number of the switch itself (1..5)
|
||||
*/
|
||||
void RCSwitch::switchOff(const char* sGroup, int nChannel) {
|
||||
const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
|
||||
this->switchOff(sGroup, code[nChannel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch on (Type A with 10 pole DIP switches)
|
||||
*
|
||||
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
* @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
*/
|
||||
void RCSwitch::switchOn(const char* sGroup, const char* sDevice) {
|
||||
this->sendTriState( this->getCodeWordA(sGroup, sDevice, true) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a remote switch off (Type A with 10 pole DIP switches)
|
||||
*
|
||||
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
* @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
||||
*/
|
||||
void RCSwitch::switchOff(const char* sGroup, const char* sDevice) {
|
||||
this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a char[13], representing the code word to be send.
|
||||
*
|
||||
*/
|
||||
char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) {
|
||||
static char sReturn[13];
|
||||
int nReturnPos = 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0';
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0';
|
||||
}
|
||||
|
||||
sReturn[nReturnPos++] = bStatus ? '0' : 'F';
|
||||
sReturn[nReturnPos++] = bStatus ? 'F' : '0';
|
||||
|
||||
sReturn[nReturnPos] = '\0';
|
||||
return sReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoding for type B switches with two rotary/sliding switches.
|
||||
*
|
||||
* The code word is a tristate word and with following bit pattern:
|
||||
*
|
||||
* +-----------------------------+-----------------------------+----------+------------+
|
||||
* | 4 bits address | 4 bits address | 3 bits | 1 bit |
|
||||
* | switch group | switch number | not used | on / off |
|
||||
* | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 |
|
||||
* +-----------------------------+-----------------------------+----------+------------+
|
||||
*
|
||||
* @param nAddressCode Number of the switch group (1..4)
|
||||
* @param nChannelCode Number of the switch itself (1..4)
|
||||
* @param bStatus Whether to switch on (true) or off (false)
|
||||
*
|
||||
* @return char[13], representing a tristate code word of length 12
|
||||
*/
|
||||
char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) {
|
||||
static char sReturn[13];
|
||||
int nReturnPos = 0;
|
||||
|
||||
if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F';
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F';
|
||||
}
|
||||
|
||||
sReturn[nReturnPos++] = 'F';
|
||||
sReturn[nReturnPos++] = 'F';
|
||||
sReturn[nReturnPos++] = 'F';
|
||||
|
||||
sReturn[nReturnPos++] = bStatus ? 'F' : '0';
|
||||
|
||||
sReturn[nReturnPos] = '\0';
|
||||
return sReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like getCodeWord (Type C = Intertechno)
|
||||
*/
|
||||
char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) {
|
||||
static char sReturn[13];
|
||||
int nReturnPos = 0;
|
||||
|
||||
int nFamily = (int)sFamily - 'a';
|
||||
if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// encode the family into four bits
|
||||
sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0';
|
||||
|
||||
// encode the device and group
|
||||
sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0';
|
||||
sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0';
|
||||
|
||||
// encode the status code
|
||||
sReturn[nReturnPos++] = '0';
|
||||
sReturn[nReturnPos++] = 'F';
|
||||
sReturn[nReturnPos++] = 'F';
|
||||
sReturn[nReturnPos++] = bStatus ? 'F' : '0';
|
||||
|
||||
sReturn[nReturnPos] = '\0';
|
||||
return sReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoding for the REV Switch Type
|
||||
*
|
||||
* The code word is a tristate word and with following bit pattern:
|
||||
*
|
||||
* +-----------------------------+-------------------+----------+--------------+
|
||||
* | 4 bits address | 3 bits address | 3 bits | 2 bits |
|
||||
* | switch group | device number | not used | on / off |
|
||||
* | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 |
|
||||
* +-----------------------------+-------------------+----------+--------------+
|
||||
*
|
||||
* Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/
|
||||
*
|
||||
* @param sGroup Name of the switch group (A..D, resp. a..d)
|
||||
* @param nDevice Number of the switch itself (1..3)
|
||||
* @param bStatus Whether to switch on (true) or off (false)
|
||||
*
|
||||
* @return char[13], representing a tristate code word of length 12
|
||||
*/
|
||||
char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) {
|
||||
static char sReturn[13];
|
||||
int nReturnPos = 0;
|
||||
|
||||
// sGroup must be one of the letters in "abcdABCD"
|
||||
int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A';
|
||||
if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F';
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F';
|
||||
}
|
||||
|
||||
sReturn[nReturnPos++] = '0';
|
||||
sReturn[nReturnPos++] = '0';
|
||||
sReturn[nReturnPos++] = '0';
|
||||
|
||||
sReturn[nReturnPos++] = bStatus ? '1' : '0';
|
||||
sReturn[nReturnPos++] = bStatus ? '0' : '1';
|
||||
|
||||
sReturn[nReturnPos] = '\0';
|
||||
return sReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sCodeWord a tristate code word consisting of the letter 0, 1, F
|
||||
*/
|
||||
void RCSwitch::sendTriState(const char* sCodeWord) {
|
||||
// turn the tristate code word into the corresponding bit pattern, then send it
|
||||
unsigned long code = 0;
|
||||
unsigned int length = 0;
|
||||
for (const char* p = sCodeWord; *p; p++) {
|
||||
code <<= 2L;
|
||||
switch (*p) {
|
||||
case '0':
|
||||
// bit pattern 00
|
||||
break;
|
||||
case 'F':
|
||||
// bit pattern 01
|
||||
code |= 1L;
|
||||
break;
|
||||
case '1':
|
||||
// bit pattern 11
|
||||
code |= 3L;
|
||||
break;
|
||||
}
|
||||
length += 2;
|
||||
}
|
||||
this->send(code, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sCodeWord a binary code word consisting of the letter 0, 1
|
||||
*/
|
||||
void RCSwitch::send(const char* sCodeWord) {
|
||||
// turn the tristate code word into the corresponding bit pattern, then send it
|
||||
unsigned long code = 0;
|
||||
unsigned int length = 0;
|
||||
for (const char* p = sCodeWord; *p; p++) {
|
||||
code <<= 1L;
|
||||
if (*p != '0')
|
||||
code |= 1L;
|
||||
length++;
|
||||
}
|
||||
this->send(code, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit the first 'length' bits of the integer 'code'. The
|
||||
* bits are sent from MSB to LSB, i.e., first the bit at position length-1,
|
||||
* then the bit at position length-2, and so on, till finally the bit at position 0.
|
||||
*/
|
||||
void RCSwitch::send(unsigned long code, unsigned int length) {
|
||||
if (this->nTransmitterPin == -1)
|
||||
return;
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
// make sure the receiver is disabled while we transmit
|
||||
int nReceiverInterrupt_backup = nReceiverInterrupt;
|
||||
if (nReceiverInterrupt_backup != -1) {
|
||||
this->disableReceive();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) {
|
||||
for (int i = length-1; i >= 0; i--) {
|
||||
if (code & (1L << i))
|
||||
this->transmit(protocol.one);
|
||||
else
|
||||
this->transmit(protocol.zero);
|
||||
}
|
||||
this->transmit(protocol.syncFactor);
|
||||
}
|
||||
|
||||
// Disable transmit after sending (i.e., for inverted protocols)
|
||||
digitalWrite(this->nTransmitterPin, LOW);
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
// enable receiver again if we just disabled it
|
||||
if (nReceiverInterrupt_backup != -1) {
|
||||
this->enableReceive(nReceiverInterrupt_backup);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit a single high-low pulse.
|
||||
*/
|
||||
void RCSwitch::transmit(HighLow pulses) {
|
||||
uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? LOW : HIGH;
|
||||
uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? HIGH : LOW;
|
||||
|
||||
digitalWrite(this->nTransmitterPin, firstLogicLevel);
|
||||
delayMicroseconds( this->protocol.pulseLength * pulses.high);
|
||||
digitalWrite(this->nTransmitterPin, secondLogicLevel);
|
||||
delayMicroseconds( this->protocol.pulseLength * pulses.low);
|
||||
}
|
||||
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
/**
|
||||
* Enable receiving data
|
||||
*/
|
||||
void RCSwitch::enableReceive(int interrupt) {
|
||||
this->nReceiverInterrupt = interrupt;
|
||||
this->enableReceive();
|
||||
}
|
||||
|
||||
void RCSwitch::enableReceive() {
|
||||
if (this->nReceiverInterrupt != -1) {
|
||||
RCSwitch::nReceivedValue = 0;
|
||||
RCSwitch::nReceivedBitlength = 0;
|
||||
#if defined(RaspberryPi) // Raspberry Pi
|
||||
wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt);
|
||||
#else // Arduino
|
||||
attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable receiving data
|
||||
*/
|
||||
void RCSwitch::disableReceive() {
|
||||
#if not defined(RaspberryPi) // Arduino
|
||||
detachInterrupt(this->nReceiverInterrupt);
|
||||
#endif // For Raspberry Pi (wiringPi) you can't unregister the ISR
|
||||
this->nReceiverInterrupt = -1;
|
||||
}
|
||||
|
||||
bool RCSwitch::available() {
|
||||
return RCSwitch::nReceivedValue != 0;
|
||||
}
|
||||
|
||||
void RCSwitch::resetAvailable() {
|
||||
RCSwitch::nReceivedValue = 0;
|
||||
}
|
||||
|
||||
unsigned long RCSwitch::getReceivedValue() {
|
||||
return RCSwitch::nReceivedValue;
|
||||
}
|
||||
|
||||
unsigned int RCSwitch::getReceivedBitlength() {
|
||||
return RCSwitch::nReceivedBitlength;
|
||||
}
|
||||
|
||||
unsigned int RCSwitch::getReceivedDelay() {
|
||||
return RCSwitch::nReceivedDelay;
|
||||
}
|
||||
|
||||
unsigned int RCSwitch::getReceivedProtocol() {
|
||||
return RCSwitch::nReceivedProtocol;
|
||||
}
|
||||
|
||||
unsigned int* RCSwitch::getReceivedRawdata() {
|
||||
return RCSwitch::timings;
|
||||
}
|
||||
|
||||
/* helper function for the receiveProtocol method */
|
||||
static inline unsigned int diff(int A, int B) {
|
||||
return abs(A - B);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) {
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
const Protocol &pro = proto[p-1];
|
||||
#else
|
||||
Protocol pro;
|
||||
memcpy_P(&pro, &proto[p-1], sizeof(Protocol));
|
||||
#endif
|
||||
|
||||
unsigned long code = 0;
|
||||
//Assuming the longer pulse length is the pulse captured in timings[0]
|
||||
const unsigned int syncLengthInPulses = ((pro.syncFactor.low) > (pro.syncFactor.high)) ? (pro.syncFactor.low) : (pro.syncFactor.high);
|
||||
const unsigned int delay = RCSwitch::timings[0] / syncLengthInPulses;
|
||||
const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100;
|
||||
|
||||
/* For protocols that start low, the sync period looks like
|
||||
* _________
|
||||
* _____________| |XXXXXXXXXXXX|
|
||||
*
|
||||
* |--1st dur--|-2nd dur-|-Start data-|
|
||||
*
|
||||
* The 3rd saved duration starts the data.
|
||||
*
|
||||
* For protocols that start high, the sync period looks like
|
||||
*
|
||||
* ______________
|
||||
* | |____________|XXXXXXXXXXXXX|
|
||||
*
|
||||
* |-filtered out-|--1st dur--|--Start data--|
|
||||
*
|
||||
* The 2nd saved duration starts the data
|
||||
*/
|
||||
const unsigned int firstDataTiming = (pro.invertedSignal) ? (2) : (1);
|
||||
|
||||
for (unsigned int i = firstDataTiming; i < changeCount - 1; i += 2) {
|
||||
code <<= 1;
|
||||
if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance &&
|
||||
diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) {
|
||||
// zero
|
||||
} else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance &&
|
||||
diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) {
|
||||
// one
|
||||
code |= 1;
|
||||
} else {
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (changeCount > 7) { // ignore very short transmissions: no device sends them, so this must be noise
|
||||
RCSwitch::nReceivedValue = code;
|
||||
RCSwitch::nReceivedBitlength = (changeCount - 1) / 2;
|
||||
RCSwitch::nReceivedDelay = delay;
|
||||
RCSwitch::nReceivedProtocol = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RECEIVE_ATTR RCSwitch::handleInterrupt() {
|
||||
|
||||
static unsigned int changeCount = 0;
|
||||
static unsigned long lastTime = 0;
|
||||
static unsigned int repeatCount = 0;
|
||||
|
||||
const long time = micros();
|
||||
const unsigned int duration = time - lastTime;
|
||||
|
||||
if (duration > RCSwitch::nSeparationLimit) {
|
||||
// A long stretch without signal level change occurred. This could
|
||||
// be the gap between two transmission.
|
||||
if (diff(duration, RCSwitch::timings[0]) < 200) {
|
||||
// This long signal is close in length to the long signal which
|
||||
// started the previously recorded timings; this suggests that
|
||||
// it may indeed by a a gap between two transmissions (we assume
|
||||
// here that a sender will send the signal multiple times,
|
||||
// with roughly the same gap between them).
|
||||
repeatCount++;
|
||||
if (repeatCount == 2) {
|
||||
for(unsigned int i = 1; i <= numProto; i++) {
|
||||
if (receiveProtocol(i, changeCount)) {
|
||||
// receive succeeded for protocol i
|
||||
break;
|
||||
}
|
||||
}
|
||||
repeatCount = 0;
|
||||
}
|
||||
}
|
||||
changeCount = 0;
|
||||
}
|
||||
|
||||
// detect overflow
|
||||
if (changeCount >= RCSWITCH_MAX_CHANGES) {
|
||||
changeCount = 0;
|
||||
repeatCount = 0;
|
||||
}
|
||||
|
||||
RCSwitch::timings[changeCount++] = duration;
|
||||
lastTime = time;
|
||||
}
|
||||
#endif
|
||||
184
Devices/main-module/src/RCSwitch.h
Normal file
184
Devices/main-module/src/RCSwitch.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
RCSwitch - Arduino libary for remote control outlet switches
|
||||
Copyright (c) 2011 Suat Özgür. All right reserved.
|
||||
|
||||
Contributors:
|
||||
- Andre Koehler / info(at)tomate-online(dot)de
|
||||
- Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
|
||||
- Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46
|
||||
- Dominik Fischer / dom_fischer(at)web(dot)de
|
||||
- Frank Oltmanns / <first name>.<last name>(at)gmail(dot)com
|
||||
- Max Horn / max(at)quendi(dot)de
|
||||
- Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
|
||||
|
||||
Project home: https://github.com/sui77/rc-switch/
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _RCSwitch_h
|
||||
#define _RCSwitch_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific
|
||||
#include "Energia.h"
|
||||
#elif defined(RPI) // Raspberry Pi
|
||||
#define RaspberryPi
|
||||
|
||||
// Include libraries for RPi:
|
||||
#include <string.h> /* memcpy */
|
||||
#include <stdlib.h> /* abs */
|
||||
#include <wiringPi.h>
|
||||
#elif defined(SPARK)
|
||||
#include "application.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// At least for the ATTiny X4/X5, receiving has to be disabled due to
|
||||
// missing libm depencies (udivmodhi4)
|
||||
#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ )
|
||||
#define RCSwitchDisableReceiving
|
||||
#endif
|
||||
|
||||
// Number of maximum high/Low changes per packet.
|
||||
// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync
|
||||
#define RCSWITCH_MAX_CHANGES 67
|
||||
|
||||
class RCSwitch {
|
||||
|
||||
public:
|
||||
RCSwitch();
|
||||
|
||||
void switchOn(int nGroupNumber, int nSwitchNumber);
|
||||
void switchOff(int nGroupNumber, int nSwitchNumber);
|
||||
void switchOn(const char* sGroup, int nSwitchNumber);
|
||||
void switchOff(const char* sGroup, int nSwitchNumber);
|
||||
void switchOn(char sFamily, int nGroup, int nDevice);
|
||||
void switchOff(char sFamily, int nGroup, int nDevice);
|
||||
void switchOn(const char* sGroup, const char* sDevice);
|
||||
void switchOff(const char* sGroup, const char* sDevice);
|
||||
void switchOn(char sGroup, int nDevice);
|
||||
void switchOff(char sGroup, int nDevice);
|
||||
|
||||
void sendTriState(const char* sCodeWord);
|
||||
void send(unsigned long code, unsigned int length);
|
||||
void send(const char* sCodeWord);
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
void enableReceive(int interrupt);
|
||||
void enableReceive();
|
||||
void disableReceive();
|
||||
bool available();
|
||||
void resetAvailable();
|
||||
|
||||
unsigned long getReceivedValue();
|
||||
unsigned int getReceivedBitlength();
|
||||
unsigned int getReceivedDelay();
|
||||
unsigned int getReceivedProtocol();
|
||||
unsigned int* getReceivedRawdata();
|
||||
#endif
|
||||
|
||||
void enableTransmit(int nTransmitterPin);
|
||||
void disableTransmit();
|
||||
void setPulseLength(int nPulseLength);
|
||||
void setRepeatTransmit(int nRepeatTransmit);
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
void setReceiveTolerance(int nPercent);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Description of a single pule, which consists of a high signal
|
||||
* whose duration is "high" times the base pulse length, followed
|
||||
* by a low signal lasting "low" times the base pulse length.
|
||||
* Thus, the pulse overall lasts (high+low)*pulseLength
|
||||
*/
|
||||
struct HighLow {
|
||||
uint8_t high;
|
||||
uint8_t low;
|
||||
};
|
||||
|
||||
/**
|
||||
* A "protocol" describes how zero and one bits are encoded into high/low
|
||||
* pulses.
|
||||
*/
|
||||
struct Protocol {
|
||||
/** base pulse length in microseconds, e.g. 350 */
|
||||
uint16_t pulseLength;
|
||||
|
||||
HighLow syncFactor;
|
||||
HighLow zero;
|
||||
HighLow one;
|
||||
|
||||
/**
|
||||
* If true, interchange high and low logic levels in all transmissions.
|
||||
*
|
||||
* By default, RCSwitch assumes that any signals it sends or receives
|
||||
* can be broken down into pulses which start with a high signal level,
|
||||
* followed by a a low signal level. This is e.g. the case for the
|
||||
* popular PT 2260 encoder chip, and thus many switches out there.
|
||||
*
|
||||
* But some devices do it the other way around, and start with a low
|
||||
* signal level, followed by a high signal level, e.g. the HT6P20B. To
|
||||
* accommodate this, one can set invertedSignal to true, which causes
|
||||
* RCSwitch to change how it interprets any HighLow struct FOO: It will
|
||||
* then assume transmissions start with a low signal lasting
|
||||
* FOO.high*pulseLength microseconds, followed by a high signal lasting
|
||||
* FOO.low*pulseLength microseconds.
|
||||
*/
|
||||
bool invertedSignal;
|
||||
};
|
||||
|
||||
void setProtocol(Protocol protocol);
|
||||
void setProtocol(int nProtocol);
|
||||
void setProtocol(int nProtocol, int nPulseLength);
|
||||
|
||||
private:
|
||||
char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus);
|
||||
char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus);
|
||||
char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus);
|
||||
char* getCodeWordD(char group, int nDevice, bool bStatus);
|
||||
void transmit(HighLow pulses);
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
static void handleInterrupt();
|
||||
static bool receiveProtocol(const int p, unsigned int changeCount);
|
||||
int nReceiverInterrupt;
|
||||
#endif
|
||||
int nTransmitterPin;
|
||||
int nRepeatTransmit;
|
||||
|
||||
Protocol protocol;
|
||||
|
||||
#if not defined( RCSwitchDisableReceiving )
|
||||
static int nReceiveTolerance;
|
||||
volatile static unsigned long nReceivedValue;
|
||||
volatile static unsigned int nReceivedBitlength;
|
||||
volatile static unsigned int nReceivedDelay;
|
||||
volatile static unsigned int nReceivedProtocol;
|
||||
const static unsigned int nSeparationLimit;
|
||||
/*
|
||||
* timings[0] contains sync timing, followed by a number of bits
|
||||
*/
|
||||
static unsigned int timings[RCSWITCH_MAX_CHANGES];
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
150
Devices/main-module/src/datatypes.h
Normal file
150
Devices/main-module/src/datatypes.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
enum FB_FileType {
|
||||
FB_PHOTO,
|
||||
FB_AUDIO,
|
||||
FB_DOC,
|
||||
FB_VIDEO,
|
||||
FB_GIF,
|
||||
FB_VOICE,
|
||||
};
|
||||
|
||||
struct FB_msg {
|
||||
String& userID; // ID юзера
|
||||
String& username; // ник (в API это first_name)
|
||||
String& chatID; // ID чата
|
||||
int32_t messageID; // ID сообщения
|
||||
String& text; // текст
|
||||
String& data; // callback дата
|
||||
bool query; // запрос
|
||||
bool& edited; // сообщение отредактировано
|
||||
bool isBot; // сообщение от бота
|
||||
bool OTA; // запрос на OTA обновление
|
||||
uint32_t unix; // время сообщения
|
||||
String& fileName; // имя файла
|
||||
String& replyText; // текст ответа, если он есть
|
||||
bool isFile; // это файл
|
||||
String& fileUrl; // адрес файла для загрузки
|
||||
|
||||
// legacy
|
||||
String& usrID; // ID юзера
|
||||
String& first_name; // имя
|
||||
String& last_name; // фамилия
|
||||
int32_t ID; // ID сообщения
|
||||
|
||||
// вся информация одной строкой
|
||||
String toString() {
|
||||
String s;
|
||||
s.reserve(200);
|
||||
s += F("userID: ");
|
||||
s += userID;
|
||||
s += F(", username: ");
|
||||
s += username;
|
||||
s += F(", isBot: ");
|
||||
s += isBot;
|
||||
s += '\n';
|
||||
|
||||
s += F("chatID: ");
|
||||
s += chatID;
|
||||
s += F(", messageID: ");
|
||||
s += messageID;
|
||||
s += F(", edited: ");
|
||||
s += edited;
|
||||
s += '\n';
|
||||
|
||||
s += F("text: ");
|
||||
s += text;
|
||||
s += F(", replyText: ");
|
||||
s += replyText;
|
||||
s += '\n';
|
||||
|
||||
s += F("query: ");
|
||||
s += query;
|
||||
s += F(", data: ");
|
||||
s += data;
|
||||
s += '\n';
|
||||
|
||||
s += F("isFile: ");
|
||||
s += isFile;
|
||||
s += F(", fileName: ");
|
||||
s += fileName;
|
||||
s += F(", OTA: ");
|
||||
s += OTA;
|
||||
s += '\n';
|
||||
|
||||
s += F("fileUrl: ");
|
||||
s += fileUrl;
|
||||
s += '\n';
|
||||
|
||||
s += F("unix: ");
|
||||
s += unix;
|
||||
s += '\n';
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
struct FB_Time {
|
||||
FB_Time(uint32_t unix = 0, int16_t gmt = 0) {
|
||||
if (!unix) return;
|
||||
if (abs(gmt) <= 12) gmt *= 60;
|
||||
unix += gmt * 60L;
|
||||
second = unix % 60ul;
|
||||
unix /= 60ul;
|
||||
minute = unix % 60ul;
|
||||
unix /= 60ul;
|
||||
hour = unix % 24ul;
|
||||
unix /= 24ul;
|
||||
dayWeek = (unix + 4) % 7;
|
||||
if (!dayWeek) dayWeek = 7;
|
||||
unix += 719468;
|
||||
uint8_t era = unix / 146097ul;
|
||||
uint16_t doe = unix - era * 146097ul;
|
||||
uint16_t yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
year = yoe + era * 400;
|
||||
uint16_t doy = doe - (yoe * 365 + yoe / 4 - yoe / 100);
|
||||
uint16_t mp = (doy * 5 + 2) / 153;
|
||||
day = doy - (mp * 153 + 2) / 5 + 1;
|
||||
month = mp + (mp < 10 ? 3 : -9);
|
||||
year += (month <= 2);
|
||||
}
|
||||
|
||||
uint8_t second = 0;
|
||||
uint8_t minute = 0;
|
||||
uint8_t hour = 0;
|
||||
uint8_t day = 0;
|
||||
uint8_t month = 0;
|
||||
uint8_t dayWeek = 0;
|
||||
uint16_t year = 0;
|
||||
|
||||
// получить строку времени формата ЧЧ:ММ:СС
|
||||
String timeString() {
|
||||
String str;
|
||||
if (!year) return str = F("Not sync");
|
||||
str.reserve(8);
|
||||
if (hour < 10) str += '0';
|
||||
str += hour;
|
||||
str += ':';
|
||||
if (minute < 10) str += '0';
|
||||
str += minute;
|
||||
str += ':';
|
||||
if (second < 10) str += '0';
|
||||
str += second;
|
||||
return str;
|
||||
}
|
||||
|
||||
// получить строку даты формата ДД.ММ.ГГГГ
|
||||
String dateString() {
|
||||
String str;
|
||||
if (!year) return str = F("Not sync");
|
||||
str.reserve(10);
|
||||
if (day < 10) str += '0';
|
||||
str += day;
|
||||
str += '.';
|
||||
if (month < 10) str += '0';
|
||||
str += month;
|
||||
str += '.';
|
||||
str += year;
|
||||
return str;
|
||||
}
|
||||
};
|
||||
551
Devices/main-module/src/main.cpp
Normal file
551
Devices/main-module/src/main.cpp
Normal file
@@ -0,0 +1,551 @@
|
||||
#include <Arduino.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "RCSwitch.h"
|
||||
#include "pca9554.h"
|
||||
|
||||
#include "HalRelays.h"
|
||||
#include "HalTimer.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "FastBot.h"
|
||||
|
||||
// Set i2c address
|
||||
#define sda 12
|
||||
#define scl 14
|
||||
/*
|
||||
Received 6277048 / 24bit Protocol: 1
|
||||
Received 6277044 / 24bit Protocol: 1
|
||||
Received 6277042 / 24bit Protocol: 1
|
||||
Received 6277041 / 24bit Protocol: 1
|
||||
*/
|
||||
|
||||
RCSwitch mySwitch = RCSwitch();
|
||||
|
||||
int rfPin = D1;
|
||||
|
||||
// PCA9554 io(0x38, sda, scl);
|
||||
// PCA9554 io(0x20, sda, scl);
|
||||
// HalRelays _rel(sda, scl, _rel_save, _rel_load);
|
||||
HalRelays _rel(sda, scl, _fs_write, _fs_read);
|
||||
|
||||
Config _config;
|
||||
FastBot _bot;
|
||||
ESP8266WebServer _web(80);
|
||||
|
||||
unsigned long _radio_last_value;
|
||||
|
||||
void _timer1_handler()
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
}
|
||||
|
||||
HalTimer timer1(1 * 1000, _timer1_handler, true);
|
||||
|
||||
void _timer2_handler()
|
||||
{
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
_bot.tick();
|
||||
}
|
||||
}
|
||||
|
||||
HalTimer timer2(3 * 1000, _timer2_handler, true);
|
||||
|
||||
void sendHello()
|
||||
{
|
||||
String hi = F("Power On ") + (String)_config.version;
|
||||
uint8 status = _bot.sendMessage(hi, _config.chatId);
|
||||
Serial.println("Power On [" + (String)status + "]");
|
||||
}
|
||||
|
||||
void bot_handler(FB_msg &msg)
|
||||
{
|
||||
Serial.print(msg.chatID); // ID чата
|
||||
Serial.print(", ");
|
||||
Serial.print(msg.username); // логин
|
||||
Serial.print(", ");
|
||||
Serial.println(msg.text); // текст
|
||||
|
||||
if (msg.OTA && msg.text == "-update")
|
||||
{
|
||||
String upd = F("update...");
|
||||
Serial.println(upd);
|
||||
_bot.update();
|
||||
Serial.println(upd + "OK");
|
||||
}
|
||||
|
||||
if (msg.text == "-this")
|
||||
{
|
||||
_config.chatId = msg.chatID;
|
||||
_config_save();
|
||||
_bot.sendMessage(F("Set this OK"), msg.chatID);
|
||||
}
|
||||
|
||||
if (msg.text == "-reset")
|
||||
{
|
||||
// ESP.reset();
|
||||
}
|
||||
|
||||
if (msg.text == "/set")
|
||||
{
|
||||
String hi = F("Настройки [") + (String)_config.version + "]\r\n";
|
||||
_bot.sendMessage(hi, _config.chatId);
|
||||
_bot.sendMessage(_rel.printChannelsToString(), _config.chatId);
|
||||
}
|
||||
|
||||
if (msg.text == "/radio")
|
||||
{
|
||||
Serial.print(F("Radio Last: "));
|
||||
Serial.println((String)_radio_last_value);
|
||||
_bot.sendMessage(((String)_radio_last_value), msg.chatID);
|
||||
_rel.printChannels();
|
||||
Serial.print(F("load"));
|
||||
_rel.load();
|
||||
_rel.printChannels();
|
||||
}
|
||||
|
||||
if (msg.text.startsWith(F("/r-")))
|
||||
{
|
||||
int number = (msg.text.substring(msg.text.indexOf("-") + 1, msg.text.length())).toInt();
|
||||
if (0 <= number && number <= 7)
|
||||
{
|
||||
_rel.switchState(number);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.text.startsWith(F("/radio rel=")))
|
||||
{
|
||||
int number = (msg.text.substring(msg.text.indexOf("=") + 1, msg.text.length())).toInt();
|
||||
if (0 <= number && number <= 7)
|
||||
{
|
||||
_rel.Channels[number].Code = _radio_last_value;
|
||||
_rel.save();
|
||||
String str = F("Set radio [") + (String)_radio_last_value + F("] to channel: [") + (String)number + F("]");
|
||||
Serial.println(str);
|
||||
_bot.sendMessage(str, msg.chatID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
pinMode(rfPin, INPUT);
|
||||
|
||||
// delay(100);
|
||||
_fs_init();
|
||||
|
||||
_rel.init();
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println(F("SYSTEM: HAL9000"));
|
||||
Serial.println(F("UNIT: HOMEON"));
|
||||
|
||||
if (!_config_load())
|
||||
{
|
||||
Serial.println(F("setup DEFAULT config"));
|
||||
_config.ssidAP = "HomeOn";
|
||||
_config.passAP = "012345678";
|
||||
_config.ssid = "paranetm";
|
||||
_config.pass = "78515169";
|
||||
_config.version = VERSION;
|
||||
_config.updateUrl = "http://iot.paranet.ru/cloudupdate/getup/?platform=HomeOn";
|
||||
_config.token = "314329238:AAHIQa9Jvae3bnfAQBiOoC6kJZ70jtkYLks";
|
||||
// 314329238:AAHIQa9Jvae3bnfAQBiOoC6kJZ70jtkYLks
|
||||
_config.chatId = "";
|
||||
|
||||
_config_save();
|
||||
}
|
||||
_config.version = VERSION;
|
||||
|
||||
// String _rel_conf = _rel_load();
|
||||
_rel.load();
|
||||
|
||||
_wifi_check();
|
||||
_web_init();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
Serial.println(F("set token : ") + _config.token);
|
||||
_bot.setToken(_config.token);
|
||||
_bot.detach();
|
||||
_bot.attach(bot_handler);
|
||||
sendHello();
|
||||
}
|
||||
|
||||
mySwitch.enableReceive(rfPin);
|
||||
}
|
||||
|
||||
bool radio()
|
||||
{
|
||||
if (mySwitch.available())
|
||||
{
|
||||
_radio_last_value = mySwitch.getReceivedValue();
|
||||
Serial.print(F("Received: "));
|
||||
Serial.println(_radio_last_value);
|
||||
|
||||
mySwitch.resetAvailable();
|
||||
if (_rel.cmd(_radio_last_value))
|
||||
{
|
||||
String text = F("Переключение: ") + (String)_radio_last_value;
|
||||
_bot.sendMessage(text, _config.chatId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
radio();
|
||||
timer1.tick();
|
||||
radio();
|
||||
timer2.tick();
|
||||
// if (WiFi.status() == WL_CONNECTED)
|
||||
// {
|
||||
// _bot.tick();
|
||||
// }
|
||||
radio();
|
||||
//_web.handleClient();
|
||||
}
|
||||
|
||||
void loop2()
|
||||
{
|
||||
delay(5000);
|
||||
|
||||
Serial.println(F("ping"));
|
||||
// io.digitalWrite(pin, LOW);
|
||||
_rel.offAll();
|
||||
delay(5000);
|
||||
// io.digitalWrite(pin, HIGH);
|
||||
_rel.onAll();
|
||||
|
||||
return;
|
||||
|
||||
if (mySwitch.available())
|
||||
{
|
||||
/*
|
||||
Received 6277044 / 24bit Protocol: 1
|
||||
Received 6277042 / 24bit Protocol: 1
|
||||
Received 6277041 / 24bit Protocol: 1
|
||||
Received 6277048 / 24bit Protocol: 1
|
||||
*/
|
||||
/**
|
||||
unsigned long code = mySwitch.getReceivedValue();
|
||||
int pin = -1;
|
||||
int le = sizeof(codes) / sizeof(unsigned long);
|
||||
Serial.println("RC: " + (String)code);
|
||||
for (byte i = 0; i < le; i++)
|
||||
{
|
||||
if (code == codes[i])
|
||||
{
|
||||
pin = i + 1;
|
||||
if (pin == 3)
|
||||
{
|
||||
Serial.println("set off-timer");
|
||||
off_timer = 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
**/
|
||||
|
||||
Serial.print("Received ");
|
||||
Serial.print(mySwitch.getReceivedValue());
|
||||
Serial.print(" / ");
|
||||
Serial.print(mySwitch.getReceivedBitlength());
|
||||
Serial.print("bit ");
|
||||
Serial.print("Protocol: ");
|
||||
Serial.println(mySwitch.getReceivedProtocol());
|
||||
|
||||
mySwitch.resetAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
/** base rutine **/
|
||||
|
||||
void _wifi_init(void)
|
||||
{
|
||||
// Попытка подключения к точке доступа
|
||||
WiFi.setAutoConnect(false);
|
||||
WiFi.BSSID(0);
|
||||
WiFi.mode(WIFI_STA);
|
||||
byte tries = 17;
|
||||
|
||||
String _ssid = _config.ssid;
|
||||
String _pass = _config.pass;
|
||||
|
||||
Serial.print("MAC address: ");
|
||||
Serial.println(WiFi.macAddress());
|
||||
|
||||
Serial.println("SSID: " + _ssid);
|
||||
Serial.println("PASS: " + _pass);
|
||||
WiFi.begin(_ssid.c_str(), _pass.c_str());
|
||||
|
||||
// Делаем проверку подключения до тех пор пока счетчик tries не станет равен нулю или не получим подключение
|
||||
while (--tries && WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
// Если не удалось подключиться запускаем в режиме AP
|
||||
Serial.println("");
|
||||
Serial.println("WiFi up AP");
|
||||
_wifi_apmode();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Иначе удалось подключиться отправляем сообщение о подключении и выводим адрес IP
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
WiFi.setAutoReconnect(true);
|
||||
_isApMode = false;
|
||||
}
|
||||
|
||||
//_config.ip = WiFi.localIP().toString();
|
||||
}
|
||||
|
||||
bool _wifi_apmode()
|
||||
{
|
||||
String _ssidAP = _config.ssidAP;
|
||||
String _passAP = _config.passAP;
|
||||
|
||||
Serial.println("AP SSID: " + _ssidAP);
|
||||
Serial.println("AP PASS: " + _passAP);
|
||||
|
||||
// Отключаем WIFI
|
||||
WiFi.disconnect();
|
||||
// Меняем режим на режим точки доступа
|
||||
WiFi.mode(WIFI_AP);
|
||||
// Задаем настройки сети
|
||||
Serial.print("Setting soft-AP configuration ... ");
|
||||
Serial.println(WiFi.softAPConfig(IPAddress(192, 168, 10, 1), IPAddress(192, 168, 10, 1), IPAddress(255, 255, 255, 0)) ? "Ready" : "Failed!");
|
||||
// Включаем WIFI в режиме точки доступа с именем и паролем
|
||||
Serial.print("Setting soft-AP ... ");
|
||||
Serial.println(WiFi.softAP(_ssidAP.c_str(), _passAP.c_str()) ? "AP Ready" : "AP Failed!");
|
||||
// Serial.println(WiFi.softAP("test1", "12345678") ? "AP Ready" : "AP Failed!");
|
||||
_isApMode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _wifi_check()
|
||||
{
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
_wifi_init();
|
||||
return;
|
||||
}
|
||||
if (_isApMode)
|
||||
{
|
||||
_wifi_init();
|
||||
}
|
||||
}
|
||||
|
||||
bool _config_load(void)
|
||||
{
|
||||
String str = _fs_read(_config.getFileName());
|
||||
if (!_config.convertFromString(str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Serial.println(F("LOAD CONFING"));
|
||||
Serial.println(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _config_save(void)
|
||||
{
|
||||
String str = _config.convertToString();
|
||||
Serial.println(F("SAVE CONFING"));
|
||||
return _fs_write(str, _config.getFileName());
|
||||
}
|
||||
|
||||
void _web_init(void)
|
||||
{
|
||||
_web.on("/get", HTTP_GET, _handle_get);
|
||||
_web.on("/gett", HTTP_GET, _handle_gett);
|
||||
_web.on("/list", HTTP_GET, _handle_file_list);
|
||||
_web.on("/set", HTTP_GET, _handle_set);
|
||||
_web.onNotFound([]()
|
||||
{
|
||||
if (!_handle_file_read(_web.uri())) _web.send(404, "text/plain", "File Not Found: " + _web.uri()); });
|
||||
|
||||
_web.begin();
|
||||
}
|
||||
|
||||
void _handle_set(void)
|
||||
{
|
||||
_config.ssid = _web.arg("ssid");
|
||||
_config.pass = _web.arg("pass");
|
||||
_config.token = _web.arg("bottoken");
|
||||
_config.updateUrl = _web.arg("updateurl");
|
||||
|
||||
if (_config_save())
|
||||
{
|
||||
_web.send(200, "text/plain", "SUCCESS");
|
||||
ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
_web.send(500, "text/plain", "ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_get(void)
|
||||
{
|
||||
/***
|
||||
String root = "{}";
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject &json = jsonBuffer.parseObject(root);
|
||||
_config.ToJson(json);
|
||||
// Помещаем созданный json в переменную root
|
||||
root = "";
|
||||
json.printTo(root);
|
||||
_web.send(200, "text/json", root);
|
||||
***/
|
||||
}
|
||||
|
||||
void _handle_gett(void)
|
||||
{
|
||||
/***
|
||||
String root = "{}";
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject &json = jsonBuffer.parseObject(root);
|
||||
// Помещаем созданный json в переменную root
|
||||
root = "";
|
||||
json.printTo(root);
|
||||
_web.send(200, "text/json", root);
|
||||
***/
|
||||
}
|
||||
|
||||
void _handle_file_list(void)
|
||||
{
|
||||
String path = "/";
|
||||
if (_web.hasArg("dir"))
|
||||
path += _web.arg("dir");
|
||||
Dir dir = LittleFS.openDir(path);
|
||||
path = String();
|
||||
String output = "[";
|
||||
while (dir.next())
|
||||
{
|
||||
File entry = dir.openFile("r");
|
||||
if (output != "[")
|
||||
output += ',';
|
||||
bool isDir = false;
|
||||
output += "{\"type\":\"";
|
||||
output += (isDir) ? "dir" : "file";
|
||||
output += "\",\"name\":\"";
|
||||
output += String(entry.name()); //.substring(1);
|
||||
output += "\"}";
|
||||
entry.close();
|
||||
}
|
||||
output += "]";
|
||||
_web.send(200, "text/json", output);
|
||||
}
|
||||
|
||||
bool _handle_file_read(String path)
|
||||
{
|
||||
if (path.endsWith("/"))
|
||||
path += "index.html";
|
||||
String contentType = _getContentType(path);
|
||||
String pathWithGz = path + ".gz";
|
||||
if (LittleFS.exists(pathWithGz) || LittleFS.exists(path))
|
||||
{
|
||||
if (LittleFS.exists(pathWithGz))
|
||||
path += ".gz";
|
||||
File file = LittleFS.open(path, "r");
|
||||
size_t sent = _web.streamFile(file, contentType);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String _getContentType(String filename)
|
||||
{
|
||||
if (_web.hasArg("download"))
|
||||
return "application/octet-stream";
|
||||
else if (filename.endsWith(".htm"))
|
||||
return "text/html";
|
||||
else if (filename.endsWith(".html"))
|
||||
return "text/html";
|
||||
else if (filename.endsWith(".json"))
|
||||
return "application/json";
|
||||
else if (filename.endsWith(".css"))
|
||||
return "text/css";
|
||||
else if (filename.endsWith(".js"))
|
||||
return "application/javascript";
|
||||
else if (filename.endsWith(".png"))
|
||||
return "image/png";
|
||||
else if (filename.endsWith(".gif"))
|
||||
return "image/gif";
|
||||
else if (filename.endsWith(".jpg"))
|
||||
return "image/jpeg";
|
||||
else if (filename.endsWith(".ico"))
|
||||
return "image/x-icon";
|
||||
else if (filename.endsWith(".xml"))
|
||||
return "text/xml";
|
||||
else if (filename.endsWith(".pdf"))
|
||||
return "application/x-pdf";
|
||||
else if (filename.endsWith(".zip"))
|
||||
return "application/x-zip";
|
||||
else if (filename.endsWith(".gz"))
|
||||
return "application/x-gzip";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
void _fs_init(void)
|
||||
{
|
||||
LittleFS.begin();
|
||||
{
|
||||
Dir dir = LittleFS.openDir("/");
|
||||
while (dir.next())
|
||||
{
|
||||
String fileName = dir.fileName();
|
||||
size_t fileSize = dir.fileSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _fs_write(String str, String file_name)
|
||||
{
|
||||
File file = LittleFS.open(file_name, "w");
|
||||
if (!file)
|
||||
{
|
||||
Serial.println(F("Failed to open file for writing"));
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
file.print(str);
|
||||
file.close();
|
||||
Serial.println(F("SAVE FILE:"));
|
||||
Serial.println(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
String _fs_read(String file_name)
|
||||
{
|
||||
String str = "";
|
||||
File file = LittleFS.open(file_name, "r");
|
||||
if (!file)
|
||||
return str;
|
||||
str = file.readString();
|
||||
file.close();
|
||||
return str;
|
||||
}
|
||||
27
Devices/main-module/src/main.h
Normal file
27
Devices/main-module/src/main.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#define VERSION "20260109:1"
|
||||
|
||||
bool _isApMode = false;
|
||||
|
||||
String _rel_load(void);
|
||||
bool _rel_save(String config);
|
||||
|
||||
void _fs_init(void);
|
||||
bool _fs_write(String str, String file_name);
|
||||
String _fs_read(String file_name);
|
||||
|
||||
void _web_init(void);
|
||||
void _wifi_init(void);
|
||||
bool _handle_file_read(String path);
|
||||
bool _wifi_apmode(void);
|
||||
void _handle_get(void);
|
||||
void _handle_gett(void);
|
||||
void _handle_file_list(void);
|
||||
void _handle_set(void);
|
||||
bool _config_load(void);
|
||||
String _getContentType(String filename);
|
||||
bool _config_save(void);
|
||||
int sensor_run(void);
|
||||
void _termostat();
|
||||
void _wifi_check();
|
||||
150
Devices/main-module/src/pca9554.h
Normal file
150
Devices/main-module/src/pca9554.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// PCA9554 Addressing
|
||||
// Address A2 A1 A0
|
||||
// 0x20 L L L
|
||||
// 0x21 L L H
|
||||
// 0x22 L H L
|
||||
// 0x23 L H H
|
||||
// 0x24 H L L
|
||||
// 0x25 H L H
|
||||
// 0x26 H H L
|
||||
// 0x27 H H H
|
||||
|
||||
#define INPUTPORT 0x00
|
||||
#define OUTPUTPORT 0x01
|
||||
#define POLINVPORT 0x02
|
||||
#define CONFIGPORT 0x03
|
||||
|
||||
#define ALLOUTPUT 0x00
|
||||
#define ALLINPUT 0xFF
|
||||
|
||||
class PCA9554
|
||||
{
|
||||
private:
|
||||
int _address;
|
||||
byte _sda = SDA;
|
||||
byte _scl = SCL;
|
||||
TwoWire *_wire;
|
||||
|
||||
bool twiRead(byte ®isterAddress)
|
||||
{
|
||||
_wire->beginTransmission(_address);
|
||||
_wire->write(registerAddress);
|
||||
|
||||
if (_wire->endTransmission() == 0)
|
||||
{
|
||||
delay(15);
|
||||
_wire->requestFrom(_address, 1, true);
|
||||
while (_wire->available() < 1)
|
||||
;
|
||||
registerAddress = _wire->read();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool twiWrite(byte registerAddress, byte dataWrite)
|
||||
{
|
||||
_wire->beginTransmission(_address);
|
||||
_wire->write(registerAddress);
|
||||
_wire->write(dataWrite);
|
||||
|
||||
if (_wire->endTransmission() == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
PCA9554() {}
|
||||
PCA9554(byte address, byte sda, byte scl)
|
||||
{
|
||||
_wire = &Wire;
|
||||
_address = address;
|
||||
_sda = sda;
|
||||
_scl = scl;
|
||||
_wire->begin(_sda, _scl);
|
||||
}
|
||||
|
||||
bool pinMode(byte pinNumber, bool state)
|
||||
{
|
||||
byte oldValue = CONFIGPORT;
|
||||
if (this->twiRead(oldValue) && (pinNumber <= 7))
|
||||
{
|
||||
if (!state)
|
||||
{
|
||||
oldValue |= (1 << pinNumber);
|
||||
if (this->portMode(oldValue))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (state)
|
||||
{
|
||||
oldValue &= ~(1 << pinNumber);
|
||||
if (this->portMode(oldValue))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool portMode(byte value)
|
||||
{
|
||||
if (this->twiWrite(CONFIGPORT, value))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool digitalWrite(byte pinNumber, bool state)
|
||||
{
|
||||
byte oldValue = OUTPUTPORT;
|
||||
if (this->twiRead(oldValue) && pinNumber <= 7)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
oldValue |= (1 << pinNumber);
|
||||
if (this->digitalWritePort(oldValue))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (!state)
|
||||
{
|
||||
oldValue &= ~(1 << pinNumber);
|
||||
if (this->digitalWritePort(oldValue))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool digitalWritePort(byte value)
|
||||
{
|
||||
if (this->twiWrite(OUTPUTPORT, value))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool digitalRead(byte &pinNumber)
|
||||
{
|
||||
byte oldValue = INPUTPORT;
|
||||
if (this->twiRead(oldValue) && (pinNumber <= 7))
|
||||
{
|
||||
oldValue &= (1 << pinNumber);
|
||||
if (oldValue > 0)
|
||||
pinNumber = 1;
|
||||
else
|
||||
pinNumber = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool digitalReadPort(byte &value)
|
||||
{
|
||||
value = INPUTPORT;
|
||||
if (this->twiRead(value))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
109
Devices/main-module/src/utils.cpp
Normal file
109
Devices/main-module/src/utils.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "utils.h"
|
||||
|
||||
void FB_escHTML(String& s) {
|
||||
String out;
|
||||
out.reserve(s.length());
|
||||
for (uint16_t i = 0; i < s.length(); i++) {
|
||||
switch (s[i]) {
|
||||
case '<': out += F("<"); break;
|
||||
case '>': out += F(">"); break;
|
||||
case '&': out += F("&"); break;
|
||||
default: out += s[i]; break;
|
||||
}
|
||||
}
|
||||
s = out;
|
||||
}
|
||||
|
||||
static const char FB_escList[] = ">-={}.!";
|
||||
|
||||
void FB_escMarkdown(String& s) {
|
||||
String out;
|
||||
out.reserve(s.length());
|
||||
for (uint16_t i = 0; i < s.length(); i++) {
|
||||
if (strchr(FB_escList, s[i]) != 0) out += '\\';
|
||||
out += s[i];
|
||||
}
|
||||
Serial.println(out);
|
||||
s = out;
|
||||
}
|
||||
|
||||
int64_t FB_str64(const String &s) {
|
||||
return atoll(s.c_str());
|
||||
}
|
||||
String FB_64str(int64_t id) {
|
||||
String s;
|
||||
int32_t s1 = (int64_t)id % 1000000000;
|
||||
int32_t s2 = (int64_t)id / 1000000000;
|
||||
if (s2) {
|
||||
s += s2;
|
||||
s += abs(s1);
|
||||
} else {
|
||||
s += s1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// упрощённый urlencode (до 38 ASCII + space)
|
||||
// по хорошему нужно пропускать только a-Z, 0-9 и -_.!~*'()
|
||||
void FB_urlencode(const String& s, String& dest) {
|
||||
dest.reserve(s.length() + 10);
|
||||
char c;
|
||||
for (uint16_t i = 0; i < s.length(); i++) {
|
||||
c = s[i];
|
||||
if (c == ' ') dest += '+';
|
||||
else if (c <= 38 || c == '+') {
|
||||
dest += '%';
|
||||
dest += (char)((c >> 4) + (((c >> 4) > 9) ? 87 : 48));
|
||||
dest += (char)((c & 0xF) + (((c & 0xF) > 9) ? 87 : 48));
|
||||
}
|
||||
else dest += c;
|
||||
}
|
||||
}
|
||||
|
||||
// разработано Глебом Жуковым, допилено AlexGyver
|
||||
// https://vk.com/wall-97877471_783011
|
||||
void FB_unicode(String &uStr) {
|
||||
if (!uStr.length()) return;
|
||||
String out;
|
||||
out.reserve(uStr.length() / 3);
|
||||
int32_t uBytes, buf = 0;
|
||||
char x0[] = "\0x";
|
||||
for (uint16_t i = 0; i < uStr.length(); i++) {
|
||||
if (uStr[i] != '\\') out += uStr[i];
|
||||
else {
|
||||
switch (uStr[++i]) {
|
||||
case '0': break;
|
||||
case 'n': out += '\n'; break;
|
||||
case 'r': out += '\r'; break;
|
||||
case 't': out += '\t'; break;
|
||||
case 'u':
|
||||
uBytes = strtol(uStr.c_str() + i + 1, NULL, HEX);
|
||||
i += 4;
|
||||
if ((uBytes >= 0xD800) && (uBytes <= 0xDBFF)) buf = uBytes;
|
||||
else if ((uBytes >= 0xDC00) && (uBytes <= 0xDFFF)) {
|
||||
uBytes = (0x10000 + ((buf - 0xD800) * 0x0400) + (uBytes - 0xDC00));
|
||||
out += (char)(0b11110000 | ((uBytes >> 18) & 0b111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | ((uBytes >> 12) & 0b111111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | ((uBytes >> 6) & 0b111111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | (uBytes & 0b111111));
|
||||
} else if (uBytes < 0x800) {
|
||||
out += (char)(0b11000000 | ((uBytes >> 6) & 0b11111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | (uBytes & 0b111111));
|
||||
} else if (uBytes >= 0x800) {
|
||||
out += (char)(0b11100000 | ((uBytes >> 12) & 0b1111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | ((uBytes >> 6) & 0b111111));
|
||||
out += x0;
|
||||
out += (char)(0b10000000 | (uBytes & 0b111111));
|
||||
}
|
||||
break;
|
||||
default: out += uStr[i]; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
uStr = out;
|
||||
}
|
||||
49
Devices/main-module/src/utils.h
Normal file
49
Devices/main-module/src/utils.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
void FB_unicode(String &uStr);
|
||||
void FB_urlencode(const String& s, String& dest);
|
||||
void FB_escHTML(String& s);
|
||||
void FB_escMarkdown(String& s);
|
||||
|
||||
int64_t FB_str64(const String &s);
|
||||
String FB_64str(int64_t id);
|
||||
|
||||
struct FB_Parser {
|
||||
FB_Parser() {
|
||||
str.reserve(20);
|
||||
}
|
||||
|
||||
bool parseNT(const String& s) {
|
||||
while (!end) {
|
||||
char c1 = s[++i];
|
||||
if (c1 == '\t' || c1 == '\n' || c1 == '\0') {
|
||||
int to = i;
|
||||
if (s[to - 1] == ' ') to--;
|
||||
if (s[from] == ' ') from++;
|
||||
str = s.substring(from, to);
|
||||
from = i + 1;
|
||||
end = (c1 == '\0');
|
||||
div = c1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool parse(const String& s) {
|
||||
if (i >= (int)s.length() - 1) return 0;
|
||||
i = s.indexOf(',', from);
|
||||
if (i < 0) i = s.length();
|
||||
int to = i;
|
||||
if (s[to - 1] == ' ') to--;
|
||||
if (s[from] == ' ') from++;
|
||||
str = s.substring(from, to);
|
||||
from = i + 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i = 0, from = 0;
|
||||
char div;
|
||||
bool end = false;
|
||||
String str;
|
||||
};
|
||||
Reference in New Issue
Block a user