合宙Air700Eを使った4G環境監視ノード(温湿度・気圧など)、MQTTでAlibaba Cloud IoTプラットフォームへ送信

合宙Air700Eを使った4G環境監視ノード(温度、湿度、気圧などのデータ)を、MQTTで阿里云IoTプラットフォームへアップロードする。

紹介

合宙Air700E 4Gモジュールがセンサー(温湿度気圧など)データを読み取り、MQTTプロトコルで阿里云IoTプラットフォームへ送信すると同時に、0.96インチのOLED画面にU8g2グラフィックライブラリを使って表示する。

ESP32C3 WiFiモジュールはMQTTプロトコルで4Gノードが送信したデータを購読し、LCD画面にLVGLグラフィックライブラリを使って表示する。

これは私の課題設計で、適当に作ったので完璧ではない。

課題要件:無線通信システムを設計・製作し、前期で学んだ知識を活かして、ローカルで取得した温湿度/超音波距離を無線でホスト/受信側へ送信し、受信側でLCDに情報を表示すること。具体的要求:

⑴ ブルートゥース無線伝送システムの設計・実装

⑵ WiFi無線伝送システムの設計・実装

⑶ ZigBee無線伝送システムの設計・実装

⑷ GPRS/GSM無線伝送システムの設計・実装

(5) 4G無線伝送システムの設計・実装

(6) NB-IoT/LoRa無線伝送システムの設計・実装

ヒント:8/16/32ビットプロセッサをメインコントローラにし、グループで選んだ総合内容に応じて選定。最低2小項目を完了し、(1)(2)(3)から1つ、(4)(5)(6)から1つ選択して完了させる。

Air700EもESP32C3もLuatOSシステム+Luaスクリプトで開発した。

LuatOSファームウェアは合宙のクラウドビルドで生成した。

ESP32C3の内蔵4MByteフラッシュを8MByteに換装し、LVGLグラフィックライブラリと複数フォントを含めたためファームウェアは4MBを超える。

阿里云学生割引URL:https://www.aliyun.com/daily-act/ecs/activity_share?userCode=jdjc69nf

注意: 阿里云IoTプラットフォームではデバイス同士で相手のトピックを購読できず、自デバイスのトピックのみ購読可能。よって メッセージ転送→クラウド製品転送 でルールを追加し、4Gノードデバイスが公開したメッセージをWiFiノードデバイスのトピックへ転送する必要がある。

立創オープンソースプラットフォーム公開リンク:https://oshwhub.com/zeruns/wen-shi-du-cai-ji-4g-shang-chuan

電子・マイコン技術交流QQ群:2169025065

合宙Air700E紹介

Air700Eは合宙通信が発売したLTE Cat.1 bis通信モジュールで、移芯EC618プラットフォームを採用し、LTE 3GPP Rel.13技術に対応。LTE TDDバンドのみを残し、中国移動の主流バンドに適合し、超小型パッケージと極限コストで小型化・低コスト需求に応える。

主な特徴:

  • 単1.8/3.3V USIMインターフェース対応
  • 1.8/3.3V設定可能なシリアルポート対応
  • USB 2.0対応
  • リモートOTAファームウェアアップデート対応
  • PSMデジタル音声インターフェース対応
  • USBインターネット共有、標準AT開発、open CPU二次開発(LuatOS,C-SDK)など複数の開発方式に対応

Air700Eは豊富なネットワークプロトコルを内蔵し、複数の産業標準インターフェースを統合し、Windows 7/8/8.1/10、Linux、AndroidなどOS向けUSBドライバなど、多彩なドライバ・ソフトウェア機能をサポート。CPE、ルータ、データカード、タブレット、車載、セキュリティ、産業用PDAなどM2M分野への応用を大きく拡大する。

Air700E技術仕様:

LTE-TDDバンド: B34/B38/B39/B40/B41

LTE-TDDデータレート:

  • 上り下り比率2時、最大8Mbps(DL)/最大2Mbps(UL)
  • 上り下り比率1時、最大6Mbps(DL)/最大4Mbps(UL)

インターフェース:

  • USB 2.0ハイスピードインターフェース1系統(最大480Mbps)
  • 1.8V/3.0V (U)SIMカードインターフェース1系統
  • NETLIGHTインターフェース1系統 (NET_STATUS)
  • デジタルI2Sインターフェース1系統、外付けcodec対応
  • UARTインターフェース3系統(メイン、汎用、デバッグ)
  • PWRKEY(ローアクティブ)
  • ADCインターフェース2系統
  • 汎用GPIO13本+割込入力2系統
  • I2Cインターフェース1系統

