ESP8266开发之旅 小程序之阿里云篇① “IOT菜鸟”小程序,小白简单配置就可以玩起来

授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

共同学习成长QQ群 622368884,不喜勿加,里面有一大群志同道合的探路人

快速导航
单片机菜鸟的博客快速索引(快速找到你要的)

重点说一下,麻烦三连点赞,你的点赞是博主创作的前进动力

1.前言

从本篇开始,博主将陆续更新多篇关于小程序对接物联网平台以及ESP8266的文章,敬请期待。

这里就开始我们的第一个小程序,群主叫它“IOT菜鸟”。

对于阿里云物联网(要求至少要会创建产品、创建设备、创建AccessKey,这些都是非常简单的,跟着博主思路去创建即可)不了解的同学,可以先去查看官方文档或者阅读博主发布过的文章:

因为涉及到小程序的使用,博主会把学习者区分为:

  • 使用爱好者
    仅仅是想通过简单的配置把小程序使用起来,不在意小程序底层代码。对于这类同学,只需要扫描小程序二维码即可,按照本篇文章说明即可。
    在这里插入图片描述
    由于目前尚未审核通过,需要的读者可以私信博哥获取体验者权限或者点赞留言邮箱,博哥私发小程序源码,一键导入小程序开发者工具即可。

  • 自由编程者
    想深入了解小程序代码底层如何实现,请继续阅读接下来的原理讲解,干货多多。

2.IOT小程序

2.1 小程序初衷

  • 小程序作为时代的潮流产物,可以省去编写AndroidIOS App的双端困扰,学习成本和难度比App开发低,非常适合IOT学习者
  • 通过一个简单的IOT菜鸟小程序,展示如何和物联网平台对接功能,初学者可以在学习demo之后发挥自己的创造力去创造更多好玩的小程序,麻雀虽小五脏俱全,博哥只是抛砖引玉;

2.2 小程序功能

  • 小程序阿里云Blinker,通过小程序去控制点灯

2.2 小程序页面

第一个版本的IOT菜鸟小程序非常简单,就只有三个界面:

  • 提示界面
    在这里插入图片描述

  • 配置界面
    在这里插入图片描述

  • 控制界面
    在这里插入图片描述
    读者按照下面的步骤进行操作即可。

3.阿里云设置

阿里云物联网配置也非常简单,请按照下面的图示进行创建即可。

3.1 创建产品

首先,在阿里云物联网上直接创建一个产品,博主这里暂且命名为“LED小程序”
在这里插入图片描述

  • 我们这里选择官方提供的“智能城市/智能楼宇/灯光设备

创建完产品后,点击查看产品,点击功能定义,新增一个物模型:
在这里插入图片描述
第一步就完成了。

这一步我们我们会获取到:ProductKey

3.2 创建设备

为了方便测试,博主这里创建了多个设备,读者按照自己的需求去创建:
在这里插入图片描述

在这里插入图片描述
填写备注名称的时候请在最后面加上以下标识(目的是为了显示不一样的灯图片)中的一个:

  • _lamp1
    在这里插入图片描述
  • _lamp2
    在这里插入图片描述
  • _led1
    在这里插入图片描述
  • _led2
    在这里插入图片描述
  • _led3
    在这里插入图片描述

举个例子,比如你想在小程序显示名称为“大厅灯”,那么,你可以填写为“大厅灯_led2” 。

记住,备注名称一定要写。

这一步我们会得到:DeviceNameDeviceSecret,web配置ESP8266的时候需要用到。

3.3 创建AccessKey

在个人头像里面有一个AccessKey管理,点击创建一个即可。
在这里插入图片描述
这一步,会得到:AccessKeyIdAccessKeySecret

4. 配置小程序

接下来,我们就可以配置小程序了,非常简单,请按照图示填入参数配置:
在这里插入图片描述

在这里插入图片描述
确定保存成功之后,会自动返回上一个页面。以下是博主的正常显示界面。
在这里插入图片描述
控制界面,会显示4类设备,只有在线设备可以正常控制,并且支持下拉刷新。

5. 配置ESP8266 NodeMcu

ESP8266的代码,博主基于 ESP8266开发之旅 阿里云物联网平台篇⑥ LED智能灯控制系统 全面讲解,上手一个小项目(MQTT客户端直连 + Web配网 + WebSocket局域网通信)
做了些许改动。

  • ali_led2
