Documentation

This is machine translation

Translated by Microsoft
Mouseover text to see original. Click the button below to return to the English version of the page.

Note: This page has been translated by MathWorks. Click here to see
To view all translated materials including this page, select Country from the country navigator on the bottom of this page.

Remote Sensor Control Using MQTT Publish and Subscribe

The MQTT protocol is a low-overhead device messaging system. This example demonstrates the MQTT publish / subscribe architecture in ThingSpeak™. Using MQTT, subscribe to field 1 of a control channel. When you update the control channel, the posted value is sent to your device. The servo rotates to that angle. Then the device measures the network strength and updates the storage channel.

Supported Hardware

  • ESP8266, NODEMCU, WEMOS

  • Arduino MKR1000

  • Arduino Uno, Mega, Due, or Leonardo with wireless network connection

  • Particle Photon (with some code and schematic adjustments)

The example is designed to work with few extra components, namely a single servo motor. You use the on-board WiFi antenna to make WiFi strength measurements.

These images show sample channel output from the storage channel. Field 1 stores the angle of the servo motor that is set from the control channel, and field 2 shows the measured WiFi strength value.

Analysis of the data shows a directional effect of the orientation of the Wemos hardware on the measured signal strength.

Prerequisites

  1. Create a ThingSpeak Channel as the subscribe control. The subscribe channel holds the angle for the servo motor. When the servo motor angle is updated, the subscribed device receives the angle as an MQTT message. The device sets the servo angle and measures the new wireless network strength at that angle.

  2. Create another ThingSpeak Channel for the published data. The publish channel contains the angle written and the signal strength data.

  3. In the Channel Settings view, enable fields 1 and 2 for the publish channel. You can provide an informative title for each field as well.

  4. Note the appropriate read and write API keys from the API Keys tab in the Channel Settings view (circled in the image).

Required Hardware

  • WEMOS d1 mini or similar device

  • Alternative devices (require some change in libraries used): NODEMCU, ESP8266-01, ESP8266-04, ESP8266-12, ESP8266-12E, Arduino® MKR1000, or other Arduino with Ethernet or wireless network connection

  • Servo Motor (for example, Futaba S3003)

  • Jumper wires (at least 3)

  • USB cable

Schematic and Connections

  1. Connect D5 on the WEMOS D1 Mini the signal line of the servo.

  2. Connect the ground wire of the servo to the ground on the WEMOS board.

  3. Connect the servo power to 3.3 V. Using 5 V directly uses too much power.

Programming Your Arduino

