Android e Arduino: semplice comunicazione bidirezionale attraverso la porta audio (parte 1)

Per un progetto al quale sto lavorando, avevo la necessità di realizzare una comunicazione bidirezionale tra un device Android e un Arduino.

Scartando le (troppo) costose interfacce IOIO, mi sono reso conto che il sistema più semplice (ed economico!) era quello di utilizzare la porta audio di Android. Il jack auricolari/microfono, per intenderci.

Usando il jack audio, infatti, si può realizzare una semplicissima porta di comunicazione bidirezionale, usando il segnale audio out per inviare dati all’arduino, e il segnale microfono per ricevere dati da quest’ultimo. (In questo primo post è trattata la comunicazione da Android verso Arduino).

Nonostante esistano numerosi articoli online su come stabilire una comunicazione dati bidirezionale usando la codifica FSK, a me serviva un protocollo di segnalazione decisamente più semplice. Per questo motivo, ho deciso di implementare una segnalazione basata sul conteggio di impulsi, che ho definito, con tanta fantasia,  Simple Pulse Count Communication Protocol.

Simple Pulse Count Communication Protocol

Il protocollo è molto semplice: il contenuto informativo è dato semplicemente da un treno di impulsi ad onda quadra, di numero variabile. Ogni impulso ha una durata di circa 12msec, e la sequenza viene terminata con una pausa di almeno 200msec di “end-of-burst”. Una semplice applicazione Arduino rileva i fronti di discesa dell’onda quadra, e al termine del conteggio, riporta il valore sulla porta seriale. Dal lato Android, viene generato il treno di impulsi in tempo reale usando la classe AudioTrack.
Visto che il segnale audio in uscita dal device Android è troppo basso rispetto ai normali livelli digitali, ho usato un ingresso analogico sull’Arduino.

 

update 17/10/2013: è possibile scaricare il tarball con i sorgenti completi (progetto Android per NetBeans e sketch Arduino) da qui

 

Codice Arduino

/* Simple Pulse Communications between Android and Arduino

 * version 0.5 (only Android to Arduino direction)
 *
 * this application simply counts the number of pulses sent by Android trough
 * audio jack, and, at the end of the sequence, writes the count on the serial port.
 * The Android audio output LEFT (Pin#1 on the audio jack) is connected to analog 0
 * pin (A0) of the Arduino.
 *
 * written by S.Carotenuto of Startup Solutions
 * mailto: carotenuto@startupsolutions.it - www.startupsolutions.it
 */

int sensorPin = A0; // analog input pin
int ledPin = 13; // select the pin for the LED
int treshold = 10; // treshold between LOW and HIGH condition
int prevLevel = LOW; // variable to store previous level read
int currentLevel = HIGH; // variable to store current level read
int detectedPulseCount = 0; // stores detected pulse count
int sameStatusDuration = 0; // used for end-of-sequence detection

void setup()
 {
 // declare the ledPin as an OUTPUT:
 pinMode(ledPin, OUTPUT);
 //Initialize serial
 Serial.begin(9600);
 //
 // prints title with ending line break
 Serial.println("SimplePulseCommunications for Arduino");
 Serial.println("ultimoistante of StartUp Solutions - Oct 22, 2012");
 }

void risingEdgeDetected()
 {
 }

void fallingEdgeDetected()
 {
 detectedPulseCount++;
 }

void silenceDetected()
 {
 if(detectedPulseCount > 0)
 Serial.println(detectedPulseCount);
 detectedPulseCount = 0;
 }

void loop()
 {
 // reads the value from the sensor
 int analogValue = analogRead(sensorPin);
 currentLevel = (analogValue > treshold) ? HIGH : LOW;
 digitalWrite(ledPin, currentLevel);

if(currentLevel != prevLevel)
 {
 // condition changed
 sameStatusDuration = 0;
 if(currentLevel == HIGH)
 risingEdgeDetected();
 else
 fallingEdgeDetected();
 }
 else
 {
 // same condition. Let's detect silence
 sameStatusDuration++;
 if(sameStatusDuration > 200)
 {
 silenceDetected();
 }
 }

 prevLevel = currentLevel;

delay(1);
 }

Codice Android

package com.startupsolutions.android.audioserialout;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class SimplePulseCommunication extends Activity
   {
   AudioPulseGenerator pulseGenerator;
   Thread generatorThread;
   EditText pulseCountEdit;
   /**
     * Called when the activity is first created.
     */
   @Override
   public void onCreate(Bundle savedInstanceState)
     {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     pulseGenerator = new AudioPulseGenerator();
     this.generatorThread = new Thread(pulseGenerator);
     generatorThread.start();
     this.pulseCountEdit = (EditText) findViewById(R.id.PulseCountEdit);

     final Button button = (Button)findViewById(R.id.SendButton);
     button.setOnClickListener(new View.OnClickListener()
       {
       public void onClick(View v)
          {
          int pulses = Short.parseShort(pulseCountEdit.getText().toString());
          pulseGenerator.writePulses(pulses);
          }
       });
    }
 }