/**
 * 功能: LED智能灯控制系统 设备端1
 * 作者: 单片机菜鸟哥
 * 时间: 2020-05-02
 * 通信方式:MQTT客户端直连 + Web配网,通过小程序控制
 */

#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
#include <ESP8266WebServer.h>
#include <PubSubClient.h>

//for LED status
#include <Ticker.h>
#include <EEPROM.h>
#include <CustomWiFiManager.h>         // https://github.com/tzapu/WiFiManager
#include "utils_hmac.h"
#include <OneButton.h>

#define DEBUG //是否开启debug功能

#ifdef DEBUG
#define DebugBegin(b)    Serial.begin(b)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)
#else
#define DebugBegin(b)
#define DebugPrintln(message)
#define DebugPrint(message)
#endif

const char yellowWifi[] PROGMEM = "data:image/png;base64,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yqKo0OXf6/P5CDc0cPZML3vuu5cPBibZu9OgqkwSSxbeDRacAqahj6E/9ynoGoJ/PQp1Yb2hkGr2upsFotBoZdsMwyCZlOzd+wX+7MtfgVSCcH39RVY+1NR6lxwd4Yfff4FvfuspTnaM8MDtJp9uds97lF70SCyrGz4TnvuZPhJLZvRW2W8yi1MCDU7BzoTAcRSRSC3bt2+fAi17UjTzoGTW84BSEpm3Yriuy+DAIEfb3uH4yQ+J1MB9m0y2rXNJO7kYLgoAPXrenA7CSa/Kc7wXhmKzg3WlLoDI82QhSoEn2kULJdPB04EkMzltyZplQFMj/O4qkz+4TrJisWIiqSM/H6/mXRdwJZQHwGfAyCT0DXsFTqFFrtwPr5yEF97U9YFs/aCQFZvbMy1bRoslYfctsOHaXM1QKc3QJbWSqjKF7eg6wnyPxue9FzAN3WnCOyhdvWT2CXBzVK8Wz/0UQgFd8XULsNotdt43o9+UrQ8399wKO27SZ/9TI+j5kHZ01UiIC6sLXNBu0BC6R1flqjVT5k2VPbfC0jo4+LIesYogUyyZrwlytYXxBNSGYd8fwqbVMJ4s8MwFBj713KUujkqvONo5CN/9Cfy0Q18v8+u5CrkfS8wMOrsQOG6OxreshD//NFxZT9Hl7GLtkgMAOTEEOPpLOHxcl8cnkrmRMvJGLFsadySgoDoEv3MN3L4GWpu8stcFzOuSAwC5s/hQQI9215Aup717BvpGdJYZ8wCpLNOANUXguiu0llzdoL9j0tsHzafM9WsFQD4QoIUzYGn9sB0tbCmvxlDm121+SwOS9kTvcgaetcv+G6FsAGlb0zi7nlsmVHq9S6nreGnbS7XF5Q/8VwZA1oSYfi6vFDgeO7ICeLl/EFVSAOYEpZSde/ax/6HkAgCldqDUtgBAqR0otS0AUGoHSm0LAJTagVLbAgCldqDU9n+vbbJY13ln1QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wMS0wOVQwMjo0Njo0MCswODowMELuAxIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDEtMDlUMDI6NDY6NDArMDg6MDAzs7uuAAAAQ3RFWHRzb2Z0d2FyZQAvdXNyL2xvY2FsL2ltYWdlbWFnaWNrL3NoYXJlL2RvYy9JbWFnZU1hZ2ljay03Ly9pbmRleC5odG1svbV5CgAAABh0RVh0VGh1bWI6OkRvY3VtZW50OjpQYWdlcwAxp/+7LwAAABh0RVh0VGh1bWI6OkltYWdlOjpIZWlnaHQANTEyj41TgQAAABd0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAA1MTIcfAPcAAAAGXRFWHRUaHVtYjo6TWltZXR5cGUAaW1hZ2UvcG5nP7JWTgAAABd0RVh0VGh1bWI6Ok1UaW1lADE1NDY5NzMyMDBYBo4+AAAAEnRFWHRUaHVtYjo6U2l6ZQAzMjE5MUJVXU35AAAAYnRFWHRUaHVtYjo6VVJJAGZpbGU6Ly8vaG9tZS93d3dyb290L25ld3NpdGUvd3d3LmVhc3lpY29uLm5ldC9jZG4taW1nLmVhc3lpY29uLmNuL2ZpbGVzLzExMi8xMTIzMjIxLnBuZ7vHiF8AAAAASUVORK5CYII=";

