Main Content

このページは機械翻訳を使用して翻訳されました。元の英語を参照するには、ここをクリックします。

セキュアな MQTT パブリッシュおよびサブスクライブを使用したリモート センサー制御

この例では、 ThingSpeakで MQTT パブリッシュおよびサブスクライブ アーキテクチャを使用する方法を示します。MQTT プロトコルは、オーバーヘッドの低いデバイス メッセージング システムです。MQTT を使用して、制御チャネルのフィールド 1 をサブスクライブします。制御チャネルを更新すると、ポストされた値がデバイスに送信されます。サーボは指定した角度まで回転します。デバイスはネットワーク強度を測定し、ストレージチャネルを更新します。コード内のコメントは、この例を安全でない接続に適応させる方法を示しています。

サポートされるハードウェア

  • ESP8266、NodeMCU、WeMOS

  • Arduino MKR1000

  • ワイヤレスネットワーク接続を備えたArduino Uno、Mega、Due、またはLeonardo

  • Particle Photon (わずかなコードと回路図の調整あり)

この例は、いくつかの追加コンポーネント、つまり単一のサーボ モーターのみで動作するように設計されています。Wi-Fi 強度の測定には、オンボード Wi-Fi アンテナを使用します。

これらの画像は、ストレージチャネルからのサンプルチャネル出力を示しています。フィールド 1 には制御チャネルから設定されたサーボ モーターの角度が格納され、フィールド 2 には測定された Wi-Fi 強度の値が表示されます。

データ分析により、WeMOS ハードウェアの方向が測定された信号強度に方向性の影響を与えることがわかりました。

≪前提条件≫

1) Collect Data in a New Channelに示すように、サブスクライブ コントロールのThingSpeakチャネルを作成します。サブスクライブチャネルはサーボ モーターの角度を保持します。サーボ モーターの角度が更新されると、加入デバイスはその角度を MQTT メッセージとして受信します。デバイスはサーボ角度を設定し、その角度での新しいワイヤレス ネットワークの強度を測定します。

2) パブリッシュされたデータ用に別のThingSpeakチャネルを作成します。パブリッシュチャネルは、設定された角度と信号強度データを記録します。

3) 「チャネル設定」「」ビューで、パブリッシュチャネルのフィールド 1 と 2 を有効にします。フィールドを区別するには、各フィールドにわかりやすい名前を付けます。

4) 「チャネル設定」ビューの「API キー」タブにある読み取りおよび書き込み API キーをメモします (画像の丸で囲んだ部分)。

5) ページ上部の「デバイス」 > 「MQTT」をクリックし、「新しいデバイスの追加」をクリックして、MQTT デバイスを作成します。デバイスをセットアップするときに、両方のチャネルのパブリッシュとサブスクライブを承認します。詳細については、ThingSpeak MQTT デバイスの作成を参照してください。

6) 新しいデバイスを追加するときに、Download Credentials > Arduino (mqtt_secrets.h) をクリックします。このダウンロードしたシークレット ファイルは、以下の「コード」セクションでアクセスできるように保管してください。

≪必要なハードウェア≫

  • WeMOS D1 Mini、または使用するライブラリが変更された次のデバイスのいずれか:NodeMCU、ESP8266-01、ESP8266-04、ESP8266-12、ESP8266-12E、Arduino® MKR1000、またはイーサネットまたはワイヤレス ネットワーク接続を備えたその他の Arduino

  • サーボモーター(例:Futaba S3003)

  • ジャンパー ワイヤー (3 本以上)

  • USB ケーブル

回路図と接続

1) WeMOS D1 Mini の D5 をサーボの信号線に接続します。

2) サーボのアース線をWeMOS基板上のアースに接続します。

3) サーボ電源を 3.3 V に接続します。5 V を直接使用すると、場合によっては USB 電源制限を超える可能性があります。

Arduinoをプログラムする

Arduino IDE を使用してデバイスをプログラムします。最新の Arduino IDE hereをダウンロードできます。

1) ESP8266 ボード パッケージを追加します。

a. File > Preferences で、Additional ボード マネージャー URLshttps://arduino.esp8266.com/stable/package_esp8266com_index.jsonを入力します。

