Controllo remoto dei sensori tramite pubblicazione e sottoscrizione MQTT sicure
Questo esempio mostra come utilizzare l'architettura di pubblicazione e sottoscrizione MQTT in ThingSpeak. Il protocollo MQTT è un sistema di messaggistica per dispositivi a basso overhead. Utilizzando MQTT, iscriviti al campo 1 di un canale di controllo. Quando aggiorni il canale di controllo, il valore pubblicato viene inviato al tuo dispositivo. Il servo ruota fino all'angolo specificato. Il dispositivo misura la potenza della rete e aggiorna il canale di archiviazione. I commenti nel codice indicano come adattare questo esempio per una connessione non sicura.
Hardware supportato
ESP8266, NodeMCU, WeMOS
Arduino MKR1000
Arduino Uno, Mega, Due o Leonardo con connessione di rete wireless
Particella Fotone (con lievi modifiche al codice e allo schema)
L'esempio è progettato per funzionare con solo pochi componenti aggiuntivi, vale a dire un singolo servomotore. Per effettuare misurazioni della potenza del segnale Wi-Fi, è possibile utilizzare l'antenna Wi-Fi integrata.

Queste immagini mostrano un esempio di output del canale dal canale di archiviazione. Il campo 1 memorizza l'angolo del servomotore impostato dal canale di controllo, mentre il campo 2 mostra il valore di potenza Wi-Fi misurato.

L'analisi dei dati mostra che l'orientamento dell'hardware WeMOS ha un effetto direzionale sulla potenza del segnale misurata.

Prerequisiti
1) Creare un canale ThingSpeak per il controllo di sottoscrizione come mostrato in Raccogliere dati in un nuovo canale. Il canale di sottoscrizione contiene l'angolo per il servomotore. Quando l'angolo del servomotore viene aggiornato, il dispositivo sottoscritto riceve l'angolo come messaggio MQTT. Il dispositivo imposta l'angolo del servo e misura la potenza della nuova rete wireless a quell'angolo.
2) Creare un altro canale ThingSpeak per i dati pubblicati. Il canale di pubblicazione registra l'angolo impostato e i dati sulla potenza del segnale.
3) Nella vista Impostazioni canale , abilita i campi 1 e 2 per il canale di pubblicazione. Per distinguere i campi, assegnare a ciascun campo un nome descrittivo.

4) Notare le chiavi API di lettura e scrittura nella scheda Chiavi API nella vista Impostazioni canale (evidenziate nell'immagine).
5) Crea un dispositivo MQTT cliccando su Dispositivi > MQTT nella parte superiore della pagina, quindi su Aggiungi un nuovo dispositivo. Quando configuri il dispositivo, autorizza entrambi i canali per la pubblicazione e l'iscrizione. Per i dettagli, vedere Crea un dispositivo MQTT ThingSpeak.
6) Durante l'aggiunta del nuovo dispositivo, fare clic su Scarica credenziali > Arduino (mqtt_secrets.h). Conservare il file dei segreti scaricato per accedervi nella sezione Codice qui sotto.
Hardware richiesto
WeMOS D1 Mini o uno dei seguenti dispositivi con modifiche alle librerie utilizzate: NodeMCU, ESP8266-01, ESP8266-04, ESP8266-12, ESP8266-12E, Arduino® MKR1000 o altri Arduino con connessione di rete Ethernet o wireless
Servomotore (ad esempio, Futaba S3003)
Cavi jumper (almeno 3)
Cavo USB
Schema e connessioni
1) Collegare D5 sul WeMOS D1 Mini alla linea del segnale del servo.
2) Collegare il filo di terra del servo alla terra della scheda WeMOS.
3) Collegare l'alimentazione del servo a 3,3 V. In alcuni casi, l'utilizzo diretto di 5 V può sovraccaricare il limite di alimentazione USB.