/******************产品/设备配置(每个人需要根据自己的产品设备信息去动态更换)**********************/
#define REGION_ID   "cn-shanghai"  //地域 填写自己的
#define PRODUCT_KEY "a1bXlwV5MSn"  //产品key  从产品详情获取
// 服务端相关
#define MQTT_SERVER   PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com" //阿里云MQTT服务地址
#define MQTT_PORT     1883                                               //MQTT服务端口
#define HTTPS_SERVER "iot-auth." REGION_ID ".aliyuncs.com" //认证地址
#define HTTPS_PORT 443

// 相关主题
#define TOPIC_SET_PROPERTY "/sys/" PRODUCT_KEY "/{dName}/thing/service/property/set"
/*************************************************************************************************/

static char MQTT_CLIENT_ID[50]; // Mqtt ClientID
static char MQTT_USERNAME[40];  // Mqtt UserName
static char MQTT_PASSWORD[50];  // Mqtt Psw
static char MQTT_TOPIC_STATUS[100];

#define CONFIG_NUMBER 0xAA
#define NOCONFIG_NUMBER 0x00

void initWifiManager();
void saveConfigCallback();
void configModeCallback (WiFiManager *myWiFiManager);
void doWiFiTick();
void connectToMqtt();
bool connectHTTPS();
bool loadROOTcert();
void deviceAuthen();
void sendHTTPSrequest();
void readHTTPSreponse();
void mqtt_callback (char* topic, byte* payload, unsigned int length);
void parseMqttResponse(char* payload);
String charToString(char *src,int len);
void onButtonClick();

// 结构体 存储到eeprom
struct device_config{
  char deviceName[40];  //设备deviceName  从设备详情获取
  char deviceSecret[40];  //对应的设备秘钥
  char ssid[20];
  char psw[20];
  uint8_t magic;
} config;

// https
WiFiClientSecure client_s;
// mqtt
PubSubClient mqttclient(MQTT_SERVER, MQTT_PORT, &mqtt_callback, client_s);

//flag for saving data
bool shouldSaveConfig = false;
int state = 0;
//for LED status
Ticker ticker;
#define buttonPin D4
OneButton button(buttonPin, true);

void setup() {
  // put your setup code here, to run once:
  delay(2000);
  DebugBegin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  button.attachDoubleClick(onButtonClick);
  digitalWrite(LED_BUILTIN, state);
  WiFi.disconnect();
  loadConfig();
  initWifiManager();
  client_s.setInsecure();
}

void loop() {
  // put your main code here, to run repeatedly:
  ESP.wdtFeed();
  button.tick();
  doWiFiTick();
  if (WiFi.status() == WL_CONNECTED) {
      connectToMqtt();
      mqttclient.loop();
    }
}

/**
 * 功能描述:初始化wifimanager
 */
