Node giám sát môi trường 4G dựa trên Air700E (dữ liệu nhiệt độ, độ ẩm, áp suất, v.v.), tải lên nền tảng Internet vạn vật Alibaba Cloud qua MQTT.
Giới thiệu
Module 4G Air700E của [Hezhou] đọc dữ liệu từ cảm biến (nhiệt độ - độ ẩm, áp suất, v.v.) và tải lên nền tảng Internet vạn vật Alibaba Cloud qua giao thức MQTT, đồng thời hiển thị dữ liệu trên màn hình OLED 0,96 inch, sử dụng thư viện đồ họa U8g2.
Module WiFi ESP32C3 đăng ký dữ liệu do node 4G tải lên qua MQTT và hiển thị trên màn hình LCD, sử dụng thư viện đồ họa LVGL.
Đây là một bài thiết kế môn học của tôi, làm cho có, chưa hoàn thiện lắm.
Yêu cầu đề bài: Thiết kế và chế tạo hệ thống truyền thông không dây, kết hợp kiến thức đã học kỳ trước, thu thập nhiệt độ, độ ẩm / khoảng cách đo siêu âm cục bộ, truyền đến máy chủ / thiết bị thu qua phương thức không dây, và hiển thị thông tin liên quan trên LCD ở thiết bị thu, cụ thể như sau:
⑴ Thiết kế và hiện thực hệ thống truyền dẫn không dây Bluetooth
⑵ Thiết kế và hiện thực hệ thống truyền dẫn không dây WiFi
⑶ Thiết kế và hiện thực hệ thống truyền dẫn không dây ZigBee
⑷ Thiết kế và hiện thực hệ thống truyền dẫn không dây GPRS/GSM
(5) Thiết kế và hiện thực hệ thống truyền dẫn không dây 4G
(6) Thiết kế và hiện thực hệ thống truyền dẫn không dây NBIoT/LoRa
Gợi ý: Có thể dùng bộ xử lý 8/16/32 bit làm bộ điều khiển chính, chọn loại theo nội dung tổng hợp nhóm đã chọn, hoàn thành ít nhất 2 mục nhỏ, trong đó chọn 1 trong (1)(2)(3), chọn 1 trong (4)(5)(6).
Tôi phát triển cả Air700E và ESP32C3 bằng hệ thống LuatOS + kịch bản Lua.
Firmware LuatOS do tôi build qua cloud compile của Hezhou.
Flash 4 MByte tích hợp trên ESP32C3 đã được tôi thay bằng 8 MByte, firmware cũng lớn hơn 4 MB vì thêm thư viện đồ họa LVGL và vài kiểu font.
Link ưu đãi sinh viên Alibaba Cloud: https://www.aliyun.com/daily-act/ecs/activity_share?userCode=jdjc69nf
Lưu ý: Do nền tảng Internet vạn vật Alibaba Cloud không cho các thiết bị khác nhau đăng ký topic của nhau, chỉ đăng ký topic của thiết bị mình, nên cần thêm một quy tắc tại Chuyển tiếp tin nhắn → Chuyển tiếp sản phẩm đám mây, chuyển tin nhắn do thiết bị 4G publish sang một topic của thiết bị WiFi.
Link mở nguồn trên nền tảng Lichuang: https://oshwhub.com/zeruns/wen-shi-du-cai-ji-4g-shang-chuan
Nhóm kỹ thuật điện tử/vi điều khiển: 2169025065
Giới thiệu Air700E
Air700E là module truyền thông LTE Cat.1 bis do Hezhou Communication ra mắt, dùng nền tảng Yixin EC618, hỗ trợ công nghệ LTE 3GPP Rel.13. Module chỉ giữ lại băng tần LTE TDD, tương thích với băng tần chính của China Mobile, có kích thước siêu nhỏ và chi phí tối ưu, đáp ứng nhu cầu thu nhỏ và giá rẻ.
Đặc điểm chính gồm:
- Hỗ trợ giao diện USIM đơn 1,8/3,3 V
- Hỗ trợ cổng nối tiếp 1,8/3,3 V cấu hình được
- Hỗ trợ USB 2.0
- Hỗ trợ nâng cấp firmware OTA từ xa
- Hỗ trợ giao diện âm thanh số PSM
- Hỗ trợ nhiều cách phát triển như USB lên mạng, phát triển AT chuẩn, phát triển thứ cấp open CPU (LuatOS, C-SDK) v.v.
Air700E tích hợp sẵn nhiều giao thức mạng, tích hợp nhiều giao diện tiêu chuẩn công nghiệp và hỗ trợ nhiều driver và chức năng phần mềm (như driver USB trên Windows 7/8/8.1/10, Linux, Android v.v.), mở rộng mạnh mẽ ứng dụng trong lĩnh vực M2M như CPE, router, thẻ dữ liệu, máy tính bảng, ô tô, an ninh và PDA công nghiệp.
Thông số kỹ thuật Air700E:
Băng tần LTE-TDD: B34/B38/B39/B40/B41
Tốc độ dữ liệu LTE-TDD:
- Tỷ lệ up/down 2: tối đa 8 Mbps (DL) / tối đa 2 Mbps (UL)
- Tỷ lệ up/down 1: tối đa 6 Mbps (DL) / tối đa 4 Mbps (UL)
Giao diện:
- 1 cổng USB 2.0 tốc độ cao (tối đa 480 Mbps)
- 1 giao diện thẻ (U)SIM 1,8 V/3,0 V
- 1 giao diện NETLIGHT (NET_STATUS)
- 1 giao diện I2S số, hỗ trợ codec ngoài
- 3 giao diện UART (cổng chính, cổng chung, cổng gỡ lỗi)
- PWRKEY (mức thấp có hiệu lực)
- 2 giao diện ADC
- 13 GPIO chung + 2 ngõ vào ngắt
- 1 giao diện I2C
Giới thiệu Hezhou ESP32-C3
Bo mạch lõi CORE ESP32 được thiết kế dựa trên ESP32-C3 của Espressif, kích thước chỉ 21 mm × 51 mm, viền bo dạng tem thư, thuận tiện cho nhà phát triển trong nhiều kịch bản. Bo mạch hỗ trợ UART, GPIO, SPI, I2C, ADC, PWM v.v., có thể chọn theo nhu cầu thực tế.
Tài nguyên phần cứng:
- Kích thước 21 mm × 51 mm
- 1 SPI FLASH, 4 MB trên bo, hỗ trợ tối đa 16 MB
- 2 giao diện UART, UART0~UART1, trong đó cổng tải là UART0
- 5 ADC 12 bit, tốc độ lấy mẫu tối đa 100 KSPS
- 1 giao diện SPI tốc độ thấp, hỗ trợ chế độ chủ
- 1 bộ điều khiển IIC
- 4 giao diện PWM, có thể dùng bất kỳ GPIO
- 15 chân GPIO ngoài, có thể tái sử dụng
- 2 đèn LED dán
- 1 nút reset + 1 nút BOOT
- 1 cổng tải/gỡ lỗi USB-TTL
- Antenna PCB 2,4 GHz trên bo
Ảnh thực tế
Video demo: https://www.bilibili.com/video/BV1MH4y1B7Df/
Toàn cảnh
Node 4G
Node WiFi
Khi kết nối WiFi:
Sau khi kết nối WiFi, bắt đầu kết nối máy chủ MQTT và đồng bộ thời gian NTP:
Hiển thị dữ liệu thu thập từ node 4G:
Hiển thị đường cong thay đổi nhiệt độ - độ ẩm:
Nền tảng Internet vạn vật
Dữ liệu hiển thị trên nền tảng Internet vạn vật Alibaba Cloud:
Sơ đồ nguyên lý
Node 4G
Node WiFi
PCB
Node 4G
Package module 4G này không chính xác lắm.
Lớp trên:
Lớp dưới:
Node WiFi
Lớp trên:
Lớp dưới:
Code
Tôi không hướng dẫn chi tiết cách tải code, tự xem tài liệu chính thức.
Tài liệu Air700E: https://doc.openluat.com/wiki/44?wiki_page_id=4730
Handbook LVGL cho LuatOS: https://url.zeruns.com/7z7fN
Tài liệu ESP32-C3: https://url.zeruns.com/497AP
Học Lua: https://url.zeruns.com/Pc4PA
Node 4G
Link tải firmware: https://url.zeruns.com/2G3K7
File main.lua:
-- LuaTools cần PROJECT và VERSION
PROJECT = "Node giám sát môi trường"
VERSION = "1.1.0"
-- Nhập thư viện cần thiết (viết bằng lua), thư viện nội bộ không cần require
sys = require("sys")
aht10 = require "aht10"
bmx = require "bmx"
-- Thêm watchdog cứng tránh treo chương trình
if wdt then
wdt.init(9000) -- khởi tạo watchdog 9 s
sys.timerLoopStart(wdt.feed, 3000) -- feed mỗi 3 s
end
print(_VERSION)
log.info(mobile.ipv6(true)) -- bật ipv6
local ProductKey = "xxxxxx" -- product key
local DeviceName = "xxxxxx" -- đổi tên thiết bị của bạn
local DeviceSecret = "xxxxxx" -- đổi mật khẩu thiết bị của bạn
local client_id, user_name, password = iotauth.aliyun(ProductKey, DeviceName ,DeviceSecret) -- tạo bộ ba MQTT
log.info("Thông số MQTT", client_id, user_name, password)
local softI2C = i2c.createSoft(29, 31, 2) -- khởi tạo I2C mềm
local aht10_data,bmx_data
local RH,Temp,Press,High,distance,BAT_Voltage,CPU_T = 0,0,0,0,0,0,0 -- trung bình, độ ẩm, nhiệt độ, áp suất, độ cao, khoảng cách, điện áp pin, nhiệt độ CPU
local RH_C,Temp_C,Press_C,High_C,distance_C,BAT_Voltage_C,CPU_T_C = 0,0,0,0,0,0,0 -- giá trị hiện tại
local AHT10_flag,BMP180_flag,US100_flag,BAT_Voltage_flag,CPU_T_flag = false,false,false,false,false
all_data = {params = {CurrentVoltage = 0,CurrentTemperature = 0, CurrentHumidity = 0,Atmosphere = 0, Altitude = 0,DetectDistance = 0,CPUTemperature = 0}}
function US_100() -- đọc module đo khoảng cách siêu âm US-100
uart.write(1, string.char(0x55)) -- gửi hex 0x55
sys.wait(50) -- delay 50 ms
local hData,lData = string.byte(uart.read(1, 2),1,2) -- nhận 2 byte qua uart
uart.rxClear(1) -- xóa bộ đệm nhận
if hData and lData then -- kiểm tra đã nhận đủ
local Distance_mm = tonumber((hData * 256 ) + lData) -- dịch 8 bit cao 8 bit rồi cộng 8 bit thấp
if Distance_mm > 4500 then -- kiểm tra vượt ngưỡng
Distance_mm = 4500
end
--log.info("Siêu âm: "..Distance_mm.."mm") -- log khoảng cách
return (Distance_mm / 10) -- trả về cm
end
end
-- https://blog.zeruns.com
sys.taskInit(function() -- tạo luồng, đọc các cảm biến
sys.wait(500) -- delay 500 ms
aht10.init(softI2C) -- khởi tạo AHT10, truyền i2c_id
bmx.init(softI2C) -- khởi tạo BMP180, truyền i2c_id
uart.setup(1, 9600, 8, 1, uart.NONE) -- khởi tạo uart1
adc.open(0) -- mở kênh adc0 và đọc
adc.open(adc.CH_CPU)-- mở kênh ADC - nhiệt độ CPU
--adc.setRange(adc.ADC_RANGE_3_8) -- bật phân áp ADC0,1, phạm vi 0~3,8 V
sys.wait(500) -- delay 500 ms
local RH_sum, Temp_sum, Press_sum, High_sum, distance_sum,BAT_Voltage_sum,CPU_T_sum = 0,0,0,0,0,0,0 -- tổng, tính trung bình
local avg_count, avg_count2,avg_count3,avg_count4,avg_count5 = 0,0,0,0,0
while 1 do
aht10_data = aht10.get_data() -- đọc AHT10
bmx_data = bmx.get_data() -- đọc BMP180
distance_C = US_100() -- đọc US-100
-- lấy mẫu điện áp pin, tỷ lệ phân áp: 4,7 k/10 k
BAT_Voltage_C = ((adc.get(0) / 1000 / 1007) * 1473) -- đọc AD0, tính điện áp pin
CPU_T_C = adc.get(adc.CH_CPU) / 1000 -- đọc nhiệt độ CPU
-- tính trung bình nhiệt độ - độ ẩm AHT10
if aht10_data.RH and aht10_data.T then -- kiểm tra AHT10 đã đọc
all_data.params.CurrentTemperature = aht10_data.T
all_data.params.CurrentHumidity = aht10_data.RH*100
if avg_count < 6 then -- nếu < 6, cộng dồn trung bình
RH_C,Temp_C = aht10_data.RH*100, aht10_data.T -- đọc giá trị hiện tại
RH_sum = RH_C + RH_sum
Temp_sum = Temp_C + Temp_sum
avg_count = avg_count + 1
elseif avg_count == 6 then
RH = RH_sum / 6 -- tính trung bình độ ẩm
Temp = Temp_sum / 6 -- tính trung bình nhiệt độ
log.info("AHT10_data.RH: "..(string.format("%.2f", RH)).." %"," AHT10_data.T: "..(string.format("%.2f", Temp)).." ℃") -- log
AHT10_flag = true -- đặt cờ, thu thập xong
sys.publish("MQTT_P") -- publish, tính xong, MQTT có thể gửi
avg_count,RH_sum,Temp_sum = 0,0,0 -- reset bộ đếm và tổng
end
end
```--BMP180 tính toán giá trị trung bình áp suất và độ cao
if bmx_data.press and bmx_data.high then
all_data.params.Atmosphere = bmx_data.press
all_data.params.Altitude = bmx_data.high
if avg_count2 < 10 then --kiểm tra xem có nhỏ hơn 10 không, tích lũy để tính trung bình
Press_C,High_C = bmx_data.press, bmx_data.high --đọc giá trị hiện tại của áp suất và độ cao
Press_sum = Press_C + Press_sum
High_sum = High_C + High_sum
avg_count2 = avg_count2 + 1
elseif avg_count2 == 10 then
Press = Press_sum / 10
High = High_sum / 10
log.info("BMP180_data.press: "..(string.format("%.2f", Press)).." hPa"," BMP180_data.high: "..(string.format("%.2f", High)).." m")
BMP180_flag = true
sys.publish("MQTT_P")
avg_count2,Press_sum,High_sum=0,0,0
end
end
-- US-100 đo khoảng cách siêu âm tính toán giá trị trung bình
if distance_C then --kiểm tra xem có đọc được dữ liệu không
all_data.params.DetectDistance = distance_C
if avg_count3 < 3 then
distance_sum = distance_C + distance_sum
avg_count3 = avg_count3 + 1
elseif avg_count3 == 3 then
distance = distance_sum / 3
log.info("US-100: "..(string.format("%.1f", distance)).." cm")
US100_flag = true
sys.publish("MQTT_P")
avg_count3,distance_sum = 0,0
end
end
-- Điện áp pin
if BAT_Voltage_C then --kiểm tra xem có đọc được dữ liệu không
all_data.params.CurrentVoltage = BAT_Voltage_C
if avg_count4 < 20 then
BAT_Voltage_sum = BAT_Voltage_C + BAT_Voltage_sum
avg_count4 = avg_count4 + 1
elseif avg_count4 == 20 then
BAT_Voltage = BAT_Voltage_sum / 20
log.info("BAT_Voltage: "..(string.format("%.2f", BAT_Voltage)).." V")
BAT_Voltage_flag = true
sys.publish("MQTT_P")
avg_count4,BAT_Voltage_sum = 0,0
end
end
-- Nhiệt độ CPU
if CPU_T_C then --kiểm tra xem có đọc được dữ liệu không
all_data.params.CPUTemperature = CPU_T_C
if avg_count5 < 20 then
CPU_T_sum = CPU_T_C + CPU_T_sum
avg_count5 = avg_count4 + 1
elseif avg_count5 == 20 then
CPU_T = CPU_T_sum / 20
log.info("CPU_T: "..(string.format("%.2f", CPU_T)).." ℃")
CPU_T_flag = true
sys.publish("MQTT_P")
avg_count5,CPU_T_sum = 0,0
end
end
sys.wait(500)
end
end)
--[[sys.taskInit(function() --tạo một tác vụ, đo khoảng cách siêu âm, mỗi 1,5 giây một lần
uart.setup(1, 9600, 8, 1, uart.NONE) --khởi tạo cổng nối tiếp 1
sys.wait(1000)
while 1 do
distance = US_100() --đọc dữ liệu US-100
if distance then --kiểm tra xem có đọc được dữ liệu không
log.info("US-100: "..(string.format("%.1f", distance)).." cm")
US100_flag = true
sys.publish("MQTT_P")
end
sys.wait(1500)
end
end)]]
sys.taskInit(function() --tạo một luồng, khởi tạo MQTT và báo cáo dữ liệu
mqttc = mqtt.create(nil, "a1sJbDQiEqr.iot-as-mqtt.cn-shanghai.aliyuncs.com", 1883,true,true)
mqttc:auth(client_id, user_name, password)
mqttc:keepalive(120) -- giá trị mặc định 240s
mqttc:autoreconn(true, 3000) -- cơ chế tự động kết nối lại
mqttc:on(function(mqtt_client, event, data, payload)
if event == "conack" then
sys.publish("mqtt_conack")
log.info("mqtt", "mqtt đã kết nối")
mqtt_client:subscribe("/sys/"..ProductKey.."/"..DeviceName.."/thing/service/property/set")
elseif event == "recv" then
log.info("mqtt", "nhận được tin nhắn", data, payload)
local mqtt_date = json.decode(payload)
elseif event == "sent" then
log.info("mqtt", "sent", "pkgid", data)
end
end)
mqttc:connect()
while true do
sys.waitUntil("MQTT_P", 3000)
if AHT10_flag then
local json_str = json.encode({params = {CurrentTemperature = Temp, CurrentHumidity = RH}}, "2f") -- chuyển dữ liệu sang định dạng JSON, giữ 2 chữ số thập phân
mqttc:publish("/sys/"..ProductKey.."/".. DeviceName.."/thing/event/property/post", json_str) -- báo cáo thuộc tính qua MQTT
mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f")) -- báo cáo thuộc tính qua MQTT
AHT10_flag = false
end
if BMP180_flag then
local json_str = json.encode({params = {Atmosphere = Press, Altitude = High}}, "2f") -- chuyển dữ liệu sang định dạng JSON, giữ 2 chữ số thập phân
mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str) -- báo cáo thuộc tính qua MQTT
mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f")) -- báo cáo thuộc tính qua MQTT
BMP180_flag = false
end
if US100_flag then
local json_str = json.encode({params = {DetectDistance = distance}}, "1f") -- chuyển dữ liệu sang định dạng JSON, giữ 2 chữ số thập phân
mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str) -- báo cáo thuộc tính qua MQTT
mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f")) -- báo cáo thuộc tính qua MQTT
US100_flag = false
end
if BAT_Voltage_flag then
local json_str = json.encode({params = {CurrentVoltage = BAT_Voltage}}, "2f") -- chuyển dữ liệu sang định dạng JSON, giữ 2 chữ số thập phân
mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str) -- báo cáo thuộc tính qua MQTT
mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f")) -- báo cáo thuộc tính qua MQTT
BAT_Voltage_flag = false
end
if CPU_T_flag then
local json_str = json.encode({params = {CPUTemperature = CPU_T}}, "2f") -- chuyển dữ liệu sang định dạng JSON
mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str) -- báo cáo thuộc tính qua MQTT
mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f")) -- báo cáo thuộc tính qua MQTT
CPU_T_flag = false
end
end
end)
-- https://blog.zeruns.com
sys.taskInit(function()
u8g2.begin({ic = "ssd1306",direction = 0,mode="i2c_hw",i2c_id=1,i2c_speed = i2c.FAST}) -- direction có thể chọn 0 90 180 270
u8g2.ClearBuffer() --xóa màn hình
u8g2.SetFont(u8g2.font_opposansm10) --chuyển font chữ
u8g2.DrawUTF8("IMEI:", 0, 16)
u8g2.DrawUTF8(mobile.imei(), 0, 32)
u8g2.SendBuffer()
--đồng bộ thời gian qua ntp
--socket.sntp()
socket.sntp({"ntp.aliyun.com","ntp1.aliyun.com","ntp2.aliyun.com"}) --địa chỉ máy chủ sntp tùy chỉnh
--socket.sntp(nil, socket.ETH0) --sntp tùy chỉnh số thứ tự adapter
sys.subscribe("NTP_UPDATE", function()
log.info("sntp", "time", os.date())
sys.publish("NTP_OK")
end)
sys.subscribe("NTP_ERROR", function()
log.info("socket", "sntp error")
socket.sntp()
end)
sys.wait(1000)
sys.waitUntil("NTP_OK", 3000)
u8g2.ClearBuffer() --xóa màn hình
u8g2.SetFont(u8g2.font_opposansm10) --chuyển font chữ
u8g2.DrawUTF8(os.date("%Y-%m-%d"), 0, 16) --hiển thị thời gian
u8g2.DrawUTF8(os.date("%H:%M:%S"), 0, 32) --hiển thị thời gian
local IP = socket.localIP() --hiển thị địa chỉ IP
if IP then
u8g2.DrawUTF8("IP:"..IP, 0, 63)
end
u8g2.SendBuffer()
sys.wait(2500)
while true do
u8g2.ClearBuffer() --xóa màn hình
u8g2.SetFont(u8g2.font_sarasa_m10_ascii)
if Temp_C then
u8g2.DrawUTF8("T:"..(string.format("%.2f", Temp_C)).."°C", 0, 16)
end
if RH_C then
u8g2.DrawUTF8("RH:"..(string.format("%.2f", RH_C)).."%", 0, 32)
end
if Press_C then
u8g2.DrawUTF8("P:"..(string.format("%.2f", Press_C)).."hPa", 0, 48)
end
if distance_C then
u8g2.DrawUTF8("D:"..(string.format("%.1f", distance_C)).."cm", 0, 63)
end
if High_C then
u8g2.DrawUTF8("H:"..(string.format("%.2f", High_C)).."m", 61, 16)
end
if BAT_Voltage_C then
u8g2.DrawUTF8("B:"..(string.format("%.2f", BAT_Voltage_C)).."V", 66, 32)
end
if CPU_T_C then
u8g2.DrawUTF8("CPUT:"..(string.format("%d", CPU_T_C)).."°C", 63, 63)
end
u8g2.SendBuffer() --cập nhật dữ liệu hiển thị lên màn hình
sys.wait(200)
end
end)sys.taskInit(function() --Tạo một luồng, LED nhấp nháy mỗi 500 ms
gpio.setup(27, 0) -- Cấu hình gpio27 làm ngõ ra, mức logic ban đầu thấp, dùng điện trở kéo mặc định của phần cứng
while true do
gpio.toggle(27) --Đảo mức gpio
sys.wait(500)
end
end)
--[[
Các font được hỗ trợ
["unifont_t_symbols","open_iconic_weather_6x_t","opposansm8","opposansm10","opposansm12","opposansm16","opposansm20",
"opposansm24","opposansm32","sarasa_m8_ascii","sarasa_m10_ascii","sarasa_m12_ascii","sarasa_m14_ascii",
"sarasa_m16_ascii","sarasa_m18_ascii","sarasa_m20_ascii","sarasa_m22_ascii"]
]]
-- Kết thúc phần mã người dùng---------------------------------------------
-- Luôn kết thúc bằng câu lệnh này
sys.run()
-- Không được thêm bất kỳ câu lệnh nào sau sys.run()!!!!!
Nút WiFi
Địa chỉ tải firmware: https://url.zeruns.com/a7eXJ
Tệp main.lua:
-- LuaTools cần PROJECT và VERSION
PROJECT = "Nút hiển thị dữ liệu"
VERSION = "1.0.5"
-- Thư viện sys là bắt buộc
_G.sys = require("sys")
require("sysplus")
--Thêm watchdog cứng để tránh treo chương trình
wdt.init(9000)--Khởi tạo watchdog 9 s
wdt_timer_id = sys.timerLoopStart(function()
wdt.feed()
end, 2000) --Nháy watchdog mỗi 2 s
local ProductKey= "xxxxxx" --Khóa sản phẩm
local DeviceName= "xxxxxx" --Đổi thành tên thiết bị của bạn
local DeviceSecret = "xxxxxx" --Đổi thành khóa bí mật thiết bị của bạn
local client_id, user_name, password = iotauth.aliyun(ProductKey, DeviceName ,DeviceSecret) --Tạo bộ ba MQTT
log.info("Thông số MQTT", client_id, user_name, password)
local local_CPU_T_C = 0;
local all_data = {params = {CurrentVoltage = 0,CurrentTemperature = 0, CurrentHumidity = 0,
Atmosphere = 0, Altitude = 0,DetectDistance = 0,CPUTemperature = 0}};
local key1_flag,key2_flag = false,false
sys.taskInit(function() -- Tạo một luồng
log.info("wlan", "wlan_init:", wlan.init()) -- Khởi tạo wlan
wlan.setMode(wlan.STATION) -- Đặt chế độ wlan là STATION
wlan.connect("Mate 40 Pro", "123456789", 1) -- Kết nối mạng wlan
local result, data = sys.waitUntil("IP_READY",6000)-- Chờ sự kiện IP_READY
log.info("wlan", "IP_READY", result, data) -- In kết quả và dữ liệu sự kiện IP_READY
if result then
socket.setDNS(socket.STA, 1, "114.114.114.114")
log.info("wlan", "info", json.encode(wlan.getInfo())) -- In chi tiết mạng wlan
sys.publish("WiFi_connected_OK",data) -- Phát thông điệp WiFi đã kết nối thành công
--Đồng bộ thời gian qua ntp
--socket.sntp()
socket.sntp({"ntp.aliyun.com","time.windows.com","ntp.tencent.com","ntp2.aliyun.com"}, socket.STA) --Địa chỉ máy chủ sntp tùy chỉnh
--socket.sntp(nil, socket.STA) --Adapter tùy chỉnh cho sntp
-- Đăng ký sự kiện NTP_UPDATE
sys.subscribe("NTP_UPDATE", function()
sys.publish("NTP_OK") -- Phát thông điệp đồng bộ NTP thành công
log.info("sntp", "time", os.date()) -- In thời gian hệ thống
end)
sys.subscribe("NTP_ERROR", function()
log.info("socket", "sntp error")
socket.sntp()
end)
else
while true do
sys.wait(1000)
end
end
adc.open(adc.CH_CPU)-- Mở kênh ADC-nhiệt độ CPU
local CH_CPU = adc.get(adc.CH_CPU) / 1000 -- Đọc nhiệt độ CPU
if CH_CPU then -- Kiểm tra dữ liệu có hợp lệ
local_CPU_T_C = CH_CPU
end
mqttc = mqtt.create(nil, "a1sJbDQiEqr.iot-as-mqtt.cn-shanghai.aliyuncs.com", 1883,true) -- Tạo client MQTT
mqttc:auth(client_id, user_name, password) -- Xác thực MQTT
mqttc:keepalive(60) -- Thời gian giữ kết nối MQTT, mặc định 60 s
mqttc:autoreconn(true, 3000) -- Bật tự động kết nối lại
mqttc:on(function(mqtt_client, event, data, payload)-- Định nghĩa hàm callback sự kiện MQTT
if event == "conack" then -- Khi nhận được sự kiện xác nhận kết nối (conack)
sys.publish("mqtt_conack") -- Phát thông điệp mqtt_conack
log.info("mqtt", "mqtt đã kết nối") -- In thông báo kết nối thành công
mqtt_client:subscribe("/sys/"..ProductKey.."/"..DeviceName.."/thing/service/property/set") -- Đăng ký chủ đề
mqtt_client:subscribe("/"..ProductKey.."/"..DeviceName.."/user/get")
elseif event == "recv" then -- Khi nhận được sự kiện tin nhắn (recv)
if data == "/"..ProductKey.."/"..DeviceName.."/user/get" then -- Kiểm tra chủ đề nhận
all_data = json.decode(payload) -- Giải mã nội dung tin nhắn sang JSON
mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", payload) -- Gửi thuộc tính qua MQTT
else
log.info("mqtt", "nhận tin nhắn", data, payload) -- In thông tin tin nhắn nhận được
local mqtt_date = json.decode(payload) -- Giải mã nội dung tin nhắn sang JSON
end
elseif event == "sent" then -- Khi nhận được sự kiện gửi tin nhắn (sent)
--log.info("mqtt", "sent", "pkgid", data)-- In thông tin tin nhắn đã gửi
end
end)
mqttc:connect()
while true do
local CH_CPU = adc.get(adc.CH_CPU) / 1000 -- Đọc nhiệt độ CPU
if CH_CPU then -- Kiểm tra dữ liệu có hợp lệ
CPU_T_C = CH_CPU
end
gpio.toggle(12) --Đảo mức gpio
sys.wait(500)
end
end)
sys.taskInit(function() -- Tạo một luồng
spi.setup(2, 7, 0, 0, 8, 80 * 1000 * 1000, spi.MSB, 1, 1) -- Khởi tạo SPI
log.info("lcd.init", lcd.init("st7789",{
port = 2, -- cổng spi
-- pin_cs = 7, -- Chọn SPI
pin_dc = 8, -- Chân lệnh/dữ liệu lcd
-- pin_pwr = 18, -- Chân nền lcd, tùy chọn, có thể bỏ
pin_rst = 6, -- Chân reset lcd
direction = 2, -- Hướng màn hình lcd 0:0° 1:180° 2:270° 3:90°
w = 320, -- Độ phân giải ngang lcd
h = 172, -- Độ phân giải dọc lcd
xoffset = 0, -- Dịch x (tùy IC màn hình và hướng)
yoffset = 34; -- Dịch y (tùy IC màn hình và hướng)
}))
log.info("lvgl", lvgl.init()) --- Khởi tạo LVGL
local style_screen_label_main = lvgl.style_create() -- Tạo đối tượng style tên style_screen_label_main
lvgl.style_set_text_font(style_screen_label_main, lvgl.STATE_DEFAULT, lvgl.font_get("opposans_m_16")) -- Đặt font mặc định cho nhãn
```local Init_scr = lvgl.obj_create(nil, nil) -- tạo một đối tượng màn hình
lvgl.scr_load(Init_scr) -- tải đối tượng màn hình, hiển thị trên màn hình
local spinner = lvgl.spinner_create(Init_scr, nil) -- tạo một đối tượng biểu tượng quay
lvgl.obj_set_size(spinner, 100, 100) -- thiết lập kích thước đối tượng biểu tượng quay
lvgl.obj_align(spinner, nil, lvgl.ALIGN_CENTER, 0, -20) -- căn giữa đối tượng biểu tượng quay
local label_WiFiConnecting = lvgl.label_create(Init_scr, nil) -- tạo một đối tượng nhãn
lvgl.label_set_recolor(label_WiFiConnecting, true) -- bật chức năng tô màu lại nhãn, tức là tự động cập nhật màu khi nội dung văn bản thay đổi
lvgl.obj_add_style(label_WiFiConnecting, lvgl.LABEL_PART_MAIN, style_screen_label_main) -- gán đối tượng kiểu cho phần main của đối tượng nhãn
lvgl.label_set_text(label_WiFiConnecting, "Đang kết nối WiFi...") -- thiết lập nội dung văn bản của đối tượng nhãn
lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- thiết lập cách căn chỉnh văn bản của đối tượng nhãn là căn giữa
lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 46) -- căn giữa đối tượng nhãn
local result, ip_data = sys.waitUntil("WiFi_connected_OK", 5000) -- chờ tin nhắn kết nối WiFi thành công, timeout 5 giây
if result == false then
lvgl.label_set_text(label_WiFiConnecting, "#ff0000 Kết nối WiFi thất bại!#\\nVui lòng khởi động lại thiết bị hoặc kiểm tra mạng") -- thiết lập nội dung văn bản của đối tượng nhãn (hiển thị kết nối WiFi thất bại)
lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- thiết lập cách căn chỉnh văn bản của đối tượng nhãn là căn giữa
lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 51) -- căn giữa đối tượng nhãn
while true do
sys.wait(1000) -- chờ trong vòng lặp để ngăn chương trình tiếp tục thực thi
end
end
lvgl.label_set_text(label_WiFiConnecting, "Kết nối WiFi thành công!") -- thiết lập nội dung văn bản của đối tượng nhãn (hiển thị kết nối WiFi thành công)
lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- thiết lập cách căn chỉnh văn bản của đối tượng nhãn là căn giữa
lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 51) -- căn giữa đối tượng nhãn
sys.wait(500) -- chờ 500 mili giây
lvgl.obj_del(label_WiFiConnecting) -- xóa đối tượng nhãn
lvgl.obj_align(spinner, nil, lvgl.ALIGN_CENTER, 0, 0) -- căn giữa đối tượng biểu tượng quay
local label_Init_log = lvgl.label_create(Init_scr, nil) -- tạo một đối tượng nhãn
lvgl.label_set_recolor(label_Init_log, true) -- bật chức năng tô màu lại nhãn, tức là tự động cập nhật màu khi nội dung văn bản thay đổi
lvgl.label_set_align(label_Init_log, lvgl.LABEL_ALIGN_LEFT) -- thiết lập cách căn chỉnh văn bản của đối tượng nhãn là căn trái
lvgl.obj_align(label_Init_log, nil, lvgl.ALIGN_IN_TOP_LEFT, 10, 5) -- căn chỉnh đối tượng nhãn vào góc trên bên trái màn hình
local Init_log = "#0000ff Nhật ký khởi tạo: #\\nKết nối WiFi thành công!\\n" -- thông tin nhật ký khởi tạo
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
Init_log = Init_log.."IP: "..ip_data.."\\nĐang kết nối máy chủ MQTT...\\n" -- cập nhật thông tin nhật ký
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
if sys.waitUntil("mqtt_conack", 10000) == false then
Init_log = Init_log.."#ff0000 Kết nối máy chủ MQTT thất bại!\\nVui lòng khởi động lại thiết bị!#\\n" -- cập nhật thông tin nhật ký (hiển thị kết nối MQTT thất bại)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
mqttc:disconnect() -- ngắt kết nối MQTT
mqttc:close() -- đóng kết nối MQTT
while true do
sys.wait(1000) -- chờ trong vòng lặp để ngăn chương trình tiếp tục thực thi
end
end
Init_log = Init_log.."Kết nối máy chủ MQTT thành công!\\n" -- cập nhật thông tin nhật ký (hiển thị kết nối MQTT thành công)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
Init_log = Init_log.."Đang đồng bộ thời gian NTP...\\n" -- cập nhật thông tin nhật ký
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
local result = sys.waitUntil("NTP_OK", 3500) -- chờ tin nhắn đồng bộ thời gian NTP thành công, timeout 3 giây
if result then
Init_log = Init_log.."Đồng bộ thời gian NTP thành công!\\n" -- cập nhật thông tin nhật ký (hiển thị đồng bộ NTP thành công)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
else
Init_log = Init_log.."Đồng bộ thời gian NTP thất bại!\\n" -- cập nhật thông tin nhật ký (hiển thị đồng bộ NTP thất bại)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
end
Init_log = Init_log.."Thời gian hiện tại: "..(os.date("%Y-%m-%d %H:%M:%S")).."\\n" -- cập nhật thông tin nhật ký (hiển thị thời gian hiện tại)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
Init_log = Init_log.."Khởi tạo hệ thống hoàn tất!\\n" -- cập nhật thông tin nhật ký (hiển thị khởi tạo hệ thống hoàn tất)
lvgl.label_set_text(label_Init_log, Init_log) -- thiết lập nội dung văn bản của đối tượng nhãn
sys.wait(2000)
local alldata_scr = lvgl.obj_create(nil, nil) -- tạo một đối tượng màn hình để hiển thị tất cả dữ liệu, không có đối tượng cha và kiểu
lvgl.scr_load(alldata_scr) -- tải đối tượng màn hình, hiển thị trên màn hình
local label_alldata = lvgl.label_create(alldata_scr, nil) -- tạo một đối tượng Label để hiển thị tất cả dữ liệu
lvgl.label_set_recolor(label_alldata, true) -- bật chức năng tô màu lại nhãn, tức là tự động cập nhật màu khi nội dung văn bản thay đổi
lvgl.label_set_align(label_alldata, lvgl.LABEL_ALIGN_LEFT) -- thiết lập cách căn chỉnh nhãn là căn trái
lvgl.obj_align(label_alldata, nil, lvgl.ALIGN_IN_TOP_LEFT, 10, 5) -- căn chỉnh nhãn vào góc trên bên trái màn hình, cách lề trái 10 pixel, lề trên 5 pixel
lvgl.obj_add_style(label_alldata, lvgl.LABEL_PART_MAIN, style_screen_label_main)-- gán đối tượng kiểu cho phần main của nhãn label_alldata
local label_time = lvgl.label_create(alldata_scr, nil) -- tạo một đối tượng Label để hiển thị thời gian hiện tại
lvgl.label_set_text(label_time, os.date("%Y-%m-%d").."\\n"..os.date("%H:%M:%S"))
lvgl.label_set_align(label_time, lvgl.LABEL_ALIGN_CENTER) -- thiết lập cách căn chỉnh nhãn là căn giữa
lvgl.obj_align(label_time, nil, lvgl.ALIGN_IN_TOP_RIGHT, -25, 10)-- căn chỉnh nhãn vào góc trên bên phải màn hình, cách lề trái 10 pixel, lề trên 5 pixel
lvgl.obj_add_style(label_time, lvgl.LABEL_PART_MAIN, style_screen_label_main)
lvgl.obj_del(Init_scr)
local chart_scr = lvgl.obj_create(nil, nil) -- tạo một đối tượng màn hình để hiển thị biểu đồ, không có đối tượng cha và kiểu
local chart = lvgl.chart_create(chart_scr, nil);
lvgl.obj_set_size(chart, 310, 150);
lvgl.obj_align(chart, nil, lvgl.ALIGN_CENTER, 0, 0);
lvgl.chart_set_type(chart, lvgl.CHART_TYPE_LINE); --Hiển thị cả đường và điểm*/
lvgl.chart_set_point_count(chart, 20)
lvgl.chart_set_y_range(chart, lvgl.CHART_AXIS_PRIMARY_Y, 0, 100)
local ser1 = lvgl.chart_add_series(chart, lvgl.color_make(0xFF, 0x00, 0x00));
local ser2 = lvgl.chart_add_series(chart, lvgl.color_make(0x00, 0x80, 0x00));
-- In thông tin sử dụng bộ nhớ
log.info("mem.lua", rtos.meminfo())
log.info("mem.sys", rtos.meminfo("sys"))
while true do
lvgl.label_set_text(label_alldata, string.format([[#0000ff Nhiệt độ môi trường: %.2f°C#
#ff00ff Độ ẩm môi trường: %.2f%%#
#ff0000 Áp suất khí quyển: %.2fhPa#
#800080 Độ cao so với mực nước biển: %.2fm#
#0000ff Khoảng cách siêu âm: %.2fcm#
#ff00ff Điện áp pin nút từ xa: %.2fV#
#ff0000 Nhiệt độ CPU nút từ xa: %.0f°C#
#800080 Nhiệt độ CPU nút cục bộ: %.0f°C#
]],all_data.params.CurrentTemperature,all_data.params.CurrentHumidity,all_data.params.Atmosphere,
all_data.params.Altitude,all_data.params.DetectDistance,all_data.params.CurrentVoltage,
all_data.params.CPUTemperature,local_CPU_T_C)); -- thiết lập văn bản nhãn
lvgl.label_set_text(label_time, os.date("%Y-%m-%d").."\\n"..os.date("%H:%M:%S"))
lvgl.chart_set_next(chart, ser1, all_data.params.CurrentTemperature);
lvgl.chart_set_next(chart, ser2, all_data.params.CurrentHumidity);
lvgl.chart_refresh(chart);
if key1_flag then
lvgl.scr_load(chart_scr)
key1_flag = false
end
if key2_flag then
lvgl.scr_load(alldata_scr)
key2_flag = false
end
gpio.toggle(12) --đảo mức gpio
sys.wait(300)
end
end)
sys.taskInit(function() --tạo một luồng, LED nhấp nháy 500ms một lần
gpio.setup(13, 0) -- thiết lập gpio13 làm đầu ra, mức ban đầu thấp, sử dụng cấu hình pull-up/pull-down mặc định của phần cứng
gpio.setup(12, 0) -- thiết lập gpio12 làm đầu ra, mức ban đầu thấp, sử dụng cấu hình pull-up/pull-down mặc định của phần cứng
while true do
gpio.toggle(13) --đảo mức gpio
sys.wait(500)
end
end)
sys.taskInit(function() --tạo một luồng, quét phím
gpio.setup(18, nil, gpio.PULLUP)
gpio.setup(19, nil, gpio.PULLUP)
while true do
if gpio.get(18) == 0 then
sys.wait(20)
if gpio.get(18) == 0 then
key1_flag = true
end
end
if gpio.get(19) == 0 then
sys.wait(20)
if gpio.get(19) == 0 then
key2_flag = true
end
end
sys.wait(50)
end
end)
--[[
Font được hỗ trợ ["sarasa_m8_ascii","sarasa_m10_ascii","sarasa_m12_ascii",
"sarasa_m14_ascii","sarasa_m16_ascii","sarasa_m18_ascii","sarasa_m20_ascii","sarasa_m22_ascii"]
]]
-- Mã người dùng đã kết thúc---------------------------------------------
-- Luôn là câu này ở cuối
sys.run()
-- Không được thêm bất kỳ câu lệnh nào sau sys.run()!!!!!
## Phần cứng được sử dụng
Các mô-đun phần cứng được sử dụng và địa chỉ mua hàng:- Mô-đun 4G: Air700E, [https://s.click.taobao.com/gMXE14u](https://uland.taobao.com/coupon/edetail?e=LUUvClZZaoalhHvvyUNXZfh8CuWt5YH5OVuOuRD5gLJMmdsrkidbOUV9IBA4kmjLCKSz6NMEOpOk6DkEOOy39ANONOd65bn6k%2BQD1WLWk7D58pWrh7e7ViECg58T7b6E7qOXUj4MT3dyBOK%2B8KjzSuzY3MUSAX0G1TP3uC6T%2BzrKa4jyh4U%2Bo5gBVnx58RcK3x24QQZ%2BkKRI%2Bf4%2BtcDu44oLvKgaQ5D83qmGq7GPCXsH2XAjD13xyT%2FhMZgljYbZj4NAR0X%2B9c23Sv8LMk9JOkIQtV1V6tmtIciWFcjrsPzH6emLlAySAVD9ZzTfOOXe%2Bm9PGdJ%2FlaUZUAGa9XIpkqJ7%2BkHL3AEW&traceId=213d811317025749037687621e65ab&union_lens=lensId%3APUB%401702574899%40212be3f7_0d90_18c695f4255_c520%400212cIwjWn6h4JZ67isD7Et6%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_ea9ed1dc-121c-4a26-85aa-1c27c9c4e982_)
- Mô-đun WiFi: ESP32-C3, [https://s.click.taobao.com/VU6E14u](https://s.click.taobao.com/t?e=m%3D2%26s%3DZlxzb7VA%2BERw4vFB6t2Z2ueEDrYVVa64YUrQeSeIhnK53hKxp7mNFhiPRfK6WZZbW%2BG7BH3F%2BVP0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMA7PT7P4MIjv0iDnQnCfxRCKLWMw3EOEsyJN2owMjhufwDudUsQ2T%2BdtwdcXrvJnQ7ZHuYE9Ro5OOZ8DKCp9t3JAIHG%2FUGGufvqH4FkV%2B42f2z1OkmXn0f%2BU%2FuprW1TdmBLeMqtJBmsqBWpXpRELkuYcbEo3ADJdJgcSpj5qSCmbA%3D&union_lens=lensId%3APUB%401702575245%4021334feb_0c8e_18c6964886f_3037%4002FXjqZ71jUAMAWD59wyxDr%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_86a564e3-eb01-4328-b966-1f368fd8dd53_)
- Cảm biến nhiệt độ & độ ẩm: AHT10, [https://s.click.taobao.com/0bmD14u](https://s.click.taobao.com/t?e=m%3D2%26s%3D771Jk01ERgJw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbARW0aicXof30JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAhscfsB2%2FyzZJq71CBMBeP%2F1SarTXhIOTsgIpc1WFZiJNubylQlnZt9wh0O3n7rEs7VSraZkgkhwlqDRRVlGxRqvyzFinE2vlF34xAtPUu%2BiG3j8B3S26WYwmLWBgEm80VKxcI130SjETn2JhMaQNtYWLhXkoGmYy4m7hkqalpkwvY%2BXqRXyCksYOae24fhW0&union_lens=lensId%3APUB%401702575301%4021055555_16ff_18c69656404_8e69%40024dxCNyvH1jSaZZpYn2bbD3%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_e4951d39-761b-41f5-8840-673a3c0c7f1f_)
- Cảm biến áp suất: BMP180, [https://s.click.taobao.com/lj5E14u](https://s.click.taobao.com/t?e=m%3D2%26s%3DbnhyRgad73lw4vFB6t2Z2ueEDrYVVa64YUrQeSeIhnK53hKxp7mNFhiPRfK6WZZbbJVKleC4LJX0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAFBcM8wkWlGmBPeR%2FUFCetKLWMw3EOEsyJN2owMjhufwDudUsQ2T%2BduXhptvf7sm0vQrr15PsJfJByQLpIiyxZXY%2Fio3PXH82JxEGSxl4yKUtXkuY7xjNZU%2FuprW1TdmBLeMqtJBmsqAZVo9hr9WaqYgTf7YQgcVJcSpj5qSCmbA%3D&union_lens=lensId%3APUB%401702575360%402106d2ff_0cdf_18c69664982_7aaf%40021C2JTg214QOPnilxsDb2w%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_f8a710ac-0037-4ec2-bbff-896f3c1af2c5_)
- Mô-đun đo khoảng cách siêu âm: US-100 (dùng chế độ giao tiếp UART), [https://s.click.taobao.com/K0o5X3u](https://s.click.taobao.com/t?e=m%3D2%26s%3DInz6n3%2B%2FZwxw4vFB6t2Z2ueEDrYVVa64YUrQeSeIhnK53hKxp7mNFhiPRfK6WZZbq%2FcxWpaeRwj0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAFBcM8wkWlGmBPeR%2FUFCetKLWMw3EOEsyJN2owMjhufwDudUsQ2T%2Bdg68505k9IiwAZQz22%2FRFMA5pQILDo6IOf16Gn%2BAIx7L96W3Eb16pLNyU%2FAPg17%2FFE%2FuprW1TdmBLeMqtJBmsqB4r3mjD%2F4H9bgUNPZMnoHFcSpj5qSCmbA%3D&skuId=4276770277565&union_lens=lensId%3APUB%401702575435%40213e7f1b_0c9e_18c69676fe9_4d3a%40036cIiBD2TZI9c9YU2Vchohd%40eyJmbG9vcklkIjo2MTQyOSwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfZGV0YWlsX2h0bSIsInNyY0Zsb29ySWQiiOiiI4MDY3NCJ9%3Bscm%3A1007.30148.329090.pub_search-item_972f304d-0e03-4fbf-b740-fd750c7af1d6_)
- Màn hình LCD: ST7789 1,47 inch, độ phân giải 172×320, [https://s.click.taobao.com/qalD14u](https://s.click.taobao.com/t?e=m%3D2%26s%3DgP4fG3vZI%2FNw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbfpmjeIyJHHf0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAhscfsB2%2FyzZJq71CBMBeP%2F1SarTXhIOTsgIpc1WFZiJNubylQlnZt8KAViuQdyPNc8ER84P6KktsP7OMD4hiwaj%2Fx4cHJV5ILJ9HqoY3BUsLMohkjCbsqowmLWBgEm80VKxcI130SjETn2JhMaQNtYWLhXkoGmYyruoikO2OymYJLixcAe8pyMYOae24fhW0&union_lens=lensId%3APUB%401702575502%40212c48cb_0c8f_18c69687670_23aa%40024B9sLEl2iRKmna6a9po7FA%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_5e0d1922-0ca6-44f9-99a9-821166c9de7d_)
- Màn hình OLED: SSD1306 0,96 inch, giao tiếp I²C, [https://s.click.taobao.com/otWD14u](https://s.click.taobao.com/t?e=m%3D2%26s%3DKH7sw7eX%2F2Bw4vFB6t2Z2ueEDrYVVa64YUrQeSeIhnK53hKxp7mNFhiPRfK6WZZbUnl%2Fg%2F1GNuL0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAc7QBxLpsf4TZIsq6%2BWiNSqLWMw3EOEsyJN2owMjhufwDudUsQ2T%2BdqwhF6wjgAjmYU48%2FUVSeeMab%2F%2BCSuws4OiHLQ4Yn8csh%2BdzzS7mHW11b5eySA3UA0%2FuprW1TdmBLeMqtJBmsqChj81ryXyWXeLqYXvNN1cOcSpj5qSCmbA%3D&union_lens=lensId%3APUB%401702575558%40213f6e5c_0cfd_18c69694f33_999c%40021vqRKMT5nzj9ALhoKTH8lx%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_b4129703-d754-49d5-b48a-82cdfefb2b5f_)
- IC sạc/xả pin: IP5306-CK (chú ý mua phiên bản CK, bản thường khi dòng xả quá nhỏ sẽ tự ngắt, không phù hợp cho vi điều khiển), [https://s.click.taobao.com/eu85X3u](https://s.click.taobao.com/t?e=m%3D2%26s%3DjX7JisUJSllw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbiJKehPSaPYD0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMAhscfsB2%2FyzZJq71CBMBeP%2F1SarTXhIOTsgIpc1WFZiJNubylQlnZt4mEKVwjSJcGFXvRQPy5FMG3%2FxXe%2BxQvKcLHPTEEdUKrO5xtKw8OA8bUMx%2BC19Mn3YwmLWBgEm80VKxcI130SjETn2JhMaQNtYWLhXkoGmYy5%2FXtHaC5QvUT2J2P4FlLNcYOae24fhW0&union_lens=lensId%3APUB%401702575629%4021055b80_0c9f_18c696a64f6_4c08%40022k2R4UBS6bcyB2aIphUK2l%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_9fa456ea-a73f-470b-94dc-a6e6199bf4a4_)
- Pin: EVE 35V-18650, 3500 mAh, [https://s.click.taobao.com/uUc5X3u](https://s.click.taobao.com/t?e=m%3D2%26s%3DS952IFxDAbxw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbhQCvBpU9uKb0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4QyiY4z4rjZDGVMA2wPTjyt1VFhJDk80jsGTwv1SarTXhIOT%2FcbizUjsed%2BEr%2BInjt7kJlMJo4xVYoI6QTU2mSOmawi8xTwUuWBJq9spaWqoW5XN2MTzYpfP%2FlmjO9AJYjY8CXJ%2BwEVkOqHFdIW9JNkz7%2FgDXc3BRboPm%2FkFZVUCNgR0oMbC7PHmBq9RLBgaW5udaw%3D%3D&union_lens=lensId%3APUB%401702575711%400b8b8151_148e_18c696ba6d8_29d5%40021a7vszDH3fGcvCMrLJyErd%40eyJmbG9vcklkIjo4MDY3NCwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfcHJvbW9fZ29vZHNfaW5kZXhfaHRtIiiwiic3JjRmxvb3JJZCI6IjgwNjc0In0ie%3Bscm%3A1007.30148.329090.pub_search-item_52261151-42b3-4abc-8483-08f2c5c3cc3d_)
**Nên mua linh kiện tại LCSC, đăng ký ưu đãi: [https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html](https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html)**
**Tất cả linh kiện trên bo đều có sẵn tại LCSC; trong liên kết mã nguồn, bấm “Mua ngay tại LCSC” trong bảng BOM để nhập tự động toàn bộ linh kiện vào giỏ hàng.**
## Hướng dẫn cấu hình nền tảng IoT
Trước tiên tạo sản phẩm mới trên nền tảng IoT của Alibaba Cloud, chọn kiểu nút là “Thiết bị kết nối trực tiếp”.

Thiết lập định nghĩa chức năng:

Tiếp theo thêm hai thiết bị

Sửa khóa sản phẩm và các tham số trong script, sau đó tải firmware và script xuống Air700E, kiểm tra xem thiết bị có kết nối và đẩy dữ liệu bình thường không.

Vào “Chuyển tiếp tin nhắn” → “Chuyển tiếp sản phẩm đám mây” để tạo nguồn dữ liệu:

Bấm “Xem” bên phải nguồn dữ liệu vừa tạo, thêm Topic: cái thứ nhất chọn “Tùy chỉnh”, thứ hai chọn sản phẩm bạn tạo, thứ ba chọn thiết bị 4G, thứ tư chọn `user/update`.

Tạo đích dữ liệu, chọn thao tác “Publish sang Topic khác”, sản phẩm vẫn chọn sản phẩm bạn tạo.

Tạo trình phân tích (parser), bấm “Đi tới chỉnh sửa” hoặc “Xem”.

Chọn nguồn dữ liệu bạn vừa tạo:

Chọn đích dữ liệu bạn vừa tạo

Tại phần script parser, đổi `deviceName()` thành tên thiết bị WiFi của bạn như hình.

Sau khi sửa xong bấm “Publish”, rồi khởi động; hệ thống sẽ tự động chuyển tiếp dữ liệu từ chủ đề `/ProductKey/DeviceName/user/update` sang chủ đề `/ProductKey/DeviceName/user/get`.
## Các dự án mã nguồn mở khác đề xuất- Bo mạch hệ thống tối thiểu STM32F030C8T6 và đèn chạy (sơ đồ nguyên lý & PCB): [https://blog.zeruns.com/archives/715.html](https://blog.zeruns.com/archives/715.html)
- Vẽ xong bo mạch hệ thống tối thiểu MSP430F149, đã mở nguồn: [https://blog.zeruns.com/archives/713.html](https://blog.zeruns.com/archives/713.html)
- Đề nguồn kỳ thi điện tử 2007: mô-đun DCDC tăng áp 30–36 V điều chỉnh được (UC3843): [https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842](https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842)
- Bo mạch hệ thống tối thiểu STC12C5A60S2 / vi điều khiển 51 hiển thị nhiệt độ và quạt điều khiển theo nhiệt độ: [https://blog.zeruns.com/archives/721.html](https://blog.zeruns.com/archives/721.html)
- Template dự án STM32F407 thư viện chuẩn đã port sẵn thư viện đồ họa U8g2: [https://blog.zeruns.com/archives/722.html](https://blog.zeruns.com/archives/722.html)
- Mở nguồn bo mạch hệ thống tối thiểu QinHeng CH32V307VCT6: [https://blog.zeruns.com/archives/726.html](https://blog.zeruns.com/archives/726.html)
- Mô-đun nguồn DCDC tăng–giảm áp tự động LM25118 điều chỉnh được: [https://blog.zeruns.com/archives/727.html](https://blog.zeruns.com/archives/727.html)
- Mở nguồn mô-đun tăng áp đồng bộ công suất lớn EG1164, hiệu suất cực đại 97 %: [https://blog.zeruns.com/archives/730.html](https://blog.zeruns.com/archives/730.html)
## Bài viết đề xuất
- **Gợi ý VPS/máy chủ đám mây giá rẻ, tỷ lệ chi phí/hiệu năng cao:** [https://blog.zeruns.com/archives/383.html](https://blog.zeruns.com/archives/383.html)
- Hướng dẫn dựng máy chủ Minecraft: [https://blog.zeruns.com/tag/mc/](https://blog.zeruns.com/tag/mc/)
- Khoe tủ mạng gia đình, gợi ý thiết bị mạng cho nhà ở: [https://blog.zeruns.com/archives/732.html](https://blog.zeruns.com/archives/732.html)
- Mở hộp & đánh giá bo phát triển Orange Pi 3B (RK3566): [https://blog.zeruns.com/archives/729.html](https://blog.zeruns.com/archives/729.html)
- Nâng cấp PC, thay card màn hình Yeston RTX3070: [https://blog.zeruns.com/archives/746.html](https://blog.zeruns.com/archives/746.html)












