Tutorial Arduino Yun Bridge port

Qualche settimana fa l’amico ed appassionato di arduino e programmazione Roberto Pompermaier mi ha scritto dopo aver realizzato un interessante esperimento con l’Arduino Yun Bridge port

EmptyResponse

Subito dopo aver letto la sua email, in cui descriveva il suo test e l’esperimento condotto, ho chiesto a Roberto di scrivere qualche riga sulla sua esperienza e su se stesso.

Lascio alle sue parole questo articolo:

About Roberto

​Roberto Pompermaier , classe 1985, ​dopo la laurea in Informatica all’università Trento ha frequentato un master in Domotica e Case Digitali presso la Politecnica di Madrid.

Si occupa principalmente di Domotica, SmartHome e Iot (Internet Of Things). Oltre a divertirsi su qualsiasi tipo di tavola (windsurf, snwoboard, surf, wakeboard…) negli ultimi anni ha sviluppato il progetto John O.S., una piattaforma per creare oggetti e servizi IoT con il minimo sforzo.

Il Tutorial Arduino Yun Bridge  port

L’altro giorno, facendo uno dei tanti esperimenti con Arduino Yun, ho provato lo sketch d’esempio Bridge.
Tramite questo sketch è possibile configurare e controllare i PIN dell’Arduino Yun tramite richieste Web…. o meglio tramite una connessione TCP/IP.
Dopo alcune prove ho scoperto uno strano comportamento dei componenti YunServer e YunClient.
Indagando su questo comportamento ho scoperto un work-around per ottenere maggiore controllo sulle connessioni Web da parte dell’Arduino Yun.

Hacking YunClient

FullAddressWorking

Dopo aver seguito il tutorial arduino yun bridge sullo sketch Bridge, tutto funzionava a meraviglia.
Studiando lo sketch (e non la documentazione 🙂 ) ho trovato che la porta di default del YunServer è la 5555 e che l’elaborazione della richiesta nella funzione process() iniziava verificando il comando da eseguire (ovvero il secondo livello del url). Allora ho provato a digitare nel browser il seguente indirizzo:
Come risultato ho ricevuto un messaggio d’errore a causa di una risposta da parte del server vuota.
EmptyResponse
dopo aver modificando la funzione process() per stampare tutti i dati ricevuti con la richiesta, scopro che utilizzando l’indirizzo modificato, il YunServer riceve una richiesta HTTP a tutti gli effetti.
void process(YunClient client) {
  // read the command
  String command = client.readStringUntil('/');

  // is "digital" command?
  if (command == "digital") {
    digitalCommand(client);
    return;
  }

  // is "analog" command?
  if (command == "analog") {
    analogCommand(client);
    return;
  }

  // is "mode" command?
  if (command == "mode") {
    modeCommand(client);
    return;
  }

  client.print(F("Unknow command: "));
  client.print(command);
  client.print(client.readString());
}
HTTPRequest
Qui di seguito lo sketch Bridge.ino modificato per poter funzionare correttamente con entrambi i tipi di indirizzo quello specificato nella documentazione
/*
  Arduino Yún Bridge example

 This example for the Arduino Yún shows how to use the
 Bridge library to access the digital and analog pins
 on the board through REST calls. It demonstrates how
 you can create your own API when using REST style
 calls through the browser.

 Possible commands created in this shetch:

 * "/arduino/digital/13"     -> digitalRead(13)
 * "/arduino/digital/13/1"   -> digitalWrite(13, HIGH)
 * "/arduino/analog/2/123"   -> analogWrite(2, 123)
 * "/arduino/analog/2"       -> analogRead(2)
 * "/arduino/mode/13/input"  -> pinMode(13, INPUT)
 * "/arduino/mode/13/output" -> pinMode(13, OUTPUT)

 This example code is part of the public domain

 http://www.arduino.cc/en/Tutorial/Bridge

 */

#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>

// Listen to the default port 5555, the Yún webserver
// will forward there all the HTTP requests you send
YunServer server;

void setup() {
  // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);

  // Listen for incoming connection only from localhost
  // (no one from the external network could connect)
  //server.listenOnLocalhost();
  server.noListenOnLocalhost();
  server.begin();
}

void loop() {
  // Get clients coming from server
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    // Process request
    process(client);

    // Close connection and free resources.
    client.stop();
  }

  delay(50); // Poll every 50ms
}

void process(YunClient client) {
  // read the command
  String command = client.readStringUntil('/');
  if (command=="GET ")
    command = client.readStringUntil('/');
  if (command=="POST ")
    command = client.readStringUntil('/');

  // is "digital" command?
  if (command == "digital") {
    digitalCommand(client);
    return;
  }

  // is "analog" command?
  if (command == "analog") {
    analogCommand(client);
    return;
  }

  // is "mode" command?
  if (command == "mode") {
    modeCommand(client);
    return;
  }

  client.print(F("Unknow command:\n"));
  client.print(command);
  client.print(client.readString());
}

void digitalCommand(YunClient client) {
  int pin, value;

  // Read pin number
  pin = client.parseInt();

  // If the next character is a '/' it means we have an URL
  // with a value like: "/digital/13/1"
  if (client.read() == '/') {
    value = client.parseInt();
    digitalWrite(pin, value);
  }
  else {
    value = digitalRead(pin);
  }

  // Send feedback to client
  client.print(F("Pin D"));
  client.print(pin);
  client.print(F(" set to "));
  client.println(value);

  // Update datastore key with the current pin value
  String key = "D";
  key += pin;
  Bridge.put(key, String(value));
}

