W55RP20 C言語開発実践:MQTTプロトコルとOneNET IoTプラットフォーム連携

本稿はWIZnet W55RP20チップのC言語チュートリアルの第14回で、公式最新ファームウェアに基づき作成されたコードはすべて実際に検証され、そのまま書き込み実行可能です。

はじめに

前回の実践チュートリアルでは、W55RP20チップのMQTTプロトコル基本通信を完了し、Alibabaクラウドサーバーとのメッセージ送受信を実現しました。本稿では、産業用IoTシナリオに焦点を当て、W55RP20がMQTTプロトコルを介してOneNET IoTプラットフォームと接続し、温度・湿度データを定期的にアップロードする方法を実装します。OneNETプラットフォームのデバイス登録、MQTT接続設定、物モデルデータ形式のパッケージングなどの核心スキルを習得し、産業用デバイスのクラウド接続要件に適合します。

W55RP20はハードウェアTCP/IPプロトコルスタックを統合し、MQTTクライアントライブラリを組み合わせることで、C言語プログラミングによってOneNETプラットフォームとの安定した通信を実現できます。ソケットの詳細を気にする必要はなく、WIZnet公式ドライバーライブラリとOneNET物モデル仕様に依存することで、組み込みデバイスのクラウド接続開発のハードルを大幅に下げ、スマートモニタリング、データ収集などの各種IoTシナリオに適しています。

本稿を学習すると、以下のスキルを習得できます:

  • OneNET IoTプラットフォームのデバイス登録、プロダクト作成、およびMQTT接続パラメータの取得方法
  • W55RP20とOneNETプラットフォーム接続のC言語実装(完全に実行可能なコード)
  • OneNET物モデルJSONペイロードの構築(標準形式の自動パッケージング)
  • DNS解像度の最適化、定期的なデータアップロード、ハートビート維持などの安定メカニズムの実装
  • OneNETプラットフォームのデータ表示、デバッグ、および一般的な問題のトラブルシューティング方法

シリーズチュートリアル学習パス

本シリーズは全16回で、W55RP20-EVB-PicoモジュールのC言語開発全プロセスを段階的にカバーしています:

  1. 第1回:静的IP設定とネットワーク基礎
  2. 第2回:DHCP自動接続とネットワーク診断
  3. 第3回:TCPクライアント通信
  4. 第4回:TCPサーバー通信
  5. 第5回:UDPユニキャスト通信
  6. 第6回:UDPマルチキャスト/ブロードキャスト通信
  7. 第7回:DNSドメイン名解決
  8. 第8回:NTPによるネットワークからの時間取得
  9. 第9回:HTTPクライアントリクエスト
  10. 第10回:HTTPサーバー構築
  11. 第11回:HTTPプロトコルとOneNETプラットフォームデータクラウド接続
  12. 第12回:MQTTプロトコル基本通信検証
  13. 第13回:MQTTプロトコルとAlibabaクラウドプラットフォーム連携
  14. 第14回:MQTTプロトコルとOneNETプラットフォーム連携(本稿)
  15. 第15回:MQTTプロトコルとThingSpeakプラットフォーム連携
  16. 第16回:Modbus産業プロトコル通信

目次

  1. 準備

1.1. ソフトウェア準備 1.2. ハードウェア準備 1.3. OneNETプラットフォーム準備

  1. W55RP20 C言語開発環境構築

2.1. ドライバーライブラリ設定 2.2. ファームウェア書き込み

  1. ハードウェア接続とシリアルポート設定

3.1. ハードウェア接続 3.1.1. 基本接続(電源+デバッグ) 3.1.2. イーサネット接続 3.1.3. モジュールと開発ボードの接続(分離式モジュール用) 3.2. シリアルデバッグ設定

  1. OneNET MQTTプロトコル仕様解説

4.1. 核心接続仕様(必読) 4.2. 物モデルデータ形式仕様 4.3. 標準トピック形式(必須) 4.4. 最小限のワークフロー

  1. 核心C言語コードのコピーとコンパイル・書き込み

5.1. 依存ライブラリ説明 5.2. 完全なC言語コード(メインプログラム) 5.3. コードの重要設定説明 5.4. コンパイルと書き込み手順

  1. 実行結果とOneNETプラットフォーム検証

6.1. シリアル出力結果 6.2. OneNETプラットフォームデータ検証

  1. 一般的な問題のトラブルシューティング

  2. W55RP20の核心的優位性比較

  3. 典型的なアプリケーションシナリオ

  4. シリーズ予告とリソース取得

10.1. シリーズ予告 10.2. リソース取得

  1. 準備

1.1. ソフトウェア準備

必要なソフトウェアはすべて無料バージョンで、ダウンロード・インストールするだけで追加料金は不要です。C言語開発要件に適合し、OneNETプラットフォーム関連操作ツールが追加されています。

