Первый комит в котором пока почти ничего не работает

This commit is contained in:
2021-05-03 16:22:16 +06:00
commit 5ed6161c7c
16 changed files with 2592 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

353
data/index.html Normal file
View File

@ -0,0 +1,353 @@
<!DOCTYPE html><html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:image/gif;base64,R0lGODlhIAAgAOf/AAAjgAAkghAkfQAphQcpgQAriAAtigEwhxAshAAxjAAyjgA0kQA3kxcyhAA5jwA6lgI6kRE2jRA3iAY7kgA+mgk8kzQ4OjY4NQBCmBA/lgBEmjk7OQZFnBRCkwBLoQ5Inz9BPhtGlwBOpBJJoD9DRRxJlARPpidGkwBUqSJMmABWrEVJSxBSqURKUUhKSABZrxBVpSBVoD9RYARdrRhYqQBftExQUgtcsxNcoE9RTgBhtgtfrwBjuRNetVBUVjBZoFNVUhNhsgRmvABotxZisxhjtABqvwtnvldZVgBswVZaXBlmsRxlti5hpwBuwxJpwDZhogBvxE9fagBwxxFsvFxeWzxlpwpzygB3zChup1NmdgB5zgB61hN1zAB70VZng2NnaThwqhl3zjxvsFRpowB/2wx82UNvq2hqZzB0wBx50Etuq2drbhN+2wCD5jh2tk1ynUdzr2pucReA3QeF6G5wbTN600t2sx2B3wyG6WZ0ej57u014tG9zdlJ4rxKH6yKE4QGN9xiJ7Vp8lgCQ9HN3enV3dF57m0l/uhiM6R2K73p4fFh+thCQ+nl7eCKM8SqL4nV7oRmS/XGBjHx/fCiP9Ht/ghmW+VqIvyGV/2WGuYGDgCyU8iWW/1WNwl2Lwl6MtjKU+SOZ/TCW9X6HlVyM3WSNv4aIhWCPxj6X8Cqc/zuZ/4eLjoqMiU6Z4EGd7y6i/0mb/D6f/o+RjlCf34+TlmmbzD+k/E+f/1Ch7m6cx06k6kOm/5WXlEao/5OaonSizVGp/Eet/3ajz4GiyZugon2mzIei3lSw/Hqo05qhu1ey/m2v8aOlomG0+2S2/m20/aWqrKiqp224+pexzqyuq2a9/XC7/ZG32Hu7/3e9+W/A+qyzu4y4/6K2yLG0sJu36HPD/pa73X3C/7K3zITE+5rA4X3H/J7B3Lq9uoXK+pbI+4nN/ZHM/cHEwZDQ+qzL4ZjU+MrMyZzX/K3T9cvQ0s3QzKLa+NLV0drc2dzg4+Ll4ezv6/T38/P4+0JCQiH5BAEKAP8ALAAAAAAgACAAAAj+AP8JHEiwoMGDCBP+E5QokcJi0f6xUviPzh9BjwhxSvUq1y5ag9zlS9drEUFLCPFcDNSok8tQlzi9ApWjHz97zeQQrGWwjEpBLVXBGqqqU6UsJIDs4yevVx1DAiMW9NKGDtBOsIIh2xrsFiQZK3rhw5eujw9DbAoZ3GJGZSCswp5d22ZtGjJXk1jl6yet2iklYMDUKbiFrRugqnw923ZOnbpw05h509dPXqtZmyg5MgSVoJMuVYHKEmbt3Lt58dRty0bNX71TVZDMaqW5s0AhTsRUfdRJFjJr6uLdm/du3Dp65LiBcbEBRKtvs6oQ1JE7dCPf10yjVleuXTdwyvT+rLBwwUWdTVoG6tCRpIsZQIiDLVbHTt05bdBwxToWSUoLG2icAocHAwWhwxPuuQXXM9Zss800y/CySiOVlELGF6T8cogIHAx0ww1HOMGFdaLAIgwyziwjDC+qUPgHHnakEQYOKIiAwUAq3MBegn+w1IlQt9wiS4uBCOJGG2J0ccQNLIyQwUAovKBDiLr91EhLLl35iJFtcNGFEzq8wGEFEAiEQo48tDfiT4JUEkglW/5xpBlf8sDkCBg8UOY/MKCgY4hdiGHGHHhY9McfdLiBR5dfHtFDjRxQEMFANIhAQw06GPHZFlyY0cYcoLZhhhlJOnFEmCJ8kMEDEwy0hAf+MMygQxFGJLHpFmpwoYYYW3yZxKl3YgABBCEM9AYMHtAwQxC0JmFrF11gAa0TTvyqQw0oeIDBBBF0EMNAe4yBQbI7MMvEEFSkm0S6RhxRRBDY4jnBAhH88ANBmMQwwQexBsFsEeeeO0QRRAQxAwqqPrBAAiH4UdAnmHTAAAaWLusvERj7O0OlHFTAwAIGOMBIHAYlwwcEC0AwLgw0KDvDxjCMiXICBhywBjAIoXOHAwkswIDKGHAgNAZkRrCwAQUgAIU4iCAEDDw7F2BAAj0vYPXCVCM9gNLoYKLQMPBo0gECBZRtttkDBCBAA2tg4zVF/5hDDBQSEBDA3QEAoDcxAAicoIkucBNkizGmWFGCBAgQQAACEqRwBiZNB16QJ6hgwocVMcTQxBh33CH555IHBAA7">
<title>Конфигурация</title>
<style>
* {
margin: 0;
padding: 0;
font-size: 16px;
}
html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
body{
background-color: #4f4f4f;
}
h1 {
font-size: 40px;
line-height: 50px;
color: #1655f5
}
h3 {
font-size: 24px;
font-weight: normal;
margin-top: 5px;
margin-bottom: 5px;
}
h4 {
padding-top: 5px;
padding-bottom: 5px;
}
p{
font-size: 20px;
padding-top: 5px;
padding-bottom: 5px;
}
input[type="text"],input[type="number"]
{
font-size:24px;
width: 100%;
}
.wifi_list{
width: 100%;
cursor: pointer;
background-color: #ffffff;
border-bottom: 1px solid #000000;
}
.content1 {
width: 100%;
max-width: 400px;
margin-left: auto;
margin-right: auto;
background-color: lightblue;
}
.content2 {
width: 95%;
margin-left: auto;
margin-right: auto;
}
.counter {
position: sticky;
top: 8px;
font-weight: bold;
color: #f53410;
}
.button{
background-color: #4CAF50;
border: none;
color: white;
padding: 16px 40px;
text-decoration: none;
font-size: 30px;
margin: 2px;
cursor: pointer;
border-radius: 20px;
}
.lock {
width:16px;
height:24px;
background: url('data:image/gif;base64,R0lGODlhEAAYAIQWADVMdVlhbIF5YGqDuo6AXWuDummEumqEumuEu6WOVrmjc7qkcrukccKna9erSdesSey3Q/K6QfK7QPK7QfO7QfK8QgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAB8ALAAAAAAQABgAAAVf4CeOQzmM6FiY5pGKbHyiLCIaZkGXr7nPv2DKR+INjTBkEfghCj8LBaMxqVobDMXiY+16rZHKdzwRk89dSCIBQVcTAEDCPRHEBXQ7AO8mxAl0DgEBD3SGhxSGElx0iyEAOw==') no-repeat;
}
.field{
font-size:24px;
}
.restore_box
{
width: 24px;
height: 23px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAXCAYAAAARIY8tAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMzQAADM0B9/0h3QAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGkSURBVEiJtdUxSFVRGAfwn+WgQaJN4dRUBPnGl5BDNQQNYQjR0tIS4RLtEVlIjQYFtWRTDW01RQSiEUhtBW3hIAkaFBSk2fM5XKXzzjvvvXPD/vDBOff+z/87/+/e850u1DXiIS4H80GM4iQOYB9+YBFv8QIftEE9igeB8GOsJThxzKBaJsE5fMsQDqOGO9gVindpLtEcjsVEfME8VtCHCg5vaYR4igvYaOVgI5q/wYkW7g9iOrHmdkhqZ/tmwkkKZ/Az2uRIpwR3MRBFT5skp/EnWP8+x0Ec1zo4uRfxh3Psl8GkwsU2zu90giXMBvPqTicgqD0OdWOqxOL5DM73YNzTjaslEuSgLxiv/Y8SVYLx51ak+PjnYgC//P1N78eE3biBJ/+Y4JbGc3AqfLkfr4OXV0qKH9XY2j+K2kxFo70aLmWKV/E12v1oinhRc3t4puiaKfQryhJfSo+2CamPOYHr0bM63m3FMvbiCI5rboAvcRarLTYFxvE74aZTTKO3nXCIIbzKFP6EsZRIzv8+pLgChxVX5B6sY0HROp4rylJLLd4ExpG2mIQ1Ut0AAAAASUVORK5CYII=");
background-repeat: no-repeat;
margin-left: 5px;
cursor: pointer;
}
</style>
<script>
var g_ticks=999;
function delChild(obj)
{
if (typeof obj === 'string' || obj instanceof String)
obj=document.getElementById(obj);
if(obj!=null)
{
while(true)
{
let c=obj.firstChild;
if(c!=null) obj.removeChild(c); else break;
}
}
return false;
}
function loadData(){
let req = new XMLHttpRequest();
req.onreadystatechange = function(req)
{
return function()
{
if (req.readyState == 4)
{
let obj=JSON.parse(req.responseText);
document.getElementById("ssid").value=obj.ssid;
document.getElementById("pass").value=obj.pass;
document.getElementById("mqtt").value=obj.mqtt;
document.getElementById("uuid").value=obj.uuid;
document.getElementById("topic").value=obj.topic;
document.getElementById("login").value=obj.login;
document.getElementById("password").value=obj.password;
updateSensors();
}
}
}(req);
req.open("GET",'/data/',true);
req.send();
}
function updateSensors(){
let req = new XMLHttpRequest();
req.onreadystatechange = function(req)
{
return function()
{
if (req.readyState == 4)
{
let obj=JSON.parse(req.responseText);
g_ticks = obj.ticks;
document.getElementById("cold").innerHTML=obj.cold;
document.getElementById("hot").innerHTML=obj.hot;
if(obj.leak) {
document.getElementById("leak").innerHTML = "Протечка обнаружена";
document.getElementById("leak").style.color='red';
}else {
document.getElementById("leak").innerHTML = "Протечка не обнаружена";
document.getElementById("leak").style.color='black';
}
let networks = document.getElementById("networks");
delChild(networks);
obj.networks.sort((a, b) => a.strength < b.strength ? 1 : -1);
for(let i=0;i<obj.networks.length;i++)
{
let quality;
if(obj.networks[i].strength <= -100)
quality = 0;
else if(obj.networks[i].strength >= -50)
quality = 100;
else
quality = 2 * (obj.networks[i].strength + 100);
let radio=`<table class="wifi_list" style="width: 100%;" onclick="document.getElementById('ssid').value='{name}'; document.getElementById('ch{id}').checked=true;"><tr><td style="width: 20px;"><input id="ch{id}" type="radio" name="radio" {checked}></td><td style="font-family:Verdana,sans-serif;">{name}</td><td class="divTableCell" style="width: 18px;"><div {lock}></div></td><td style="width: 45px;">{quality}%</td></tr></table>`;
radio = radio.replaceAll('{id}',i);
radio = radio.replaceAll('{name}',obj.networks[i].name.substring(0,22));
if(obj.networks[i].name==document.getElementById("ssid").value)
radio = radio.replace('{checked}','checked="checked"');
else
radio = radio.replace('{checked}','');
if(obj.networks[i].lock!=7)
radio = radio.replace('{lock}','class="lock"');
radio = radio.replaceAll('{quality}',quality);
let div = document.createElement('div');
div.innerHTML=radio;
networks.appendChild(div);
}
}
}
}(req);
req.open("GET",'/sensors/',true);
req.send();
}
function sendData() {
let formData = new FormData();
formData.append("ssid", document.getElementById("ssid").value);
formData.append("pass", document.getElementById("pass").value);
formData.append("hot", document.getElementById("newhot").value);
formData.append("cold", document.getElementById("newcold").value);
formData.append("mqtt", document.getElementById("mqtt").value);
formData.append("uuid", document.getElementById("uuid").value);
formData.append("topic", document.getElementById("topic").value);
formData.append("login", document.getElementById("login").value);
formData.append("password", document.getElementById("password").value);
let xhr = new XMLHttpRequest();
xhr.open("POST","/save/");
xhr.send(formData);
xhr.onload = function() {
if (xhr.status != 200) {
alert(`Ошибка ${xhr.status}: ${xhr.statusText}`);
} else {
//alert(`Готово, получили ${xhr.response.length} байт = `+xhr.response);
if(xhr.response!='ok') {
alert("Ошибка при записи, обновите страницу и попробуйте ещё раз.");
location.reload();
}else {
alert("Данные записаны.");
location.reload();
}
document.getElementById("newhot").value="";
document.getElementById("newcold").value="";
}
};
xhr.onerror = function() {
alert("Запрос не удался");
};
}
window.onload = function () {
loadData();
setInterval(function () {
if(g_ticks>0) {
updateSensors();
}
}, 5000);
setInterval(function () {
if(g_ticks>0) {
if(g_ticks!=999) {
document.getElementById("timerId").innerText = g_ticks;
g_ticks--;
}
if (g_ticks==0) {
alert('Нептун заснул.');
location.reload();
}
}
}, 1000);
document.getElementById("chbox").checked=false;
};
</script>
</head>
<body>
<div class="content1"><div class="content2">
<h1>Настройка</h1>
<p class="counter">Устройство заснёт через: <a id="timerId">___</a> секунд</p>
<h3>Выберите свою Wi-Fi сеть</h3>
<div id="networks" style="width: 100%; height: 150px; overflow-y: scroll; background-color: #e9e9e9; border-width: 1px;border-color: #4f4f4f; border-style: solid;">
</div>
<h3>Точка доступа</h3>
<input id="ssid" name="ssid" type="text" size="40">
<h3>Пароль</h3>
<input id="pass" name="pass" type="text" size="40">
<p>
Для проверки работоспособности спустите не меньше 4х литров воды
</p>
<h4>Текущее показание холодной воды</h4>
<h4 id="cold" style="color: #1655f5">___</h4>
<h3>Новое показание холодной воды</h3>
<table style="width: 100%;">
<tr>
<td>
<input class="it" id="newcold" min="0" max="99999999" step="1" type="number" autocomplete="off">
</td>
<td>
Литры
</td>
</tr>
</table>
<h4>Текущее показание горячей воды</h4>
<h4 id="hot" style="color: #f53410">___</h4>
<h3>Новое показание горячей воды</h3>
<table style="width: 100%;">
<tr>
<td>
<input class="it" id="newhot" min="0" max="99999999" step="1" type="number" autocomplete="off">
</td>
<td>
Литры
</td>
</tr>
</table>
<h3 id="leak">Протечка не обнаружена</h3>
<p>
<input id="chbox" name="chbox" style="width:20px;height:20px;" onclick="if(this.checked) document.getElementById('settings').style.display='block'; else document.getElementById('settings').style.display='none';" type="checkbox">
<label for="chbox" style="font-size:20px;">Дополнительные настройки</label>
</p>
<div id="settings" style="display: none;">
<p>Адрес MQTT сервера(брокера)</p>
<table style="width: 100%;">
<tr>
<td>
<input id="mqtt" name="mqtt" type="text" size="40" style="width: 100%;">
</td>
<td>
<div class="restore_box" onclick="document.getElementById('mqtt').value='ssl://observer.kz:8883';"></div>
</td>
</tr>
</table>
<p>Уникальный ID клиента</p>
<input id="uuid" name="uuid" type="text" size="40" disabled="disabled">
<p>Топик</p>
<table style="width: 100%;">
<tr>
<td>
<input id="topic" name="topic" type="text" size="40" value="neptune">
</td>
<td>
<div class="restore_box" onclick="document.getElementById('topic').value='neptune';"></div>
</td>
</tr>
</table>
<p>Логин</p>
<input id="login" name="login" type="text" size="40" value="">
<p>Пароль</p>
<input id="password" name="password" type="text" size="40" value="">
</div>
<br>
<button class="button" onclick="sendData();">Сохранить</button>
<br><br>
</div></div>
</body>
</html>

