ESP8266/32通过 Arduino连接 Tuyalink

ESP8266/32通过 Arduino连接 Tuyalink
起因最近手上有一些闲置的ESP8266/32模组,在网上查找各种设备上云远程控制的方法,发现了涂鸦的Tuyalink接入方式(之前使用过涂鸦产品,有联动控制和消息推送)但是涂鸦官方没有提供Tuyalink Arduino库,所以只能自己动手啦,在使用Arduino开发接入整体还是非常方便高效的,但是也踩了几个坑,所以这里整理笔记记录下,方便感兴趣的同学参考。至于为什么使用Arduino只能说DDDD(懂得都懂)YYDS,简单方便,有各种开源库,也可以通过看别人写的库学习编程技巧,真是何乐而不为呢。

什么是Tuyalink?

TuyaLink协议是涂鸦 平台面向物联网开发领域设计的一种数据交换规范,数据格式为 JSON,主要用于设备端和涂鸦 IoT 开发平台的双向通信,更便捷地实现了设备端和平台之间的业务数据交互。

主要用于使用非涂鸦模组接入涂鸦平台,使用MQTT 3.1.1协议接入,是生态设备接入涂鸦的一种方式。 (下面引用涂鸦官网对Tuyalink的接入流程图,个人觉得流程很清晰所以这里引用一下啦)
在这里插入图片描述

简单说就是Tuyalink就是涂鸦提供了一个MQTT服务器让设备可以连接到涂鸦云服务,然后通过手机或者微信小程序远程控制。

Tuyalink支持哪些功能

  1. 远程控制
  2. OTA(远程升级)
  3. 管理子设备——类似网关功能
  4. 文件上传下载服务
  5. …这里就不一 一列举了,感兴趣可以直接到涂鸦开发者平台查看。

这里提供一个涂鸦官网介绍链接:https://developer.tuya.com/cn/docs/iot/device-connection?id=Kb46bqq71kwtd

进入正题——如何连接到Tuyalink服务器?

端口和域名

涂鸦 IoT 开发平台支持全球多个区域的设备接入,故需要根据设备实际使用的区域,来选择对应的接入点。全球 6 大区 MQTT 接入点可自行查看。这里主要测试中国区接入。

区域MQTT 接入域名端口
中国数据中心m1.tuyacn.com8883

获取接入授权码

Tuyalink采用一机一密认证是预先为每个设备烧录其唯一的设备证书(即 ProductID、DeviceID 和 DeviceSecret)。当设备与云平台建立连接时,云平台对其携带的设备证书信息进行加密计算,最后使用 username/password 的方式认证。获取授权码方法见下图,需要先在涂鸦平台上创建生态设备产品,然后领取获取购买授权。

小提示:每个账号可以免费获取2个接入授权码哦

在这里插入图片描述

安装依赖

  1. 安装 ESP8266 开发板。 点击工具 -> 开发板 -> 开发板管理。搜索 ESP8266,点击安装。*注:这里最好安装最新的版本,我安装的是3.1.2,我测试过2.7.1版本MQTT连接会失败。
  2. 安装 PubSub client 库。 点击项目 -> 加载库 -> 管理库…。搜索 PubSubClient,安装 PubSubClient by Nick O’Leary。
  3. 安装 Crypto 库,后面通过SHA256计算连接MQTT的密码需要使用

这条分割线之后即认为你已经准备好了Arduino开发环境了


代码

导入必要的库

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>
#include <SHA256.h>

定义必要的变量保存参数

#define SHA256HMAC_SIZE 32

// WiFi credentials
const char *wifi_ssid = "填入自己的WiFi名";             // Replace with your WiFi name
const char *wifi_password = "密码";   // Replace with your WiFi password

// MQTT Broker settings
const int mqtt_port = 8883;  // MQTT port (TLS)
const char *mqtt_broker = "m1.tuyacn.com";  // EMQX broker endpoint
const char *mqtt_topic = "tylink/%s/thing/model/get";     // MQTT topic

//需要使用到NTP时间服务获取unix时间戳用于连接MQTT使用
// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// const char* ntp_server = "cn.pool.ntp.org"; // Recommended NTP server for users in China
const long gmt_offset_sec = 0;            // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