合宙ESP32-C3紹介

CORE ESP32コアボードは楽鑫ESP32-C3を基に設計されたコアボードで、サイズはわずか21mm×51mm。ボードエッジはスタンプ穴設計で、様々なシーンでの利用に便利。コアボードはUART、GPIO、SPI、I2C、ADC、PWMなどのインターフェースをサポートし、必要に応じて選択可能。

ハードウェアリソース:

  • サイズ 21mm×51mm
  • SPI FLASH 1系統、実装4MB、最大16MB対応
  • UARTインターフェース2系統、UART0~UART1、書き込み口はUART0
  • 12ビットADC 5系統、最大サンプリングレート100KSPS
  • 低速SPIインターフェース1系統、マスターモード対応
  • I2Cコントローラ1系統
  • PWMインターフェース4系統、任意のGPIO使用可能
  • GPIO外部ピン15本、マルチプレックス可
  • 実装LEDインジケータ2系統
  • リセットボタン1つ+BOOTボタン1つ
  • USB-TTL書き込み・デバッグポート1系統
  • 2.4GHz PCB実装アンテナ

実物写真

デモ動画: https://www.bilibili.com/video/BV1MH4y1B7Df/

全体写真

4Gノード

WiFiノード

WiFi接続中:

WiFi接続後、MQTTサーバー接続とNTP時刻同期開始:

4Gノードが収集したデータ表示:

温湿度変化グラフ表示:

IoTプラットフォーム

阿里云IoTプラットフォームに表示されたデータ:

回路図

4Gノード

WiFiノード

PCB

4Gノード

この4Gモジュールのフットプリントは少し誤っている。

トップ:

ボトム:

WiFiノード

トップ:

ボトム:

コード

コードのダウンロード方法は詳述しない。公式ドキュメントを参照。

Air700Eドキュメント:https://doc.openluat.com/wiki/44?wiki_page_id=4730

LVGL for LuatOSマニュアル:https://url.zeruns.com/7z7fN

ESP32-C3ドキュメント:https://url.zeruns.com/497AP

Luaチュートリアル:https://url.zeruns.com/Pc4PA

4Gノード

ファームウェアダウンロードURL:https://url.zeruns.com/2G3K7

main.luaファイル:

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "環境監視ノード"
VERSION = "1.1.0"

-- 引入必要的库文件(lua编写), 内部库不需要require
sys = require("sys")
aht10 = require "aht10"
bmx = require "bmx"

--添加硬狗防止程序卡死
if wdt then
    wdt.init(9000)--初始化watchdog设置为9s
    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
end

print(_VERSION)

log.info(mobile.ipv6(true)) --开启ipv6

local ProductKey= "xxxxxx"                         --产品key
local DeviceName= "xxxxxx"                --改为你自己的设备名
local DeviceSecret = "xxxxxx" --改为你自己的设备密钥
local client_id, user_name, password = iotauth.aliyun(ProductKey, DeviceName ,DeviceSecret) --生成MQTT三元组
log.info("MQTT参数", client_id, user_name, password)

local softI2C = i2c.createSoft(29, 31, 2)   --初始化软件I2C
local aht10_data,bmx_data
local RH,Temp,Press,High,distance,BAT_Voltage,CPU_T = 0,0,0,0,0,0,0                 --平均值,湿度、温度、气压、海拔、距离、电池电压、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   --当前值
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()   --US-100超声波测距模块读取数据
    uart.write(1, string.char(0x55))  --发送16进制0x55
    sys.wait(50)    --延时50毫秒
    local hData,lData  = string.byte(uart.read(1, 2),1,2)   --串口接收2字节
    uart.rxClear(1) --清空接收缓存
    if hData and lData then --判断是否接收到数据
        local Distance_mm = tonumber((hData * 256 ) + lData)    --将高8位左移8位后和低8位数据相加
        if Distance_mm > 4500 then  --判断是否超出测量范围
            Distance_mm = 4500
        end
        --log.info("超声波:"..Distance_mm.."mm")     --日志输出距离
        return (Distance_mm / 10)                  --算出cm并返回
    end