8
data/settings.txt Normal file
View File

@ -0,0 +1,8 @@
ssid=Node6
pass=isecretk
mqtt_server=192.168.200.100
mqtt_port=1883
mqtt_fingerprint=32:95:81:45:9C:32:F3:BD:B3:09:63:58:D3:94:AC:19:78:45:3B:15
mqtt_user=xxxxxx
mqtt_pass=xxxxxx
mqtt_topic=home/lights/main

1
data/uuid.txt Normal file
View File

@ -0,0 +1 @@
123456789012345

305
include/Bounce2.h Normal file
View File

@ -0,0 +1,305 @@
/*
The MIT License (MIT)
Copyright (c) 2013 thomasfredericks
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Main code by Thomas O Fredericks (tof@t-o-f.info)
Previous contributions by Eric Lowry, Jim Schimpf and Tom Harkaway
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef Bounce2_h
#define Bounce2_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
// Uncomment the following line for "LOCK-OUT" debounce method
//#define BOUNCE_LOCK_OUT
// Uncomment the following line for "BOUNCE_WITH_PROMPT_DETECTION" debounce method
//#define BOUNCE_WITH_PROMPT_DETECTION
#include <inttypes.h>
/**
@example bounce_basic.ino
Basic example of the Bounce class.
*/
/**
@example button_basic.ino
Basic example of the Button class.
*/
/**
@brief The Debouce class. Just the deboucing code separated from all harware.
*/
class Debouncer
{
// Note : this is private as it migh change in the futur
private:
static const uint8_t DEBOUNCED_STATE = 0b00000001;
static const uint8_t UNSTABLE_STATE = 0b00000010;
static const uint8_t CHANGED_STATE = 0b00000100;
private:
inline void changeState();
inline void setStateFlag(const uint8_t flag) {state |= flag;}
inline void unsetStateFlag(const uint8_t flag) {state &= ~flag;}
inline void toggleStateFlag(const uint8_t flag) {state ^= flag;}
inline bool getStateFlag(const uint8_t flag) {return((state & flag) != 0);}
public:
/*!
@brief Create an instance of the Debounce class.
@endcode
*/
Debouncer();
/**
@brief Sets the debounce interval in milliseconds.
@param interval_millis
The interval time in milliseconds.
*/
void interval(uint16_t interval_millis);
/*!
@brief Updates the pin's state.
Because Bounce does not use interrupts, you have to "update" the object before reading its value and it has to be done as often as possible (that means to include it in your loop()). Only call update() once per loop().
@return True if the pin changed state.
*/
bool update();
/**
@brief Returns the pin's state (HIGH or LOW).
@return HIGH or LOW.
*/
bool read();
/**
@brief Returns true if pin signal transitions from high to low.
*/
bool fell();
/**
@brief Returns true if pin signal transitions from low to high.
*/
bool rose();
public:
/**
@brief Returns true if the state changed on last update.
@return True if the state changed on last update. Otherwise, returns false.
*/
bool changed( ) { return getStateFlag(CHANGED_STATE); }
/**
@brief Returns the duration in milliseconds of the current state.
Is reset to 0 once the pin rises ( rose() ) or falls ( fell() ).
@return The duration in milliseconds (unsigned long) of the current state.
*/
unsigned long duration();
/**
@brief Returns the duration in milliseconds of the previous state.
Takes the values of duration() once the pin changes state.
@return The duration in milliseconds (unsigned long) of the previous state.
*/
unsigned long previousDuration();
void setStateChangeLastTime(unsigned long time){ stateChangeLastTime=time; };
protected:
void begin();
virtual bool readCurrentState() =0;
unsigned long previous_millis;
uint16_t interval_millis;
uint8_t state;
unsigned long stateChangeLastTime;
unsigned long durationOfPreviousState;
};
/**
@brief The Debouncer:Bounce class. Links the Deboucing class to a hardware pin.
*/
class Bounce : public Debouncer
{
public:
/*!
@brief Create an instance of the Bounce class.
@code
// Create an instance of the Bounce class.
Bounce() button;
@endcode
*/
Bounce();
/*!
@brief Attach to a pin and sets that pin's mode (INPUT, INPUT_PULLUP or OUTPUT).
@param pin
The pin that is to be debounced.
@param mode
A valid Arduino pin mode (INPUT, INPUT_PULLUP or OUTPUT).
*/
void attach(int pin, int mode);
/**
Attach to a pin for advanced users. Only attach the pin this way once you have previously set it up. Otherwise use attach(int pin, int mode).
*/
void attach(int pin);
Bounce(uint8_t pin, unsigned long interval_millis ) : Bounce() {
attach(pin);
interval(interval_millis);
}
////////////////
// Deprecated //
////////////////
/**
@brief Deprecated (i.e. do not use). Included for partial compatibility for programs written with Bounce version 1
*/
bool risingEdge() { return rose(); }
/**
@brief Deprecated (i.e. do not use). Included for partial compatibility for programs written with Bounce version 1
*/
bool fallingEdge() { return fell(); }
/**
@brief Deprecated (i.e. do not use). Included for partial compatibility for programs written with Bounce version 1
*/
protected:
uint8_t pin;
virtual bool readCurrentState() { return digitalRead(pin); }
virtual void setPinMode(int pin, int mode) {
#if defined(ARDUINO_ARCH_STM32F1)
pinMode(pin, (WiringPinMode)mode);
#else
pinMode(pin, mode);
#endif
}
};
/**
@brief The Debouncer:Bounce:Button class. The Button class matches an electrical state to a physical action.
*/
namespace Bounce2 {
// code declarations
class Button : public Bounce{
protected:
bool stateForPressed = 1; //
public:
/*!
@brief Create an instance of the Button class. By default, the pressed state is matched to a HIGH electrical level.
@code
// Create an instance of the Button class.
Button() button;
@endcode
*/
Button(){ }
/*!
@brief Set the electrical state (HIGH/LOW) that corresponds to a physical press. By default, the pressed state is matched to a HIGH electrical level.
@param state
The electrical state (HIGH/LOW) that corresponds to a physical press.
*/
void setPressedState(bool state){
stateForPressed = state;
}
/*!
@brief Get the electrical state (HIGH/LOW) that corresponds to a physical press.
*/
inline bool getPressedState() {
return stateForPressed;
};
/*!
@brief Returns true if the button is currently physically pressed.
*/
inline bool isPressed() {
return read() == getPressedState();
};
/*!
@brief Returns true if the button was physically pressed
*/
inline bool pressed() {
return changed() && isPressed();
};
/*!
@brief Returns true if the button was physically released
*/
inline bool released() {
return changed() && !isPressed();
};
};
};
#endif