b. ツール > ボード > ボード マネージャーを選択します。検索バーに ESP8266 と入力し、パッケージをインストールします。

2) 許可されるパケット サイズを変更します。

a. pub sub ライブラリ ヘッダー ファイル (通常はDocuments\Arduino\libraries\PubSubClient\src )のあるフォルダーに移動します。

b. PubSubClient.h to cを編集して、最大パケット サイズを 4096 に変更します。完了すると、次の行が表示されます。

#define MQTT_MAX_PACKET_SIZE 4096

3) アプリケーションを作成します。

a. Arduino IDE で新しいウィンドウを開き、ファイルを保存します。

b. 「コード」セクションに提供されているコードを追加します。

c. コード内のワイヤレス ネットワーク情報とチャネルID を必ず編集してください。

4) ライブラリとシークレット ファイルをスケッチに追加します。

a. まだ存在していない場合は、スケッチ > ライブラリを含める > ライブラリの管理 を選択して、ライブラリ マネージャーに次のライブラリを追加します。各ライブラリの名前を検索し、「インストール」を選択します。

  • PubSubClient

  • ESP8266Wifi

  • servo

b. mqtt_secrets.hファイルを追加します。

デバイスをテストする

プログラムを正常にアップロードすると、シリアル モニターを使用して出力を監視できるようになります。0 ~ 175 の範囲の値をThingSpeakコントロールチャネルにアップロードします。「API キー」タブからGET要求の形式をコピーするか、書き込み API キーを使用してこのテキストを変更できます。各 URL をブラウザのアドレス バーに直接入力し、[書き込み API キー] をチャネルの書き込み API キーに変更します。

https://api.thingspeak.com/update?api_key=YOUR_WRITE_API_KEY&field1=ANGLE_VALUE

デバイスは、サブスクライブチャネルに投稿するたびに、角度と Wi-Fi 信号強度をストレージチャネルに公開します。角度の値が 0 ~ 175 の範囲内であることを確認してください。

コード

1) 必要なライブラリを組み込み、データ フィールドを定義します。

#include <PubSubClient.h>
#include <WiFiClientSecure.h>                         // Needed only if using secure connection.
#include <ESP8266WiFi.h>
#include <Servo.h> 
#include "mqtt_secrets.h"
#define ANGLE_FIELD 0   
#define DATA_FIELD 1                                  // Data field to post the signal strength to.

2) 変数を定義して初期化します。ワイヤレス ネットワーク情報、チャネルID、および認証情報を必ず編集してください。チャネルのメイン ページの上部でチャネルID を見つけます。

char ssid[] = "YOUR_SSID";                   // Change to your network SSID (name).
char pass[] = "YOUR_WIFI_PASSWORD";          // Change to your network password.
const char* server = "mqtt3.thingspeak.com";
char mqttUserName[] = SECRET_MQTT_USERNAME;  // Change to your MQTT device username.    
char mqttPass[] = SECRET_MQTT_PASSWORD;      // Change to your MQTT device password.
char clientID[] = SECRET_MQTT_CLIENT_ID;     // Change to your MQTT device clientID.
long readChannelID = 85;
long writeChannelID = 86;

// Here's how to get ThingSpeak server fingerprint: https://www.a2hosting.com/kb/security/ssl/a2-hostings-ssl-certificate-fingerprints
const char* thingspeak_server_fingerprint = "27 18 92 dd a4 26 c3 07 09 b9 7a e6 c5 21 b9 5b 48 f7 16 e1";

// WiFiClient client;                                 // Initialize the Wi-Fi client library. Uncomment for nonsecure connection.
WiFiClientSecure client;                              // Uncomment for secure connection.  
PubSubClient mqttClient( client );                    // Initialize the PuBSubClient library.
Servo myservo;  // Create servo object to control a servo .

int fieldsToPublish[8]={1,1,0,0,0,0,0,0};             // Change to allow multiple fields.
float dataToPublish[8];                               // Holds your field data.
int changeFlag=0;                                     // Let the main loop know there is new data to set.
int servo_pos=0;                                      // Servo position

3) このコードで関数プロトタイプを定義します。

//  
// Prototypes
//