end
-- https://blog.zeruns.com
sys.taskInit(function() -- 创建一个线程,读取各个传感器数据

    sys.wait(500)       -- 延时500毫秒
    aht10.init(softI2C) -- 初始化AHT10,传入i2c_id
    bmx.init(softI2C)   -- 初始化BMP180,传入i2c_id
    uart.setup(1, 9600, 8, 1, uart.NONE)    --初始化串口1
    adc.open(0)         -- 打开adc通道0,并读取
    adc.open(adc.CH_CPU)-- 打开ADC通道-CPU内部温度通道
    --adc.setRange(adc.ADC_RANGE_3_8) -- 开启ADC0,1分压电阻,范围0~3.8V
    sys.wait(500)       -- 延时500毫秒

    local RH_sum, Temp_sum, Press_sum, High_sum, distance_sum,BAT_Voltage_sum,CPU_T_sum = 0,0,0,0,0,0,0 --总和,求平均值用
    local avg_count, avg_count2,avg_count3,avg_count4,avg_count5 = 0,0,0,0,0

    while 1 do
        aht10_data = aht10.get_data()   --读取AHT10的数据
        bmx_data = bmx.get_data()       --读取BMP10的数据
        distance_C = US_100()           --读取US-100的数据
        -- 电池电压采样,分压电阻比例:4.7K/10K
        BAT_Voltage_C = ((adc.get(0) / 1000 / 1007) * 1473) -- 读取AD0电压值,计算电池电压
        CPU_T_C = adc.get(adc.CH_CPU) / 1000                -- 读取CPU温度

        -- AHT10温湿度平均值计算
        if aht10_data.RH and aht10_data.T  --判断AHT10是否读取到数据
            all_data.params.CurrentTemperature = aht10_data.T
            all_data.params.CurrentHumidity = aht10_data.RH*100
            if avg_count < 6 then       --判断是否小于3,累加计平均值
                RH_C,Temp_C = aht10_data.RH*100, aht10_data.T   --读取温度和湿度的当前值
                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         -- 算出湿度平均值
                Temp = Temp_sum / 6     -- 算出温度平均值
                log.info("AHT10_data.RH: "..(string.format("%.2f", RH)).." %"," AHT10_data.T: "..(string.format("%.2f", Temp)).." ℃")    --日志输出
                AHT10_flag = true       -- 标志位置位,数据采集完毕
                sys.publish("MQTT_P")   -- 发布消息,数据采集计算完毕,MQTT可以上报了
                avg_count,RH_sum,Temp_sum = 0,0,0   --计数值和总和清零
            end
        end
```--BMP180気圧と高度の平均値計算
        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       --10未満かどうかを判定し、平均値を累積計算
                Press_C,High_C = bmx_data.press, bmx_data.high  --気圧と高度の現在値を読み取る
                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超音波測距 距離の平均値計算
        if distance_C then    --データを読み取ったかどうかを判定
            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

        -- バッテリー電圧
        if BAT_Voltage_C then    --データを読み取ったかどうかを判定
            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

        -- CPU温度
        if CPU_T_C then    --データを読み取ったかどうかを判定
            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() --タスクを作成し、超音波測距を1.5秒ごとに実行
    uart.setup(1, 9600, 8, 1, uart.NONE)    --シリアルポート1を初期化
    sys.wait(1000)
    while 1 do
        distance = US_100() --US-100のデータを読み取る
        if distance then    --データを読み取ったかどうかを判定
            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() --スレッドを作成し、MQTT初期化とデータ報告を実行
    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) -- デフォルト値240s
    mqttc:autoreconn(true, 3000) -- 自動再接続メカニズム
    mqttc:on(function(mqtt_client, event, data, payload)
        if event == "conack" then
            sys.publish("mqtt_conack")
            log.info("mqtt", "mqtt接続済み")
            mqtt_client:subscribe("/sys/"..ProductKey.."/"..DeviceName.."/thing/service/property/set")
        elseif event == "recv" then
            log.info("mqtt", "メッセージ受信", 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") -- データをJSON形式に変更し、小数点2桁で保持
            mqttc:publish("/sys/"..ProductKey.."/".. DeviceName.."/thing/event/property/post", json_str)  -- MQTTでプロパティを報告
            mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f"))  -- MQTTでプロパティを報告
            AHT10_flag = false
        end
        if BMP180_flag then
            local json_str = json.encode({params = {Atmosphere = Press, Altitude = High}}, "2f") -- データをJSON形式に変更し、小数点2桁で保持
            mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str)  -- MQTTでプロパティを報告
            mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f"))  -- MQTTでプロパティを報告
            BMP180_flag = false
        end
        if US100_flag then
            local json_str = json.encode({params = {DetectDistance = distance}}, "1f") -- データをJSON形式に変更し、小数点2桁で保持
            mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str)  -- MQTTでプロパティを報告
            mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f"))  -- MQTTでプロパティを報告
            US100_flag = false
        end
        if BAT_Voltage_flag then
            local json_str = json.encode({params = {CurrentVoltage = BAT_Voltage}}, "2f") -- データをJSON形式に変更し、小数点2桁で保持
            mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str)  -- MQTTでプロパティを報告
            mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f"))  -- MQTTでプロパティを報告
            BAT_Voltage_flag = false
        end
        if CPU_T_flag then
            local json_str = json.encode({params = {CPUTemperature = CPU_T}}, "2f") -- データをJSON形式に変更
            mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", json_str)  -- MQTTでプロパティを報告
            mqttc:publish("/"..ProductKey.."/".. DeviceName.."/user/update", json.encode(all_data,"2f"))  -- 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 オプション0 90 180 270
    u8g2.ClearBuffer()  --画面クリア
    u8g2.SetFont(u8g2.font_opposansm10)  --フォント切り替え
    u8g2.DrawUTF8("IMEI:", 0, 16)
    u8g2.DrawUTF8(mobile.imei(), 0, 32)
    u8g2.SendBuffer()

    --ntp時刻同期
    --socket.sntp()
    socket.sntp({"ntp.aliyun.com","ntp1.aliyun.com","ntp2.aliyun.com"}) --sntpカスタムサーバーアドレス
    --socket.sntp(nil, socket.ETH0) --sntpカスタムアダプター番号
    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()  --画面クリア
    u8g2.SetFont(u8g2.font_opposansm10)  --フォント切り替え
    u8g2.DrawUTF8(os.date("%Y-%m-%d"), 0, 16) --時刻表示
    u8g2.DrawUTF8(os.date("%H:%M:%S"), 0, 32) --時刻表示
    local IP = socket.localIP()     --IPアドレスを表示
    if IP then
        u8g2.DrawUTF8("IP:"..IP, 0, 63)
    end
    u8g2.SendBuffer()
    sys.wait(2500)

    while true do
        u8g2.ClearBuffer()  --画面クリア
        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()   --画面にデータ更新を表示
        sys.wait(200)
    end
end)sys.taskInit(function() --スレッドを作成、500ミリ秒ごとにLEDを点滅
    gpio.setup(27, 0)   -- gpio27を出力に設定、初期レベルはLOW、ハードウェアのデフォルトプルアップ/プルダウン設定を使用
    while true do
        gpio.toggle(27) -- gpioレベルを反転
        sys.wait(500)
    end
end)

--[[
    サポートフォント
    ["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"]
]]
-- ユーザーコードは終了---------------------------------------------
-- 最後は必ずこの文
sys.run()
-- sys.run()の後に何も文を追加しない!!!!!

WiFiノード

ファームウェアダウンロードアドレス:https://url.zeruns.com/a7eXJ

main.luaファイル:

-- LuaToolsにはPROJECTとVERSIONの2つの情報が必要
PROJECT = "データ表示ノード"
VERSION = "1.0.5"

-- sysライブラリは標準装備
_G.sys = require("sys")
require("sysplus")

--ハードウェアウォッチドッグを追加してプログラムのフリーズを防止
wdt.init(9000)-- ウォッチドッグを初期化、6秒に設定
wdt_timer_id = sys.timerLoopStart(function()
    wdt.feed()
end, 2000)  -- 2秒ごとにウォッチドッグをフィード

local ProductKey= "xxxxxx"                         -- プロダクトキー
local DeviceName= "xxxxxx"                -- 自分のデバイス名に変更
local DeviceSecret = "xxxxxx" -- 自分のデバイスシークレットに変更
local client_id, user_name, password = iotauth.aliyun(ProductKey, DeviceName ,DeviceSecret) -- MQTTの3要素を生成
log.info("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() -- スレッドを作成
    
    log.info("wlan", "wlan_init:", wlan.init()) -- wlanを初期化
    wlan.setMode(wlan.STATION)                  -- wlanモードをSTATIONに設定
    wlan.connect("Mate 40 Pro", "123456789", 1)     -- wlanネットワークに接続
    local result, data = sys.waitUntil("IP_READY",6000)-- IP_READYイベントを待機
    log.info("wlan", "IP_READY", result, data)  -- IP_READYイベントの結果とデータを出力
    if result then         
        socket.setDNS(socket.STA, 1, "114.114.114.114")    
        log.info("wlan", "info", json.encode(wlan.getInfo()))   -- wlanネットワークの詳細情報を出力
        sys.publish("WiFi_connected_OK",data)   -- メッセージを発行、WiFi接続成功
        --ntp時刻同期
        --socket.sntp()
        socket.sntp({"ntp.aliyun.com","time.windows.com","ntp.tencent.com","ntp2.aliyun.com"}, socket.STA) -- sntpカスタムサーバーアドレス
        --socket.sntp(nil, socket.STA) -- sntpカスタムアダプタ番号
        -- NTP_UPDATEイベントをサブスクライブ
        sys.subscribe("NTP_UPDATE", function()
            sys.publish("NTP_OK")   -- メッセージを発行、NTP時刻同期成功
            log.info("sntp", "time", os.date()) -- システム時刻を出力
        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)-- ADCチャンネルを開く-CPU内部温度チャンネル
    local CH_CPU = adc.get(adc.CH_CPU) / 1000    -- CPU温度を読み取る
    if CH_CPU then  -- データが有効かどうかを判定
        local_CPU_T_C = CH_CPU
    end

    mqttc = mqtt.create(nil, "a1sJbDQiEqr.iot-as-mqtt.cn-shanghai.aliyuncs.com", 1883,true)    -- MQTTクライアントを作成
    mqttc:auth(client_id, user_name, password)  -- MQTT認証を実行
    mqttc:keepalive(60)                        -- MQTTキープアライブ時間を設定、デフォルトは60秒
    mqttc:autoreconn(true, 3000)                -- 自動再接続メカニズムを有効化
    mqttc:on(function(mqtt_client, event, data, payload)-- MQTTイベントコールバック関数を定義
        if event == "conack" then               -- 接続確認イベント(conack)を受信した場合
            sys.publish("mqtt_conack")          -- mqtt_conackメッセージを発行
            log.info("mqtt", "mqtt接続済み")       -- 接続成功情報を出力
            mqtt_client:subscribe("/sys/"..ProductKey.."/"..DeviceName.."/thing/service/property/set")  -- 指定トピックをサブスクライブ
            mqtt_client:subscribe("/"..ProductKey.."/"..DeviceName.."/user/get")
        elseif event == "recv" then             -- メッセージ受信イベント(recv)の場合
            if data == "/"..ProductKey.."/"..DeviceName.."/user/get" then   -- 受信トピックを判定
                all_data = json.decode(payload)             -- メッセージ内容をJSON形式で解析
                mqttc:publish("/sys/"..ProductKey.."/"..DeviceName.."/thing/event/property/post", payload)  -- MQTTでプロパティをアップロード
            else
                log.info("mqtt", "メッセージ受信", data, payload) -- 受信メッセージ情報を出力
                local mqtt_date = json.decode(payload)      -- メッセージ内容をJSON形式で解析
            end
        elseif event == "sent" then             -- メッセージ送信イベント(sent)の場合
            --log.info("mqtt", "sent", "pkgid", data)-- 送信メッセージ情報を出力
        end
    end)
    mqttc:connect()

    while true do
        local CH_CPU = adc.get(adc.CH_CPU) / 1000    -- CPU温度を読み取る
        if CH_CPU then  -- データが有効かどうかを判定
            CPU_T_C = CH_CPU
        end

        gpio.toggle(12) -- gpioレベルを反転
        sys.wait(500)
    end
end)

sys.taskInit(function() -- スレッドを作成 
    spi.setup(2, 7, 0, 0, 8, 80 * 1000 * 1000, spi.MSB, 1, 1)   -- SPIを初期化
    log.info("lcd.init", lcd.init("st7789",{
        port = 2,           -- spiポート
        -- pin_cs = 7,         -- SPIチップセレクト
        pin_dc = 8,         -- lcdデータ/コマンド選択ピン
        -- pin_pwr = 18,       -- lcdバックライトピン オプション、設定不要の場合あり
        pin_rst = 6,        -- lcdリセットピン
        direction = 2,      -- lcdスクリーン方向 0:0° 1:180° 2:270° 3:90°
        w = 320,            -- lcd 水平解像度
        h = 172,            -- lcd 垂直解像度
        xoffset = 0,        -- xオフセット(スクリーンICや方向によって異なる)
        yoffset = 34;       -- yオフセット(スクリーンICや方向によって異なる)
    }))

    log.info("lvgl", lvgl.init())   --- LVGLを初期化

    local style_screen_label_main = lvgl.style_create()         -- style_screen_label_mainという名前のスタイルオブジェクトを作成
    lvgl.style_set_text_font(style_screen_label_main, lvgl.STATE_DEFAULT, lvgl.font_get("opposans_m_16")) -- ラベルのデフォルトフォントを設定

```local Init_scr = lvgl.obj_create(nil, nil) -- スクリーンオブジェクトを作成
    lvgl.scr_load(Init_scr)  -- スクリーンをロードしてディスプレイに表示
    local spinner = lvgl.spinner_create(Init_scr, nil) -- スピナー(回転アイコン)を作成
    lvgl.obj_set_size(spinner, 100, 100) -- スピナーのサイズを設定
    lvgl.obj_align(spinner, nil, lvgl.ALIGN_CENTER, 0, -20) -- スピナーを中央揃え
    local label_WiFiConnecting = lvgl.label_create(Init_scr, nil) -- ラベルオブジェクトを作成
    lvgl.label_set_recolor(label_WiFiConnecting, true) -- ラベルの再着色機能を有効化(テキスト変更時に色も自動更新)
    lvgl.obj_add_style(label_WiFiConnecting, lvgl.LABEL_PART_MAIN, style_screen_label_main) -- ラベルのmain部分にスタイルを適用
    lvgl.label_set_text(label_WiFiConnecting, "WiFi接続中...") -- ラベルテキストを設定
    lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- ラベルのテキストを中央揃え
    lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 46) -- ラベルを中央揃え
    local result, ip_data = sys.waitUntil("WiFi_connected_OK", 5000)    -- WiFi接続成功メッセージを5秒待機
    if result == false then
        lvgl.label_set_text(label_WiFiConnecting, "#ff0000 WiFi接続失敗!#\\nデバイスを再起動するか、ネットワークを確認してください") -- WiFi接続失敗を表示
        lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- 中央揃え
        lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 51) -- 中央揃え
        while true do
            sys.wait(1000) -- 無限待機(プログラム進行を止める)
        end
    end
    lvgl.label_set_text(label_WiFiConnecting, "WiFi接続成功!") -- WiFi接続成功を表示
    lvgl.label_set_align(label_WiFiConnecting, lvgl.LABEL_ALIGN_CENTER) -- 中央揃え
    lvgl.obj_align(label_WiFiConnecting, nil, lvgl.ALIGN_CENTER, 0, 51) -- 中央揃え
    sys.wait(500) -- 500ms待機
    lvgl.obj_del(label_WiFiConnecting) -- ラベルを削除
    lvgl.obj_align(spinner, nil, lvgl.ALIGN_CENTER, 0, 0) -- スピナーを中央に再配置
    local label_Init_log = lvgl.label_create(Init_scr, nil) -- ラベルを作成
    lvgl.label_set_recolor(label_Init_log, true) -- 再着色を有効化
    lvgl.label_set_align(label_Init_log, lvgl.LABEL_ALIGN_LEFT) -- 左揃え
    lvgl.obj_align(label_Init_log, nil, lvgl.ALIGN_IN_TOP_LEFT, 10, 5) -- 左上に配置
    local Init_log = "#0000ff 初期化ログ: #\\nWiFi接続成功!\\n" -- 初期化ログ
    lvgl.label_set_text(label_Init_log, Init_log) -- ログを表示
    Init_log = Init_log.."IP: "..ip_data.."\\nMQTTサーバーに接続中...\\n" -- ログ更新
    lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    if sys.waitUntil("mqtt_conack", 10000) == false then
        Init_log = Init_log.."#ff0000 MQTTサーバー接続失敗!\\nデバイスを再起動してください!#\\n" -- MQTT接続失敗
        lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
        mqttc:disconnect() -- MQTT切断
        mqttc:close() -- MQTTクローズ
        while true do
            sys.wait(1000) -- 無限待機
        end
    end
    Init_log = Init_log.."MQTTサーバー接続成功!\\n" -- ログ更新
    lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    Init_log = Init_log.."NTP時刻同期中...\\n" -- ログ更新
    lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    local result = sys.waitUntil("NTP_OK", 3500) -- NTP同期を3.5秒待機
    if result then
        Init_log = Init_log.."NTP時刻同期成功!\\n" -- 同期成功
        lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    else
        Init_log = Init_log.."NTP時刻同期失敗!\\n" -- 同期失敗
        lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    end
    Init_log = Init_log.."現在時刻: "..(os.date("%Y-%m-%d %H:%M:%S")).."\\n" -- 時刻表示
    lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新
    Init_log = Init_log.."システム初期化完了!\\n" -- 初期化完了
    lvgl.label_set_text(label_Init_log, Init_log) -- 表示更新

    sys.wait(2000)

    local alldata_scr = lvgl.obj_create(nil, nil)               -- 全データ表示用スクリーンを作成(親・スタイルなし)
    lvgl.scr_load(alldata_scr)                                  -- スクリーンをロード

    local label_alldata = lvgl.label_create(alldata_scr, nil)   -- 全データ表示用ラベル作成
    lvgl.label_set_recolor(label_alldata, true)                 -- 再着色有効
    lvgl.label_set_align(label_alldata, lvgl.LABEL_ALIGN_LEFT)  -- 左揃え
    lvgl.obj_align(label_alldata, nil, lvgl.ALIGN_IN_TOP_LEFT, 10, 5)   -- 左上に配置
    lvgl.obj_add_style(label_alldata, lvgl.LABEL_PART_MAIN, style_screen_label_main)-- スタイル適用
    
    local label_time = lvgl.label_create(alldata_scr, nil)      -- 時刻表示用ラベル作成
    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)   -- 中央揃え
    lvgl.obj_align(label_time, nil, lvgl.ALIGN_IN_TOP_RIGHT, -25, 10)-- 右上に配置
    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)               -- グラフ表示用スクリーン作成

    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);   -- 折れ線グラフ
    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));
    

    -- メモリ使用量を出力
    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 環境温度: %.2f°C#
        #ff00ff 環境湿度: %.2f%%#
        #ff0000 気圧: %.2fhPa#
        #800080 標高: %.2fm#
        #0000ff 超音波距離: %.2fcm#
        #ff00ff リモートノード電池電圧: %.2fV#
        #ff0000 リモートノードCPU温度: %.0f°C#
        #800080 ローカルノードCPU温度: %.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)); -- ラベル更新
        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) -- GPIOトグル
        sys.wait(300)
    end
