Reputation: 13
I'm trying to connect to the Tuya IoT Developer platform using https://developer.tuya.com/en/docs/iot/MQTT-protocol?id=Kb65nphxrj8f1 as a reference.
I've created a product in the developer portal already but can't seem to get my esp32 to connect to the Tuya MQTT servers.
Here's what I've written
#include <Seeed_mbedtls.h>
#define KEY_SIZE 32 // 256 bits for HMAC-SHA256
#define OUTPUT_SIZE 32 // SHA-256 outputs 32 bytes
// Your 256-bit secret key (32 bytes)
String str = "DeviceSecret";
const uint8_t* deviceSecret = (const uint8_t*)str.c_str();
#include <WiFi.h>
WiFiClient wifiClient;
#include <time.h> // include time library
// (utc+) TZ in hours
long timezone = 0;
byte daysavetime = 0;
#include <PubSubClient.h>
PubSubClient client(wifiClient);
// Set up wifi
#define SSID "SSID"
#define WIFI_PWD "pwd"
// MQTT set up
//MQTT server
#define MQTT_BROKER "m1.tuyacn.com"
#define MQTT_PORT (8883)
String compute_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *input, size_t input_len) {
mbedtls_md_context_t ctx;
const mbedtls_md_info_t *md_info;
uint8_t hmac_output[32]; // SHA-256 outputs 32 bytes
mbedtls_md_init(&ctx);
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (md_info == NULL) {
Serial.println("Failed to get MD info for SHA256");
mbedtls_md_free(&ctx);
return String();
}
if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
Serial.println("Failed to setup MD context");
mbedtls_md_free(&ctx);
return String();
}
if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
Serial.println("Failed to start HMAC");
mbedtls_md_free(&ctx);
return String();
}
if (mbedtls_md_hmac_update(&ctx, input, input_len) != 0) {
Serial.println("Failed to update HMAC");
mbedtls_md_free(&ctx);
return String();
}
if (mbedtls_md_hmac_finish(&ctx, hmac_output) != 0) {
Serial.println("Failed to finish HMAC");
mbedtls_md_free(&ctx);
return String();
}
mbedtls_md_free(&ctx);
// Convert the HMAC output to a hexadecimal string
String hmac_str;
for (int i = 0; i < sizeof(hmac_output); i++) {
char buf[3];
sprintf(buf, "%02x", hmac_output[i]);
hmac_str += buf;
}
return hmac_str;
}
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600);
Serial.print("Connecting to " SSID);
WiFi.begin(SSID, WIFI_PWD); // attempt to connect to an existing wifi
//Wait for wifi to connect
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(" . ");
}
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
//Time Server Set up
Serial.println("Contacting Time Server");
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
delay(1000);
time_t now;
time(&now);
//MQTT Credentials set up
String MQTT_USERNAME = "deviceID|signMethod=hmacSha256,timestamp=" + String(now) +",secureMode=1,accessType=1";
// Your original const char* message
const char *message = "deviceId=deviceID,timestamp=,secureMode=1,accessType=1";
// The String you want to insert
String additional = String(now);
// Convert the original message to a String
String messageString = String(message);
// Insert the additional String at the 43rd position
size_t insertPos = 42; // Position to insert
messageString = messageString.substring(0, insertPos) + additional + messageString.substring(insertPos);
// Convert the modified message back to const char*
const char* final_message = messageString.c_str();
// Compute HMAC-SHA256
String MQTT_PWD = compute_hmac_sha256(deviceSecret, KEY_SIZE, (const uint8_t *)final_message, strlen(final_message));
Serial.println(now);
Serial.print("MQTT_PWD: ");
Serial.println(MQTT_PWD);
Serial.println("init successfull");
String tuyalink = "tuyalink_DeviceId";
// Handle MQTT connection.
client.setServer(MQTT_BROKER, MQTT_PORT); // set broker settings
while (! client.connected()) { // check connected status
if ( client.connect(tuyalink.c_str(),MQTT_USERNAME.c_str(),MQTT_PWD.c_str())){ // connect with random id
Serial.println("MQTT connected."); // report success
//client.subscribe("Test/ra542");
} else {
Serial. printf (" failed , rc=%d try again in 5 seconds", client.state ()); // report error
delay(5000); // wait 5 seconds
}
}
}
// the loop function runs over and over again forever
void loop() {
Serial.println("Hello World!");
delay(5000);
}
In my final code I've replaced the deviceId and DeviceSecret with the credentials I've gotten from tuya
On Connecting to the MQTT Server I get
failed , rc=-4 try again in 5 seconds failed
According to https://pubsubclient.knolleary.net/api#state rc -4 means the server didn't respond within the keepalive time.
I've tried increasing the keep alive time to 60s but that doesn't change anything
uint16_t keepAlive = 60000;
client.setKeepAlive(keepAlive);
When Testing with test.mosquitto.org and port 1883 without credentials it seems to work fine.
I'm not sure where I'm going wrong here. Any ideas/pointers would be greatly appreciated
Upvotes: 0
Views: 821
Reputation: 11
I successfully connected tuya on ESP8266, first of all, you can follow this step to check your code.
My environment is using Arduino IDE with ESP8266, without arduino chip, use third-party libraries for MQTT, SHA256 calculation, not the Tuya iot sdk.
My code is based on this tutorial: https://blog.csdn.net/weixin_42172619/article/details/137993680
There are some points to consider.
You can use the Tuya iot SDK header file directly, don't change any format. examples\custom_protocol_basic_demo\tuya_cacert.h
BearSSL::X509List *certList;
certList = new BearSSL::X509List(tuya_cacert_pem);
espClient.setTrustAnchors(certList);
Note: I think setCACert() is deprecated, but setTrustAnchors() can be used instead.
If you don't set the time, you may get failed, rc=-2
error,timestamp
can use the same timestamp as Tuya username.
espClient.setX509Time(timestamp);
https://developer.tuya.com/en/docs/iot/MQTT-protocol?id=Kb65nphxrj8f1
I believe it can be verified from the official tutorials that you can get the same results using the example content
and DeviceSecret
, and result is 9088f16***6a5ea1104(Same as tutorial)
Upvotes: 1
Reputation: 1
You must connect to the Tuya server in a secure connection
According to Tuya documentation insert link description here
#include <WiFiClientSecure.h>
WiFiClientSecure wifiClient;
And add the root certificate before connecting to the server
wifiClient.setCACert(Tuya_CERT_CA);
Upvotes: 0
Reputation: 4034
configTime(3600timezone, daysavetime3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); delay(1000);
The access to an NTP server with configTime() take time, it could varies from a few ms to a minute. So the delay(1000) isn't adequate, and if it failed, the rest of the code of trying to insert the timestamp would insert the incorrect data.
Here is a suggestion for ensuring the configTime get the correct timestamp.
int timeOut = 0;
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
while (time(nullptr) < 1716086610) { // 1716086610 is just a valid timestamp in recent past
delay(1000);
};
time_t now = time(NULL);
Update
Further to your comment, if you are using PORT 8883, you probably need a secure TCP connection. I don't have tuya account, but I test the following code on test.mosquitto.org:port 8883
with the following code that you might want to adopt it for your application.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
// Set up wifi
#define SSID "wifi_ssid"
#define WIFI_PWD "wifi_password"
// MQTT set up
#define MQTT_BROKER "test.mosquitto.org"
#define MQTT_PORT 8883
WiFiClientSecure wifiClient;
PubSubClient client(wifiClient);
void setup() {
Serial.begin(115200);
Serial.print("Connecting to WiFi");
WiFi.begin(SSID, WIFI_PWD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
//Time Server Set up
Serial.println("Contacting Time Server");
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
while (time(nullptr) < 1716086610) { // 1716086610 is just a valid timestamp in recent past
delay(1000);
};
time_t now = time(NULL);
Serial.println("Contacting to MQTT broker");
wifiClient.setInsecure(); // not to validate server certificate
client.setServer(MQTT_BROKER, MQTT_PORT); // set broker settings
while (! client.connected()) { // check connected status
if (client.connect("my_mqtt_client") {
Serial.println("MQTT connected."); // report success
} else {
Serial. printf (" failed , rc=%d try again in 5 seconds", client.state ()); // report error
delay(5000); // wait 5 seconds
}
}
}
Upvotes: 0