void analogCommand(YunClient client) {
  int pin, value;

  // Read pin number
  pin = client.parseInt();

  // If the next character is a '/' it means we have an URL
  // with a value like: "/analog/5/120"
  if (client.read() == '/') {
    // Read value and execute command
    value = client.parseInt();
    analogWrite(pin, value);

    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" set to analog "));
    client.println(value);

    // Update datastore key with the current pin value
    String key = "D";
    key += pin;
    Bridge.put(key, String(value));
  }
  else {
    // Read analog pin
    value = analogRead(pin);

    // Send feedback to client
    client.print(F("Pin A"));
    client.print(pin);
    client.print(F(" reads analog "));
    client.println(value);

    // Update datastore key with the current pin value
    String key = "A";
    key += pin;
    Bridge.put(key, String(value));
  }
}

void modeCommand(YunClient client) {
  int pin;

  // Read pin number
  pin = client.parseInt();

  // If the next character is not a '/' we have a malformed URL
  if (client.read() != '/') {
    client.println(F("error"));
    return;
  }

  String mode = client.readStringUntil('\r');
  if (mode.indexOf(' ')!=-1)
    mode = mode.substring(0,mode.indexOf(' '));

  if (mode == "input") {
    pinMode(pin, INPUT);
    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" configured as INPUT!"));
    return;
  }

  if (mode == "output") {
    pinMode(pin, OUTPUT);
    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" configured as OUTPUT!"));
    return;
  }

  client.print(F("error: invalid mode "));
  client.print(mode);
}

Teoria

Quando richiamiamo da un browser l’indirizzo che troviamo nella documentazione dell’esempio http://arduino.local/arduino/... allora la richiesta viene ricevuta prima dal WebServer principale dell’Arduino Yun (LuCI) e dopo essere stata semplificata viene passata al componente YunServer del nostro sketch.
Se invece digiti l’url http://arduino.local:5555/... allora la richiesta HTTP arriva direttamente al componente YunServer dello sketch senza alcuna semplificazione.
Solo in questo modo possiamo ottenere tutti i dati della richiesta (Gli header HTTP) direttamente all’interno dello sketch.
Qui di seguito le due richieste da parte dei due tipi di url ricercati tramite browser.
Il primo (url da documentazione) mostra solamente la parte finale dell’url;
digital/13
mentre il secondo (url con il numero di porta) ci fornisce una richiesta HTTP standard.
GET /digital/13 HTTP/1.1
Host: arduino.local:5555
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: it-IT,it;q=0.8,es;q=0.6,en-US;q=0.4,en;q=0.2,en-GB;q=0.2
Per ottenere il contenuto delle due richieste basta modificare la funzione loop() con il codice seguente:
void loop() {
  // Get clients coming from server
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    // Process request
    //process(client);
    client.print(client.readString());

    // Close connection and free resources.
    client.stop();
  }

  delay(50); // Poll every 50ms
}

Conclusioni

Senza alcun dubbio lo sketch Bridge.ino originale è sufficiente nella maggior parte dei casi.
Le API REST messe a disposizione dallo sketch sono richiamabili anche tramite il comando curl.
A volte però vogliamo che il nostro sketch si comporti differentemente a seconda di alcune informazioni come il tipo di browser, la lingua utilizzata dall’utente o altri dati contenuti negli header HTTP.
Per poter analizzare gli header HTTP e dunque eseguire il comando corretto dobbiamo utilizzare l’indirizzo contenente il numero di porta del YunServer (5555 di default).
  • Questo sito ed i suoi contenuti è fornito "così com'è" e Mauro Alfieri non rilascia alcuna dichiarazione o garanzia di alcun tipo, esplicita o implicita, riguardo alla completezza, accuratezza, affidabilità, idoneità o disponibilità del sito o delle informazioni, prodotti, servizi o grafiche correlate contenute sul sito per qualsiasi scopo.
  • Ti chiedo di leggere e rispettare il regolamento del sito prima di utilizzarlo
  • Ti chiedo di leggere i Termini e Condizioni d'uso del sito prima di utilizzarlo
  • In qualità di Affiliato Amazon io ricevo un guadagno dagli acquisti idonei qualora siano presenti link al suddetto sito.

Permalink link a questo articolo: https://www.mauroalfieri.it/informatica/tutorial-arduino-yun-bridge-port.html

2 commenti

    • Carlo il 18 Maggio 2021 alle 15:19
    • Rispondi

    Ciao,
    invece di leggere solo un pin di arduino yun, sarebbe possibile leggere tramite uno script python un valore calcolato all’interno di uno sketch eseguito su yun?

    Vorrei inviare la distanza calcolata (quindi non un singolo valore di un pin) con un sensore ad ultrasuoni collegato allo Yun, ad un server python in ascolto su un Raspberry

    1. Ciao Carlo,
      a memoria ricordo che la libreria Bridge della Yun possa interfacciarsi con molte informazioni presenti nello sketch.
      Dovresti poter scambiare dati tra lo sketch e la parte openWRT leggendo nella documentazione ufficiale della libreria.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.