Programma il tuo Arduino
Utilizza l'IDE Arduino per programmare il tuo dispositivo. Puoi scaricare l'ultimo IDE Arduino qui.
1) Aggiungere il pacchetto della scheda ESP8266:
UN. In File > Preferenze, inserisci https://arduino.esp8266.com/stable/package_esp8266com_index.json in URL aggiuntivi del gestore bacheche.
B. Seleziona Strumenti > Bacheche > Gestore bacheche. Inserisci ESP8266 nella barra di ricerca e installa il pacchetto.
2) Modificare la dimensione consentita del pacchetto.
UN. Passare alla cartella contenente il file di intestazione della sottolibreria pub, in genere Documents\Arduino\libraries\PubSubClient\src.
B. Modifica PubSubClient.h to c modificando la dimensione massima del pacchetto a 4096. Una volta completata, la riga dovrebbe essere la seguente:
#define MQTT_MAX_PACKET_SIZE 4096
3) Creare l'applicazione:
UN. Apri una nuova finestra nell'IDE di Arduino e salva il file.
B. Aggiungere il codice fornito nella sezione Codice.
C. Assicurati di modificare le informazioni sulla rete wireless e gli ID dei canali nel codice.
4) Aggiungere librerie e file segreti allo sketch:
UN. Se non sono già presenti, aggiungere le seguenti librerie al gestore librerie selezionando Schizzo > Includi libreria > Gestisci librerie. Per ogni libreria cerca il suo nome e seleziona Installa.
PubSubClientESP8266Wifiservo
B. Aggiungere il file mqtt_secrets.h.
Prova il tuo dispositivo
Dopo aver caricato correttamente il programma, è possibile monitorarne l'output utilizzando il monitor seriale. Carica un valore sul tuo canale di controllo ThingSpeak compreso tra 0 e 175. Puoi copiare il formato della richiesta GET dalla scheda Chiavi API oppure modificare questo testo con la tua chiave API di scrittura. Inserisci ciascun URL direttamente nella barra degli indirizzi del tuo browser, modificando la TUA CHIAVE API DI SCRITTURA nella chiave API di scrittura per il tuo canale.
https://api.thingspeak.com/update?api_key=YOUR_WRITE_API_KEY&field1=ANGLE_VALUE
Ogni volta che pubblichi post sul canale di iscrizione, il dispositivo pubblica l'angolazione e la potenza del segnale Wi-Fi sul canale di archiviazione. Assicurati che il valore dell'angolo sia compreso tra 0 e 175.
Codice
1) Includere le librerie richieste e definire i campi dati:
#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) Definire e inizializzare le variabili. Assicurati di modificare le informazioni sulla rete wireless, l'ID del canale e le credenziali. Puoi trovare l'ID del tuo canale nella parte superiore della pagina principale del tuo canale.
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) Definire i prototipi delle funzioni in questo codice.
// // 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) Inizializzare i pin per l'input e l'output, avviare il monitor seriale e inizializzare il client MQTT nella routine setup.
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) Ogni volta che viene eseguito il ciclo principale, verificare se i dati della sottoscrizione MQTT sono disponibili per l'elaborazione. Quindi impostare la posizione del servo in modo che corrisponda ai dati. Assicurarsi che i client wireless e MQTT siano attivi e mantengano una connessione al server client.
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) Utilizzare la funzione mqttSubscriptionCallback per gestire i messaggi MQTT in arrivo. Il programma funziona in modo più fluido se il ciclo principale esegue i passaggi di elaborazione anziché il callback. In questa funzione, utilizzare i flag per apportare modifiche al ciclo principale.
/**
* 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) Utilizzare la funzione MQTTConnect per impostare e mantenere una connessione al 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) Utilizzare mqttSubscribe per ricevere aggiornamenti dal campo di controllo LED. In questo esempio, ti iscrivi a un campo, ma puoi usare questa funzione anche per iscriverti all'intero feed del canale. Chiama la funzione con field = 0 per iscriverti all'intero 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, 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) La funzione mqttUnsubscribe non è utilizzata nel codice, ma è possibile utilizzarla per terminare un abbonamento.
/**
* 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) Utilizzare la funzione mqttPublish per inviare i dati RSSI dell'angolo e del Wi-Fi a un canale 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) Collega il tuo dispositivo a una rete wireless utilizzando la funzione 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) Utilizzare la funzione updateRSSIValue per leggere la potenza del segnale della rete a cui si è attualmente connessi.
void updateRSSIValue(){
long rssi = WiFi.RSSI();
Serial.print( "RSSI:" );
Serial.println(rssi);
dataToPublish[ DATA_FIELD ]=float( rssi );
}