Use Arduino IDE to program your device.

  1. Download the latest Arduino IDE.

  2. Add the PubSubClient to the library manager, if it is not already there.

    Select Sketch > Include Library > Manage Libraries. Search for PubSubClient. Select the PubSubClient Library and select Install.

  3. Add the PubSubClient to the sketch.

    Select Sketch > Include Library > Manage Libraries. Select PubSubClient to add it to your sketch.

  4. Add the ESP8266Wifi library in the library manager and sketch, if it is not already there. Follow the same steps as for the PubSubCLient library.

  5. Add the servo library to the library manager and sketch, if it is not already there. Follow the same steps as for the libraries.

  6. Create the application:

    Open a new window in the Arduino IDE, and save the file. Add the code provided here. Be sure to adjust the wireless network information, channel IDs, and API keys.

  7. After you successfully upload your program, you can monitor the output using the serial monitor. Upload a value to your ThingSpeak control channel in the range from 0 through 175. You can copy the GET request format from the API Keys tab or modify this text with your write API key. Enter each URL directly to the address bar of your browser, changing the text in bold:

    https://api.thingspeak.com/update?api_key=YOUR WRITE API KEY&field1=<angle value>
    

    The device publishes the angle and the WiFi signal strength to the storage channel any time a post is made to the subscribe channel. Be sure that your angle is in the range of 0 to 175.

     Full Code

    1. Begin by including libraries and initializing control pins.

      #include <PubSubClient.h>
      #include <ESP8266WiFi.h>
      #include <Servo.h> 
      #define ANGLE_FIELD 0   
      #define DATA_FIELD 1                                 // Data field to post the signal strength to.
      
    2. Define and initialize variables. Be sure to adjust the wireless network information, channel ID, and API keys. You can find your channel ID on the top of the main page for your channel.

      char ssid[] = "ssid";                               //  Change this to your network SSID (name).
      char pass[] = "pass!";                        // Change this your network password
      const char* server = "mqtt.thingspeak.com";
      char mqttUserName[] = "Servo_control";                 // Can be any name.
      char mqttPass[] = "MMMMMMMMMMMMMMMM";                // Change to your MQTT API Key from Account > MyProfile.
      long readChannelID=0000;
      char readAPIKey[]="XXXXXXXXXXXXXXXX";
      long writeChannelID=528510;
      char writeAPIKey[]   = "YYYYYYYYYYYYYYYY";
      
      WiFiClient client;                                   // Initialize the Wifi client library.
      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 ther is new data to set.
      int servo_pos=0;                                    // LED On / Off.
         
    3. Define function prototypes used in this code.

      //  
      // Prototypes
      //
      
      // Handle messages from MQTT subscription.
      int mqttSubscriptionCallback(char* topic, byte* payload, unsigned int length);  
      
      // Genreate 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,char* readKey, int unSub);
      
      // Publish messages to a channel feed.
      void mqttPublish(long pubChannelID, char* pubWriteAPIKey, float dataArray[], int fieldArray[]);
      
      // Connect to a given WiFi SSID
      int connectWifi();
      
      // Measure the WiFi signal strength.
      void updateRSSIValue();
      
      // Build a random client ID for MQTT connection
      void getID(char clientID[], int idLength);
    4. Initialize pins for input and output, start the serial monitor, and initialize the MQTT client in the setup routine.

          Serial.begin( 115200 );
          Serial.println( "Start" );
          int status = WL_IDLE_STATUS; // Set a temporary WiFi status.
             
          connectWifi();  // Connect to WiFi network
          mqttClient.setServer( server, 1883 ); // Set the MQTT broker details.
          mqttClient.setCallback( mqttSubscriptionCallback );   // Set the MQTT message handler function.
          myservo.attach(14);  // Attaches the servo on GIO2 to the servo object. 
          myservo.write(90);  // Start in the middle.
    5. Each time the main loop executes, check to see if data from the MQTT subscription is available to be processed. Then set the servo position to match the data. Make sure the wireless and MQTT clients are active and maintain a connection to the client server.

      void loop() {
          
          if (WiFi.status() != WL_CONNECTED) {
              connectWifi();
          }
          
          if (!mqttClient.connected())
          {
             
             mqttConnect(); // Connect if MQTT client if not connected.
              
               if(mqttSubscribe( readChannelID,1,readAPIKey,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 publish.
              Serial.println( "Servo value " + String( servo_pos ) );
              mqttPublish( writeChannelID, writeAPIKey, dataToPublish, fieldsToPublish );
          }
          
          delay(1);
      }
    6. Use the mqttSubscriptionCallback function to handle incoming MQTT messages. The program runs smoother if processing steps are performed in the main loop instead of the callback. In this function, use flags to cause changes in the main loop.

      /**
       * Process messaged 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.
       */
      
      int 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. Use the MQTTConnect function to set up and maintain a connection to the MQTT. This function generates a random client ID for connecting to the ThingSpeak MQTT server.

      void mqttConnect()
      {
          char clientID[ 9 ];
          
          // Loop until connected.
          while ( !mqttClient.connected() )
          {
      
              getID(clientID,8);
             
              // 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 http://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. Use getID to generate a random client ID for connection to the MQTT server.

      /**
       * Build a random client ID
       *   clientID - char array for output
       *   idLength - length of clientID (actual length will be one longer for NULL)
       */
      
      void getID(char clientID[], int idLength){
      static const char alphanum[] ="0123456789"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      "abcdefghijklmnopqrstuvwxyz";                        // For random generation of client ID.
      
          // Generate ClientID
          for (int i = 0; i < idLength ; i++) {
              clientID[ i ] = alphanum[ random( 51 ) ];
          }
          clientID[ idLength ] = '\0';
          
      }
    9. Use mqttSubscribe to receive updates from the LED control field. In this example, you subscribe to a field, but you can also use this function to subscribe to the whole channel feed. Call the function with field = 0 to subscribe to the whole feed.

      /**
       * 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, char* readKey, 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/json/"+String( readKey );
          }
          else{
              myTopic="channels/"+String( subChannelID )+"/subscribe/fields/field"+String( field )+"/"+String( readKey );
          }
          
          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 );
          
      }
      
    10. The mqttUnsubscribe function is not used in the code, but you can use it to end a subscription.

      /**
       * Unsubscribe channel
       *   subChannelID - channel to unsubscribe from
       *   field - field to unsubscribe subscribe from. 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/json/"+String( readKey );
          }
          else{
              myTopic="channels/"+String( subChannelID )+"/subscribe/fields/field"+String( field )+"/"+String( readKey );
          }
          return mqttClient.unsubscribe( myTopic.c_str() );
          
      }
    11. Use the mqttPublish function to send your WiFi RSSI data to a ThingSpeak channel.

      /**
       * Publish to a channel
       *   pubChannelID - channel to publish to
       *   pubWriteAPIKey - Write API Key for the channel to publish to.
       *   dataArray - bianry array inficating which fields to publish to, starting with field 1.
       *   fieldArray - array of values to publish, starting with field 1.
       */
      
      void mqttPublish(long pubChannelID, char* pubWriteAPIKey, 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/"+String( pubWriteAPIKey );
          mqttClient.publish( topicString.c_str(), dataString.c_str() );
          Serial.println( "Pubished to channel " + String( pubChannelID ) );
      }
    12. Connect your device to a wireless network using the connectWiFi function.

      int connectWifi()
      {
          
          while ( WiFi.status() != WL_CONNECTED ) {
              WiFi.begin( ssid, pass );
              delay( 2500 );
              Serial.println( "Connecting to WiFi" ); 
          }
          Serial.println( "Connected" );
      }
    13. Use the updateRSSIValue function to read the signal strength for the network that you are presently connected to.

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

See Also

|

Related Topics

External Websites