[env:esp07]
platform = espressif8266
board = esp07
framework = arduino
board_build.filesystem = littlefs
monitor_speed = 115200
#include "button_control.h"
#include "config.h"
#include "load_control.h"
#include "network.h"
#include "state.h"
ADC_MODE(ADC_VCC);
Config CFG;
State ST;
LoadsControl LC;
ButtonControl BC;
NetManager NM;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// init state
ST.init();
// read config from EEPROM
CFG.get();
// init loads
LC.init();
// init buttons
BC.init();
// init network
NM.init();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// main loop
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
// update time
ST.updateClock();
// loads control
LC.control();
// reaction fo change buttons state
BC.logic();
// controlling change state buttons
BC.check();
// network control
NM.control();
}
#ifndef CONFIG_H
#define CONFIG_H
#include <Arduino.h>
// Config
class Config {
public:
char host[32];
char user[16];
char pass[16];
bool wifimode;
char ssid[16];
char ssidpass[16];
bool ipmode;
char ip[16];
char gateway[16];
char subnet[16];
char primaryDNS[16];
char secondaryDNS[16];
uint32_t maxon;
// read config from EEPROM, if this is the first start, then write
void get();
// write config to EEPROM
void put();
};
extern Config CFG;
#endif // CONFIG_H
#include "config.h"
#include <EEPROM.h>
#define INIT_EEPROM_ADDR 0
#define INIT_EEPROM_KEY 1
#define SAVE_EEPROM_ADDR 1
#define EEPROM_MAX_SIZE 512
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// read config from EEPROM, if this is the first start, then write the value in
// EEPROM
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Config::get() {
EEPROM.begin(EEPROM_MAX_SIZE);
if (EEPROM.read(INIT_EEPROM_ADDR) != INIT_EEPROM_KEY) { // first start
EEPROM.write(INIT_EEPROM_ADDR, INIT_EEPROM_KEY); // write key
// set default values
strcpy(host, "WIFI-RELAY");
strcpy(user, "user");
strcpy(pass, "pass");
wifimode = 1;
strcpy(ssid, "xxx");
strcpy(ssidpass, "xxxxxxx");
ipmode = 1;
strcpy(ip, "192.168.1.189");
strcpy(gateway, "192.168.1.1");
strcpy(subnet, "255.255.255.0");
strcpy(primaryDNS, "192.168.1.1");
strcpy(secondaryDNS, "8.8.8.8");
maxon = 2000;
// save
put();
}
EEPROM.get(SAVE_EEPROM_ADDR, CFG);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// write config to EEPROM
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Config::put() {
EEPROM.put(SAVE_EEPROM_ADDR, CFG);
EEPROM.commit();
}
#ifndef STATE_H
#define STATE_H
#include <Arduino.h>
#include "network.h"
#undef DEBUG
#define DEBUG // for debug: DPRINT & DPRINTLN
#ifdef DEBUG
#define DPRINT(...) Serial.print(__VA_ARGS__) // debug print
#define DPRINTLN(...) Serial.println(__VA_ARGS__) // debug print with new line
#define DPRINTF(...) Serial.printf(__VA_ARGS__) // debug printf
#else
#define DPRINT(...) // blank line
#define DPRINTLN(...) // blank line
#define DPRINTF(...) // blank line
#endif
// return amount object
template <typename T, size_t n>
inline size_t arraySize(const T (&arr)[n]) {
return n;
}
class State {
public:
// set start state
void init();
// get safe mode
inline bool isSafeMode() { return FLAGS.safeMode; }
// get safe mode text
inline String getSafeModeText() { return FLAGS.safeMode ? "ON" : "OFF"; }
// set open gate by num
inline void open(uint8_t num) {
extern NetManager NM;
if (num == 0)
FLAGS.firstState = true;
else if (num == 1)
FLAGS.secondState = true;
NM.sendEvents(getData(num), "change");
}
// set close gate by num
inline void close(uint8_t num) {
extern NetManager NM;
if (num == 0)
FLAGS.firstState = false;
else if (num == 1)
FLAGS.secondState = false;
NM.sendEvents(getData(num), "change");
}
// get state gate by num
inline char* getData(uint8_t num) {
static char _return[24];
sprintf(_return, "{\"num\":%d,\"state\":%d}", (num + 1),
(uint8_t)getState(num));
return _return;
}
// get state gate by num
inline bool getState(uint8_t num) {
if (num == 0)
return FLAGS.firstState;
else if (num == 1)
return FLAGS.secondState;
return false;
}
// get state gate Text by num
inline String getStateText(uint8_t num) {
bool flag = FLAGS.firstState;
if (num == 1) flag = FLAGS.secondState;
return flag ? "ОТКРЫТО" : "ЗАКРЫТО";
}
// get color gate by num
inline String getColor(uint8_t num) {
bool flag = FLAGS.firstState;
if (num == 1) flag = FLAGS.secondState;
return flag ? "#F08080" : "#98FB98";
}
// on flag wifi connect
inline void setConnect() { FLAGS.connectWIFI = true; }
// off flag wifi connect
inline void setDisconnect() { FLAGS.connectWIFI = false; }
// get flag wifi connect
inline bool isConnect() { return FLAGS.connectWIFI; }
// fill current time
void updateClock();
// get low part current time
inline uint32_t getTime() { return lowPartCurrentTime; }
// get full current time
inline uint64_t getFullTime() {
return (uint64_t)highPartCurrentTime << 32 | lowPartCurrentTime;
}
// get uptime string
char *getUptimeText();
private:
// low part uptime
uint32_t lowPartCurrentTime;
// high part uptime
uint32_t highPartCurrentTime;
// flags state
struct {
bool safeMode : 1; // run without password in AP mode
bool firstState : 1; // state near gate
bool secondState : 1; // state far gate
bool connectWIFI : 1; // is connect to WIFI
} FLAGS;
};
extern State ST;
#endif // STATE_H
#include "state.h"
#define SERIAL_SPEED 115200
#define SERIAL_DATA 45
#define SERIAL_TIME_SLEEP 1000
#define SERIAL_TIME_WAIT 10
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// first init state
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void State::init() {
Serial.begin(SERIAL_SPEED);
#ifdef DEBUG
// Serial.setDebugOutput(true);
delay(SERIAL_TIME_SLEEP);
#endif
DPRINTLN(F("start"));
Serial.write(SERIAL_DATA);
delay(SERIAL_TIME_WAIT);
uint8_t incomingByte = 0;
if (Serial.available() > 0) {
incomingByte = Serial.read();
if (SERIAL_DATA == incomingByte)
FLAGS.safeMode = true;
else
FLAGS.safeMode = false;
}
updateClock();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// set current time
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void State::updateClock() {
uint32_t newCurrentTime = millis();
if (newCurrentTime < lowPartCurrentTime) highPartCurrentTime++;
lowPartCurrentTime = newCurrentTime;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// get string uptime
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
char* State::getUptimeText() {
uint64_t mills = getFullTime();
static char _return[32];
uint32_t secs = mills / 1000;
uint32_t mins = secs / 60;
uint16_t hours = mins / 60;
uint16_t days = hours / 24;
mills -= secs * 1000;
secs -= mins * 60;
mins -= hours * 60;
hours -= days * 24;
sprintf(_return, "%d days %2.2d:%2.2d:%2.2d %3.3d", (uint8_t)days,
(uint8_t)hours, (uint8_t)mins, (uint8_t)secs, (uint16_t)mills);
return _return;
}
#ifndef NETWORK_H
#define NETWORK_H
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
class NetManager {
private:
static String processor(const String &var);
void connect();
void setAnsvers();
public:
void init();
void control();
void sendEvents(const char *data, const char *tag);
void postProcessing(AsyncWebServerRequest *request);
};
#endif // NETWORK_H
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// setting ansvers for network requests
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void NetManager::setAnsvers() {
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
request->send(LittleFS, "/index.html", String(), false, processor);
});
// Route for /set web page
server.on("/set", HTTP_ANY, [this](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
if (request->method() == HTTP_POST) {
postProcessing(request);
}
request->send(LittleFS, "/settings.html", String(), false, processor);
});
// Route for /reset web page
server.on("/reset", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
request->send(LittleFS, "/settings.html", String(), false, processor);
ESP.restart();
});
// Button 1 control
server.on("/button1", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
LC.on(L_NEAR);
request->send(200, "application/json", "{'ok':1}");
});
// Button 2 control
server.on("/button2", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
LC.on(L_FAR);
request->send(200, "application/json", "{'ok':1}");
});
// api
server.on("/sensors", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->authenticate(CFG.user, CFG.pass) && !ST.isSafeMode())
return request->requestAuthentication();
String answer = "hostname:" + String(CFG.host);
answer += ";GATE1:" + String(ST.getState(0));
answer += ";GATE2:" + String(ST.getState(1));
answer += ";";
request->send(200, "text/html", answer);
});
// route roe all static files
server.serveStatic("/", LittleFS, "/").setCacheControl("max-age:6000000");
// if page not found
server.onNotFound([](AsyncWebServerRequest* request) {
request->send(404, "404: Not found");
});
}
#ifndef LOAD_CONTROL_H
#define LOAD_CONTROL_H
#include <Arduino.h>
class LoadsControl {
public:
// init load control
void init();
// set on
void on(uint8_t num);
// set off
void off(uint8_t num);
// check
void control();
// get state load
inline bool isOn(uint8_t num) { return L[num].flag.state; }
private:
// one load
struct Load {
uint8_t pin; // pin load
uint32_t start; // start time
// flags for load
struct LoadFlag_t {
bool state : 1;
bool autoOff : 1;
} flag; // load's flags
};
// array loads
static Load L[];
// amount loads
uint8_t countL = 0;
};
extern LoadsControl LC;
enum loadNum_t {
L_NEAR, // 0
L_FAR, // 1
L_LED, // 2
};
#endif // LOAD_CONTROL_H
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// control loads
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void LoadsControl::control() {
// auto off relay
for (uint8_t i = 0; i < countL; i++) {
if (L[i].flag.state && L[i].flag.autoOff && CFG.maxon &&
((ST.getTime() - L[i].start) > CFG.maxon)) {
off(i);
}
}
// switching the connection led
if (ST.isConnect() && !L[L_LED].flag.state) {
on(L_LED);
} else if (!ST.isConnect() && L[L_LED].flag.state) {
off(L_LED);
}
}
#ifndef BUTTON_CONTROL_H
#define BUTTON_CONTROL_H
#include <Arduino.h>
struct Button {
uint8_t pin;
bool state; // current state
uint32_t startChange; // time begin change
uint32_t timeProtect; // bounce protect timer
bool change;
};
class ButtonControl {
private:
uint8_t countB;
static Button B[];
public:
void init();
void check();
void logic();
inline uint8_t getCount() { return countB; }
inline bool isChangeDown(uint8_t num) {
return B[num].change && !B[num].state;
}
inline bool isChangeUp(uint8_t num) {
return B[num].change && B[num].state;
}
};
enum buttonNum_t {
BT_FIRST, // 0
BT_SECOND, // 1
BT_FIRST_SENS, // 2
BT_SECOND_SENS, // 3
};
#endif // BUTTON_CONTROL_H
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Управление воротами</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
</head>
<body>
<div class="pure-menu pure-menu-horizontal">
<ul class="pure-menu-list">
<li class="pure-menu-item pure-menu-selected">
<a href="/" class="pure-menu-link">Управление</a>
</li>
<li class="pure-menu-item">
<a href="/set" class="pure-menu-link">Настройки</a>
</li>
</ul>
</div>
<div class="pure-g">
<div class="pure-u-2-3">
<div>
<h1>СОСТОЯНИЕ ВОРОТ</h1>
<table class="pure-table pure-table-bordered">
<tbody>
<tr>
<td>УЛИЦА</td>
<td id="gate1" style="background-color:%BGCOLOR1%;">%GATE1%</td>
</tr>
<tr>
<td>ГАРАЖ</td>
<td id="gate2" style="background-color:%BGCOLOR2%;">%GATE2%</td>
</tr>
</tbody>
</table>
<h1>УПРАВЛЕНИЕ ВОРОТАМИ</h1>
<p>
<button name="button1" class="pure-button pure-button-primary button-xlarge">УЛИЦА</button>
</p>
<p>
<button name="button2" class="pure-button pure-button-primary button-xlarge">ГАРАЖ</button>
</p>
</div>
</div>
</div>
</body>
<script>
const buttons = document.querySelectorAll(".pure-button");
buttons.forEach(function (bt) {
bt.addEventListener('click', function (event) {
bt.classList.remove("pure-button-primary");
fetch('/' + bt.name).then(function (response) {
if (response.status == 200) {
setTimeout(() => {
bt.classList.add("pure-button-primary");
}, 800);
} else {
alert("Error HTTP: " + response.status);
}
})
.catch(function (err) {
alert("Error network: " + err);
});
})
})
if (!!window.EventSource) {
var source = new EventSource('/events');
source.addEventListener('open', (e) => {
console.log("Events Connected");
}, false);
source.addEventListener('error', (e) => {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnected");
}
}, false);
source.addEventListener('change', (e) => {
console.log(e.data);
const data = JSON.parse(e.data);
const { num, state } = data;
const id = 'gate' + num;
document.getElementById(id).innerHTML = state ? 'ОТКРЫТО' : 'ЗАКРЫТО';
document.getElementById(id).style.backgroundColor = state ? '#F08080' : '#98FB98';
}, false);
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Управление воротами</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
</head>
<body>
<div class="pure-menu pure-menu-horizontal">
<ul class="pure-menu-list">
<li class="pure-menu-item">
<a href="/" class="pure-menu-link">Управление</a>
</li>
<li class="pure-menu-item pure-menu-selected">
<a href="/set" class="pure-menu-link">Настройки</a>
</li>
</ul>
</div>
<div class="pure-g">
<div class="pure-u-2-3">
<div>
<h1>STATE</h1>
<table class="pure-table pure-table-bordered">
<tbody>
<tr>
<td>Safe mode</td>
<td>%SAFEMODE%</td>
</tr>
<tr>
<td>RSSI</td>
<td>%RSSI% dBm</td>
</tr>
<tr>
<td>Free memory</td>
<td>%MEM% B</td>
</tr>
<tr>
<td>Voltage</td>
<td>%VOLTAGE% mV</td>
</tr>
<tr>
<td>Uptime</td>
<td>%UPTIME%</td>
</tr>
</tbody>
</table>
<h1>SETTINGS</h1>
<form class="pure-form pure-form-aligned" method="post" action="/set" onsubmit="return validate()">
<fieldset>
<legend>Base</legend>
<div class="pure-control-group">
<label for="host">Host</label>
<input name="host" type="text" id="host" placeholder="Host name" value="%HOST%">
</div>
<legend>HTTP basic authentication</legend>
<div class="pure-control-group">
<label for="name">Username</label>
<input name="name" type="text" id="name" placeholder="Username" value="%USER%">
</div>
<div class="pure-control-group">
<label for="password">Password</label>
<input name="password" type="password" id="password" placeholder="Password" value="%PASSWORD%">
<button class="pure-button toggle-button" type="button">show</button>
</div>
<legend>WIFI settings</legend>
<div class="pure-control-group">
<label for="wifimode">WIFI mode</label>
<select name="wifimode" id="wifimode">
<option %APSELECTED% value="ap">AP</option>
<option %STSELECTED% value="station">STATION</option>
</select>
</div>
<div class="pure-control-group">
<label for="ssid">SSID</label>
<input name="ssid" type="text" id="ssid" placeholder="SSID" value="%SSID%">
</div>
<div class="pure-control-group">
<label for="ssidpass">WIFI-Password</label>
<input name="ssidpass" type="password" id="ssidpass" placeholder="WIFI password" value="%SSIDPASS%">
<button class="pure-button toggle-button" type="button">show</button>
</div>
<div class="pure-control-group">
<label for="ipmode">IP mode</label>
<select name="ipmode" id="ipmode">
<option %DHCPSELECTED% value="dhcp">DHCP</option>
<option %FIXSELECTED% value="static">STATIC</option>
</select>
</div>
<div class="pure-control-group">
<label for="ip">IP</label>
<input name="ip" type="text" id="ip" placeholder="xxx.xxx.xxx.xxx" value="%IP%" class="ipaddress">
</div>
<div class="pure-control-group">
<label for="gateway">Gateway</label>
<input name="gateway" type="text" id="gateway" placeholder="xxx.xxx.xxx.xxx" value="%GATEWAY%"
class="ipaddress">
</div>
<div class="pure-control-group">
<label for="subnet">Subnet</label>
<input name="subnet" type="text" id="subnet" placeholder="xxx.xxx.xxx.xxx" value="%SUBNET%"
class="ipaddress">
</div>
<div class="pure-control-group">
<label for="primaryDNS">Primary DNS</label>
<input name="primaryDNS" type="text" id="primaryDNS" placeholder="xxx.xxx.xxx.xxx" value="%PRIMARYDNS%"
class="ipaddress">
</div>
<div class="pure-control-group">
<label for="secondaryDNS">Secondary DNS</label>
<input name="secondaryDNS" type="text" id="secondaryDNS" placeholder="xxx.xxx.xxx.xxx"
value="%SECONDARYDNS%" class="ipaddress">
</div>
<legend>Device settings</legend>
<div class="pure-control-group">
<label for="maxon">On time</label>
<input name="maxon" type="text" id="maxon" placeholder="On time" value="%MAXON%">
</div>
<div class="pure-controls">
<button type="submit" class="pure-button pure-button-primary">Save</button>
</div>
</fieldset>
</form>
</div>
<p>
<a href="/reset">reset</a>
</p>
</div>
</div>
<script>
const toggleButtons = document.querySelectorAll(".toggle-button");
toggleButtons.forEach(function (tb) {
tb.addEventListener('click', function () {
if (this.previousElementSibling.type === 'password') {
this.previousElementSibling.type = 'text';
this.textContent = 'hide';
} else if (tb.previousElementSibling.type === 'text') {
this.previousElementSibling.type = 'password';
this.textContent = 'show';
}
})
})
function validate() {
const ips = document.querySelectorAll(".ipaddress");
const ipformat = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
let retval = true;
ips.forEach(function (ip) {
if (!ip.value.match(ipformat)) {
ip.classList.add("invalid");
retval = false;
ip.onfocus = function () {
if (this.classList.contains('invalid')) {
this.classList.remove('invalid');
}
}
}
})
return retval;
}
</script>
</body>
</html>
+54 |
4335
84
|
+173 |
4024
57
|
К ТЗ есть вопросы. И мелкие спорные моменты по конструкции. К примеру, индикация срабатывания реле нафиг не нужна в итоговом изделии, а для отладки положить на плату пару деталек не проблема. В качестве итоговой индикации я бы использовал звуковое оповещение, огонек не всегда информативен. Ну или более заметный проблесковый.
ЗЫ. Вроде паста нормальная, но на фото припой не очень блестит.
Может ракурс фото не очень, паста блестит.
А недоступность модуля лучше мониторить внешними средствами. И оперативно информировать пользователя.
И про какой правильный припой вы говорите?
Но если применить безотмывоочный флюс и припой и не мыть. Все будет блестеть.
1. Вариант вайфай реле с поддержкой чего-то типа Tuya делает не то же самое?
2. «VT1, VT2 — биполярные транзисторы 2n2222, нужны для управления реле» а для чего так? Я не спец в радиоэлектронике, просто любопытно. Что такое и для чего подтягивающие резисторы я понимаю, а это вот зачем?
1 если сделать прошивку под эти модули (нам требуется на короткое время включить реле и выключить) то будет похоже (в том варианте что продается не подходит), но там придется повозиться с выводом gpio и их подтяжкой, реле там большое силовое и одно, а здесь двое ворот ну и еще ряд моментов
2 во первых реле 5 вольтовые, во вторых ток удержания реле превышает возможности контроллера
Этот геморрой с выдумыванием велосипеда нужен только когда это гараж с большим количеством людей.
датчики реального положения ворот и калитки вы как реализовали?
второй момент — там реле силовые — они имеют ограничения на ток как снизу так и сверху… и им может стать нехорошо от отсутствия нагрузки через время.
ну и это менее компактно, к тому же без своей прошивки зависит от облака вендора и без интернета совсем не будет работать
Но почти уверен что повторяемости не будет.
Это же всё надо делать или на работе или убивать выходные на это дело.
Или вообще быть безработным.
Какой смысл в этом.
Все блоки готовые продаются.
Осталось только собрать и потратить на это день.
А так конечно молодец
1 — к вам приехала доставка, вас нет — открыли ворота (хоть из другого города) доставка выложила все — сам многократно этим пользовался
2 — пришли например строители (гости, родственники и тп), в ваше отсутствие — вы им открыли ворота — они делают работу, не нужно им давать брелок
3 — брелок занимает место в кармане и нужно следить за батарейками — пошли вы в магазин — взяли телефон, в нем уже есть ключ от ворот
и так можно продолжать довольно долго, к тому же брелок то не перестал работать, появились альтернативы
пс п.1 и 2 у меня по старинке: дети или супруга)))
Дети и супруга дороже описанного решения :) к тому же и они могут отлучиться
У всех по разному — мне удобнее так.
Брелок у меня лежит в машине — там быстрее на нем, а в остальных случаях предпочту достать телефон. Телефон то вы все-равно берете.
Впрочем я никого ни к чему не призываю, кому то удобнее так, кому то иначе. Я только показал как сделал и мне это подходит.
Я такое фиг соберу ))) хотя пригодилось бы тоже.
я правильно понимаю, что теперь можно и со смартфона управлять воротами?
И про выключатели не очень понял: выключателем внутри можно и открыть и закрыть, а выключателем снаружи только закрыть?
Внутри первых ворот можно их открыть и закрыть, но выйдя на улицу при открытых воротах можно нажать на выключатель и закрыть
Внутри вторых открыть и закрыть можно как первые так и вторые ворота
Конечно, со смартфона можно управлять и динамически видеть состояние ворот — для этого все и затевалось
Спасибо за инфу. Пощупаю.
Как минимум в качестве пруфа можно найти даташит на ЕХ от 2015го года
cdn-shop.adafruit.com/product-files/2471/0A-ESP8266__Datasheet__EN_v4.3.pdf
И он уже версии 4.3 к тому времени был. Значит ЕХ шли ещё раньше.
И уже тогда был аппаратный вотчдог:
When in sleep mode, only the calibrated real-time clock and watchdog remains active
Как только в проекте или i2c или SSL или оба, то каждые 3..4 месяца глюки и перезагрузка.
Для него все закончилось хорошо, даже не сильно били.
за прямые руки 5.
пы.сы. сам разочаровался в esp8266 и потихоньку перевожу проекты на esp32.
Пы.сы. Я не критикую, просто спрашиваю тк постоянно учусь на чужом коде. Вот и интересно стало почему у вас так.
Да и масштабировать проще, объявив несколько экземпляров.
Хотя я и для одного экземпляра всовываю все в класс. Если критично (время исполнения / размер кода) — делаю все члены статичными.
так переменные тоже можно спрятать, просто объявив их не в заголовке *.h, а в *.cpp. функцию тоже не проблема — объявив ее static.
поясните пожалуйста, не понял что вы имели ввиду?
Всякие там потоки, работу с файлами можно пропускать. Наследование, виртуальные функции можно отложить на потом.
Я начал переползать на С++ в микроконтроллерах в тот момент, когда понял что на С «переизобретаю» С++. Кстати, переход с ассемблера на С происходил аналогичным образом.
уличные устройства лучше делать с опторазвязкой, хотя они тоже не 100% помогают
Собираюсь делать такое де для шлагбаума, прочитал про герконы, надо видимо тоже добавить, но выходит что магнит на палке будет, в геркон внутри ящика где привод и управление. Так вот вопрос — будет работать геркон через металлический корпус?
Вот что по этому поводу пишут профи на профильном сайте:
Хорошо хоть есть возможность проверки на необходимость перегрузки, а не просто «раз в N часов».
Но проблема жесткой привязи к своему облаку есть, и она везде. Америку тут пинать нет резона- в случае чего, ваша Алиса превратится в такую же тыкву, как и наша Алекса.
Ясно, понятно. Штош, так и запишем.
В принципе это ожидаемо. Если облако компании сдохнет, то 90% пользователей все равно не стали бы заморачиваться с локальным интерфейсом. К тому же дохлое облако скорее всего означает смерть всей фирмы, и судьба девайсов уже никого не волнует. Я стараюсь не покупать зависящее от облака железо, но обычно такого просто нет в природе.
И как всегда там будет целый букет «нюансов», здесь же решение которое четко соответствует поставленной задаче и легко модифицируется под изменяемые требования. Себестоимость его, думаю, сами можете прикинуть
Ищите по ключевым словам Wifi RF converter
Себестоимость ~20$ Нюансов не обнаружил. Работает локально и через интернет.
Вот и прикиньте по вашим затратам) Но плюс есть — ваше решение это и хобби, и решение проблемы)
по сути в приложение перетащит брелок — ни обратной связи ни кнопок… ничего
останется открытый эфир…
это мимо
Кнопки просто дублируются дешёвыми переключателями с поддержкой Tuya. Но их я не делал, так как не видел для себя обходимости. Мне проще голосом)
а можно решить что нужно этим устройством
сами же знаете недостатки всего этого
— это оно?
Я так умею. Но не умею именно от камеры статус получать. Хочу научиться. Или камера какая-то специальная нужна? Мои вводные: frigate, home assistant, камеры реолинк. Направьте, пожалуйста, в какую сторону мне читать?
Как этот сигнал будет обрабатываться камерой — другой вопрос. Можно поставить на ворота нормально закрытый геркон в крайнем нижнем положении ворот. Все остальные положения, кроме полностью закрытых ворот, будут считаться тревогой
Но камера определяла просто движение во всём кадре или на участке кадра.
Статус «процент открытия ворот» оно не выдаст.
Но статус «закрыто»\«не закрыто» камера отдаст
Цена от 4 до 8 долларов за шт, есть и на 4,8,16 каналов. Свои приложения и агрегация с Алисой, Алехой, самсунг, и тп
А вот у меня почему-то возник вопрос совершенно не в тему, глядя на фото гаража — а это у Вас гараж теплый? Если теплый, то почему крыша только жестяным профлистом защищает от окружающей среды?
теплый сам гараж, а показан привод тамбура, см первый рисунок
Будь мне нужно что-то такое — с удовольствием бы вам понес тенге)
Выше же выкладывали ссылки на готовые устройства. Купите Sonoff, прошейте Tasmota, настройте. Нужно больше GPIO (например, для датчиков) — придётся браться за паяльник.
Ну, или купите готовую плату ESP32, плату с реле, соедините проводочками… И дерзайте! :)
Я со всякими Ардуинами, еспшками и пиками как-то не взлетел..)
который задается в интерфейсе
Но зачастую достаточно парооль и доступ из внутренней сети, из вне впн.
С брелоками вон вообще открытым текстом все в эфир
а брелоки разные бывают.
Когда сравниваю свое решение с вашим, у меня текут слезы. Понижайка, блок реле, esp8266 — завернуты в пупырку(иначе висло в сильные морозы) и втиснуты в стандартную монтажную коробку 80*80. Зашита тасмота, управляется из Home Assistant. Соседи (ворота общие на въезд) задолбали просьбами поработать швейцаром — прикрутил им открытие из телеги(обратная связь сообщением о событии и/или кнопка снапшота с камеры). Вроде бы и работает все это «и так сойдёт» несколько лет, но такие вот посты заставляют задуматься, что я где-то свернул не туда.
Сейчас стоит примерно такая же задача — наружные ворота + две гаражные двери (через гараж можно войти в дом, то есть безопасность критична).
Планирую всё управление сделать исключительно по knx, и радиоканал тоже гонять через knx, купил контроллер с rolling code, завязал ради пробы с homelink в машине — отлично работает, надо теперь будет просто подключить к бинарным входам.
Ну а из полезного что пока сделал — вместо блокировочной перемычки на гаражных воротах воткнул реле, которые отключают возможность открытия ворот если включена сигнализация (у меня ring с z-wave, так что это было достаточно просто).
Летом вообще бесплатно всё, даже доплачивают за электричество сданное в сеть (не смотрел сколько начислили, если честно), зимой платим треть от того что должны были бы.
Не, ничего не меняли и не переделывали, в этом году много других забот было, к сожалению.
Давеча четырёхзначный счёт за воду пришёл за несколько месяцев. Оказывается дюймовую стальную трубу, проложенную к дому почти 60 лет назад, где-то прорвало. Кинули по верху времянку пока, но до декабря надо бы полноценно закопать, а то придут минусовые температуры и всё.
Ну и можно запросить от компании-поставщика воды чтобы списали эти потери, потому что никто не заметил и это неумышленная трата, но вначале надо оплатить всё и починить всё.
Ну и чот весь год в таком духе.