184
include/PubSubClient.h Normal file
View File

@ -0,0 +1,184 @@
/*
PubSubClient.h - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#ifndef PubSubClient_h
#define PubSubClient_h
#include <Arduino.h>
#include "IPAddress.h"
#include "Client.h"
#include "Stream.h"
#define MQTT_VERSION_3_1 3
#define MQTT_VERSION_3_1_1 4
// MQTT_VERSION : Pick the version
//#define MQTT_VERSION MQTT_VERSION_3_1
#ifndef MQTT_VERSION
#define MQTT_VERSION MQTT_VERSION_3_1_1
#endif
// MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize().
#ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 256
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds. Override with setKeepAlive()
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 15
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. Override with setSocketTimeout()
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 15
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
// pass the entire MQTT packet in each write call.
//#define MQTT_MAX_TRANSFER_SIZE 80
// Possible values for client.state()
#define MQTT_CONNECTION_TIMEOUT -4
#define MQTT_CONNECTION_LOST -3
#define MQTT_CONNECT_FAILED -2
#define MQTT_DISCONNECTED -1
#define MQTT_CONNECTED 0
#define MQTT_CONNECT_BAD_PROTOCOL 1
#define MQTT_CONNECT_BAD_CLIENT_ID 2
#define MQTT_CONNECT_UNAVAILABLE 3
#define MQTT_CONNECT_BAD_CREDENTIALS 4
#define MQTT_CONNECT_UNAUTHORIZED 5
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
#define MQTTPUBLISH 3 << 4 // Publish message
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
#define MQTTPINGREQ 12 << 4 // PING Request
#define MQTTPINGRESP 13 << 4 // PING Response
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
#define MQTTReserved 15 << 4 // Reserved
#define MQTTQOS0 (0 << 1)
#define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1)
// Maximum size of fixed header and variable length size header
#define MQTT_MAX_HEADER_SIZE 5
#if defined(ESP8266) || defined(ESP32)
#include <functional>
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
#else
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
#endif
#define CHECK_STRING_LENGTH(l,s) if (l+2+strnlen(s, this->bufferSize) > this->bufferSize) {_client->stop();return false;}
class PubSubClient : public Print {
private:
Client* _client;
uint8_t* buffer;
uint16_t bufferSize;
uint16_t keepAlive;
uint16_t socketTimeout;
uint16_t nextMsgId;
unsigned long lastOutActivity;
unsigned long lastInActivity;
bool pingOutstanding;
MQTT_CALLBACK_SIGNATURE;
uint32_t readPacket(uint8_t*);
boolean readByte(uint8_t * result);
boolean readByte(uint8_t * result, uint16_t * index);
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
// Build up the header ready to send
// Returns the size of the header
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
IPAddress ip;
const char* domain;
uint16_t port;
Stream* stream;
int _state;
public:
PubSubClient();
PubSubClient(Client& client);
PubSubClient(IPAddress, uint16_t, Client& client);
PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
PubSubClient(uint8_t *, uint16_t, Client& client);
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
PubSubClient(const char*, uint16_t, Client& client);
PubSubClient(const char*, uint16_t, Client& client, Stream&);
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
~PubSubClient();
PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port);
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
PubSubClient& setClient(Client& client);
PubSubClient& setStream(Stream& stream);
PubSubClient& setKeepAlive(uint16_t keepAlive);
PubSubClient& setSocketTimeout(uint16_t timeout);
boolean setBufferSize(uint16_t size);
uint16_t getBufferSize();
boolean connect(const char* id);
boolean connect(const char* id, const char* user, const char* pass);
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
void disconnect();
boolean publish(const char* topic, const char* payload);
boolean publish(const char* topic, const char* payload, boolean retained);
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
boolean publish_P(const char* topic, const char* payload, boolean retained);
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
// Start to publish a message.
// This API:
// beginPublish(...)
// one or more calls to write(...)
// endPublish()
// Allows for arbitrarily large payloads to be sent without them having to be copied into
// a new buffer and held in memory at one time
// Returns 1 if the message was started successfully, 0 if there was an error
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
// Finish off this publish message (started with beginPublish)
// Returns 1 if the packet was sent successfully, 0 if there was an error
int endPublish();
// Write a single byte of payload (only to be used with beginPublish/endPublish)
virtual size_t write(uint8_t);
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
// Returns the number of bytes written
virtual size_t write(const uint8_t *buffer, size_t size);
boolean subscribe(const char* topic);
boolean subscribe(const char* topic, uint8_t qos);
boolean unsubscribe(const char* topic);
boolean loop();
boolean connected();
int state();
};
#endif

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

14
include/tools.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef TOOLS_H_
#define TOOLS_H_
#include <Arduino.h>
#include <LittleFS.h>
String readUUID();
String readLine(File& f);
String getIMEI();
String BeforeFirst(String& str,const char ch);
String AfterFirst(String& str,const char ch);
String CutBeforeFirst(String& str,char ch,bool cutch);
#endif /* TOOLS_H_ */

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