ソフトウェア名 バージョン要件 ダウンロード先 説明
VS Code(C/C++プラグイン付き) 最新安定版 VS Code公式ダウンロード 軽量コードエディタ、C言語構文ハイライト、コンパイルデバッグをサポートし、初心者に優しい
W55RP20-EVB-PicoモジュールC言語ドライバーライブラリ 最新安定版 WIZnet公式ファームウェア/ドライバーライブラリダウンロード W55RP20-EVB-Picoモジュール専用に作成され、WIZnetハードウェアドライバ、TCP/IPプロトコルスタック、MQTTクライアントライブラリを統合
シリアルデバッグツール(SecureCRTなど) 任意のバージョン 公式ダウンロードまたはサードパーティツール シリアル出力の実行ログ、デバッグ情報の表示に使用し、接続とデータアップロード問題の特定に役立つ
OneNET IoTプラットフォーム オンライン版 OneNET IoTプラットフォーム公式サイト プロダクト作成、デバイス登録、MQTT接続パラメータの取得、アップロードされた温度・湿度データの表示に使用

1.2. ハードウェア準備

  • W55RP20-EVB-Pico × 1
  • Micro USBデータケーブル(データ転送をサポートする必要があり、充電専用ケーブルは不可)× 1
  • 標準イーサネットケーブル × 1
  • DHCP機能を有効にしたルーター/スイッチ × 1(ネットワークパラメータの取得とDNS解像に使用)

1.3. OneNETプラットフォーム準備

コード開発前に、OneNETプラットフォームでプロダクト作成とデバイス登録を完了し、MQTT接続に必要な核心パラメータ(プロダクトID、デバイス名、トークン)を取得する必要があります。手順は以下の通りです:

  1. OneNET IoTプラットフォームにログイン(アカウント登録が必要、個人版は無料)し、「IoTコアキット」→「プロダクト」に移動し、「プロダクト作成」をクリック;
  2. プロダクト設定:プロダクト名は任意(例:W55RP20温度・湿度収集)、プロトコルは「MQTT」、データ形式は「JSON」、デバイスタイプは「直接接続デバイス」を選択し、「作成を確認」をクリック;
  3. デバイス追加:作成したプロダクトに移動し、「デバイス管理」→「デバイス追加」をクリック、デバイス名は任意(例:W5500_01)とし、「確認」をクリックしてデバイス登録を完了;
  4. 接続パラメータ取得:デバイス詳細ページに移動し、以下の3つの核心パラメータを記録(後でコードに置換):
  • プロダクトID(ONENET_PRODUCT_ID):プロダクト詳細ページ上部で取得、文字列形式
  • デバイス名(ONENET_DEVICE_NAME):デバイス作成時に任意指定した名前
  • トークン(ONENET_MQTT_PASSWORD):パスワード生成ツールを使用:https://open.iot.10086.cn/doc/iot_platform/images/tools/token.exe(注:トークンには有効期限があり、期限切れの場合は再生成が必要)
  1. 物モデル追加(必須):プロダクトの「物モデル定義」に「temp」(温度、浮動小数点型)と「humi」(湿度、浮動小数点型)の2つの属性を追加し、フィールド名はコード内と一致させる必要があります(プラットフォームで温度・湿度データを直感的に確認するため)。

  2. W55RP20 C言語開発環境構築


2.1. ドライバーライブラリ設定

  1. W55RP20公式C言語ドライバーライブラリをダウンロードし、解凍して核心ファイル(wizchip_conf.h、socket.h、mqtt_interface.h、MQTTClient.hなどを含む)を取得;
  2. VS Codeを開き、新規プロジェクトを作成し、ドライバーライブラリのヘッダファイル(.h)をプロジェクトのincludeフォルダに、ソースファイル(.c)をsrcフォルダに配置;
  3. プロジェクトのコンパイルオプションを設定し、コンパイラ(ARM GCCなど)を指定し、ドライバーライブラリファイルを関連付け、コンパイル時にハードウェアドライバ、ネットワークインターフェース、MQTT関連関数を正常に呼び出せるようにする。

2.2. ファームウェア書き込み

W55RP20-EVB-PicoモジュールはラズベリーパイPicoのUF2ファームウェア書き込み方式に完全互換で、追加の書き込みデバイスが不要なため操作が簡単で、初心者でもすぐに使いこなせます:

  1. RP2040開発ボードのBOOTSELボタンを押し続ける;

  2. Micro USBデータケーブルで開発ボードとPCを接続;

  3. PCがRPI-RP2という名前のUSBドライブとして認識されたら、BOOTSELボタンを離す;

  4. コンパイルで生成されたUF2ファームウェアファイルをUSBドライブにドラッグ&ドロップ;

  5. 開発ボードが自動的に再起動し、ファームウェア書き込みが完了。

  6. ハードウェア接続とシリアルポート設定


