目录

esp8266接入阿里云物联网平台MQTT客户端直连

esp8266接入阿里云物联网平台(MQTT客户端直连)

这里不介绍怎么进行详细开发,只讲如何快速实现接入物联网平台,具体操作可以先看文档

我们这次的实验用的是8266的Node-MCU开发板,通过8266接入阿里云平台,然后使用MQTT.fx模拟远程控制的客户端(手机、电脑),远程控制8266板载的LED亮灭。

1、创建设备(默认你们已经注册好账号了)

https://i-blog.csdnimg.cn/direct/b8e460d2573a4c90bf44d753de614a3f.png

https://i-blog.csdnimg.cn/direct/bf6b779366a64b53ac88bd746ed50767.png

2、添加设备

我创建了如图所示的设备

https://i-blog.csdnimg.cn/direct/ec52a03dbb494f08a9b26d87bed279a9.png

3、激活设备(重点)

https://i-blog.csdnimg.cn/direct/3e42d2d9d97f45029646512787b7fa54.png

你们新创建的设备一定和我上图的LED2一样是未激活状态,因为你们的设备还没有接入到阿里云平台。现在就操作一下把MQTT.fx接入到平台上。

先下载MQTT.fx客户端

安装完成后打开软件

https://i-blog.csdnimg.cn/direct/43a7feb0670e4b33b896f3d7b5cbcef5.png

https://i-blog.csdnimg.cn/direct/e832c602c6694d57ac4609e76a815f8e.png

https://i-blog.csdnimg.cn/direct/fe828e36216147a6a1507fc2bb80f685.png

ProfileName

MQTT(设备名字)

Broker Address

格式: {YourProductKey}.iot-as-mqtt.{region}.aliyuncs.com

其中,{ YourProductKey }这个我就不用说了,强调了很多遍了。{ region }需替换为您物联网平台服务所在地域的代码。可以参考 我的区是华东2上海 cn-shanghai

Client ID

MQTT_Device|securemode=3,signmethod=hmacsha1| ,表示 TCP直连 (不加密)

MQTT_Device|securemode=2,signmethod=hmacsha1| ,表示 TLS直连 (加密)

1){clientId}为设备的ID信息。可取任意值,我这里等同于DeviceName,长度在64字符以内。建议使用设备的MAC地址或SN码。

2)securemode为安全模式,TCP直连模式设置为securemode=3,TLS直连为securemode=2。

3)signmethod为算法类型,支持hmacmd5和hmacsha1(基本上用这个)。

输入Client ID信息后,请勿单击Generate

User Credentials

设置用户名和密码

https://i-blog.csdnimg.cn/direct/e8a64708831e48118e714a8b631e0e56.png

User Name固定格式: {YourDeviceName}&{YourPrductKey}

Password:这个就得用工具生成了,工具下载

下载解压后打开里面的html网页,按照图片输入自己的信息就可以了

https://i-blog.csdnimg.cn/direct/2a7fad10294544b88eefcdf59044c391.png

其实也不需要工具也可以产生password,阿里云在点击设备的时候会有一个MQTT连接参数,里面有我们需要的password和client_id,但是复制的client_id需要考虑securemode。 https://i-blog.csdnimg.cn/direct/e7a145227b564bd98b240d9ccaba2252.png

securemode

直连模式的我们不需要考虑这个,配置完后保存即可

https://i-blog.csdnimg.cn/direct/765638feec1349fba7164e7b193f2fb5.png

4、MQTT(发布和订阅)

MQTT协议不熟悉的以后有机会再讲吧,目前其他博主介绍的更详细。8266也用类似的方法接入阿里云平台。

//esp8266代码
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>

const char* WIFI_SSID     = "Redmi K50 Ultra"; // WiFi账号密码
const char* WIFI_PASSWORD = "20000906"; // WiFi密码

/******************产品/设备配置(每个人需要根据自己的产品设备信息去动态更换)**********************/
#define PRODUCT_KEY "xxxxxxx"  //产品key  从产品详情获取
#define DEVICE_NAME "xxxx"  //设备deviceName  从设备详情获取
// 服务端相关
#define MQTT_SERVER   PRODUCT_KEY ".iot-as-mqtt.cn-shanghai.aliyuncs.com" //阿里云MQTT服务地址
#define MQTT_PORT     1883                                               //MQTT服务端口
// 校验三元组
#define MQTT_CLIENT_ID  DEVICE_NAME "|securemode=3,signmethod=hmacsha1|" //mqtt clientid
#define MQTT_USERNAME   DEVICE_NAME "&" PRODUCT_KEY                      //mqtt username
#define MQTT_PASSWORD   "4E48894AB34F7B0FCB83EA0C19F8FB46EC592CD9"       //mqtt password 通过生成工具获得
// 相关主题 自定义一个主题
#define TOPIC1 "/" PRODUCT_KEY "/" DEVICE_NAME "/user/setled"
/*************************************************************************************************/

void mqtt_callback (char* topic, byte* payload, unsigned int length);
void doWiFiTick();
void connect_wifi();
void connectToMqtt();
const __FlashStringHelper* connectErrorToString(int8_t code);