end)

sys.taskInit(function() -- 500ms周期でLED点滅タスク
    gpio.setup(13, 0)   -- GPIO13を出力・初期LOW・ハードウェアプルアップ
    gpio.setup(12, 0)   -- GPIO12を出力・初期LOW・ハードウェアプルアップ
    while true do
        gpio.toggle(13) -- GPIOトグル
        sys.wait(500)
    end
end)

sys.taskInit(function() -- キースキャンタスク
    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)


--[[
    対応フォント ["sarasa_m8_ascii","sarasa_m10_ascii","sarasa_m12_ascii",
    "sarasa_m14_ascii","sarasa_m16_ascii","sarasa_m18_ascii","sarasa_m20_ascii","sarasa_m22_ascii"]
]]

-- ユーザーコード終了---------------------------------------------
-- 最後は必ずこの一文
sys.run()
-- sys.run()以降に何も記述しない!!!!!

## 使用ハードウェア

使用ハードウェアモジュールと購入先:- 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_)
- 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_)
- 温湿度センサー:AHT10、[https://s.click.taobao.com/0bmD14u](https://s.click.taobao.com/t?e=m%3D2%26s%3D771Jk01ERgJw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbARW0aicXof30JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4yY4z4rjZDGVMAhscfsB2%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_)
- 気圧センサー: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_)
- 超音波距離測定モジュール:US-100(シリアル通信モード使用)、[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_)
- LCD画面:1.47インチST7789、解像度172×320、[https://s.click.taobao.com/qalD14u](https://s.click.taobao.com/t?e=m%3D2%26s%3DgP4fG3vZI%2FNw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbfpmjeIyJHHf0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4yY4z4rjZDGVMAhscfsB2%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_)
- OLED画面:0.96インチSSD1306、I2Cインターフェース、[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:IP5306-CK(CK版を購入すること。通常版は放電電流が小さいと出力が自動的に遮断され、マイコン用途に不向き)、[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_)
- バッテリー:億緯35V-18650、3500mAh、[https://s.click.taobao.com/uUc5X3u](https://s.click.taobao.com/t?e=m%3D2%26s%3DS952IFxDAbxw4vFB6t2Z2ueEDrYVVa64g3vZOarmkFi53hKxp7mNFhiPRfK6WZZbhQCvBpU9uKb0JlhLk0Jl4QTquP0kWxBLBDnvz6xo38xspWc9%2BCL4bTGF1ceZMhPo8mL8HhJ3EdVrH4ks4yY4z4rjZDGVMA2wPTjyt1VFhJDk80jsGTwv1SarTXhIOT%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_)

**部品購入はLCSCモールがおすすめ、割引登録リンク:[https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html](https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html)**

**基板上のすべての部品はLCSCモールで購入可能です。オープンソースリンク内のBOM表から「すぐにLCSCモールで注文」をクリックすると、使用する部品が一括でカートに追加されます。**

## モノのインターネットプラットフォーム設定手順

まずAlibaba Cloud IoT Platformで新製品を作成し、ノードタイプは「直接接続デバイス」を選択。

![](upload://xLPj0bhLZ1rL7RZEilLQN6ik7Bg.png)

機能定義を設定:

![](upload://zZrpMZ1m81Csig5pO9hwdwzZdVd.png)

次にデバイスを2つ追加:

![](upload://2GKNz5ZfLb0gJ4QKViv65uUFYQx.png)

スクリプト内のプロダクトキーなどのパラメータを修正し、ファームウェアとスクリプトをAir700Eにダウンロードして、デバイスが正常にオンラインしデータをアップロードできるか確認。

![](upload://AsnIwH6TlnbEPb35i9fHDakJ7SQ.png)

メッセージ転送→クラウド製品フロー でデータソースを作成:

![](upload://bEfZtuEN8lpD89diTlYSFGzClTK.png)

作成したデータソースの右の「表示」をクリックし、Topicを追加。1つ目は「カスタム」、2つ目は作成した製品、3つ目は4Gノードのデバイス、4つ目は`user/update`を選択。

![](upload://mrY2OUxHOIUvmF53kY8QYf5sxzj.png)

データ転送先を作成し、アクションは「別のTopicに公開」を選択し、製品も上記で作成したものを選ぶ。

![](upload://55n4tbfW0Ag9TVFQ3laxDjBmNIB.png)

パーサーを作成し、「編集へ進む」をクリック、または右の「表示」をクリック。

![](upload://rTDklqhUCwOY2zZ5ZkEfyJzo9At.png)

データソースの関連付けで先ほど作成したデータソースを選択:

![](upload://vw789eZvaAKycMT2jbHJupaoWk4.png)

データ転送先の関連付けで先ほど作成したデータ転送先を選択。

![](upload://rGanTyGyLN3DK8NVT5MJFI02Fvx.png)

パーサースクリプトで`deviceName()`をWiFiノードのデバイス名に変更(下図参照)。

![](upload://ctS29c3tGAFXsA5Oz6vCWFGRZeN.png)

編集完了後、「公開」をクリックし、起動すると、`/ProductKey/DeviceName/user/update`トピックのデータが`/ProductKey/DeviceName/user/get`トピックに自動転送される。

## その他のオープンソースプロジェクトおすすめ- STM32F030C8T6 最小システムボードと流水灯(回路図とPCB):[https://blog.zeruns.com/archives/715.html](https://blog.zeruns.com/archives/715.html)
- MSP430F149 最小システムボードを描いてオープンソース化しました:[https://blog.zeruns.com/archives/713.html](https://blog.zeruns.com/archives/713.html)
- 2007年電子工作コンテスト電源問題:30〜36V可変昇圧DCDCモジュール(UC3843):[https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842](https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842)
- STC12C5A60S2 最小システムボード/51マイコン温度表示・温度制御ファン:[https://blog.zeruns.com/archives/721.html](https://blog.zeruns.com/archives/721.html)
- U8g2 グラフィックライブラリを移植済みの STM32F407 標準ライブラリプロジェクトテンプレート:[https://blog.zeruns.com/archives/722.html](https://blog.zeruns.com/archives/722.html)
- 沁恒 CH32V307VCT6 最小システムボード オープンソース:[https://blog.zeruns.com/archives/726.html](https://blog.zeruns.com/archives/726.html)
- LM25118 自動昇降圧可変DCDC電源モジュール:[https://blog.zeruns.com/archives/727.html](https://blog.zeruns.com/archives/727.html)
- EG1164 大電力同期整流昇圧モジュール オープンソース、最大効率97%:[https://blog.zeruns.com/archives/730.html](https://blog.zeruns.com/archives/730.html)

## おすすめ記事

- **コスパ最強で安いVPS/クラウドサーバーおすすめ:** [https://blog.zeruns.com/archives/383.html](https://blog.zeruns.com/archives/383.html)
- Minecraft サーバー構築チュートリアル:[https://blog.zeruns.com/tag/mc/](https://blog.zeruns.com/archives/tag/mc/)
- 自宅のネットワークラックを紹介、家庭用ネットワーク機器おすすめ:[https://blog.zeruns.com/archives/732.html](https://blog.zeruns.com/archives/732.html)
- 香橙派 Orange Pi 3B(RK3566)開発ボード 開封レビュー:[https://blog.zeruns.com/archives/729.html](https://blog.zeruns.com/archives/729.html)
- 自宅PCをアップグレード、グラボを交換、盈通 RTX3070:[https://blog.zeruns.com/archives/746.html](https://blog.zeruns.com/archives/746.html)