3.1. ハードウェア接続

W55RP20-EVB-Picoモジュールの接続は、電源/デバッグとイーサネット接続の2段階で行い、操作は簡単で複雑な配線は不要です。基本MQTT実践接続方式と同じです:

3.1.1. 基本接続(電源+デバッグ)

Micro USBデータケーブルでRP2040開発ボードとPCを接続し、開発ボードの電源供給、ファームウェア書き込み、シリアルデバッグに使用します。

3.1.2. イーサネット接続

イーサネットケーブルでW55RP20-EVB-PicoモジュールのイーサネットインターフェースとルーターのLANポート(またはPCのネットワークポートに直接接続、PCのIPを開発ボードと同じセグメントに手動設定する必要あり)を接続します。

3.1.3. モジュールと開発ボードの接続(分離式モジュール用)

分離式モジュールと開発ボードを使用する場合は、以下のピン対応で接続します(SPI通信):

【ハードウェア予約】ここにハードウェア接続図を挿入

3.2. シリアルデバッグ設定

VS Codeのシリアルデバッグツールを開き、以下のパラメータで設定し、開発ボードの実行ログとデバッグ情報を確認し、OneNET MQTT接続とデータアップロード問題の特定に使用します:

  • ボーレート:115200
  • データビット:8
  • ストップビット:1
  • パリティビット:なし
  • フロー制御:なし
  • 開発ボード対応のシリアルポート(通常COMxと表示)を選択し、「シリアルポートを開く」をクリック。
  1. OneNET MQTTプロトコル仕様解説

OneNETプラットフォームはMQTT 3.1.1プロトコルを使用します(コード内の設定と一致)、パブリッシュ/サブスクライブモデルに基づいてデバイスとプラットフォームの双方向通信を実現します。パブリックMQTTサーバーと比較した場合、核心的な違いは接続認証が厳格であり、物モデルデータ形式が標準化されている点です。これらが開発の重点と難点です。

4.1. 核心接続仕様(必読)

  • MQTTバージョン:3.1.1バージョンを使用する必要があります(コード内ではMQTTVersion = 4と設定)、OneNETプラットフォームと完全互換;
  • サーバーアドレス:固定で「studio-mqtt.heclouds.com」(OneNET MQTT公式ドメイン)、地域パラメータの変更は不要;
  • ポート:1883を使用(非暗号化ポート、開発・デバッグに適用;本番環境では8883暗号化ポートを使用可能);
  • 接続パラメータ:ClientID=デバイス名、Username=プロダクトID、Password=プラットフォーム生成のトークン、3つは厳密に一致させる必要があり、不一致の場合は接続に失敗します。

4.2. 物モデルデータ形式仕様

OneNETプラットフォームは、デバイスがアップロードするデータを指定されたJSON形式にする必要があります。核心にはid、version、paramsの3つのフィールドが含まれます。例は以下の通り:

{ "id":"123456789", "version":"1.0", "params":{ "temp":{ "value":35.7 }, "humi":{ "value":60.2 } } }

説明:

  • id:ランダム数またはタイムスタンプ、メッセージを識別し、重複を避けるために使用、コード内で自動生成;
  • version:「1.0」に固定、物モデルデータ形式のバージョンを表す;
  • params:デバイスがアップロードする具体的なデータ、フィールド名(temp、humi)はOneNET物モデルで定義された属性名と一致する必要があり、valueは具体的な数値;
  • timeフィールドは手動で追加する必要はなく、コード内で最小限の形式に最適化され、プラットフォームの正常な解析が保証されます。

4.3. 標準トピック形式(必須)

OneNET物モデル通信では固定トピックを使用する必要があり、カスタマイズは不要です。プロダクトIDとデバイス名を置換するだけです:

  • 属性報告トピック(デバイス→プラットフォーム):$sys/プロダクトID/デバイス名/thing/property/post
  • 報告応答トピック(プラットフォーム→デバイス):$sys/プロダクトID/デバイス名/thing/property/post/reply

コード内ではトピック形式がカプセル化されており、プロダクトIDとデバイス名を置換するだけで、手動で連結する必要はありません。

4.4. 最小限のワークフロー

  1. W55RP20はDNS解像を介してOneNET MQTTサーバードメインを解決し、サーバーIPを取得;

  2. OneNETプラットフォーム提供のプロダクトID、デバイス名、トークンを使用してMQTT接続を確立;

  3. W55RP20はOneNET物モデル仕様に従って温度・湿度データ(JSON形式)をパッケージ化し、報告トピックにデータを公開;

  4. OneNETプラットフォームはデータを受信し、デバイス詳細ページにリアルタイムの温度・湿度データを表示し、同時に応答トピックに確認メッセージを送信し、デバイスは受信して印刷。

  5. 核心C言語コードのコピーとコンパイル・書き込み