20
platformio.ini Normal file
View File

@ -0,0 +1,20 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp01_1m]
platform = espressif8266
board = esp01_1m
framework = arduino
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.1m64.ld
upload_port = COM6
monitor_port = COM6
monitor_speed = 115200
lib_deps = bblanchon/ArduinoJson@^6.17.3

155
src/Bounce2.cpp Normal file
View File

@ -0,0 +1,155 @@
// Please read Bounce2.h for information about the liscence and authors
#include "Bounce2.h"
//////////////
// DEBOUNCE //
//////////////
Debouncer::Debouncer():previous_millis(0)
, interval_millis(10)
, state(0) {}
void Debouncer::interval(uint16_t interval_millis)
{
this->interval_millis = interval_millis;
}
void Debouncer::begin() {
state = 0;
if (readCurrentState()) {
setStateFlag(DEBOUNCED_STATE | UNSTABLE_STATE);
}
#ifdef BOUNCE_LOCK_OUT
previous_millis = 0;
#else
previous_millis = millis();
#endif
}
bool Debouncer::update()
{
unsetStateFlag(CHANGED_STATE);
#ifdef BOUNCE_LOCK_OUT
// Ignore everything if we are locked out
if (millis() - previous_millis >= interval_millis) {
bool currentState = readCurrentState();
if ( currentState != getStateFlag(DEBOUNCED_STATE) ) {
previous_millis = millis();
changeState();
}
}
#elif defined BOUNCE_WITH_PROMPT_DETECTION
// Read the state of the switch port into a temporary variable.
bool readState = readCurrentState();
if ( readState != getStateFlag(DEBOUNCED_STATE) ) {
// We have seen a change from the current button state.
if ( millis() - previous_millis >= interval_millis ) {
// We have passed the time threshold, so a new change of state is allowed.
// set the STATE_CHANGED flag and the new DEBOUNCED_STATE.
// This will be prompt as long as there has been greater than interval_misllis ms since last change of input.
// Otherwise debounced state will not change again until bouncing is stable for the timeout period.
changeState();
}
}
// If the readState is different from previous readState, reset the debounce timer - as input is still unstable
// and we want to prevent new button state changes until the previous one has remained stable for the timeout.
if ( readState != getStateFlag(UNSTABLE_STATE) ) {
// Update Unstable Bit to macth readState
toggleStateFlag(UNSTABLE_STATE);
previous_millis = millis();
}
#else
// Read the state of the switch in a temporary variable.
bool currentState = readCurrentState();
// If the reading is different from last reading, reset the debounce counter
if ( currentState != getStateFlag(UNSTABLE_STATE) ) {
previous_millis = millis();
toggleStateFlag(UNSTABLE_STATE);
} else
if ( millis() - previous_millis >= interval_millis ) {
// We have passed the threshold time, so the input is now stable
// If it is different from last state, set the STATE_CHANGED flag
if (currentState != getStateFlag(DEBOUNCED_STATE) ) {
previous_millis = millis();
changeState();
}
}
#endif
return changed();
}
// WIP HELD
unsigned long Debouncer::previousDuration() {
return durationOfPreviousState;
}
unsigned long Debouncer::duration() {
return (millis() - stateChangeLastTime);
}
inline void Debouncer::changeState() {
toggleStateFlag(DEBOUNCED_STATE);
setStateFlag(CHANGED_STATE) ;
durationOfPreviousState = millis() - stateChangeLastTime;
stateChangeLastTime = millis();
}
bool Debouncer::read()
{
return getStateFlag(DEBOUNCED_STATE);
}
bool Debouncer::rose()
{
return getStateFlag(DEBOUNCED_STATE) && getStateFlag(CHANGED_STATE);
}
bool Debouncer::fell()
{
return !getStateFlag(DEBOUNCED_STATE) && getStateFlag(CHANGED_STATE);
}
////////////
// BOUNCE //
////////////
Bounce::Bounce()
: pin(0)
{}
void Bounce::attach(int pin) {
this->pin = pin;
// SET INITIAL STATE
begin();
}
void Bounce::attach(int pin, int mode){
setPinMode(pin, mode);
this->attach(pin);
}

769
src/PubSubClient.cpp Normal file
View File