定义Tuyalink连接参数

//productId、deviceId、deviceSecret是涂鸦平台获取到授权码信息
const char productId[] = "rwosj58aaqjk **** ";
const char deviceId[] = "6c95875d0f5ba69607 **** ";
const char deviceSecret[] = " ******************* ";

char clientID[50] ;
char username[100];
char password[96] ;

证书

因为Tuyalink连接MQTT服务器使用TLS加密连接,所以需要导入服务器证书,这里的证书可以在官方提供IoT Core SDK(C)中找到。如果不想研究源码的这里可以直接复制下面的代码即可。

// SSL certificate for MQTT broker
static const char ca_cert[]
{
  "-----BEGIN CERTIFICATE-----\n"
  "MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx\n"
  "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT\n"
  "EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp\n"
  "ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz\n"
  "NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH\n"
  "EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE\n"
  "AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw\n"
  "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD\n"
  "E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH\n"
  "/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy\n"
  "DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh\n"
  "GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR\n"
  "tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA\n"
  "AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n"
  "FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX\n"
  "WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu\n"
  "9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr\n"
  "gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo\n"
  "2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO\n"
  "LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI\n"
  "4uJEvlz36hz1\n"
  "-----END CERTIFICATE-----\n"
};

const char iot_dns_cert_der[] = {
    "-----BEGIN CERTIFICATE-----\n"
    "MIICGDCCAb2gAwIBAgIRAI4kVSI/DR6TlRqvv0C7A4EwCgYIKoZIzj0EAwIwNTEdMBsGA1UECgwU\n"
    "U2luYmF5IEdyb3VwIExpbWl0ZWQxFDASBgNVBAMMC0Nsb3VkIFJDQSAyMCAXDTIyMDUzMTE2MDAw\n"
    "MFoYDzIwNzIwNjMwMTU1OTU5WjA1MR0wGwYDVQQKDBRTaW5iYXkgR3JvdXAgTGltaXRlZDEUMBIG\n"
    "A1UEAwwLQ2xvdWQgUkNBIDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATqjfuzyXh8P0MuuWrH\n"
    "PUSoOp9OqsSHnCvDL18EK/Wfo1MOaQoIAy82zaC+ggjQph0AwCICTfzauMr0AUKw28Vko4GrMIGo\n"
    "MA4GA1UdDwEB/wQEAwIBBjBFBgNVHSUEPjA8BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMD\n"
    "BggrBgEFBQcDCAYIKwYBBQUHAwQGCCsGAQUFBwMJMA8GA1UdEwQIMAYBAf8CAQEwHwYDVR0jBBgw\n"
    "FoAUjW5pdbOF5Bmvn+MrD+yG6tcJ7yowHQYDVR0OBBYEFI1uaXWzheQZr5/jKw/shurXCe8qMAoG\n"
    "CCqGSM49BAMCA0kAMEYCIQDaNnFTr66LnhYY+55C234I7MWBveU3RLg5pcVzb5EYUAIhAJN4+4go\n"
    "F3rrb03/o2AsmPMLLZ+UjTjeCXrTXUyxBt2N\n"
    "-----END CERTIFICATE-----\n"};

计算连接密钥

不得不说涂鸦还是很注重数据安全的,这里MQTT连接服务器的用户名和密码还需要通过前面获取的授权码通过hmacSha256加密算法计算得到。计算格式这里也从官方文档搬运到这里了,方便感兴趣的同学查看。

