智能插座改点灯软件,Web配网和密钥,Web OTA,需要的功能都有-Arduino中文社区 - Powered by Discuz! Archiver

胡奚曷 发表于 2021-8-13 22:21

智能插座改点灯软件,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文件






Specher 发表于 2021-8-20 17:38

好帖 谢谢分享

xiaomadema 发表于 2021-9-3 21:46

最近卖的板子和你这个已经不是一批货了,拆下8266板的时候有一个1K的电阻不容易焊接,这个是按钮的下拉电阻,如果不好焊接可以更换一个普通的电阻上去替换掉原来的贴片电阻,测试效果应该是一样的

gzm001 发表于 2021-9-4 09:06

楼主还在吗? 这个wifiManager库,在管理库里找不到 , 自己去github找了一个,但是版本又不对

tjlimk 发表于 2021-9-5 16:00

谢谢分享!

胡奚曷 发表于 2021-9-9 21:00

gzm001 发表于 2021-9-4 09:06
楼主还在吗? 这个wifiManager库,在管理库里找不到 , 自己去github找了一个,但是版本又不对 ...

不会找不到的,藏的比较深,慢慢找

许普查 发表于 2021-9-11 17:02

学习中,谢谢楼主

zying 发表于 2021-12-21 00:22

大佬!真是大佬!高手啊!真心感谢!太好用了!

njbhqm 发表于 2021-12-21 12:59

刷写程序(略)

Highnose 发表于 2021-12-21 17:46

8266就够本了,对不
页: [1] 2
查看完整版本: 智能插座改点灯软件,Web配网和密钥,Web OTA,需要的功能都有