ESP32CAM-MQTT傳輸圖片(WEB)
# FastWeb之ESP32CAM-MQTT傳輸圖片
- 適用平臺:WEB(桌面)
# 1. 說明
本範例採用MQTT通訊協議,獲取ESP32CAM的圖片數據。MQTT是由IBM開發的通訊協議,為感測器提供一個輕量可靠的二進制通訊設定,使得開發MQTT與物聯網,機器之間的通訊變得非常簡單。MQTT的報文長度至多可達256MB,故可使用該特性來傳輸影象視訊等二進制數據。
在網頁端可以使用WebSocket實現對MQTT協議的連線以及訊息的傳輸,該種傳輸方式不需要經過FastWeb所在伺服器的中轉,直接由MQTT Broker向網頁客戶端傳輸,與傳統的MQTT傳輸相比響應速度更快,效能更好。ESP32-CAM模組採用TTL模式進行輸出。
通過本範例學習,可以掌握UgMQTTws控制元件的基本使用方法,並根據此來實現影象檢視的功能。
# 2. 零件連線圖
# 3. 使用零件
序 | 零件名稱 | 數量 |
---|---|---|
1 | ESP32CAM 開發板 | 1 |
2 | USB-TTL模組 | 1 |
3 | 杜邦線 | 若干 |
# 4. Arduino流程圖
# 5. Arduino程式
使用Arduino IDE 編譯並上傳以下Arduino程式。該程式使用的是esp32開發板,需要在Arduino IDE中新增安裝該開發板環境,並在編譯時選擇開發板環境為ESP32系列開發板。在Arduino IDE中新增ESP32開發板環境 (opens new window)。注意燒錄時ESP32CAM的開發板中IO0
針腳需要進行接地操作,否則會導致刷寫失敗。
#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
#include "driver/rtc_io.h"
//#include "StringArray.h"
#include <PubSubClient.h>
#include <base64.h>
#include <libb64/cencode.h>
//esp32-cam針腳定義 拍照相關
constexpr int kCameraPin_PWDN = 32;
constexpr int kCameraPin_RESET = -1; // NC
constexpr int kCameraPin_XCLK = 0;
constexpr int kCameraPin_SIOD = 26;
constexpr int kCameraPin_SIOC = 27;
constexpr int kCameraPin_Y9 = 35;
constexpr int kCameraPin_Y8 = 34;
constexpr int kCameraPin_Y7 = 39;
constexpr int kCameraPin_Y6 = 36;
constexpr int kCameraPin_Y5 = 21;
constexpr int kCameraPin_Y4 = 19;
constexpr int kCameraPin_Y3 = 18;
constexpr int kCameraPin_Y2 = 5;
constexpr int kCameraPin_VSYNC = 25;
constexpr int kCameraPin_HREF = 23;
constexpr int kCameraPin_PCLK = 22;
// 此處需替換連線的無線網路為自己使用的無線網路
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// 此處需替換MQTT服務端為自己使用的服務端
const char* mqtt_server = "YOUR_MQTT_BROKER_HOSTNAME";
const int mqtt_port = 1883;
const char* mqtt_user = "YOUR_MQTT_BROKER_USERNAME";
const char* mqtt_password = "YOUR_MQTT_BROKER_PASSWORD";
//設定主題
const char* mqtt_TopicName = "/devices/esp32/data";
//設定相機幀的解析度大小
framesize_t resolution_ = FRAMESIZE_QVGA;
//定義延時時間1000=1s
#define SLEEP_DELAY 60000 //延遲60s
#define FILE_PHOTO "/photo.jpg"
// OV2640 相機模組的針腳定義
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
// 選擇相機型號
#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
//#define CAMERA_MODEL_AI_THINKER
//#define ESP32_CLIENT_ID = WiFi.macAddress()
//const char* esp_client_id = WiFi.macAddress()
WiFiClient mqttClient;
PubSubClient client(mqttClient);
const int LED_BUILTIN = 4;
void setup_camera() {
// OV2640 camera module
camera_config_t config;
config.pin_pwdn = kCameraPin_PWDN;
config.pin_reset = kCameraPin_RESET;
config.pin_xclk = kCameraPin_XCLK;
config.pin_sscb_sda = kCameraPin_SIOD;
config.pin_sscb_scl = kCameraPin_SIOC;
config.pin_d7 = kCameraPin_Y9;
config.pin_d6 = kCameraPin_Y8;
config.pin_d5 = kCameraPin_Y7;
config.pin_d4 = kCameraPin_Y6;
config.pin_d3 = kCameraPin_Y5;
config.pin_d2 = kCameraPin_Y4;
config.pin_d1 = kCameraPin_Y3;
config.pin_d0 = kCameraPin_Y2;
config.pin_vsync = kCameraPin_VSYNC;
config.pin_href = kCameraPin_HREF;
config.pin_pclk = kCameraPin_PCLK;
config.xclk_freq_hz = 20000000;
config.ledc_timer = LEDC_TIMER_0;
config.ledc_channel = LEDC_CHANNEL_0;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 10;
config.fb_count = 1;
esp_err_t err = esp_camera_init(&config);
Serial.printf("esp_camera_init: 0x%x\n", err);
// sensor_t *s = esp_camera_sensor_get();
// s->set_framesize(s, FRAMESIZE_QVGA);
}
String msg;
int timeCount = 0;
void getimg(){//拍照分段發送到mqtt
camera_fb_t *fb = esp_camera_fb_get();
if (fb){
Serial.printf("width: %d, height: %d, buf: 0x%x, len: %d\n", fb->width, fb->height, fb->buf, fb->len);
char data[4104];
//client.publish(mqtt_TopicName, "0");
for (int i = 0; i < fb->len; i++){
sprintf(data, "%02X", *((fb->buf + i)));
msg += data;
// if (msg.length() == 4096){
// timeCount += 1;
// client.beginPublish(mqtt_TopicName, msg.length(), 0);
// client.print(msg);
// client.endPublish();
// msg = "";
// }
}
if (msg.length() > 0){
client.beginPublish(mqtt_TopicName, msg.length(), 0);
client.print(msg);
client.endPublish();
msg = "";
}
//client.publish(mqtt_TopicName, "1");
timeCount = 0;
esp_camera_fb_return(fb);
}
}
void setup_wifi() {
delay(10);
// 連線WIFI
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address : ");
Serial.println(WiFi.localIP());
}
//MQTT重連
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
setup_camera(); //設定相機
setup_wifi(); //連線WIFI
client.setServer(mqtt_server, mqtt_port); //連線MQTT Broker
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("mqtt connected");
}
}
void loop() {
getimg();
if (!client.connected()) {
reconnect();
}
delay(10000);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# 6. 設計明細
開啟FastWeb設計器,分別加入下插圖之控制元件。
①:TUgImage元件,控制元件名稱為UgImage02
。
②:TUgEdit元件,控制元件名稱為UgEdit01
。
③:TUgImage元件,控制元件名稱為UgImage01
。
④:TUgLabel元件,控制元件名稱為UgLabel01
。
⑤:TUgFSToggle元件,控制元件名稱為UgFSToggle01
。
⑥:TUgContainerPanel元件,控制元件名稱為UgContainerPanel01
。
UgWebRunFrame屬性設定
Height
:設定頁面高度=320
。Width
:設定頁面寬度=480
。
UgMQTTws01屬性設定
Visible
:設定控制元件是否可見。設定為False
。MQTTOptions
:設定MQTT選項。CleanSession
:設定為True
。ClientID
:設定客戶端ID號,與已連線的客戶端ID不能重複。HostName
:設定MQTT Broker的地址。Password
:設定MQTT Broker的帳號密碼。Port
:設定WebSocket服務的埠號,預設為9001
。Topic
:設定MQTT的主題,啟動時自動訂閱該主題。UserName
:設定MQTT Broker的連線使用者名稱。
①UgImage02屬性設定
Align
:設定圖片控制元件的對齊方式,設定為alClient
(客戶區對齊)。Stretch
:設定圖片拉伸為True
。Picture
:設定影象(背景圖片)。點選Picture
屬性右側的[√]
,打開影象編輯器,點選[Load]
按鈕打開檔案上傳界面,點選右側的[Browse...]
打開影象瀏覽界面,選擇影象后,點選[確定]
按鈕返回到檔案上傳界面,點選[Upload]
將檔案上傳至編輯器中,待圖片顯示后,點選[Save]
按鈕即可。
②UgEdit01屬性設定
Height
:設定控制元件高度=24
。Width
:設定控制元件寬度=180
。FieldLabel
:設定編輯框顯示的對應標籤名稱,設定為主題名稱
。FieldLabelAlign
:設定欄位標籤顯示相對於控制元件的位置,設定為laTop
。Text
:設定編輯內容為=/devices/esp32/camera
。
③UgImage01屬性設定
Height
:設定控制元件高度=240
。Width
:設定控制元件寬度=320
。Stretch
:設定圖片拉伸為True
。
④UgLabel01屬性設定
Caption
:設定標籤顯示的內容,設定為訂閱開關
。Font
:設定字型,在其中的Style
屬性設定fsBold
為True
,即加粗該字型。設定其Color
屬性為clWhite
,即字型顯示為白色。
⑥UgContainerPanel01屬性設定
Height
:設定控制元件高度=257
。Width
:設定控制元件寬度=342
。
# 7. 程式設計
# 7.1. 程式初始設定
一些初始化設定的程式。
//JScript
UgFSToggle01.OnToggled = &UgFSToggle01OnToggled;
UgMQTTws01.OnMessage = &UgMQTTws01OnMessage;
UGMM.LC(Self);
2
3
4
//PasScript
procedure UgWebRunFrameOnAfterRunScript(const sender: tobject);
//翻譯
begin
UGMM.LC(Self);
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
# 7.2. 事件設定
- ⑤UgFSToggle01-OnToggled事件
點選切換以開啟/關閉主題訂閱。
//JScript
function UgFSToggle01OnToggled(sender){
if (UgFStoggle01.Toggled){
UgMQTTws01.Subscribe(UgEdit01.Text);
}
Else{
UgMQTTws01.Unsubscribe(UgEdit01.Text);
}
}
2
3
4
5
6
7
8
9
//PasScript
procedure UgFSToggle01OnToggled(const value: boolean);
begin
if UgFStoggle01.Toggled Then
Begin
UgMQTTws01.Subscribe(UgEdit01.Text);
End
Else
Begin
UgMQTTws01.Unsubscribe(UgEdit01.Text);
End;
end;
2
3
4
5
6
7
8
9
10
11
12
// Make sure to add code blocks to your code group
- UgMQTTws01-OnMessage事件
當接收到訂閱資訊時解析。
//JScript
function UgMQTTws01OnMessage(atopic,apayload){
UGCM.HexToJPG(apayload,"profile-image.jpg");
UgImage01.Picture.LoadFromFile("profile-image.jpg");
}
2
3
4
5
//PasScript
procedure ugMQTTws01OnMessage(const atopic: string;apayload: string);
begin
UGCM.HexToJPG(apayload,'profile-image.jpg');
UgImage01.Picture.LoadFromFile('profile-image.jpg');
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
# 8. 運行結果
使用滑鼠點選工具欄儲存到資料庫,然後點選運行(Run),測試運行結果。打開訂閱開關,接收相機拍攝的圖片。