void initWifiManager(){
  /***  步骤一:创建 wifimanager对象 **/
  WiFiManager wifiManager;
  /*************************************/
  /*** 步骤二:进行一系列配置,参考配置类方法 **/
  // 打印调试内容
  #ifdef DEBUG
     wifiManager.setDebugOutput(true);
  #endif
  // 设置个人图标  大叔黄
  wifiManager.setHeadImgBase64(FPSTR(yellowWifi));
  wifiManager.setButtonBackground("#E08E00");
  // 设置最小信号强度
  wifiManager.setMinimumSignalQuality(40);
  // 设置固定AP信息
  IPAddress _ip = IPAddress(192, 168, 4, 1);
  IPAddress _gw = IPAddress(192, 168, 4, 1);
  IPAddress _sn = IPAddress(255, 255, 255, 0);
  wifiManager.setAPStaticIPConfig(_ip, _gw, _sn);
  // 设置进入AP模式的回调
  wifiManager.setAPCallback(configModeCallback);
  wifiManager.setConnectTimeout(5);
  // 设置点击保存的回调
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  // 设置 如果配置错误的ssid或者密码 退出配置模式
  wifiManager.setBreakAfterConfig(true);
  // 添加额外的参数 设备名字 设备秘钥
  WiFiManagerParameter custom_device_name("dn", "DeviceName", config.deviceName, 40);
  WiFiManagerParameter custom_device_secret("ds", "DeviceSecret", config.deviceSecret, 40);
  if (config.magic == CONFIG_NUMBER){
    wifiManager.setSSID(config.ssid);
    wifiManager.setPassword(config.psw);
  }

  wifiManager.addParameter(&custom_device_name);
  wifiManager.addParameter(&custom_device_secret);

  /*************************************/
  /*** 步骤三:尝试连接网络,失败去到配置页面 **/
  String mac = WiFi.macAddress();
  mac.replace(":","");
  String ssid = String("ESP") + "-" + mac;
  if (!wifiManager.autoConnect(ssid.c_str(),"12345678")) {
      DebugPrintln("failed to connect and hit timeout");
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(1000);
  }
  /*************************************/
  // 保存自定义信息
  if (shouldSaveConfig) {
      // 读取配置页面配置好的信息
      strcpy(config.deviceName, custom_device_name.getValue());
      strcpy(config.deviceSecret, custom_device_secret.getValue());
      strcpy(config.ssid, wifiManager.getSSID().c_str());
      strcpy(config.psw, wifiManager.getPassword().c_str());

      saveConfig();

      #ifdef DEBUG
         DynamicJsonBuffer jsonBuffer;
         JsonObject& json = jsonBuffer.createObject();
         json["ssid"] = config.ssid;
         json["psw"] = config.psw;
         json["device_name"] = config.deviceName;
         json["device_secret"] = config.deviceSecret;
         json.printTo(Serial);
         DebugPrintln("");
      #endif
  }

  String topicSetProperty = TOPIC_SET_PROPERTY;
  // deviceName placeholder替换
  topicSetProperty.replace("{dName}",config.deviceName);
  strcpy(TOPIC_SET_PROPERTY, topicSetProperty.c_str());

  DebugPrint(F("topicSetProperty:"));
  DebugPrintln(topicSetProperty);

  DebugPrintln(F("local ip"));
  DebugPrintln(WiFi.localIP());
  // 我们在loop里面去做检测
  WiFi.disconnect();
  ticker.detach();
}

/**
 * 功能描述:配置进入AP模式通知回调
 */
void configModeCallback (WiFiManager *myWiFiManager) {
  DebugPrintln(F("Entered config mode"));
  DebugPrintln(WiFi.softAPIP());
  // if you used auto generated SSID, print it
  DebugPrintln(myWiFiManager->getConfigPortalSSID());
  // entered config mode, make led toggle faster
  ticker.attach(0.2, tick);
}

/**
 * 功能描述:设置点击保存的回调
 */
void saveConfigCallback () {
  DebugPrintln(F("Should save config"));
  shouldSaveConfig = true;
}

/**
 * 功能描述:设置LED灯闪烁,提示用户进入配置模式
 */
void tick(){
  //toggle state
  int state = digitalRead(BUILTIN_LED);  // get the current state of GPIO1 pin
  digitalWrite(BUILTIN_LED, !state);     // set pin to the opposite state
}

/**
 * 功能:连接路由心跳函数
 */
void doWiFiTick() {
  static bool taskStarted = false;
  static bool startSTAFlag = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) {
    startSTAFlag = true;

    DebugPrint("connect to ap:");
    DebugPrintln(config.ssid);
    WiFi.disconnect();
    WiFi.mode(WIFI_STA);
    WiFi.begin(config.ssid, config.psw);
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED ) {
    if (millis() - lastWiFiCheckTick > 1000) {
      lastWiFiCheckTick = millis();
      DebugPrint(".");
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      DebugPrint("\r\nGet IP Address: ");
      DebugPrintln(WiFi.localIP());
      deviceAuthen();
    }
  }
}

/**
 * 生成 HmacSha1
 * @param sign HmacSha1字符串
 */
static ICACHE_FLASH_ATTR char* parseHmacSha1(char *sign) {//
  String chipidStr = String(ESP.getChipId(), HEX);//"ef63aa";
  String clientIDStr = String(PRODUCT_KEY) + "." + chipidStr;
  char *hmac_source;//[120];
  strcpy(MQTT_CLIENT_ID, clientIDStr.c_str());
  String str = "clientId" + clientIDStr + "deviceName" + config.deviceName + "productKey" + PRODUCT_KEY;
  uint8_t str_len = str.length() + 1;
  
  hmac_source = (char*)malloc(str_len * sizeof(char));
  memcpy(hmac_source, str.c_str(), str_len);

  DebugPrint(F("hmac_source: "));
  DebugPrintln(hmac_source);

  utils_hmac_sha1(hmac_source, strlen(hmac_source), sign, config.deviceSecret, strlen(config.deviceSecret));

  DebugPrint(F("signature: "));
  DebugPrintln(sign);

  free(hmac_source);
  return sign;
}