WiFiClient client;
PubSubClient mqttclient(MQTT_SERVER, MQTT_PORT, &mqtt_callback, client);


void setup() {
  // put your setup code here, to run once:
  delay(2000);
  Serial.begin(115200);
  doWiFiTick();
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.println("");
  connectToMqtt();
}

void loop() {

  if (WiFi.status() == WL_CONNECTED) {   
    mqttclient.loop();
  }
}

/**
 * 功能:连接到WiFi热点
 */
void connect_wifi() {
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.print(F("\nWiFi connected to "));
  Serial.println(WIFI_SSID);
  Serial.print(F("IP address: "));
  Serial.println(WiFi.localIP());
}

/**
 * 功能:连接到Mqtt
 */
void connectToMqtt() {
  if (mqttclient.connected()) {
   return;
  }

  Serial.print(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
      Serial.println(connectErrorToString(mqttclient.state()));
      Serial.println(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();
  }
  Serial.println(F("Success!"));
  // 订阅主题
  mqttclient.subscribe(TOPIC1);

}

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

  if (!startSTAFlag) {
    startSTAFlag = true;

    Serial.print("connect to ap:");
    Serial.println(WIFI_SSID);
    WiFi.disconnect();
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

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

/**
 * 功能:MQTT回调
 * 参数:
 *   1. topic   主题
 *   2. payload 载体
 *   3. length  载体长度
 */
void mqtt_callback (char* topic, byte* payload, unsigned int length) {
  byte *end = payload + length;
  for (byte *p = payload; p < end; ++p) {
    Serial.print(*((const char *)p));
  }
  Serial.println("");
  parseMqttResponse((char *)payload);
}

/**
 * 解析mqtt数据
 */
void parseMqttResponse(char* payload){
   Serial.println("start parseOneNetMqttResponse");
   StaticJsonBuffer<100> jsonBuffer;
     // StaticJsonBuffer 在栈区分配内存   它也可以被 DynamicJsonBuffer(内存在堆区分配) 代替
     // DynamicJsonBuffer  jsonBuffer;
   JsonObject& root = jsonBuffer.parseObject(payload);

     // Test if parsing succeeds.
   if (!root.success()) {
       Serial.println("parseObject() failed");
       return ;
   }

   String deviceName = root["dn"];
   int status = root["s"];

   if(strcmp(DEVICE_NAME,deviceName.c_str())== 0){
        if (status == 1) {
            digitalWrite(LED_BUILTIN, LOW);
        } else {
            digitalWrite(LED_BUILTIN, HIGH);
        }
    }
}

/**
 * 功能:打印错误
 * 参数:
 *   1. code   错误码
 */
const __FlashStringHelper* connectErrorToString(int8_t code) {
   switch (code) {
      case 1: return F("The Server does not support the level of the MQTT protocol requested");
      case 2: return F("The Client identifier is correct UTF-8 but not allowed by the Server");
      case 3: return F("The MQTT service is unavailable");
      case 4: return F("The data in the user name or password is malformed");
      case 5: return F("Not authorized to connect");
      case 6: return F("Exceeded reconnect rate limit. Please try again later.");
      case 7: return F("You have been banned from connecting. Please contact the MQTT server administrator for more details.");
      case -1: return F("Connection failed");
      case -2: return F("Failed to subscribe");
      case -3: return F("Connection Lost");
      case -4: return F("Connection Timeout");
      default: return F("Unknown error");
   }
}

这样8266也连接上了阿里云平台,我们通过8266订阅主题,然后MQTT.fx发布信息从而控制8266的LED亮灭。

如果烧录进去后一直都是“The Client identifier is correct UTF-8 but not allowed by the Server”报错可以改一下 PubSubClient.h头文件内容

// 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
#ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 1024
#endif
 
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 300
#endif
 
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 60
#endif

现在就是往阿里云的mqtt服务器添加topic,让esp8266可以订阅这个topic,mqtt.fx客户端可以对这个topic发布控制信息,esp8266解析这个订阅信息从而控制灯的亮灭。

5、结束–连接阿里云平台

当把上面的程序下载到我们自己的esp8266的时候,如果已经通过MQTT连接阿里云成功后,可以在网页中验证一下,往你的设备发布一下消息,看串口有没有打印出来,或者LED灯能不能控制亮灭。 https://i-blog.csdnimg.cn/direct/230d92494c5e47ab989a1ef96aaa4ce3.png

如果正常控制,说明协议通道已经搭建好了。但是当我们使用mqttfx客户端模拟去控制esp8266时,我们向主题发送控制信息,esp8266却没有任何反应。因为两个人发布和订阅的主题虽然都是setled,但是前面的device_name确是不一致的,这样订阅的主题不对当然获取不到有用数据。我们需要使用新的功能 消息转发的云产品流转。 自己去设置一下数据来源这些,主要就是你另一个设备向主题发布的数据转发到esp8266订阅主题的设备上去就行了。这样一个设备远程控制另一个设备就完成了。这能写这么多了,又不明白的评论区讨论一下。 https://i-blog.csdnimg.cn/direct/e6db6c1be33c4990b6cf71b5e56be1b7.png