@ -0,0 +1,769 @@
/*
PubSubClient.cpp - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#include "PubSubClient.h"
#include "Arduino.h"
PubSubClient::PubSubClient() {
this->_state = MQTT_DISCONNECTED;
this->_client = NULL;
this->stream = NULL;
setCallback(NULL);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(Client& client) {
this->_state = MQTT_DISCONNECTED;
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(addr, port);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(addr,port);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(addr, port);
setCallback(callback);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(addr,port);
setCallback(callback);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(ip, port);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(ip,port);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(ip, port);
setCallback(callback);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(ip,port);
setCallback(callback);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(domain,port);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(domain,port);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED;
setServer(domain,port);
setCallback(callback);
setClient(client);
this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED;
setServer(domain,port);
setCallback(callback);
setClient(client);
setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::~PubSubClient() {
free(this->buffer);
}
boolean PubSubClient::connect(const char *id) {
return connect(id,NULL,NULL,0,0,0,0,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
return connect(id,user,pass,0,0,0,0,1);
}
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
if (!connected()) {
int result = 0;
if(_client->connected()) {
result = 1;
} else {
if (domain != NULL) {
result = _client->connect(this->domain, this->port);
} else {
result = _client->connect(this->ip, this->port);
}
}
if (result == 1) {
nextMsgId = 1;
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
unsigned int j;
#if MQTT_VERSION == MQTT_VERSION_3_1
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 9
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 7
#endif
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
this->buffer[length++] = d[j];
}
uint8_t v;
if (willTopic) {
v = 0x04|(willQos<<3)|(willRetain<<5);
} else {
v = 0x00;
}
if (cleanSession) {
v = v|0x02;
}
if(user != NULL) {
v = v|0x80;
if(pass != NULL) {
v = v|(0x80>>1);
}
}
this->buffer[length++] = v;
this->buffer[length++] = ((this->keepAlive) >> 8);
this->buffer[length++] = ((this->keepAlive) & 0xFF);
CHECK_STRING_LENGTH(length,id)
length = writeString(id,this->buffer,length);
if (willTopic) {
CHECK_STRING_LENGTH(length,willTopic)
length = writeString(willTopic,this->buffer,length);
CHECK_STRING_LENGTH(length,willMessage)
length = writeString(willMessage,this->buffer,length);
}
if(user != NULL) {
CHECK_STRING_LENGTH(length,user)
length = writeString(user,this->buffer,length);
if(pass != NULL) {
CHECK_STRING_LENGTH(length,pass)
length = writeString(pass,this->buffer,length);
}
}
write(MQTTCONNECT,this->buffer,length-MQTT_MAX_HEADER_SIZE);
lastInActivity = lastOutActivity = millis();
while (!_client->available()) {
unsigned long t = millis();
if (t-lastInActivity >= ((int32_t) this->socketTimeout*1000UL)) {
_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
}
}
uint8_t llen;
uint32_t len = readPacket(&llen);
if (len == 4) {
if (buffer[3] == 0) {
lastInActivity = millis();
pingOutstanding = false;
_state = MQTT_CONNECTED;
return true;
} else {
_state = buffer[3];
}
}
_client->stop();
} else {
_state = MQTT_CONNECT_FAILED;
}
return false;
}
return true;
}
// reads a byte into result
boolean PubSubClient::readByte(uint8_t * result) {
uint32_t previousMillis = millis();
while(!_client->available()) {
yield();
uint32_t currentMillis = millis();
if(currentMillis - previousMillis >= ((int32_t) this->socketTimeout * 1000)){
return false;
}
}
*result = _client->read();
return true;
}
// reads a byte into result[*index] and increments index
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
uint16_t current_index = *index;
uint8_t * write_address = &(result[current_index]);
if(readByte(write_address)){
*index = current_index + 1;
return true;
}
return false;
}
uint32_t PubSubClient::readPacket(uint8_t* lengthLength) {
uint16_t len = 0;
if(!readByte(this->buffer, &len)) return 0;
bool isPublish = (this->buffer[0]&0xF0) == MQTTPUBLISH;
uint32_t multiplier = 1;
uint32_t length = 0;
uint8_t digit = 0;
uint16_t skip = 0;
uint32_t start = 0;
do {
if (len == 5) {
// Invalid remaining length encoding - kill the connection
_state = MQTT_DISCONNECTED;
_client->stop();
return 0;
}
if(!readByte(&digit)) return 0;
this->buffer[len++] = digit;
length += (digit & 127) * multiplier;
multiplier <<=7; //multiplier *= 128
} while ((digit & 128) != 0);
*lengthLength = len-1;
if (isPublish) {
// Read in topic length to calculate bytes to skip over for Stream writing
if(!readByte(this->buffer, &len)) return 0;
if(!readByte(this->buffer, &len)) return 0;
skip = (this->buffer[*lengthLength+1]<<8)+this->buffer[*lengthLength+2];
start = 2;
if (this->buffer[0]&MQTTQOS1) {
// skip message id
skip += 2;
}
}
uint32_t idx = len;
for (uint32_t i = start;i<length;i++) {
if(!readByte(&digit)) return 0;
if (this->stream) {
if (isPublish && idx-*lengthLength-2>skip) {
this->stream->write(digit);
}
}
if (len < this->bufferSize) {
this->buffer[len] = digit;
len++;
}
idx++;
}
if (!this->stream && idx > this->bufferSize) {
len = 0; // This will cause the packet to be ignored.
}
return len;
}
boolean PubSubClient::loop() {
if (connected()) {
unsigned long t = millis();
if ((t - lastInActivity > this->keepAlive*1000UL) || (t - lastOutActivity > this->keepAlive*1000UL)) {
if (pingOutstanding) {
this->_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
} else {
this->buffer[0] = MQTTPINGREQ;
this->buffer[1] = 0;
_client->write(this->buffer,2);
lastOutActivity = t;
lastInActivity = t;
pingOutstanding = true;
}
}
if (_client->available()) {
uint8_t llen;
uint16_t len = readPacket(&llen);
uint16_t msgId = 0;
uint8_t *payload;
if (len > 0) {
lastInActivity = t;
uint8_t type = this->buffer[0]&0xF0;
if (type == MQTTPUBLISH) {
if (callback) {
uint16_t tl = (this->buffer[llen+1]<<8)+this->buffer[llen+2]; /* topic length in bytes */
memmove(this->buffer+llen+2,this->buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
this->buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
char *topic = (char*) this->buffer+llen+2;
// msgId only present for QOS>0
if ((this->buffer[0]&0x06) == MQTTQOS1) {
msgId = (this->buffer[llen+3+tl]<<8)+this->buffer[llen+3+tl+1];
payload = this->buffer+llen+3+tl+2;
callback(topic,payload,len-llen-3-tl-2);
this->buffer[0] = MQTTPUBACK;
this->buffer[1] = 2;
this->buffer[2] = (msgId >> 8);
this->buffer[3] = (msgId & 0xFF);
_client->write(this->buffer,4);
lastOutActivity = t;
} else {
payload = this->buffer+llen+3+tl;
callback(topic,payload,len-llen-3-tl);
}
}
} else if (type == MQTTPINGREQ) {
this->buffer[0] = MQTTPINGRESP;
this->buffer[1] = 0;
_client->write(this->buffer,2);
} else if (type == MQTTPINGRESP) {
pingOutstanding = false;
}
} else if (!connected()) {
// readPacket has closed the connection
return false;
}
}
return true;
}
return false;
}
boolean PubSubClient::publish(const char* topic, const char* payload) {
return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,false);
}
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,retained);
}
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
return publish(topic, payload, plength, false);
}
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
if (connected()) {
if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2+strnlen(topic, this->bufferSize) + plength) {
// Too long
return false;
}
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,this->buffer,length);
// Add payload
uint16_t i;
for (i=0;i<plength;i++) {
this->buffer[length++] = payload[i];
}
// Write the header
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
return write(header,this->buffer,length-MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
return publish_P(topic, (const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0, retained);
}
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
uint8_t llen = 0;
uint8_t digit;
unsigned int rc = 0;
uint16_t tlen;
unsigned int pos = 0;
unsigned int i;
uint8_t header;
unsigned int len;
int expectedLength;
if (!connected()) {
return false;
}
tlen = strnlen(topic, this->bufferSize);
header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
this->buffer[pos++] = header;
len = plength + 2 + tlen;
do {
digit = len & 127; //digit = len %128
len >>= 7; //len = len / 128
if (len > 0) {
digit |= 0x80;
}
this->buffer[pos++] = digit;
llen++;
} while(len>0);
pos = writeString(topic,this->buffer,pos);
rc += _client->write(this->buffer,pos);
for (i=0;i<plength;i++) {
rc += _client->write((char)pgm_read_byte_near(payload + i));
}
lastOutActivity = millis();
expectedLength = 1 + llen + 2 + tlen + plength;
return (rc == (unsigned int)expectedLength);
}
boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
if (connected()) {
// Send the header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,this->buffer,length);
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
size_t hlen = buildHeader(header, this->buffer, plength+length-MQTT_MAX_HEADER_SIZE);
uint16_t rc = _client->write(this->buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
lastOutActivity = millis();
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
}
return false;
}
int PubSubClient::endPublish() {
return 1;
}
size_t PubSubClient::write(uint8_t data) {
lastOutActivity = millis();
return _client->write(data);
}
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
lastOutActivity = millis();
return _client->write(buffer,size);
}
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
uint8_t lenBuf[4];
uint8_t llen = 0;
uint8_t digit;
uint8_t pos = 0;
uint16_t len = length;
do {
digit = len & 127; //digit = len %128
len >>= 7; //len = len / 128
if (len > 0) {
digit |= 0x80;
}
lenBuf[pos++] = digit;
llen++;
} while(len>0);
buf[4-llen] = header;
for (int i=0;i<llen;i++) {
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
}
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
}
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
uint16_t rc;
uint8_t hlen = buildHeader(header, buf, length);
#ifdef MQTT_MAX_TRANSFER_SIZE
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
uint16_t bytesRemaining = length+hlen; //Match the length type
uint8_t bytesToWrite;
boolean result = true;
while((bytesRemaining > 0) && result) {
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
rc = _client->write(writeBuf,bytesToWrite);
result = (rc == bytesToWrite);
bytesRemaining -= rc;
writeBuf += rc;
}
return result;
#else
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
lastOutActivity = millis();
return (rc == hlen+length);
#endif
}
boolean PubSubClient::subscribe(const char* topic) {
return subscribe(topic, 0);
}
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
size_t topicLength = strnlen(topic, this->bufferSize);
if (topic == 0) {
return false;
}
if (qos > 1) {
return false;
}
if (this->bufferSize < 9 + topicLength) {
// Too long
return false;
}
if (connected()) {
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
this->buffer[length++] = (nextMsgId >> 8);
this->buffer[length++] = (nextMsgId & 0xFF);
length = writeString((char*)topic, this->buffer,length);
this->buffer[length++] = qos;
return write(MQTTSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::unsubscribe(const char* topic) {
size_t topicLength = strnlen(topic, this->bufferSize);
if (topic == 0) {
return false;
}
if (this->bufferSize < 9 + topicLength) {
// Too long
return false;
}
if (connected()) {
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
this->buffer[length++] = (nextMsgId >> 8);
this->buffer[length++] = (nextMsgId & 0xFF);
length = writeString(topic, this->buffer,length);
return write(MQTTUNSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
}
return false;
}
void PubSubClient::disconnect() {
this->buffer[0] = MQTTDISCONNECT;
this->buffer[1] = 0;
_client->write(this->buffer,2);
_state = MQTT_DISCONNECTED;
_client->flush();
_client->stop();
lastInActivity = lastOutActivity = millis();
}
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
const char* idp = string;
uint16_t i = 0;
pos += 2;
while (*idp) {
buf[pos++] = *idp++;
i++;
}
buf[pos-i-2] = (i >> 8);
buf[pos-i-1] = (i & 0xFF);
return pos;
}
boolean PubSubClient::connected() {
boolean rc;
if (_client == NULL ) {
rc = false;
} else {
rc = (int)_client->connected();
if (!rc) {
if (this->_state == MQTT_CONNECTED) {
this->_state = MQTT_CONNECTION_LOST;
_client->flush();
_client->stop();
}
} else {
return this->_state == MQTT_CONNECTED;
}
}
return rc;
}
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
return setServer(addr,port);
}
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
this->ip = ip;
this->port = port;
this->domain = NULL;
return *this;
}
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
this->domain = domain;
this->port = port;
return *this;
}
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
this->callback = callback;
return *this;
}
PubSubClient& PubSubClient::setClient(Client& client){
this->_client = &client;
return *this;
}
PubSubClient& PubSubClient::setStream(Stream& stream){
this->stream = &stream;
return *this;
}
int PubSubClient::state() {
return this->_state;
}
boolean PubSubClient::setBufferSize(uint16_t size) {
if (size == 0) {
// Cannot set it back to 0
return false;
}
if (this->bufferSize == 0) {
this->buffer = (uint8_t*)malloc(size);
} else {
uint8_t* newBuffer = (uint8_t*)realloc(this->buffer, size);
if (newBuffer != NULL) {
this->buffer = newBuffer;
} else {
return false;
}
}
this->bufferSize = size;
return (this->buffer != NULL);
}
uint16_t PubSubClient::getBufferSize() {
return this->bufferSize;
}
PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive) {
this->keepAlive = keepAlive;
return *this;
}
PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout) {
this->socketTimeout = timeout;
return *this;
}