/***
 * 发送验证请求
 */
void ICACHE_FLASH_ATTR sendHTTPSrequest() {
  DebugPrintln(F("sendHTTPSrequest"));
  char signature[40];
  parseHmacSha1(signature);
  String signatureStr = charToString(signature,40);
  DebugPrint(F("signatureStr: "));
  DebugPrintln(signatureStr);

  String str = String("productKey=") + PRODUCT_KEY + "&deviceName=" + config.deviceName + "&signmethod=hmacsha1&sign=" + signatureStr +
               "&version=default&clientId=" + MQTT_CLIENT_ID + "&resources=mqtt";

  String str1 = (String("POST ") + "/auth/devicename HTTP/1.1\r\nHost: " + HTTPS_SERVER + "\r\n" +
                 "Accept: text/xml,text/javascript,text/html,application/json\r\nContent-Type: application/x-www-form-urlencoded\r\n" +
                 "Content-Length: " + str.length() + "\r\n\r\n");
  client_s.print(str1);
  DebugPrint(str1);

  client_s.print(str);
  DebugPrintln(str);

  DebugPrint(F("request sent!"));
  delay(100);
}

/**
 * 处理认证请求
 */
void ICACHE_FLASH_ATTR readHTTPSreponse() {
  DebugPrintln(F("readHTTPSreponse"));
  while (client_s.connected())   {
    String line = client_s.readStringUntil('\n');
    if (line == "\r") {
      DebugPrintln(F("=========="));
      break;
    }
  }
  String resstring = client_s.readStringUntil('\n');

  DebugPrint(F("receiving: "));
  DebugPrintln(resstring);

  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(resstring);

  // Test if parsing succeeds.
  if (!root.success()) {
     DebugPrintln("parse failed");
     return;
  } else {
     #ifdef DEBUG
          // 格式化打印json
          root.prettyPrintTo(Serial);
     #endif
     DebugPrintln(F("parse success"));
  }

  strcpy(MQTT_USERNAME, root["data"]["iotId"]);
  strcpy(MQTT_PASSWORD, root["data"]["iotToken"]);

  DebugPrint(F("MQTT_CLIENT_ID: "));
  DebugPrintln(MQTT_CLIENT_ID);
  DebugPrint(F("MQTT_USERNAME: "));
  DebugPrintln(MQTT_USERNAME);
  DebugPrint(F("MQTT_PASSWORD: "));
  DebugPrintln(MQTT_PASSWORD);

  DebugPrintln(F("=========="));
  client_s.stop();
}

/*
 * 设备认证
 */
void deviceAuthen() {
  while (loadROOTcert()) {
    while (connectHTTPS()) {
      sendHTTPSrequest();
      readHTTPSreponse();
      return;
    }
    ESP.wdtFeed();
  }
}

/*
 * 连接到远程服务器并且获取状态
 */
bool ICACHE_FLASH_ATTR connectHTTPS() {
  DebugPrint(F("connecting to "));
  DebugPrintln(HTTPS_SERVER);
  if (!client_s.connect(HTTPS_SERVER, HTTPS_PORT)) {
    DebugPrintln(F("connection failed"));
    return false;
  }else {
    DebugPrintln(F("connection succeed"));
    return true;
  }
}

/*
 * 设置根证书并且返回状态
 */
bool ICACHE_FLASH_ATTR loadROOTcert() {
  return true;
}

void connectToMqtt() {
    if (mqttclient.connected()) {
     return;
    }
    DebugPrint(F("Connecting to MQTT... "));
    int8_t ret;
    uint8_t retries = 3;

    while (!mqttclient.connect(MQTT_CLIENT_ID, MQTT_USERNAME,MQTT_PASSWORD)) { // connect will return true for connected
          DebugPrintln(F("Retrying MQTT connection in 5 seconds..."));
          mqttclient.disconnect();
          delay(5000);  // wait 5 seconds
          retries--;
          if (retries == 0) {
            // basically die and wait for WDT to reset me
            while (1);
          }
          yield();
    }
    DebugPrint(F("Connect MQTT Success!"));
    // 订阅主题
    mqttclient.subscribe(TOPIC_SET_PROPERTY);
}