// Handle messages from MQTT subscription.
void mqttSubscriptionCallback(char* topic, byte* payload, unsigned int length);  

// Generate a unique client ID and connect to MQTT broker.
void mqttConnect();  

// Subscribe to a field or feed from a ThingSpeak channel.
int mqttSubscribe( long subChannelID,int field, int unSub);

// Publish messages to a channel feed.

// Connect to a given Wi-Fi SSID.
int connectWifi();

// Measure the Wi-Fi signal strength.
void updateRSSIValue();

4) setupルーチンで、入力および出力のピンを初期化し、シリアル モニターを開始し、MQTT クライアントを初期化します。

void setup() {
Serial.begin( 115200 );
Serial.println( "Start" );
int status = WL_IDLE_STATUS; // Set temporary Wi-Fi status.
       
    connectWifi();                                        // Connect to Wi-Fi network.
    // mqttClient.setServer( server, 1883 );              // Set the MQTT broker details, nonsecure port. Uncomment for nonsecure connection.
    mqttClient.setServer( server, 8883 );                 // Set the MQTT broker details, secure port. Uncomment for secure connection.
    mqttClient.setCallback( mqttSubscriptionCallback );   // Set the MQTT message handler function.
    myservo.attach(14);                                   // Attach the servo on GIO2 to the servo object. 
    myservo.write(90);                                    // Start in the middle.
}

5) メインループが実行されるたびに、MQTT サブスクリプションからのデータが処理可能かどうかを確認します。次に、データに合わせてサーボ位置を設定します。ワイヤレスおよび MQTT クライアントがアクティブであり、クライアント サーバーへの接続が維持されていることを確認します。

void loop() {
    
    if (WiFi.status() != WL_CONNECTED) {
        connectWifi();
    }
    
    if (!mqttClient.connected())
    {
       
       mqttConnect(); // Connect if MQTT client is not connected.
        
         if(mqttSubscribe( readChannelID,1,0 )==1 ){
                Serial.println( " Subscribed " );
            }
    }
    
    mqttClient.loop(); // Call the loop to maintain connection to the server.                         

    if ((servo_pos>175)||(servo_pos<0)){
    servo_pos=0;
    }
   
    if (changeFlag){
      
        changeFlag=0;
        myservo.write(servo_pos);
        dataToPublish[ANGLE_FIELD]=servo_pos;
        delay(1100);                       // Wait for ThingSpeak to publish.
        Serial.println( "Servo value " + String( servo_pos ) );
        mqttPublish( writeChannelID, dataToPublish, fieldsToPublish );
    }
    
    delay(1);
}

6) mqttSubscriptionCallback関数を使用して、受信 MQTT メッセージを処理します。コールバックの代わりにメインループが処理ステップを実行すると、プログラムはよりスムーズに実行されます。この関数では、フラグを使用してメイン ループに変更を加えます。

/**
 * Process messages received from subscribed channel via MQTT broker.
 *   topic - Subscription topic for message.
 *   payload - Field to subscribe to. Value 0 means subscribe to all fields.
 *   mesLength - Message length.
 */

void mqttSubscriptionCallback( char* topic, byte* payload, unsigned int mesLength ) {
    
    char p[mesLength + 1];
    memcpy( p, payload, mesLength );
    p[mesLength] = NULL;
    Serial.print( "Answer: " );
    Serial.println( String(p) );
    servo_pos=atoi( p );
    changeFlag=1;
}

7) MQTTConnect関数を使用して、MQTT への接続をセットアップおよび維持します。

void mqttConnect()
{
    // Loop until connected.
    while ( !mqttClient.connected() )
    {
      Serial.println(String( mqttUserName)+ " , " + mqttPass + " , " + clientID);
   
        // Connect to the MQTT broker.
        Serial.print( "Attempting MQTT connection..." );
        if ( mqttClient.connect( clientID, mqttUserName, mqttPass ) )
        {
            Serial.println( "Connected with Client ID:  " + String( clientID ) + " User "+ String( mqttUserName ) + " Pwd "+String( mqttPass ) );
           
        } else
        {
            Serial.print( "failed, rc = " );
            // See https://pubsubclient.knolleary.net/api.html#state for the failure code explanation.
            Serial.print( mqttClient.state() );
            Serial.println( " Will try again in 5 seconds" );
            delay( 5000 );
        }
    }
}