package com.startupsolutions.android.audioserialout;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import java.util.Arrays;

/**
 *
 * @author ultimoistante
 */
public class AudioPulseGenerator implements Runnable
   {
   private AudioTrack track;
   private int sampleRate = 8000;
   short[] buffer = new short[sampleRate];
   private int currentBufferIndex = 0;
   private boolean bufferChanged = false;
   public AudioPulseGenerator()
     {
     this.track = new AudioTrack( AudioManager.STREAM_MUSIC, this.sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, this.sampleRate, AudioTrack.MODE_STREAM);
     track.play();
     }
   public void writePulses(int pulses)
     {
     this.currentBufferIndex = 0;
     for(int i = 0; i < pulses; i++)
        {
        pushPulseInBuffer();
        }
     // fills remaining buffer with zeroes
     for(int i = currentBufferIndex; i< buffer.length; i++)
        buffer[i] = 0;
     //
     this.bufferChanged = true;
     }

   private void pushPulseInBuffer()
     {
     // a pulse is simply 100 samples high and 100 samples low
     Arrays.fill(buffer, currentBufferIndex, currentBufferIndex + 100, Short.MAX_VALUE);
     currentBufferIndex += 100;
     Arrays.fill(buffer, currentBufferIndex, currentBufferIndex + 100, (short)0);
     currentBufferIndex += 100;
     }
   public void run()
     {
     while(true)
        {
        if(bufferChanged)
           {
           track.write( buffer, 0, buffer.length );
           bufferChanged = false;
           }
        }
     }
  }
Annunci

7 thoughts on “Android e Arduino: semplice comunicazione bidirezionale attraverso la porta audio (parte 1)

  1. Salve, la ringrazio per l’interessamento. Purtroppo, per mancanza di tempo, non ho mai avuto modo di scrivere la seconda parte. Comunque il sistema è molto semplice: per comunicare da Arduino verso Android basta collegare un altro pin di Arduino all’ingresso MIC del jack audio di Android, e generare degli impulsi su tale linea. Dal lato Android va implementato un thread di “ascolto” usando la classe AudioRecord e monitorare quindi il buffer di ingresso per le transizioni. Per rilevare un impulso, basta rilevare un fronte di discesa al di sotto di una certa soglia, e che tale condizione persista per un certo numero di samples consecutivi. Spero di essere stato esauriente 🙂

  2. L’dea è anche economica 🙂 , direi che con qualche piccolo accorgimento si può anche migliorare di parecchio le prestazioni: attualmente l’impulso genera una freq di circa 400 hz, tale frequenza può essere aumentata al limite della banda audio: 20.000 Hz~18000Hz , in questo modo si possono mettere più info nello stesso buffer(size) (suddividendolo) .

  3. Si, certo. Infatti esistono già implementazioni opensource delle codifiche FSK e Morse Code per Android. A me invece serviva un sistema di segnalazione decisamente più semplice, quindi decisi di mantenermi sul “semplice”

  4. Buongiorno e grazie mille per l’interessantissima pubblicazione. Devo fare una ricerca di fisica relativa alle onde sonore (vado in quinta liceo scientifico) e pensavo di portare questo argomento in quanto applicazione delle onde sonore per comunicare tra dispositivi elettronici.
    Me la cavo molto bene con Arduino, ma purtroppo non ho alcun tipo di esperienza con la programmazione in Java, quindi mi chiedevo se fosse per lei possibile inviarmi il file apk già compilato.
    Grazie mille ancora e complimenti vivissimi per i suoi numerosi articoli, davvero interessanti.
    Andrea

  5. Salve, Andrea. Sono felicissimo che queste poche righe di codice ti possano essere utili! 🙂 Devo però fare una precisazione; il sistema di segnalazione che ho usato non è proprio definibile “ad onde sonore”, in quanto è un semplice sistema basato su transitorii (cioè una normale onda quadra). L’unica differenza è che il segnale passa attraverso una porta normalmente dedicata ai segnali audio…
    Comunque l’apk è già incluso nel tarball dei sorgenti allegato all’articolo: è nella sottodirectory “Android/AndroidSimpleAudioPulseCommunication/bin” e si chiama “AndroidSimpleAudioPulseCommunication-debug.apk”.

    Saluti! 🙂

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...