/**
 * 解析mqtt数据
 */
void parseMqttResponse(char* payload){
   DebugPrintln(F("parseMqttResponse"));
   StaticJsonBuffer<300> jsonBuffer;
     // StaticJsonBuffer 在栈区分配内存   它也可以被 DynamicJsonBuffer(内存在堆区分配) 代替
     // DynamicJsonBuffer  jsonBuffer;
   JsonObject& root = jsonBuffer.parseObject(payload);
     // Test if parsing succeeds.
   if (!root.success()) {
       DebugPrintln(F("parse failed"));
       return ;
   } else {

       #ifdef DEBUG
          // 格式化打印json
          root.prettyPrintTo(Serial);
       #endif

       DebugPrintln(F("parse success"));
   }

   if (root.containsKey("params")){
        JsonObject &params = root["params"];
        if (params.containsKey("status")) {
            state = !state;
            digitalWrite(LED_BUILTIN, state);
        }
   }
}

/*
 * 保存参数到EEPROM
*/
void saveConfig(){
  DebugPrintln(F("-----Save config To EEPROM!-----"));
  EEPROM.begin(300);
  config.magic = CONFIG_NUMBER;
  uint8_t *p = (uint8_t*)(&config);
  for (int i = 0; i < sizeof(config); i++){
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
  DebugPrintln(F("---------   Save End   ----------"));
}

/*
 * 从EEPROM加载参数
*/
void loadConfig(){
  DebugPrintln(F("-----Read config From EEPROM!-----"));
  EEPROM.begin(300);
  uint8_t *p = (uint8_t*)(&config);
  for (int i = 0; i < sizeof(config); i++){
    *(p + i) = EEPROM.read(i);
  }
  EEPROM.commit();

  if (config.magic == CONFIG_NUMBER){
      DebugPrint(F("deviceName:"));
      DebugPrintln(config.deviceName);
      DebugPrint(F("deviceSecret:"));
      DebugPrintln(config.deviceSecret);
      DebugPrint(F("ssid:"));
      DebugPrintln(config.ssid);
      DebugPrint(F("psw:"));
      DebugPrintln(config.psw);
  } else{
    DebugPrintln(F("Has No config"));
    config.magic == NOCONFIG_NUMBER;
    memset(config.deviceName, 0, 40);
    memset(config.deviceSecret, 0, 40);
    memset(config.ssid, 0, 40);
    memset(config.psw, 0, 40);
  }
  DebugPrintln(F("-------------  Read End  --------------"));
}

/**
 * 数组转字符串
 */
String charToString(char *src,int len){
   String str = "";
   for (byte i = 0 ; i < len; i++) {
       str += src[i];
   }
   return str;
}

// 重置
void onButtonClick() {
  DebugPrintln(F("Button  click."));
  config.magic = NOCONFIG_NUMBER;
  memset(config.deviceName, 0, 40);
  memset(config.deviceSecret, 0, 40);
  memset(config.ssid, 0, 40);
  memset(config.psw, 0, 40);
  saveConfig();
  ESP.reset();
  delay(1000);
} // click1


/**
 * 功能:MQTT回调
 * 参数:
 *   1. topic   主题
 *   2. payload 载体
 *   3. length  载体长度
 */
void mqtt_callback (char* topic, byte* payload, unsigned int length) {
  parseMqttResponse((char *)payload);
}

注意点:

#define REGION_ID   "xxxx"  //地域 填写自己
#define PRODUCT_KEY "xxxx"  //产品key  从产品详情获取

完整代码:

在这里插入图片描述
把以上代码烧录进ESP8266之后,请自行进行web配网,之后就可以愉快地玩耍了。

6. 总结

  • IOT小程序的开发初衷
  • 阿里云设置
  • 配置小程序
  • 配置硬件

需要小程序源码的,麻烦点赞评价留下自己的邮箱地址。创作不易,请点赞支持。

原创文章 153 获赞 1046 访问量 41万+

猜你喜欢

转载自blog.csdn.net/dpjcn1990/article/details/105872165