5.1. 依存ライブラリ説明

核心的な依存関係はWIZnet公式ドライバーライブラリ(wizchip_conf.h、socket.h、mqtt_interface.h、MQTTClient.hなどを含む)で、W55RP20ハードウェアの初期化、ネットワーク接続、DNS解像、MQTTクライアントの接続、公開、サブスクライブなどの基础機能の実現に使用されます。OneNET専用ライブラリの追加は不要で、コード内に接続設定、物モデルJSONパッケージング、メッセージコールバックロジックが統合されており、プロジェクトに直接関連付けて使用できます。

5.2. 完全なC言語コード(メインプログラム)

以下のコードをVS Codeプロジェクトのメインファイル(例:main.c)にコピーし、OneNETプラットフォームから取得した接続パラメータを重点的に置換します。コンパイルしてUF2ファームウェアを生成し、開発ボードに書き込みます。コードのコメントは明確で、重要な設定部分はマーキングされており、直接修正できます。

/**
    Copyright (c) 2021 WIZnet Co.,Ltd
    Modified for W55RP20 with OneNET MQTT (optimized version)
    SPDX-License-Identifier: BSD-3-Clause
*/

#include <stdio.h>
#include <string.h>
#include "port_common.h"
#include "wizchip_conf.h"
#include "wizchip_spi.h"
#include "mqtt_interface.h"
#include "MQTTClient.h"
#include "timer.h"
#include "time.h"
#include "dns.h"

/* バッファ */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)

/* ソケット */
#define SOCKET_MQTT 0
#define SOCKET_DNS  3

/* ポート */
#define PORT_MQTT 1883

/* タイムアウト */
#define DEFAULT_TIMEOUT 1000

/* ========== OneNET設定(実際に合わせて修正)========== */
#define ONENET_PRODUCT_ID   "ZGB6zBD0nS"               // プロダクトID
#define ONENET_DEVICE_NAME  "sensor_device"            // デバイス名
// MQTTパスワード(トークン)、OneNETデバイス詳細ページからコピー、有効期限に注意
#define ONENET_MQTT_PASSWORD "version=2018-10-31&res=products%2FZGB6zBD0nS%2Fdevices%2Fsensor_device&et=1780016971&method=md5&sign=AAMTeit90OTmwPpugN%2Fe%2Bg%3D%3D"

#define ONENET_MQTT_DOMAIN  "studio-mqtt.heclouds.com" // OneNET MQTTサーバードメイン

/* 物モデルトピック */
#define PROPERTY_POST_TOPIC "$sys/" ONENET_PRODUCT_ID "/" ONENET_DEVICE_NAME "/thing/property/post"
#define PROPERTY_POST_REPLY_TOPIC "$sys/" ONENET_PRODUCT_ID "/" ONENET_DEVICE_NAME "/thing/property/post/reply"

/* 温度・湿度データ(シミュレーション値、実際のセンサーに置換可能) */
#define TEMP_READING 42.3
#define HUMI_READING 65.7

/* 公開周期(ミリ秒) */
#define MQTT_PUBLISH_INTERVAL (1000 * 15)
#define MQTT_KEEP_ALIVE 60

/* ========== ネットワーク設定(実際の環境に合わせて修正)========== */
static wiz_NetInfo network_config = {
    .mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
    .ip = {192, 168, 1, 110},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 1, 1},
    .dns = {223, 5, 5, 5},
#if _WIZCHIP_ > W5500
    .lla = {0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0xdc, 0xff, 0xfe, 0x57, 0x57, 0x25},
    .gua = {0},
    .sn6 = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    .gw6 = {0},
    .dns6 = {0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88},
    .ipmode = NETINFO_STATIC_ALL
#else
    .dhcp = NETINFO_STATIC
#endif
};

/* DNS解像結果保存 */
static uint8_t mqtt_server_ip[4] = {0};

/* MQTT関連変数 */
static uint8_t mqtt_tx_buffer[ETHERNET_BUF_MAX_SIZE] = {0};
static uint8_t mqtt_rx_buffer[ETHERNET_BUF_MAX_SIZE] = {0};
static Network mqtt_network;
static MQTTClient mqtt_client;
static MQTTPacket_connectData mqtt_connect_data = MQTTPacket_connectData_initializer;
static MQTTMessage mqtt_message;

static volatile uint32_t system_millis = 0;

/* DNS解像用バッファ */
static uint8_t dns_query_buffer[ETHERNET_BUF_MAX_SIZE] = {0};

