智能插座改点灯软件,Web配网和密钥,Web OTA,需要的功能都有
本帖最后由 胡奚曷 于 2021-8-13 22:30 编辑1.前言咸鱼上有卖只能插座8.5一个,比较便宜就入手了
智能插座属于三无产品,公模外壳,app是丛云,应该已经停了
因此卖的便宜,也因此需要DIY软件,芯片是ESP8266
看了本帖,你可以学会如下技能
-某智能插座的PIN定义
-如何Web配网和密钥
-如何Web OTA和Flash不足时OTA
-接入小爱同学
2.硬件介绍
公模外壳
三无产品
带一个按键
带安全门
PIN脚定义
正面
ESP8266+1MB
因此刷机时,按如下方式接线
插座TTL板
Rx-Tx
Tx-Rx
Vcc - 3.3V
Gnd- Gnd
GPIO0-Gnd
3.软件介绍
3.1需要安装Arduino(略)
3.2需要安装Blinker(略)blinker-library-0.3.80210611
3.3需要安装依赖库
“工具”- “管理库”
WiFiManager;用于web配网
3.4代码(都是网上找的,大家将就着看吧)
继电器
Relay.ino
#define BLINKER_WIFI
#define BLINKER_MIOT_OUTLET
#include <Blinker.h>
#include <Ticker.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include "Blwifi.h"
const char* host = "SwitchUpdate";
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
BlinkerButton BtRelay("btn-rly");
#define BLUE_LED15
#define RED_LED 13
Ticker ticker;
uint8_t connFlag = 0;
void BtRelay_callback(const String & state)
{
BLINKER_LOG("get button state: ", state);
if(state=="on")
{
digitalWrite(RELAY_PIN, HIGH);
BtRelay.text("on");
BtRelay.color("#FFB90F");
BtRelay.print("on");
}
else if(state=="off")
{
digitalWrite(RELAY_PIN, LOW);
BtRelay.text("off");
BtRelay.color("#DCDCDC");
BtRelay.print("off");
}
else
{
if(digitalRead(RELAY_PIN))
{
digitalWrite(RELAY_PIN, LOW);
BtRelay.text("on");
BtRelay.color("#FFB90F");
BtRelay.print("on");
}
else
{
digitalWrite(RELAY_PIN, HIGH);
BtRelay.text("off");
BtRelay.color("#DCDCDC");
BtRelay.print("off");
}
}
}
void SetLed()
{
//if(connFlag == 1)
{
if(digitalRead(RELAY_PIN))
{
digitalWrite(BLUE_LED, HIGH);
digitalWrite(RED_LED, LOW);
}
else
{
digitalWrite(BLUE_LED, LOW);
digitalWrite(RED_LED, HIGH);
}
}
}
void tickerCount()
{
if(connFlag == 0)
digitalWrite(BLUE_LED, !digitalRead(BLUE_LED));
}
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
Blinker.vibrate();
uint32_t BlinkerTime = millis();
Blinker.print("millis", BlinkerTime);
}
void heartbeat()
{
if(digitalRead(RELAY_PIN))
{
BtRelay.text("on");
BtRelay.color("#FFB90F");
BtRelay.print("on");
}
else
{
BtRelay.text("off");
BtRelay.color("#DCDCDC");
BtRelay.print("off");
}
}
void miotPowerState(const String & state)
{
BLINKER_LOG("need set power state: ", state);
if (state == BLINKER_CMD_ON)
{
digitalWrite(RELAY_PIN, HIGH);
BlinkerMIOT.powerState("on");
BlinkerMIOT.print();
}
else if (state == BLINKER_CMD_OFF)
{
digitalWrite(RELAY_PIN, LOW);
BlinkerMIOT.powerState("off");
BlinkerMIOT.print();
}
}
void miotQuery(int32_t queryCode)
{
BLINKER_LOG("MIOT Query codes: ", queryCode);
switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER :
BLINKER_LOG("MIOT Query All");
BlinkerMIOT.powerState(digitalRead(RELAY_PIN) ? "on" : "off");
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER :
BLINKER_LOG("MIOT Query Power State");
BlinkerMIOT.powerState(digitalRead(RELAY_PIN) ? "on" : "off");
BlinkerMIOT.print();
break;
default :
BlinkerMIOT.powerState(digitalRead(RELAY_PIN) ? "on" : "off");
BlinkerMIOT.print();
break;
}
}
void setup()
{
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);
connFlag = 0;
ticker.attach(1, tickerCount);
Blwifi_InitWiFi();
Blinker.begin(wifiSettings.auth_key, wifiSettings.ssid, wifiSettings.pswd);
Blinker.attachData(dataRead);
Blinker.attachHeartbeat(heartbeat);
BlinkerMIOT.attachPowerState(miotPowerState);
BlinkerMIOT.attachQuery(miotQuery);
BtRelay.attach(BtRelay_callback);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
pinMode(BLUE_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
digitalWrite(RED_LED, LOW);
MDNS.begin(host);
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
connFlag = 1;
}
void loop()
{
Blinker.run();
Blwifi_Loop();
httpServer.handleClient();
SetLed();
}引用的文件
Blwifi.cpp
#include <WiFiManager.h>
#include <EEPROM.h>
#include "GpioButton.h"
#include "Blwifi.h"
WiFiManager wifiManager;
void ResetWifi()
{
ClearWiFiInfo();
wifiManager.resetSettings();
ESP.restart();
}
void mbt_press_callback()
{
#ifdef SUPPORT_DEBUG
Serial.println("<Event>Click");
#endif
if(digitalRead(RELAY_PIN))
{
digitalWrite(RELAY_PIN, LOW);
}
else
{
digitalWrite(RELAY_PIN, HIGH);
}
}
void mbt_long_press_callback()
{
#ifdef SUPPORT_DEBUG
Serial.println("<Event>Long Press Tick");
Serial.println(F("WiFi resetSettings."));
#endif
for(int i=0;i<3;i++)
{
digitalWrite(LED_IO, LOW);
delay(100);
digitalWrite(LED_IO, HIGH);
delay(400);
}
ResetWifi();
}
// 定义按钮对象,指定按钮的GPIO口
GpioButton myButton(RESET_WIFI_PIN);
bool shouldSaveConfig=false;
Settings wifiSettings;
void saveConfigCallback()
{
#ifdef SUPPORT_DEBUG
Serial.println("Should save config");
#endif
shouldSaveConfig = true;
}
bool chkAuthkey(char* key, int len)
{
if (len != 12) return false;
for (int i=0;key!=0;i++){
if (!isxdigit(key)) return false;
}
return true;
}
void ClearWiFiInfo()
{
EEPROM.begin(1280);
EEPROM.get<Settings>(1024, wifiSettings);
wifiSettings.auth_key='\0';
EEPROM.put<Settings>(1024, wifiSettings);
if (EEPROM.commit())
{
#ifdef SUPPORT_DEBUG
Serial.println(F("EEPROM successfully committed"));
#endif
}
else
{
#ifdef SUPPORT_DEBUG
Serial.println(F("ERROR! EEPROM commit failed"));
#endif
}
EEPROM.end();
}
void Blwifi_InitWiFi()
{
myButton.bindEventOnClick(mbt_press_callback);
myButton.bindEventOnLongPress(mbt_long_press_callback);
EEPROM.begin(1280);
EEPROM.get<Settings>(1024, wifiSettings);
if( wifiSettings.auth_key=='\0'||wifiSettings.auth_key==0xFF)
{
WiFi.mode(WIFI_STA);
//wifiManager.setDebugOutput(true);
wifiManager.resetSettings();
wifiManager.setAPStaticIPConfig(IPAddress(192,168,10,1), IPAddress(192,168,10,1), IPAddress(255, 255, 255, 0));
// 3分钟配网时间,如没有完成则退出配网.
// 例如原正常连接的WIFI路由掉线死机或不通电等情况, 通过配网超时后, 会重新进行连接原WIFI信号。 避免停在配网模式下等待
wifiManager.setConfigPortalTimeout(180);
//wifiManager.setConnectTimeout(240);
// 设置点击保存的回调
wifiManager.setSaveConfigCallback(saveConfigCallback);
WiFiManagerParameter custom_authkey("auth_key", "Authkey", wifiSettings.auth_key, 12);
wifiManager.addParameter(&custom_authkey);
//AP名称:ESP_AP 密码:12345678
if(!wifiManager.autoConnect("ESP_AP","12345678"))
{
#ifdef SUPPORT_DEBUG
Serial.println(F("Failed to connect. Reset and try again. . ."));
#endif
ResetWifi();
delay(5000);
}
#ifdef SUPPORT_DEBUG
Serial.println(F("Connected to Wifi."));
Serial.print(F("My IP:"));
Serial.println(WiFi.localIP());
#endif
// 保存自定义信息
if (shouldSaveConfig)
{
#ifdef SUPPORT_DEBUG
Serial.println(F("saving config..."));
#endif
//Serial.println(custom_authkey.getValue());
strncpy(wifiSettings.auth_key, custom_authkey.getValue(), 12);
wifiSettings.auth_key = '\0';
strcpy(wifiSettings.ssid, wifiManager.getWiFiSSID().c_str());
wifiSettings.ssid='\0';
strcpy(wifiSettings.pswd, wifiManager.getWiFiPass().c_str());
wifiSettings.pswd='\0';
if (!chkAuthkey(wifiSettings.auth_key, strlen(wifiSettings.auth_key)))
{
#ifdef SUPPORT_DEBUG
Serial.println(F("Authkey is wrong."));
#endif
ResetWifi();
delay(5000);
}
EEPROM.put<Settings>(1024, wifiSettings);
if (EEPROM.commit())
{
#ifdef SUPPORT_DEBUG
Serial.println(F("EEPROM successfully committed"));
#endif
}
else
{
#ifdef SUPPORT_DEBUG
Serial.println(F("ERROR! EEPROM commit failed"));
#endif
}
ESP.restart();
}
}
EEPROM.end();
wifiSettings.auth_key = '\0';
}
void Blwifi_Loop()
{
myButton.loop();
}Blwifi.h
#ifndef __BLWIFI_H__
#define __BLWIFI_H__
//#define SUPPORT_DEBUG
#define RESET_WIFI_PIN 12
#define RELAY_PIN 4
#define LED_IO 15
struct Settings
{
char auth_key;
char ssid;
char pswd;
};
extern Settings wifiSettings;
void Blwifi_InitWiFi();
void ClearWiFiInfo();
void Blwifi_Loop();
#endif
GpioButton.h
#ifndef _GPIO_BUTTON_H_
#define _GPIO_BUTTON_H_
#include <Arduino.h>
#define DEF_ELIMINATING_JITTER_MS 20 // 消抖延时毫秒数
#define DEF_LONG_CLICK_MS 3000 // 默认单次长按事件触发毫秒数
#define DEF_DB_INTERVAL_MS 300 // 默认双击事件间隔毫秒数
#define DEF_LONG_PRESS_START_MS DEF_LONG_CLICK_MS // 长按循环触发事件的起始毫秒数
#define DEF_LONG_PRESS_INTERVAL_MS500 // 长按循环触发触发事件的间隔毫秒数
#define DEF_KEY_UP HIGH
#define DEF_KEY_DOWN LOW
typedef enum {
KEY_DOWN,
KEY_UP,
NO_CHANGE
} KeyAction;
class GpioButton {
public:
// 构造函数
GpioButton(uint8_t _pin, uint8_t _mode=INPUT_PULLUP, uint8_t _up_v=DEF_KEY_UP) : BtnPin(_pin), KeyUp(_up_v) {
KeyDown = (KeyUp==HIGH)?LOW:HIGH;
pinMode(BtnPin, _mode);
}
// 事件绑定函数
void bindEventOnClick(void (*callback)()) {
on_click = callback;
}
void bindEventOnDBClick(void (*callback)()) {
on_db_click = callback;
}
void bindEventOnLongClick(void (*callback)()) {
on_long_click = callback;
on_long_press = nullptr;
}
void bindEventOnLongPress(void (*callback)()) {
on_long_press = callback;
on_long_click = nullptr;
}
void bindEventOnKeyDown(void (*callback)()) {
on_key_down = callback;
}
void bindEventOnKeyUp(void (*callback)()) {
on_key_up = callback;
}
// set,get方法
void setEliminatingJitterMs(uint16_t _ms) {EliminatingJitterMs = _ms;}
void setLongClickMS(uint16_t _ms) {LongClickMS = _ms;}
void setLongStartMS(uint16_t _ms) {LongStartMS = _ms;}
void setLongIntervalMS(uint16_t _ms) {LongIntervalMS = _ms;}
void setLongPressNextTimeOut(uint32_t _to) {LongPressNextTimeOut = _to;}
void setDblClickIntervalMS(uint16_t _ms) {DblClickIntervalMS = _ms;}
uint16_t getEliminatingJitterMs() { return EliminatingJitterMs;}
uint16_t getLongClickMS() {return LongClickMS;}
uint16_t getLongStartMS() {return LongStartMS;}
uint16_t getLongIntervalMS() {return LongIntervalMS;}
uint32_t getLongPressNextTimeOut() {return LongPressNextTimeOut;}
uint16_t getDblClickIntervalMS() {return DblClickIntervalMS;}
// 对象轮询函数
void loop() {
switch(getKeyAction()) {
case KEY_DOWN:
// Serial.println("KEY_DOWN");
keyDownProc();
break;
case KEY_UP:
// Serial.println("KEY_UP");
keyUpProc();
break;
default:
keyNoChange();
break;
}
}
private:
uint8_t BtnPin;
uint8_t KeyDown = DEF_KEY_DOWN;
uint8_t KeyUp = DEF_KEY_UP;
// 参数
uint16_t EliminatingJitterMs = DEF_ELIMINATING_JITTER_MS;
uint16_t LongClickMS = DEF_LONG_CLICK_MS;
uint16_t LongStartMS = DEF_LONG_PRESS_START_MS;
uint16_t LongIntervalMS = DEF_LONG_PRESS_INTERVAL_MS;
uint16_t DblClickIntervalMS = DEF_DB_INTERVAL_MS;
// 控制变量
uint32_t LongClickTimeOut = 0;
uint32_t LongPressNextTimeOut = 0;
uint32_t DblClickTimeOut = 0;
// 计时器
uint32_t KeyDownTimer = 0;
uint32_t LastKeyDownTimer = 0;
uint32_t KeyUpTimer = 0;
uint8_t KeyStatus = DEF_KEY_UP;
bool isDone = true;
bool isReset = true;
// event callback function ptr
void (*on_click)() = nullptr;
void (*on_db_click)() = nullptr;
void (*on_long_click)() = nullptr;
void (*on_long_press)() = nullptr;
void (*on_key_down)() = nullptr;
void (*on_key_up)() = nullptr;
// 捕获按键动作,按下,释放,无动作
KeyAction getKeyAction() {
uint8_t gpio_v = digitalRead(BtnPin);
if(gpio_v == KeyStatus) return NO_CHANGE;
KeyStatus = gpio_v;
if(gpio_v == KeyDown) return KEY_DOWN;
else return KEY_UP;
}
// 消抖函数
bool isOutJitter(uint32_t _t) {
return (_t > KeyUpTimer + EliminatingJitterMs) && (_t > KeyDownTimer + EliminatingJitterMs);
}
// 事件结束处理
void eventEndProcess(uint32_t _t) {
LongClickTimeOut = LongPressNextTimeOut = DblClickTimeOut = _t;
isReset = true;
}
// 按下处理
void keyDownProc() {
uint32_t tmpTimer = millis();
if(isOutJitter(tmpTimer)) {
if(on_key_down) on_key_down();
LongClickTimeOut = tmpTimer + LongClickMS;
LongPressNextTimeOut = tmpTimer + LongStartMS;
LastKeyDownTimer = KeyDownTimer;
KeyDownTimer = tmpTimer;
isDone = false;
isReset = false;
}
}
// 释放处理
void keyUpProc() {
uint32_t tmpTimer = millis();
if(isOutJitter(tmpTimer)) {
if(on_key_up) on_key_up();
KeyUpTimer = tmpTimer;
eventEndProcess(tmpTimer);
if(!isDone) {
DblClickTimeOut = tmpTimer + DblClickIntervalMS;
}
else {
isReset = true;
}
}
}
// 无动作处理
void keyNoChange() {
uint32_t nowTimer = millis();
uint32_t fromKeyDown = nowTimer - KeyDownTimer;
// 按键按下状态
if(!isReset && KeyStatus == KeyDown) {
if(fromKeyDown < EliminatingJitterMs) return;
if(!isDone && on_long_click && nowTimer > LongClickTimeOut) {
isDone = true;
on_long_click();
eventEndProcess(nowTimer);
}
else if(on_long_press && nowTimer > LongPressNextTimeOut) {
isDone = true;
on_long_press();
LongPressNextTimeOut += LongIntervalMS;
}
else if(!isDone && on_db_click && nowTimer < DblClickTimeOut) {
isDone = true;
DblClickTimeOut = nowTimer;
on_db_click();
eventEndProcess(nowTimer);
}
}
// 按键释放状态
else {
uint32_t fromKeyUp = nowTimer - KeyUpTimer;
if(fromKeyUp < EliminatingJitterMs) return;
if(!isDone && on_click && nowTimer > DblClickTimeOut) {
isDone = true;
on_click();
eventEndProcess(nowTimer);
}
}
}
};
#endif4. 使用方法
4.1刷写程序(略)
4.2配网
上电后创建“ESP_AP”热点,手机后自动弹出HTML配网页面,或者输入192.168.10.1
输入SSID,密码和密钥;点击Save即可,如下图
界面配置如下
{¨version¨¨2.0.0¨¨config¨{¨headerColor¨¨transparent¨¨headerStyle¨¨dark¨¨background¨{¨img¨¨assets/img/headerbg.jpg¨¨isFull¨«}}¨dashboard¨|{¨type¨¨btn¨¨ico¨¨fad fa-power-off¨¨mode¨Ê¨t0¨´关´¨t1¨¨文本2¨¨bg¨É¨cols¨Í¨rows¨Í¨key¨¨btn-rly¨´x´Ë´y´Ï¨speech¨|÷¨clr¨¨#076EEF¨¨lstyle¨Ë}÷¨actions¨|÷¨triggers¨|÷}
4.3重置WiFi
长按按键3s,即可清除密码,重新配网
4.4Web OTA
配网之后,正常运行时,在IE(推荐)浏览器中输入IP/Update,即可打开升级页面
注意,不知道IP的话去路由器中查看
点击“浏览”选择生成的bin文件
点击Update Firmware进行升级,升级成功有提示
4.5内存不够升级失败
用中转的方式解决
先刷入较小的支持OTA的固件,仅300多KB,然后再刷入正常的OTA固件
OTA代码
/**
/*
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
*/
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Ticker.h>
const char* host = "SwitchUpdate";
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
Ticker ticker;
#define BLUE_LED15
#define RED_LED 13
void tickerCount()
{
digitalWrite(RED_LED, !digitalRead(RED_LED));
}
void setup(void)
{
uint32_t wait = millis()+10*1000;
while(WiFi.waitForConnectResult() != WL_CONNECTED)
{
if(wait < millis())
ESP.restart();
}
MDNS.begin(host);
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
pinMode(RED_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
digitalWrite(BLUE_LED, HIGH);
ticker.attach(1, tickerCount);
}
void loop(void)
{
httpServer.handleClient();
}
5.注意事项
硬件分为2M和1M版本
下图是2MB版本
需要根据Flash大小来配置,否则刷进去不启动,原因不明
2MB版本可以直接OTA,不需要通过较小的OTA转
6.其他
以上全部内容打包到附件中,包括BIN文件
好帖 谢谢分享 最近卖的板子和你这个已经不是一批货了,拆下8266板的时候有一个1K的电阻不容易焊接,这个是按钮的下拉电阻,如果不好焊接可以更换一个普通的电阻上去替换掉原来的贴片电阻,测试效果应该是一样的 楼主还在吗? 这个wifiManager库,在管理库里找不到 , 自己去github找了一个,但是版本又不对 谢谢分享! gzm001 发表于 2021-9-4 09:06
楼主还在吗? 这个wifiManager库,在管理库里找不到 , 自己去github找了一个,但是版本又不对 ...
不会找不到的,藏的比较深,慢慢找 学习中,谢谢楼主 大佬!真是大佬!高手啊!真心感谢!太好用了! 刷写程序(略) 8266就够本了,对不
页:
[1]
2