593
src/main.cpp Normal file
View File

@ -0,0 +1,593 @@
/**
Created on: 01.01.2021
Счётчик воды и протечки WEB морда для ESP8266
1) Установить: http://wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:esp8266:esptool
Для 1MB флешь памяти, файловая система: http://wikihandbk.com/wiki/ESP8266:%D0%9F%D1%80%D0%BE%D1%88%D0%B8%D0%B2%D0%BA%D0%B8/Arduino/%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BE%D0%B9_%D0%B2_%D0%B0%D0%B4%D0%B4%D0%BE%D0%BD%D0%B5_ESP8266_%D0%B4%D0%BB%D1%8F_IDE_Arduino
На борту счётчика выход
2 входа для счётчика горячей и холодной (Подключено к STM8L051 геркон)
1 вход для датчика утечки (Подключено к STM8L051 200 ком резистор по моему)
1 кнопка для включения ESP8266 и перехода в настройки при длительном нажатии (10 секунд) а при коротком просто отправить данные по WIFI (Подключено к STM8L051 кнопка)
1 кнопочка для открытия кранов не смотря на утеку воды (для пожаробезопасности) (Подключено к STM8L051 кнопка)
Подробнее схемотехника на: https://easyeda.com/ru
Другая документация на O:\MyDocuments\projects\_Doc\Water_meter_observer
*/
//----------------------------------------------------------------------------------------------------
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h> // Include the WebServer library
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <Ticker.h> //Ticker Library
#include <LittleFS.h> //Прмер: https://github.com/lorol/ESPAsyncWebServer/blob/master/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino
#include <PubSubClient.h> //Для отправки данных по MQTT см.: https://www.emqx.io/blog/esp8266-connects-to-the-public-mqtt-broker
#include "Bounce2.h"
#include "tools.h"
//----------------------------------------------------------------------------------------------------
#define PIN_LED 02 //Пин светодиода
Bounce bounce = Bounce(); //Для избавления от дребезга контактов
int blink=LOW; //Для мигания при настройке
int blinkMSec=0; //Для мигания при настройке
ESP8266WebServer server(80); // Set web server and port number to 80
WiFiClient wifiClient;
WiFiClientSecure wifiClientS;
IPAddress local_IP(192,168,1,1); //Для точки доступа
IPAddress gateway(192,168,1,1); //Для точки доступа
IPAddress subnet(255,255,255,0); //Для точки доступа
IPAddress DNS_IP(192,168,1,1);
int lastPS=0; //сколько приконекченных
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;
//Для входа и выхода из режима настройки WIFI
#define CNF_TIME 300; //Сколько секунд даётся для настройки оборудования (600=5минут)
int ticks = -1; //Переменная для отсчёта секунд, как дойдёт до нуля то засыпаем
Ticker blinker; //Для подсчёта времени до входа в спящий режим (предполагаю просыпание по кнопке ресет)
String ssid="Node6";
String pass="isecretk";
PubSubClient mqttClient;
String mqtt_protocol = "tcp";
String mqtt_host = "observer.kz"; // Имя сервера MQTT
uint16_t mqtt_port = 1883; // Порт для подключения к серверу MQTT
String mqtt_fingerprint = ""; //Отпечаток сертификата
String mqtt_user = ""; // Логи для подключения к серверу MQTT
String mqtt_pass = ""; // Пароль для подключения к серверу MQTT
String mqtt_topic = "home/water/main"; /* 1=on, 0=off else toggle*/
#define LED_QOS 1 //0 - Без подтверждения 1 - С подтверждением 2 - С подтверждением и без возможности двойной отправки
int time5s=millis(); //Для реконекта каждые 5 секунд
String uuid=""; //Уникальный ID клиента (загружается из файловой системы)
String g_data; //Для накопления данных с последовательного порта
//----------------------------------------------------------------------------------------------------
int g_cold = -1; //Показание холодной воды
int g_hot = -1; //Показание горячей воды
int g_leak = -1; //Протечка 0 нет 1 есть
int g_volt = -1; //Вольт на акамуляторе
int g_tmpr = -100; //Температура на микроконтроллере
//----------------------------------------------------------------------------------------------------
bool configWebServer();
void setLampLight(int mode);
//----------------------------------------------------------------------------------------------------
//Load settings from file system to local variables
bool loadConfig()
{
File f = LittleFS.open("settings.txt", "r");
String line="";
do {
line=readLine(f);
if(line!=""){
if(BeforeFirst(line,'=')=="ssid") ssid=AfterFirst(line,'='); //Название сети
else if(BeforeFirst(line,'=')=="pass") pass=AfterFirst(line,'='); //Номер сети
else if(BeforeFirst(line,'=')=="mqtt_protocol") mqtt_protocol=AfterFirst(line,'=');
else if(BeforeFirst(line,'=')=="mqtt_host") mqtt_host=AfterFirst(line,'=');
else if(BeforeFirst(line,'=')=="mqtt_port") mqtt_port=AfterFirst(line,'=').toInt();
else if(BeforeFirst(line,'=')=="mqtt_fingerprint") mqtt_fingerprint=AfterFirst(line,'=');
else if(BeforeFirst(line,'=')=="mqtt_user") mqtt_user=AfterFirst(line,'=');
else if(BeforeFirst(line,'=')=="mqtt_pass") mqtt_pass=AfterFirst(line,'=');
else if(BeforeFirst(line,'=')=="mqtt_topic") mqtt_topic=AfterFirst(line,'=');
}
} while (line!="");
f.close();
Serial.println("SSID = "+ssid);
Serial.println("MQTT HOST = "+mqtt_host);
return true;
}
//----------------------------------------------------------------------------------------------------
//Save settings from local variables to file system
bool saveConfigs()
{
File f = LittleFS.open("settings.txt", "w");
if (!f) {
Serial.println("Count file open failed on update.");
} else {
f.println("ssid="+ssid);
f.println("pass="+pass);
f.println("mqtt_protocol="+mqtt_protocol);
f.println("mqtt_host="+mqtt_host);
f.println("mqtt_port="+String(mqtt_port));
f.println("mqtt_fingerprint="+mqtt_fingerprint);
f.println("mqtt_topic="+mqtt_topic);
f.println("mqtt_user="+mqtt_user);
f.println("mqtt_pass="+mqtt_pass);
f.close();
}
return true;
}
//=======================================================================
void handleNotFound() {
server.send(404, "text/plain", "FileNotFound");
}
//=======================================================================
//Отправляю показания датчиков при настройке нептуна
void handleSensors() {
String json;
json.reserve(128);
//Видимые сети
json = "{\"networks\":[";
int n = WiFi.scanNetworks();
Serial.print(n);
Serial.println(" network(s) found");
for (int i = 0; i < n; i++)
{
json += "{\"name\":\"";
json += WiFi.SSID(i); //Название сети
json += "\",\"lock\":";
json += WiFi.encryptionType(i); //Шифрование
json += ",\"strength\":";
json += WiFi.RSSI(i); //Уровень сигнала
json += "}";
if(i!=n-1) json += ",";
}
json += "],";
//Количество тиков до перехода в спящий режим
json += "\"ticks\":";
json += ticks;
json += ",\"hot\":123";
json += ",\"cold\":456";
json += ",\"leak\":false";
json += "}";
server.send(200, "application/json", json);
}
//=======================================================================
void handleData(){
String json;
json.reserve(128);
json = "{";
json += "\"ssid\":\"ssid\"";
json += ",\"pass\":\"pass\"";
json += ",\"mqtt\":\"mqtt\"";
json += ",\"uuid\":\"uuid\"";
json += ",\"topic\":\"topic\"";
json += ",\"login\":\"login\"";
json += ",\"password\":\"password\"";
json += "}";
server.send(200, "application/json", json);
}
//=======================================================================
void handleSave(){
Serial.println("+++Save+++");
if(server.args()>0){
for(int i=0;i<server.args();i++){
Serial.println("Nane = "+server.argName(i));
Serial.println(server.arg(i));
if(server.argName(i)=="ssid")
ssid=server.arg(i);
if(server.argName(i)=="pass")
pass=server.arg(i);
}
}
server.send(200, "text/html", "ok");
}
//=======================================================================
void handleMain() {
if(LittleFS.exists("/index.html")){
//Serial.println("File exists.");
File file = LittleFS.open("/index.html", "r");
if(file){
if(server.streamFile(file, "text/html")!= file.size()){
Serial.println("Sent less data than expected!"); //Отправленно меньше данных чем ожидалось
}
file.close();
}
}else{
Serial.println("File not exists!");
}
}
//----------------------------------------------------------------------------------------------------
//Подключиться к сохраненой точки доступа и поднять HTML сервер
bool connectToWIFIPoint(){
WiFi.mode(WIFI_STA); //WIFI_STA - Режим только клиента
WiFi.hostname("Water_meter_V01");
WiFi.setAutoReconnect(true);
WiFi.begin(ssid, pass);
/*Serial.print("Connecting to ");
int i = 60; //Чтобы больше минуты не ждал
while (WiFi.status() != WL_CONNECTED && i>0) { // Wait for the Wi-Fi to connect
delay(1000);
Serial.print(--i); Serial.print(' ');
}*/
return true;
}
//----------------------------------------------------------------------------------------------------
//Отключиться от точки доступа
bool disconnectFromWIFIPoint(){
WiFi.setAutoReconnect(false);
WiFi.disconnect(true);
return true;
}
//----------------------------------------------------------------------------------------------------
//Инициилизировать WIFI точку доступа для настройки выключателя а также поднять HTML сервер
bool createAP(){
//WiFi.mode(WIFI_AP_STA); //WIFI_AP_STA - Режим точки доступа и клиента WIFI_AP - Режим только точки доступа
WiFi.mode(WIFI_AP); //WIFI_AP - Режим только точки доступа
//Настройки точки доступа
if(WiFi.softAPConfig(local_IP, gateway, subnet)){
Serial.println("Ready WiFi.softAPConfig");
}
Serial.print("Setting soft-AP ... ");
boolean result = WiFi.softAP("Observer_V03");
if(result == true)
{
Serial.println("Ready");
IPAddress IP = WiFi.softAPIP();
Serial.println(IP);
//Serial.println(WiFi.localIP());
//Настройте DNS-сервер, перенаправляющий все домены на apIP
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", DNS_IP);
configWebServer();
}
else
{
Serial.println("Failed!");
}
return true;
}
//----------------------------------------------------------------------------------------------------
//Отключить программную точку доступа
bool deleteAP(){
Serial.print("softAPdisconnect();");
dnsServer.stop();
bool result=WiFi.softAPdisconnect(true);
//Гашу светодиод
digitalWrite(LED_BUILTIN,HIGH); //LOW
return result;
}
//----------------------------------------------------------------------------------------------------
//Функция для подсчёта времени до перехода в спящий режим
void tick_1s()
{
ticks--;
if(ticks==0){ //Переходим в спящий режим
Serial.print("Deep sleep!");
Serial.print("#0;!*"); //Отсылаю команду на STM8 чтобы он на CHIP_PU подал низский сигнал для отключения питания
}
//Если в течении 2х минут не перешли в режим глубокого сна то переходим в обычный сон
if(ticks==-600){
Serial.println("Sleep!");
Serial.print("#0;!*"); //Повторно отсылаю команду на STM8 чтобы он на CHIP_PU подал низский сигнал для отключения питания
ESP.deepSleep(0);
}
}
//=======================================================================
//Запрос на получение данных с микроконтроллера
//Для дебага написал тестовую программу клторая эиетирует STM8L по последовательному порту: O:\MyDocuments\projects\Workspace_C++Builder\NeptuneW01
bool getMode(){
Serial.println("#0;"); //Запрашиваю для какого режима работы включили WIFI
/*Serial.println("#1;"); //Запрашиваем '1'=холодная
Serial.println("#2;"); //Запрашиваем '2'=горячая
Serial.println("#3;"); //Запрашиваем '3'=протечки
Serial.println("#4;"); //Запрашиваем '4'=вольт на аккумуляторе
Serial.println("#5;"); //Запрашиваем '5'=температура на процессоре
*/
return true;
}
//----------------------------------------------------------------------------------------------------
bool configWebServer(){
Serial.println("configWebServer()");
server.on("/sensors/", HTTP_GET, handleSensors);
server.on("/data/", HTTP_GET, handleData);
server.on("/save/", HTTP_POST, handleSave);
server.on("/", HTTP_GET, handleMain);
server.onNotFound(handleNotFound);
// Start server
server.begin();
return true;
}
//----------------------------------------------------------------------------------------------------
// подключаемся к MQTT серверу
bool connectToMQTT(){
Serial.println("mqtt_protocol = "+mqtt_protocol+" mqtt_host = "+mqtt_host+" mqtt_port = " + String(mqtt_port));
// Fingerprint of the broker CA
// openssl x509 -in m2mqtt_srv.crt -sha1 -noout -fingerprint
if(mqtt_protocol=="tcp"){
mqttClient.setClient(wifiClient);
}else{
mqttClient.setClient(wifiClientS);
wifiClientS.setFingerprint(mqtt_fingerprint.c_str());
}
//Присваиваю настройки
mqttClient.setServer(mqtt_host.c_str(),mqtt_port);
if (WiFi.status() == WL_CONNECTED) {
if(!mqttClient.connected()) {
if (mqttClient.connect(uuid.c_str() )) {
Serial.println("connected");
/*mqttClient.setCallback(callback);
if(mqttClient.subscribe(String(mqtt_topic+"/set").c_str(),LED_QOS)) //Подписываемся на топик
Serial.println(String("Subscribe on ")+mqtt_topic);
else
Serial.println("Error subscribe.");*/
} else {
Serial.print("failed, status code =");
Serial.print(mqttClient.state());
}
}
}
return true;
}
//----------------------------------------------------------------------------------------------------
void setup()
{
Serial.begin(115200);
Serial.println("");
Serial.println("Init led pin");
//Настраиваем светодиод на плате
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH); // Выключаю светодиод
//Инициализирую файловую систему
if(LittleFS.begin())
Serial.println("SPIF FS ready");
else
Serial.println("SPIF FS failed");
uuid=readUUID(); //Загружаю уникальный идентификатор клиента
loadConfig(); //Загружаю настройки в локальные переменные
/*
blinker.attach(1, tick_1s); //Инициализирую таймер для подсчёта секунд работы в режиме настройки
*/
connectToWIFIPoint(); //Пытаюсь подключиться к настроенной точке доступа
/*
connectToMQTT(); //Пытаюсь опубликовать топик
*/
//Заранее инициализирую HTTP сервер чтобы не замарачиваться с логигой его работы...
configWebServer();
//Запрашиваю актуальную информацию по датчикам c STM8 микроконтролера по USART
getMode();
}
//----------------------------------------------------------------------------------------------------
//Проверяю правильный ли CRC в строке
bool checkCRC(String str){
String data=CutBeforeFirst(str,'!',false);
String crcS=CutBeforeFirst(str,'*',true);
//Подсчитываю CRC и проверяю с тем что передалось
unsigned char crcD=0;
for(unsigned int i=0;i<data.length();i++){
crcD+=data[i];
}
if(crcD==crcS.toInt()) //CRC is OK
{
return true;
}else
{
Serial.println("Incom CRC = "+crcS+" calc CRC = "+String(crcD));
return false;
}
}
//----------------------------------------------------------------------------------------------------
bool first=true;
//=======================================================================
void loop(){
mqttClient.loop();
dnsServer.processNextRequest(); //DNS
server.handleClient(); //Обрабатываем запрос клиента (без этого не берётся IP адресс)
//Как приконнектились отображаю IP адрес
if(WiFi.status() == WL_CONNECTED){
if(first){ //Если приконектились или переконектились то отображаю IP адресс
Serial.println();
Serial.print("Local IP address:\t");
Serial.println(WiFi.localIP());
first=false;
}
}else{
first=true;
}
//если кто приконектился то выводим это в консоль
if(WiFi.softAPgetStationNum()!=lastPS)
{
lastPS=WiFi.softAPgetStationNum();
Serial.printf("Stations connected to soft-AP = %d\n", WiFi.softAPgetStationNum());
}
/*
//После запуска спрашиваем в каком режиме работать настройки или передачи показаний
if(millis()-time5s>30000){
getMode();
time5s=millis();
}
*/
/*
mqttClient.loop();
dnsServer.processNextRequest(); //DNS
server.handleClient(); //Обрабатываем запрос клиента (без этого не берётся IP адресс)
if(ticks>0){
//В режиме конфигурации каждые 500 миллисекунд меняем цвет зелёного светодиода
if(millis()-blinkMSec>500){
blink=!blink;
digitalWrite(LED_BUILTIN,blink);
blinkMSec=millis();
}
}else{
//Если в нормальном режиме функционирования
//Проверяю соединение с MQTT и если не соединён то пытаюсь подключиться каждые 10 секунд
if(millis()-time5s>10000){
if(!mqttClient.connected()) {
connectToMQTT();
}
time5s=millis();
}
}
*/
//Блок кода для общения с энергоэфективным микроконтроллером
int inByte = 0;
if (Serial.available() > 0) { //если есть доступные данные то обрабатываем их
//Serial.println("available = "+String(Serial.available()));
// считываем байты в строку до символа с символа '#' до символа '*'
for(int i=0;i<Serial.available();i++){
inByte = Serial.read();
if(inByte=='#') g_data=""; //Новая строка
g_data+=(char)inByte;
if(inByte=='*') break;
}
//Если первый и последний символ в строке равен нужным сиволам то это полный ответ c CRC суммой
if(g_data[0]=='#' && g_data[g_data.length()-1]=='*')
{
g_data[0]='@'; //Чтобы команда заново не выполнилась
Serial.print("Length = "+String(g_data.length())+" ansver = "+g_data);
g_data[0]='#';
Serial.println("");
if(checkCRC(g_data)) //Проверяю CRC принятых данных
{
String cmd=CutBeforeFirst(g_data,';',false); //Отделяю команду
if(cmd=="#0;"){ //'0'=проверка зачем разбудили WIFI
String value=CutBeforeFirst(g_data,'!',true); //Если 1 то отправлять данные на сервер если 0 то войти в режим настройки в качестве точки доступа
Serial.print("value mode = ");
Serial.println(value);
if(value=="1"){ //Если разбудили для того чтобы передать данные
Serial.println("Send data mode");
if(deleteAP()){
connectToWIFIPoint();
}
}
if(value=="0"){ //Если разбудили для того чтобы настроить оборудование
Serial.println("Config mode");
if(disconnectFromWIFIPoint()){
if(createAP()){
ticks = CNF_TIME;
}
}
}
}
if(cmd=="#1;"){ //'1'=холодная
String value=CutBeforeFirst(g_data,'!',true);
Serial.print("value cold = ");
Serial.println(value);
g_cold=value.toInt();
}
if(cmd=="#2;"){ //'2'=горячая
String value=CutBeforeFirst(g_data,'!',true);
Serial.print("value hot = ");
Serial.println(value);
g_hot=value.toInt();
}
if(cmd=="#3;"){ //'3'=протечки
String value=CutBeforeFirst(g_data,'!',true);
Serial.print("value leak = ");
Serial.println(value);
g_leak=value.toInt();
}
if(cmd=="#4;"){ //'4'=вольт на аккумуляторе
String value=CutBeforeFirst(g_data,'!',true);
Serial.print("value volt = ");
Serial.println(value);
g_volt=value.toInt();
}
if(cmd=="#5;"){ //'5'=температура
String value=CutBeforeFirst(g_data,'!',true);
Serial.print("value tmrt = ");
Serial.println(value);
g_tmpr=value.toInt();
}
}
g_data=""; //Обработали ответ
}
}
//Как все данные накопились отправляем их по MQTT на сервер
if(WiFi.status() == WL_CONNECTED && g_cold!=-1 && g_hot!=-1 && g_leak!=-1 && g_volt !=-1){
String data="{";
data+="\"cold\":"+String(g_cold)+","; // Горячая
data+="\"hot\":"+String(g_hot)+","; // Холодная
data+="\"leak\":"+String(g_leak)+","; // Протечки
data+="\"volt\":"+String(g_volt); // Вольт на акумуляторе
if(g_tmpr>-100)
data+=",\"tmpr\":"+String(g_tmpr); // Температура
data+="}";
mqttClient.publish(mqtt_topic.c_str(), data.c_str(), true); //Отправляю данные в топик (С флагом RETAIN чтобы слушатели которые подключились поздно получили тек. состояние)
Serial.println(data);
//Как отправили данные устанавливаю время ожидания входа в сон на 5 секунд чтобы ESP8266 заснул
ticks=5;
//Очищаю данные
g_cold=-1;
g_hot=-1;
g_leak=-1;
g_volt=-1;
g_tmpr=-100;
}
}