/* 関数宣言 */
static void timer_callback(void);
static uint32_t get_system_millis(void);
static void create_onenet_data_package(char *data, int max_size, float temp, float humi);
static int resolve_domain(const char *domain, uint8_t *ip_address);
static void handle_incoming_message(MessageData *msg_data);

/**
 * @brief DNSドメイン名解像関数
 */
static int resolve_domain(const char *domain_name, uint8_t *ip_addr)
{
    int8_t result = 0;
    uint8_t dns_server_ip[4];
    
    dns_server_ip[0] = network_config.dns[0];
    dns_server_ip[1] = network_config.dns[1];
    dns_server_ip[2] = network_config.dns[2];
    dns_server_ip[3] = network_config.dns[3];
    
    if (dns_server_ip[0] == 0 && dns_server_ip[1] == 0 && dns_server_ip[2] == 0 && dns_server_ip[3] == 0) {
        dns_server_ip[0] = network_config.gw[0];
        dns_server_ip[1] = network_config.gw[1];
        dns_server_ip[2] = network_config.gw[2];
        dns_server_ip[3] = network_config.gw[3];
        printf("DNS: ゲートウェイをDNSサーバーとして使用 %d.%d.%d.%d\n", dns_server_ip[0], dns_server_ip[1], dns_server_ip[2], dns_server_ip[3]);
    } else {
        printf("DNS: DNSサーバーを使用 %d.%d.%d.%d\n", dns_server_ip[0], dns_server_ip[1], dns_server_ip[2], dns_server_ip[3]);
    }
    
    printf("DNS: ドメイン解像中: %s\n", domain_name);
    
    DNS_init(SOCKET_DNS, dns_query_buffer);
    result = DNS_run(dns_server_ip, (uint8_t *)domain_name, ip_addr);
    
    if (result == 1) {
        printf("DNS: 成功!IP: %d.%d.%d.%d\n", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
        return 0;
    } else {
        printf("DNS: 失敗!エラー: %d\n", result);
        return -1;
    }
}

/**
 * @brief OneNET物モデルJSONデータ構築(余分なスペースなし、timeフィールドなし)
 */
static void create_onenet_data_package(char *payload, int max_len, float temp, float humi) {
    snprintf(payload, max_len,
        "{\"id\":\"%lu\",\"version\":\"1.0\",\"params\":{\"temp\":{\"value\":%.1f},\"humi\":{\"value\":%.1f}}}",
        (unsigned long)get_system_millis(), temp, humi);
}

/* タイマーコールバック */
static void timer_callback(void) {
    system_millis++;
    MilliTimer_Handler();
}

/* ミリ秒取得 */
static uint32_t get_system_millis(void) {
    return system_millis;
}

/* メッセージ受信コールバック(応答トピックをサブスクライブ) */
static void handle_incoming_message(MessageData *msg_data) {
    MQTTMessage *msg = msg_data->message;
    printf("応答受信: トピック=%.*s, ペイロード=%.*s\n",
           msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data,
           (int)msg->payloadlen, (char*)msg->payload);
}

/**
 * @brief メイン関数
 */
int main() {
    int32_t return_value = 0;
    uint32_t last_publish_time = 0;
    uint32_t current_time = 0;
    char data_package[256];

    stdio_init_all();
    sleep_ms(3000);
    printf("\r\n=== W55RP20 OneNET MQTTデモ(最適化版) ===\r\n");

    wizchip_spi_initialize();
    wizchip_cris_initialize();
    wizchip_reset();
    wizchip_initialize();
    wizchip_check();
    wizchip_1ms_timer_initialize(timer_callback);

    network_initialize(network_config);
    print_network_information(network_config);

    /* DNS解像 */
    printf("\n--- DNS解像フェーズ ---\n");
    int dns_retry_count = 0;
    while (resolve_domain(ONENET_MQTT_DOMAIN, mqtt_server_ip) != 0) {
        dns_retry_count++;
        if (dns_retry_count >= 3) {
            printf("3回の試行後DNS解像に失敗しました!\n");
            while (1);
        }
        printf("3秒後にリトライ %d...\n", dns_retry_count);
        sleep_ms(3000);
    }

    /* TCP接続 */
    NewNetwork(&mqtt_network, SOCKET_MQTT);
    printf("OneNET MQTTブローカーに接続中 %d.%d.%d.%d:%d...\n",
           mqtt_server_ip[0], mqtt_server_ip[1], 
           mqtt_server_ip[2], mqtt_server_ip[3], PORT_MQTT);
    
    return_value = ConnectNetwork(&mqtt_network, mqtt_server_ip, PORT_MQTT);
    if (return_value != 1) {
        printf("ネットワーク接続に失敗!戻り値=%d\n", return_value);
        while (1);
    }
    printf("ネットワーク接続完了。\n");

    /* MQTT初期化 */
    MQTTClientInit(&mqtt_client, &mqtt_network, DEFAULT_TIMEOUT,
                   mqtt_tx_buffer, ETHERNET_BUF_MAX_SIZE,
                   mqtt_rx_buffer, ETHERNET_BUF_MAX_SIZE);

    /* 重要:MQTTプロトコルバージョン 4 = 3.1.1 */
    mqtt_connect_data.MQTTVersion = 4;
    mqtt_connect_data.cleansession = 1;
    mqtt_connect_data.willFlag = 0;
    mqtt_connect_data.keepAliveInterval = MQTT_KEEP_ALIVE;
    mqtt_connect_data.clientID.cstring = ONENET_DEVICE_NAME;
    mqtt_connect_data.username.cstring = ONENET_PRODUCT_ID;
    mqtt_connect_data.password.cstring = ONENET_MQTT_PASSWORD;

    printf("OneNETにMQTT接続中...\n");
    return_value = MQTTConnect(&mqtt_client, &mqtt_connect_data);
    if (return_value < 0) {
        printf("MQTT接続に失敗: %d\n", return_value);
        while (1);
    }
    printf("OneNETプラットフォームにMQTT接続完了!\n");

    /* 応答トピックのサブスクライブ(エラーメッセージ取得用) */
    return_value = MQTTSubscribe(&mqtt_client, PROPERTY_POST_REPLY_TOPIC, QOS0, handle_incoming_message);
    if (return_value < 0) {
        printf("サブスクライブ警告: %d\n", return_value);
    } else {
        printf("応答トピックをサブスクライブ: %s\n", PROPERTY_POST_REPLY_TOPIC);
    }

    sleep_ms(500);

    /* QOS0で公開(テストに便利) */
    mqtt_message.qos = QOS0;
    mqtt_message.retained = 0;
    mqtt_message.dup = 0;

    last_publish_time = get_system_millis();

    /* メインループ */
    while (1) {
        current_time = get_system_millis();

        if (current_time >= last_publish_time + MQTT_PUBLISH_INTERVAL) {
            create_onenet_data_package(data_package, sizeof(data_package), TEMP_READING, HUMI_READING);
            
            mqtt_message.payload = data_package;
            mqtt_message.payloadlen = strlen(data_package);

            printf("トピック: %s\n", PROPERTY_POST_TOPIC);
            printf("ペイロード: %s\n", data_package);
            
            return_value = MQTTPublish(&mqtt_client, PROPERTY_POST_TOPIC, &mqtt_message);
            if (return_value < 0) {
                printf("公開に失敗: %d\n", return_value);
            } else {
                printf("データ公開完了(QOS0)\n");
            }

            last_publish_time = get_system_millis();
        }

        /* Yieldタイムアウト1000ミリ秒、ネットワークパケットの適時処理を確保 */
        return_value = MQTTYield(&mqtt_client, 1000);
        if (return_value < 0) {
            printf("Yieldエラー: %d\n", return_value);
            sleep_ms(1000);
        }
    }

    return 0;
}

5.3. コードの重要設定説明

  • OneNET MQTTパラメータ(核心的な修正):
  • ONENET_PRODUCT_ID:OneNETプラットフォームプロダクト詳細ページから取得したプロダクトIDに置換、文字列形式で文字は変更不可;
  • ONENET_DEVICE_NAME:OneNETプラットフォームで作成したデバイス名に置換、プラットフォームと完全一致し、大文字小文字を区別;
  • ONENET_MQTT_PASSWORD:OneNETデバイス生成ツールからコピーしたトークンに置換、トークンには有効期限があり、期限切れ後は再生成して置換する必要あり;
  • ONENET_MQTT_DOMAIN:「studio-mqtt.heclouds.com」に固定、変更不要;
  • 物モデルトピック:コード内で自動連結され、手動で修正する必要はなく、プロダクトIDとデバイス名を置換すると自動的に有効になります。

ネットワーク設定:ルーターの実際のセグメントに合わせてnetwork_configのip、gw、dnsを修正し、ルーターと同じセグメントであることを確認;DNSはAlibabaクラウドDNS(223.5.5.5)の使用を推奨し、解像成功率を向上させます;

MQTTバージョン:コード内では3.1.1バージョン(MQTTVersion = 4)に設定済みで、変更不要、OneNETプラットフォームと互換性があります;

データアップロード周期:MQTT_PUBLISH_INTERVALを15秒に定義(単位:ミリ秒)、要件に応じて修正可能;

温度・湿度データ:現在は固定値(TEMP_READING、HUMI_READING)ですが、実際の開発ではセンサー収集のリアルタイムデータ(例:DHT11、DS18B20)に置換可能です。

5.4. コンパイルと書き込み手順

  1. 第一步:上記コードをVS Codeプロジェクトのメインファイルにコピーし、WIZnet公式ドライバーライブラリを関連付け(すべてのヘッダファイルが正常に参照され、エラーがないことを確認);

  2. 第二步:コード内のOneNET MQTTパラメータ、ネットワーク設定を修正し、ファイルを保存;

  3. 第三步:プロジェクトをコンパイルし、UF2ファームウェアファイルを生成(コンパイルエラーの場合、優先的にドライバーライブラリの関連付けが正しいか、ヘッダファイル名のスペルミスがないかを確認);

  4. 第四步:前文「ファームウェア書き込み」の手順に従い、UF2ファームウェアをW55RP20開発ボードに書き込み;

  5. 第五步:シリアルデバッグツールを開き、開発ボードの実行ログを確認し、ネットワーク初期化、DNS解像、MQTT接続の成功を確認;

  6. 第六步:OneNETプラットフォームにログインし、デバイス詳細ページに移動し、「物モデルデータ」を確認し、温度・湿度データが定期的にアップロードされていることを確認。

  7. 実行結果とOneNETプラットフォーム検証


6.1. シリアル出力結果

書き込み完了後、開発ボードは自動的に再起動し、シリアルデバッグツールを開くと以下が出力され、ネットワーク初期化、DNS解像、MQTT接続、データアップロードの成功を示します:

---- シリアルポート COM80 を開きました ----

=== W55RP20 OneNET MQTTデモ(最適化版) ===
[PIO SPIクロックスピード : 31.25 MHz] (sys=125.00 MHz)

====================================================================================================
 W5500ネットワーク設定 : 静的

 MAC         : 00:11:22:33:44:55
 IP          : 192.168.1.110
 サブネットマスク : 255.255.255.0
 ゲートウェイ     : 192.168.1.1
 DNS         : 223.5.5.5
====================================================================================================


--- DNS解像フェーズ ---
DNS: DNSサーバーを使用 223.5.5.5
DNS: ドメイン解像中: studio-mqtt.heclouds.com
DNS: 成功!IP: 218.201.45.7
OneNET MQTTブローカーに接続中 218.201.45.7:1883...
ネットワーク接続完了。
OneNETにMQTT接続中...
OneNETプラットフォームにMQTT接続完了!
応答トピックをサブスクライブ: $sys/ZGB6zBD0nS/sensor_device/thing/property/post/reply
トピック: $sys/ZGB6zBD0nS/sensor_device/thing/property/post
ペイロード: {"id":"12345","version":"1.0","params":{"temp":{"value":42.3},"humi":{"value":65.7}}}
データ公開完了(QOS0)
トピック: $sys/ZGB6zBD0nS/sensor_device/thing/property/post
ペイロード: {"id":"22346","version":"1.0","params":{"temp":{"value":42.3},"humi":{"value":65.7}}}
データ公開完了(QOS0)
トピック: $sys/ZGB6zBD0nS/sensor_device/thing/property/post
ペイロード: {"id":"32347","version":"1.0","params":{"temp":{"value":42.3},"humi":{"value":65.7}}}
データ公開完了(QOS0)

説明:「DNSリトライ」と表示される場合は正常現象で、最大3回リトライします;常に解像または接続に失敗する場合は、「一般的な問題トラブルシューティング」の部分を参照してください。

6.2. OneNETプラットフォームデータ検証

OneNET IoTプラットフォームにログインし、デバイス詳細ページに移動し、データアップロード状況を確認します。手順は以下の通りです:

  1. OneNETプラットフォーム「IoTコアキット」→「デバイス管理」に移動し、登録済みデバイスを見つけ、デバイス名をクリックして詳細ページに入る;
  2. 「物モデルデータ」→「リアルタイムデータ」をクリックすると、温度・湿度データが定期的に更新されているのがわかります(15秒ごと)、シリアル印刷のペイロードデータと一致;
  3. 「メッセージログ」をクリックすると、デバイス接続、データアップロード、応答メッセージの詳細ログを確認でき、データアップロードに失敗した場合は、ここでエラー原因を確認;
  4. (任意)「データ可視化」をクリックし、ダッシュボードを作成すると、温度・湿度データの変化傾向を直感的に表示できます。

これで、W55RP20のC言語版MQTTプロトコルとOneNET IoTプラットフォーム連携実践が完了しました!

  1. 一般的な問題のトラブルシューティング

OneNET連携の特殊性を考慮し、以下に高頻度問題とトラブルシューティング手順を示します。接続パラメータと形式の問題を優先的に確認してください:

問題現象 トラブルシューティング手順
1. シリアルに印刷がないまたは印刷が乱れている 1. シリアルパラメータ設定が正しいことを確認(ボーレート115200、パリティなし);2. ファームウェアがW55RP20専用C言語ファームウェアであること;3. ファームウェアを再書き込みし、USBデータケーブルがデータ転送をサポートしていることを確認。
2. DNS解像に失敗し、エラーが表示される 1. DNSアドレスが正しいことを確認(AlibabaクラウドDNS:223.5.5.5の使用を推奨);2. ルーターが正常に接続できることを確認;3. OneNET MQTTサーバードメインのスペルが正しいことを確認;4. ルーターと開発ボードを再起動。
3. MQTT接続に失敗し、戻り値が負数 1. TCP接続が成功していることを確認(シリアル印刷の「ネットワーク接続完了」メッセージを確認);2. MQTTバージョンが3.1.1であることを確認(コード内で設定済み、変更不要);3. プロダクトID、デバイス名、トークンがOneNETプラットフォームと一致し、スペルミスがないことを確認;4. トークンが期限切れでないことを確認、期限切れの場合は再生成が必要。
4. OneNETに接続できるが、データをアップロードできない 1. 報告トピック形式が正しいことを確認し、プロダクトIDとデバイス名が置換されていること;2. JSONペイロード形式が正しいことを確認し、構文エラーがないこと(シリアル印刷のペイロードをJSON検証ツールにコピーして検証可能);3. OneNET物モデルでの属性名がペイロードのフィールド名と一致すること(temp、humi、大文字小文字を区別)。
  1. W55RP20の核心的優位性比較

W55RP20の価値をより直感的に理解するため、現在主流の3つの組み込みイーサネットソリューションを比較します:

比較項目 W55RP20統合ソリューション 外部PHYチップソリューション 外部シリアル変イーサネットモジュールソリューション
BOMコスト 低(単チップ) 中高(MCU + モジュール + 周辺デバイス)
PCB面積 小(イーサネット回路のみ) 大(チップと配線スペースの確保が必要)
開発難易度 低(公式ドライバーライブラリ、1行でネットワーク接続) 中高(プロトコルスタックのデバッグ、ドライバ作成)
ネットワーク安定性 極めて高い(WIZnetが25年間専門でハードウェアTCP/IPプロトコルスタックを開発) 不安定(開発者に高い要求、プロトコルスタックとネットワーク開発の知識が必要で、安定性をデバッグする必要がある) 不安定(開発会社の能力レベルによる)
CPUリソース使用率 0%(プロトコルスタックのネットワーク処理は完全にハードウェアで実行) 50%以上(プロトコルスタックは完全にMCU上で実行され、関連リソースを消費) 0%
ハードウェアソケット数 8つの独立したハードウェアソケット MCUの能力に依存、理論的に多路拡張をサポート 一般的に単透伝
ネットワークスループット 最大15Mbps MCUの能力に依存 約3-5Mbps
インターフェース易用性 単チップ統合 MCUがMII/RMIIなどのインターフェースを必要とする TTLインターフェース
デプロイ難易度 低(公式C言語ドライバーライブラリが成熟し、アプリケーション層プロトコルが適応され、柔軟にデプロイ可能) 高(アプリケーション層プロトコルはオープンソースライブラリの手動移植が必要) モジュール統合状況に依存、統合されていない機能は自己パッケージング・アンパッケージングが必要
  1. 典型的なアプリケーションシナリオ

W55RP20チップはイーサネット機能を統合しており、その産業レベルの安定性により、以下のアプリケーションシナリオに非常に適しています:

  1. 産業データ収集ゲートウェイ:現場配置を簡素化し、センサーデータの安定したアップロードを実現

  2. リモートモニタリングターミナル:工場、サーバールーム、変電所などの環境のデバイス状態リモートモニタリングに使用

  3. シリアル変イーサネットデバイス:従来のRS232/RS485シリアルデバイスを迅速にイーサネットデバイスにアップグレード

  4. スマートビルディングノード:照明、空調、ドアロックなどのビルディングデバイスのネットワーク制御に使用

  5. 産業PLC拡張モジュール:PLCにイーサネット通信能力を追加し、リモートプログラミングとデータ収集を実現

  6. シリーズ予告とリソース取得


10.1. シリーズ予告

次回のチュートリアルでは、W55RP20 C開発におけるMQTT+ThingSpeakによるデータアップロードを実装します。接続確立、ハートビート維持、異常再接続などの核心メカニズムを理解し、組み込みイーサネット通信の基礎能力を習得します。

10.2. リソース取得

本稿の完全コード:WIZnet公式Giteeリポジトリ W55RP20チップマニュアル:WIZnet公式資料サイト

タグ: W55RP20 MQTT OneNET IoT C言語

7月2日 00:51 投稿