如果要發送https POST或GET需要有證書來驗證伺服器,
因此需要新增證書及使用esp_tls.h的API來實現連線。
step 1
新增證書
到cURL下載公開認可的發行CACERT.pem
step 2
包含非常多發行單位證書,因ESP32容量大小限制,只保留需要的部分。
這邊以Line Notify為例,網址是:https://notify-api.line.me/api/notify
可以看到證書如下

發行者為:GlobalSign
因此編輯cacert.pem只保留GlobalSign相關的證書
檔案大小從222KB縮小為10KB
step 3
到CMakeLists.txt中新增code把cacert.pem增加到二進制檔案中
檔案路徑修改成存放證書的路徑
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/cacert.pem" TEXT)
step 4
main新增引用esp_tls.h才能使用API
#include "esp_tls.h"
function外宣告證書起始和結束
extern const uint8_t cacert_pem_start[] asm("_binary_cacert_pem_start");
extern const uint8_t cacert_pem_end[] asm("_binary_cacert_pem_end");
編碼轉換的urlencode function
char *urlencode(const char *str) {
if (str == NULL) {
return NULL;
}
const char *hex = "0123456789ABCDEF";
size_t len = strlen(str);
char *out = malloc(3 * len + 1); // 假設每個字元都要轉換成百分比編碼,預先分配足夠的空間
if (out == NULL) {
// 記憶體分配失敗
return NULL;
}
char *ptr = out;
while (*str) {
if (isalnum((unsigned char)*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~') {
*ptr++ = *str;
} else if (*str == ' ') {
// 空格轉換為%20
*ptr++ = '%';
*ptr++ = '2';
*ptr++ = '0';
} else {
*ptr++ = '%';
*ptr++ = hex[*str >> 4];
*ptr++ = hex[*str & 0xF];
}
str++;
}
*ptr = '\0'; // 字串結尾符號
return out; // return編碼後字串
}
Send Line Notify function
void send_line_notify(const char *message) {
// 建立TLS設置結構
esp_tls_cfg_t cfg = {
.cacert_pem_buf = cacert_pem_start,
.cacert_pem_bytes = cacert_pem_end - cacert_pem_start,
};
// 使用urlencode編碼欲傳送的message
char *encoded_message = urlencode(message);
if (!encoded_message) {
ESP_LOGE(TAG, "Failed to encode message!");
return;
}
ESP_LOGI(TAG, "Encoded message: %s", encoded_message); // 顯示編碼後的訊息
ESP_LOGI(TAG, "Encoded message length: %d", strlen(encoded_message)); // 顯示編碼後訊息的長度
int content_length = strlen("message=") + strlen(encoded_message);
ESP_LOGI(TAG, "Calculated content length: %d", content_length); // 顯示編碼後加上message=的訊息長度
const char *LINE_TOKEN = ""; //替換成自己的TOKEN
const char *WEB_SERVER_URL = "https://notify-api.line.me/api/notify";
char REQUEST[1024];
// 建立Request請求
int request_length = snprintf(REQUEST, sizeof(REQUEST),
"POST /api/notify HTTP/1.1\r\n"
"Host: notify-api.line.me\r\n"
"Authorization: Bearer %s\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %d\r\n\r\n"
"message=%s",
LINE_TOKEN, strlen("message=") + strlen(encoded_message), encoded_message);
//檢查大小是否相同
if (request_length >= sizeof(REQUEST)) {
ESP_LOGE(TAG, "Request string buffer too small!");
free(encoded_message);
return;
}
ESP_LOGI(TAG, "HTTPS Request: %s", REQUEST); // 顯示完整HTTP Request請求
char buf[512];
int ret, len;
// 初始化TLS
esp_tls_t *tls = esp_tls_init();
if (!tls) {
ESP_LOGE(TAG, "Failed to allocate esp_tls handle!");
return;
}
// 建立TLS連接
if (esp_tls_conn_http_new_sync(WEB_SERVER_URL, &cfg, tls) == 1) {
ESP_LOGI(TAG, "Connection established...");
} else {
ESP_LOGE(TAG, "Connection failed...");
goto cleanup;
}
// 發送HTTP請求
size_t written_bytes = 0;
do {
ret = esp_tls_conn_write(tls, REQUEST + written_bytes, strlen(REQUEST) - written_bytes);
if (ret >= 0) {
ESP_LOGI(TAG, "%d bytes written", ret);
written_bytes += ret;
} else if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "esp_tls_conn_write returned: [0x%02X](%s)", ret, esp_err_to_name(ret));
goto cleanup;
}
} while (written_bytes < strlen(REQUEST));
// 讀取HTTP回應
ESP_LOGI(TAG, "Reading HTTP response...");
do {
len = sizeof(buf) - 1;
memset(buf, 0x00, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len);
if (ret == ESP_TLS_ERR_SSL_WANT_WRITE || ret == ESP_TLS_ERR_SSL_WANT_READ) {
continue;
} else if (ret < 0) {
ESP_LOGE(TAG, "esp_tls_conn_read returned [-0x%02X](%s)", -ret, esp_err_to_name(ret));
break;
} else if (ret == 0) {
ESP_LOGI(TAG, "connection closed");
break;
}
len = ret;
ESP_LOGD(TAG, "%d bytes read", len);
// 顯示Response
printf("%.*s", len, buf);
} while (1);
free(encoded_message); // 釋放編碼後字串
cleanup:
// 銷毀TLS連線物件
esp_tls_conn_destroy(tls);
}
備註:因Line Notify request body起始為 “message=”+編碼後訊息
因此Content-Length不能只有編碼後長度,要加上”message=”
最後在需要發送訊息的地方呼叫
send_line_notify("Subscribe /RFIDTrackingSystem/command Success");
step 5
完成,若有收到訊息,但Response為400、408等等,檢查傳送前的
文字編碼、編碼後長度、加上”message=”後的編碼長度、確認完整requests
長度可使用postman測試發送相同body內容,比較Content-Length
Visited 18 times, 1 visit(s) today

發佈留言