82
src/tools.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "tools.h"
#include <LittleFS.h>
//----------------------------------------------------------------------------------------------------
//Загрузить либо сгенерировать уникальный ID для идентификации устройства
String readUUID()
{
File fr = LittleFS.open("uuid.txt", "r");
String uuid=readLine(fr);
fr.close();
if(uuid.length()<10){
uuid=getIMEI();
File fw = LittleFS.open("uuid.txt", "w");
fw.write(uuid.c_str());
fw.close();
}
Serial.println("uuid = "+uuid);
return uuid;
}
//----------------------------------------------------------------------------------------------------
//Прочитать строку из файла, до символа \n
String readLine(File& f)
{
char str[255];
memset(str, 0, sizeof(str));
for(size_t i=0;i<sizeof(str);i++){
size_t len = f.readBytes(&str[i],1);
if(str[i]=='\n' || len<1)
{
str[i]=0;
break;
}
if(str[i]=='\r'){
str[i]=0;
i--;
}
}
return str;
}
//---------------------------------------------------------------------------
String getIMEI(){
String result="";
for(int i=0;i<15;i++){
result += String(random(10));
}
return result;
}
//---------------------------------------------------------------------------
String BeforeFirst(String& str,const char ch){
int pos=str.indexOf(ch);
if(pos>0)
return str.substring(0, pos);
else
return "";
}
//---------------------------------------------------------------------------
String AfterFirst(String& str,const char ch){
int pos=str.indexOf(ch);
if(pos>0)
return str.substring(pos+1,str.length());
else
return "";
}
//---------------------------------------------------------------------------
//Вырезать строку до заданного символа, если сивола нет то всю строку
String CutBeforeFirst(String& str,char ch,bool cutch=true)
{
int pos=str.indexOf(ch);
String result;
result.reserve(20);
if(pos>0){
if(cutch)
result=str.substring(0, pos);
else
result=str.substring(0, pos+1);
str=str.substring(pos+1);
}else{
result=str;
str="";
}
return result;
}
//----------------------------------------------------------------------------------------------------

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html