8) mqttSubscribeを使用して LED 制御フィールドから更新を受信します。この例ではフィールドをサブスクライブしますが、この関数を使用してチャネルフィード全体をサブスクライブすることもできます。field = 0を使用して関数を呼び出して、フィード全体をサブスクライブします。

/**
 * Subscribe to fields of a channel.
 *   subChannelID - Channel to subscribe to.
 *   field - Field to subscribe to. Value 0 means subscribe to all fields.
 *   readKey - Read API key for the subscribe channel.
 *   unSub - Set to 1 for unsubscribe.
 */
 
int mqttSubscribe( long subChannelID, int field, int unsubSub ){
    String myTopic;
    
    // There is no field zero, so if field 0 is sent to subscribe to, then subscribe to the whole channel feed.
    if (field==0){
        myTopic="channels/"+String( subChannelID )+"/subscribe";
    }
    else{
        myTopic="channels/"+String( subChannelID )+"/subscribe/fields/field"+String( field );
    }
    
    Serial.println( "Subscribing to " +myTopic );
    Serial.println( "State= " + String( mqttClient.state() ) );

    if ( unsubSub==1 ){
        return mqttClient.unsubscribe(myTopic.c_str());
    }
    return mqttClient.subscribe( myTopic.c_str() ,0 );
}

9) mqttUnsubscribe関数はコードでは使用されていませんが、サブスクリプションを終了するために使用できます。

/**
 * Unsubscribe channel
 *   subChannelID - Channel to unsubscribe from.
 *   field - Field to unsubscribe subscribe from. The value 0 means subscribe to all fields.
 *   readKey - Read API key for the subscribe channel.
 */

int mqttUnSubscribe(long subChannelID,int field,char* readKey){
    String myTopic;
    
    if (field==0){
         myTopic="channels/"+String( subChannelID )+"/subscribe";
    }
    else{
        myTopic="channels/"+String( subChannelID )+"/subscribe/fields/field"+String( field );
    }
    return mqttClient.unsubscribe( myTopic.c_str() );   
}

10) mqttPublish関数を使用して、角度と Wi-Fi RSSI データをThingSpeakチャネルに送信します。

/**
 * Publish to a channel
 *   pubChannelID - Channel to publish to.
 *   pubWriteAPIKey - Write API key for the channel to publish to.
 *   dataArray - Binary array indicating which fields to publish to, starting with field 1.
 *   fieldArray - Array of values to publish, starting with field 1.
 */

void mqttPublish(long pubChannelID, float dataArray[], int fieldArray[]) {
    int index=0;
    String dataString="";
    
    updateRSSIValue();  // Make sure the stored value is updated.
    
    // 
    while (index<8){
        
        // Look at the field array to build the posting string to send to ThingSpeak.
        if (fieldArray[ index ]>0){
          
            dataString+="&field" + String( index+1 ) + "="+String( dataArray [ index ] );
        }
        index++;
    }
    
    Serial.println( dataString );
    
    // Create a topic string and publish data to ThingSpeak channel feed.
     String topicString ="channels/" + String( pubChannelID ) + "/publish";
    mqttClient.publish( topicString.c_str(), dataString.c_str() );
    Serial.println( "Published to channel " + String( pubChannelID ) );
}

11) connectWiFi関数を使用してデバイスをワイヤレス ネットワークに接続します。

int connectWifi()
{
    while ( WiFi.status() != WL_CONNECTED ) {
        WiFi.begin( ssid, pass );
        delay( 8500 );
        Serial.println( "Connecting to Wi-Fi" ); 
    }
    Serial.println( "Connected" );
    client.setFingerprint(thingspeak_server_fingerprint);  // Comment this line if using nonsecure connection.
}

12) updateRSSIValue関数を使用して、現在接続しているネットワークの信号強度を読み取ります。

void updateRSSIValue(){

   long rssi = WiFi.RSSI();  
   Serial.print( "RSSI:" );
   Serial.println(rssi);
   dataToPublish[ DATA_FIELD ]=float( rssi );

}

参考

|

関連するトピック