参数名称参数说明示例
user name D e v i c e I D I s i g n M e t h o d = h m a c S h a 256 , t i m e s t a m p = {DeviceID} I signMethod=hmacSha256,timestamp= DeviceIDIsignMethod=hmacSha256,timestamp={当前 10 位时间戳},secureMode=1,accessType=1;例如:6c828cba434ff40c074wF2 I signMethod=hmacSha256,timestamp=1607837283,secureMode=1,accessType=1
passwordhmacSha256(content, DeviceSecret), content 的格式:deviceId= D e v i c e I D , t i m e s t a m p = {DeviceID},timestamp= DeviceID,timestamp={当前 10 位时间戳},secureMode=1,accessType=1 需要按照 deviceId,timestamp,secureMode,accessType 这个顺序组装明文内容。 64 位字符的 16 进制数,不足 64位时前面需要补零。content 例如:“deviceId=6c828cba434ff40c074wF2,timestamp=1607635284,secureMode=1,accessType=1”  DeviceSecret 例如:ffad8eb66ae8c717  password 例如:9088f1608df4744e2a933ff905ffdde58dc7213510f25ad786a89896a5ea1104
static String hmac256(const String& signcontent, const String& ds) {
  byte hashCode[SHA256HMAC_SIZE];
  SHA256 sha256;

  const char* key = ds.c_str();
  size_t keySize = ds.length();

  sha256.resetHMAC(key, keySize);
  sha256.update((const byte*)signcontent.c_str(), signcontent.length());
  sha256.finalizeHMAC(key, keySize, hashCode, sizeof(hashCode));

  String sign = "";
  for (byte i = 0; i < SHA256HMAC_SIZE; ++i) {
    sprintf(password + 2 * i, "%02x", hashCode[i]);
    //下面两行功能和sprintf功能一样,只不过是string类型存储的
    sign += "0123456789ABCDEF"[hashCode[i] >> 4];
    sign += "0123456789ABCDEF"[hashCode[i] & 0xf];
  }

  return sign;
}

static int tuya_mqtt_auth_signature_calculate(const char* deviceId, const char* deviceSecret) {
  if (NULL == deviceId || NULL == deviceSecret) {
    return -1;
  }

  uint32_t timestamp = time(nullptr);

  /* client ID */
  sprintf(username, "%s|signMethod=hmacSha256,timestamp=%d,secureMode=1,accessType=1", deviceId, timestamp);
  Serial.print("username:");
  Serial.println(username);

  /* username */
  sprintf(clientID, "tuyalink_%s", deviceId);
  Serial.print("clientID:");
  Serial.println(clientID);

  /* password */
  int i = 0;
  char passward_stuff[255];
  size_t slen = sprintf(passward_stuff, "deviceId=%s,timestamp=%d,secureMode=1,accessType=1", deviceId, timestamp);
  hmac256(passward_stuff, deviceSecret);

  Serial.print("password:");
  Serial.println(password);

  return 0;
}

TLS连接到MQTT


void connectToWiFi() {
    WiFi.begin(wifi_ssid, wifi_password);
    Serial.println("Connecting to WiFi ");
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Connected to WiFi Success !");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}
void connectToMQTT() {

    BearSSL::X509List cert(ca_cert);
    espClient.setTrustAnchors(&cert);
    // espClient.setFingerprint(fingerprint);
    while (!mqtt_client.connected()) {
        tuya_mqtt_auth_signature_calculate(deviceId, deviceSecret);
        Serial.printf("Connecting to MQTT Broker as %s.....\n", clientID);
        if (mqtt_client.connect(clientID,  username, password)) {
            Serial.println("Connected to MQTT broker");
            char auto_subscribe_topic[64];
            sprintf(auto_subscribe_topic, "tylink/%s/channel/downlink/auto_subscribe", deviceId);            
            mqtt_client.subscribe(auto_subscribe_topic);
            sprintf(auto_subscribe_topic,mqtt_topic,deviceId);
            // Publish message upon successful connection
            mqtt_client.publish(auto_subscribe_topic, "{\"data\":{\"format\":\"simple\"}}");
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.println(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            delay(5000);
        }
    }
}

void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.print(topic);
    Serial.print("]: ");
    for (int i = 0; i < length; i++) {
        Serial.print((char) payload[i]);
    }
    Serial.println();
}

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();
}

到了这里连接到Tuyalink的步骤已经全部讲完了

使用涂鸦App通过扫码绑定到设备就能远程控制啦——这里App的使用操作就不做赘述,有疑问的同学可以到涂鸦官网咨询。
在这里插入图片描述

运行截图
在这里插入图片描述

在这里插入图片描述

剩下的事情就是需要将下发的JSON内容解析通过8266执行即可,数据解析如果有时间后续我在另一个文章做记录,感谢大家的观看。

  • 31
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值