Professional Documents
Culture Documents
Báo Cáo Gi A K
Báo Cáo Gi A K
I. Lý thuyết:
1. MQTT:
a. Khái niệm:
Đây là một giao thức truyền thông điệp (message) theo mô hình
publish/subscribe (xuất bản – theo dõi), sử dụng băng thông thấp, độ tin cậy cao và
có khả năng hoạt động trong điều kiện đường truyền không ổn định.
b. Kiến trúc: kiến trúc mức cao của MQTT bao gồm:
Broker được coi như trung tâm, nó là điểm giao của tất cả các kết nối đến
từ client. Nhiệm vụ chính của broker là nhận mesage từ publisher, xếp các
message theo hàng đợi rồi chuyển chúng tới một địa chỉ cụ thể. Nhiệm vụ
phụ của broker là nó có thể đảm nhận thêm một vài tính năng liên quan tới
quá trình truyền thông như: bảo mật message, lưu trữ message, logs, …
Client: được chia thành 2 nhóm là publisher và subscriber. Client là các
software components hoạt động tại edge device nên chúng được thiết kế
để có thể hoạt động một cách linh hoạt (lightweight). Client chỉ làm ít nhất
một trong 2 việc là publish các message lên một topic cụ thể hoặc
subscribe một topic nào đó để nhận message từ topic này.
Trong một hệ thống sử dụng giao thức MQTT, nhiều node trạm (gọi là mqtt
client – gọi tắt là client) kết nối tới một MQTT server (gọi là broker). Mỗi client sẽ
đăng ký một vài kênh (topic), ví dụ như “/client1/channel1”, “/client1/channel2”.
Quá trình đăng ký này gọi là “subscribe”, giống như chúng ta đăng ký nhận tin trên
một kênh Youtube vậy. Mỗi client sẽ nhận được dữ liệu khi bất kỳ trạm nào khác gởi
dữ liệu và kênh đã đăng ký. Khi một client gởi dữ liệu tới kênh đó, gọi là “publish”.
Một gói tin có thể được gởi ở bất kỳ QoS nào, và các client cũng có thể
subscribe với bất kỳ yêu cầu QoS nào. Có nghĩa là client sẽ lựa chọn QoS tối đa mà
nó có để nhận tin. Ví dụ, nếu 1 gói dữ liệu được publish với QoS2, và client
subscribe với QoS0, thì gói dữ liệu được nhận về client này sẽ được broker gởi với
QoS0, và 1 client khác đăng ký cùng kênh này với QoS 2, thì nó sẽ được Broker gởi
dữ liệu với QoS2.
Một ví dụ khác, nếu 1 client subscribe với QoS2 và gói dữ liệu gởi vào kênh
đó publish với QoS0 thì client đó sẽ được Broker gởi dữ liệu với QoS0. QoS càng
cao thì càng đáng tin cậy, đồng thời độ trễ và băng thông đòi hỏi cũng cao hơn
e. Mô hình MQTT:
Các thành phần chính của MQTT là clients, servers (=brokers), sessions,
subscriptions và topics.
bit 7 6 5 4 3 2 1 0
byte 1 Loại Message Cờ DUP QoS level RETAIN
byte 2 Độ dài còn lại
Byte 1 : Chứa loại Message và các cờ (DUP, QoS level, and RETAIN). Byte
2 : (Ít nhất 1 byte) quy định độ dài còn lại.
Loại Message
Vị trí: byte 1, bits 7-4.
Một số 4-bit không dấu diễn tả các giá trị được miêu tả dưới bảng 2.5
Bảng 1.2. Loại message
Cờ này được bật khi client hoặc server đang cố chuyển lại một gói
PUBLISH, PUBREL, SUBSCRIBE hoặc UNSUBSCRIBE. Giá trị này được sử dụng
trong các mesage mà có QoSS lớn hơn 0 và yêu cầu ACK. Khi bit DUP được set, phần
header thay đổi sẽ chứa Message ID. Nhìn vào giá trị này sẽ biết được gói tin đã nhận
được trước đó hay không. Nó không nên sử dụng để tin ngay rằng có duplicates hay
không.
* QoS : Vị trí byte 1, bits 2-1.
Cờ này sẽ cho biết độ đảm bảo việc nhận message PUBLISH. Giá trị của QoS được
miêu tả trong bảng 2.7
Bảng 1.4. Giá trị QoS
Hình 1.3 Session và subscription được thiết lập với clean session flag = 1
Quy trình truyền nhận dữ liệu trong MQTT khi session flag = 1:
- Client và Server được kết nối qua giao thức TCP.
- Client gửi gói tin CONNECT yêu cầu kết nối đến Server, clean session = 1.
Đây là thời điểm đánh dấu session được thiết lập.
- Server gởi gói CONNACK xác nhận thiết lập kết nối thành công.
- Client thực hiện SUBSCRIBE đến topic XYZ. Đây là thời điểm bắt đầu timeout
của một subscription.
- Server gởi gói SUBACK xác nhận quá trình subscription.
- Client PUBLISH để gởi topic - message đến server.
- Sau khi nhận đủ thông tin, client gửi gói UNSUBSCRIBE topic XYZ để kết thúc
quá trình Subscribe.
- Server trả về gói UNSUBACK.
- Client gửi gói DISCONNECT để kết thúc session truyền thông.
Trường hợp 2: Session và subscription được thiết lập với clean session flag = 0
(durable subscription).
Hình 1.4. Session và subscription được thiết lập với clean session flag = 0
Quy trình truyền nhận dữ liệu trong MQTT khi session flag = 0:
- Subscription lifetime đã được thiết lập trước.
- Client và Server được kết nối qua giao thức TCP.
- Client gửi gói tin CONNECT yêu cầu kết nối đến Server, clean session = 0.
Đây là thời điểm đánh dấu session được thiết lập.
- Server gởi gói CONNACK xác nhận thiết lập kết nối thành công.
- Client PUBLISH để gởi topic - message đến server.
- Client gửi gói DISCONNECT để kết thúc session truyền thông.
PUBLISH message flows
2. HTTP:
II. Lập trình:
1. MQTT:
*main.c:
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "smartconfig_mode_sta.h"
#include "wifi_mode_sta.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
#define CONFIG_BROKER_URL "mqtt://mqtt.eclipse.org:1883"
static const char *TAG = "MQTT_EXAMPLE";
esp_mqtt_client_handle_t client;
void mqtt_task(void *param);
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
client = event->client;
int msg_id;
// your_context_t *context = event->context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_publish(client, "/temp", "data_3", 0, 1,
0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
xTaskCreate(&mqtt_task, "mqtt_task", 2048, client, 7, NULL);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0,
0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event-
>msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.uri = CONFIG_BROKER_URL,
.event_handle = mqtt_event_handler,
};
#if CONFIG_BROKER_URL_FROM_STDIN
char line[128];
if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) {
int count = 0;
printf("Please enter url of mqtt broker\n");
while (count < 128) {
int c = fgetc(stdin);
if (c == '\n') {
line[count] = '\0';
break;
} else if (c > 0 && c < 127) {
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
mqtt_cfg.uri = line;
printf("Broker url: %s\n", line);
} else {
ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
abort();
}
#endif /* CONFIG_BROKER_URL_FROM_STDIN */
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
//esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_hand
ler, client);
esp_mqtt_client_start(client);
}
void mqtt_task(void *param)
{
int msg_id;
while(1)
{
msg_id = esp_mqtt_client_publish(client, "/temp", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
vTaskDelay(2000);
}
}
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
//ESP_ERROR_CHECK(nvs_flash_init());
//ESP_ERROR_CHECK(esp_netif_init());
//ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuc
onfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
wifi_init();
wifi_connect_station_mode();
mqtt_app_start();
}
*smartconfig_sta_mode.c:
#include "smartconfig_mode_sta.h"
/* FreeRTOS event group to signal when we are connected & ready to make a requ
est */
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const int DISCONNECT_BIT = BIT2;
static const char *TAG = "smartconfig";
static void smartconfig_example_task(void * parm);
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096
, NULL, 3, NULL);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNE
CTED) {
//xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
xEventGroupSetBits(s_wifi_event_group,DISCONNECT_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
ESP_LOGI(TAG, "Scan done");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
ESP_LOGI(TAG, "Found channel");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
ESP_LOGI(TAG, "Got SSID and password");
smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_p
swd_t *)event_data;
wifi_config_t wifi_config;
uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 };
bzero(&wifi_config, sizeof(wifi_config_t)); //clear wifi_config (trả v
ề 0)
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
memcpy(wifi_config.sta.password, evt->password, sizeo
f(wifi_config.sta.password));
wifi_config.sta.bssid_set = evt->bssid_set;
if (wifi_config.sta.bssid_set == true) {
memcpy(wifi_config.sta.bssid, evt->bssid, sizeo
f(wifi_config.sta.bssid));
}
memcpy(ssid, evt->ssid, sizeof(evt->ssid));
memcpy(password, evt->password, sizeof(evt->password));
ESP_LOGI(TAG, "SSID:%s", ssid);
ESP_LOGI(TAG, "PASSWORD:%s", password);
ESP_ERROR_CHECK( esp_wifi_disconnect() );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_connect() );
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
}
}
void initialise_wifi(void)
{
tcpip_adapter_init();
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
//ESP_ERROR_CHECK(esp_wifi_set_ps(Wifi_));
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &e
vent_handler, NULL) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void smartconfig_example_task(void * parm)
{
EventBits_t uxBits;
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTO
UCH_DONE_BIT | DISCONNECT_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi Connected to ap");
//tcp_client_connect();
}
if(uxBits & DISCONNECT_BIT) {
ESP_LOGI(TAG, "Reconnecting ...");
ESP_ERROR_CHECK( esp_wifi_connect() );
}
if(uxBits & ESPTOUCH_DONE_BIT) {
ESP_LOGI(TAG, "smartconfig over");
esp_smartconfig_stop();
}
}
}
*smartconfig_sta_mode.h:
2. HTTP:
III. Mô phỏng:
1. MQTT:
*Truyền: (topic /temp)
Hình 3.1: Mô phỏng nhận dữ liệu
*Nhận: (topic /humi)