mardi 6 août 2013

Communication avec une application

Comment faire communiquer Arduino avec une application Windows fait maison ?


Pour l'exemple, j'ai prévu un petit protocole sur le Arduino avec 3 fonctions basiques :

  • a : allumer la LED
  • e : éteindre la LED
  • h : renvoyer Hello World!

J’appellerai ces fonctions à 2 secondes d'intervalles.

L'échange se fait via un port série, il y a une sortie pour l'envoi (Tx Transmission) et une entrée pour la réception (Rx Réception).


Du côté de l'embarqué

Sur le Arduino, le programme est simple et ressemble à ça :

int led = 13;

void setup() {                
  pinMode(led, OUTPUT);     
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    int inByte = Serial.read();
    if(inByte != -1) {
      Serial.println(inByte);
      switch (inByte) {
        case 'a':
          digitalWrite(led, HIGH);
          break;
        case 'e':
          digitalWrite(led, LOW);
          break;
        case 'h':
          Serial.println("Hello World!");
          break;
        default:
          break;
      }
    }
  }
}


Tout d'abord, je me suis naturellement tourné vers le Java...

Je développe sous Windows et pour utiliser le port série ce n'est pas évident puisque Java ne prévoit rien pour le faire. Il faut donc une dll spéciale pour pouvoir exploiter les ressources de la machine et une bibliothèque jar pour faire le pont vers le java. C'est à peu près le même principe sur Linux.
La librairie s'appelle RXTX.
Un tutorial est disponible sur Playground Arduino.

Donc une fois la DLL posée dans le répertoire système et la librairie inclue au projet, le code permettant d'appeler le Arduino ressemble à ça :

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent; 
import gnu.io.SerialPortEventListener; 
import java.util.Enumeration;


public class SerialTest implements SerialPortEventListener {
SerialPort serialPort;
        /** The port we're normally going to use. */
private static final String PORT_NAMES[] = { 
"COM3"
};
/**
* A BufferedReader which will be fed by a InputStreamReader 
* converting the bytes into characters 
* making the displayed results codepage independent
*/
private BufferedReader input;
/** The output stream to the port */
private OutputStream output;
/** Milliseconds to block while waiting for port open */
private static final int TIME_OUT = 2000;
/** Default bits per second for COM port. */
private static final int DATA_RATE = 9600;

public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

//First, Find an instance of serial port as set in PORT_NAMES.
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
if (portId == null) {
System.out.println("Could not find COM port.");
return;
}

try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);

// set port parameters
serialPort.setSerialPortParams(DATA_RATE,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);

// open the streams
input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
output = serialPort.getOutputStream();

// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
}

/**
* This should be called when you stop using the port.
* This will prevent port locking on platforms like Linux.
*/
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
}

/**
* Handle an event on the serial port. Read the data and print it.
*/
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
String inputLine=input.readLine();
System.out.println(inputLine);
} catch (Exception e) {
System.err.println(e.toString());
}
}
// Ignore all the other eventTypes, but you should consider the other ones.
}

public static void main(String[] args) throws Exception {
SerialTest main = new SerialTest();
main.initialize();
Thread t=new Thread() {
public void run() {
//the following line will keep this app alive for 1000 seconds,
//waiting for events to occur and responding to them (printing incoming messages to console).
try {Thread.sleep(1000000);} catch (InterruptedException ie) {}
}
};
t.start();
System.out.println("Started");
Thread.sleep(2000);
main.output.write('a');
Thread.sleep(2000);
main.output.write('e');
Thread.sleep(2000);
main.output.write('h');

}
}

Le programme est en attente d'un événement sur le port série COM3. Les lignes contenant "l'intelligence" de mon code se trouvent en bas. La LED s'allume et s’éteint, tout va bien.

Mais pour la suite je me dis que faire des interfaces en Java, ce n'est pas très drôle, du coup j'ai exploré d'autres pistes,


et puis j'ai essayé le .NET... <troll>Oh sacrilège!</troll>

Si vous ne voulez pas installer l'usine à gaz visuelle de plusieurs Go qui en installe les 3/4 sous C quand vous lui avez dit d'installer sous D (grrr), je vous conseille SharpDevelop qui est une bonne alternative à VS.

En arrivant sur .NET, bonne nouvelle, tout est là pour gérer le port série. Du coup, la taille du code en prend un coup :

using System;
using System.IO.Ports;
using System.Threading;

namespace Arduino
{
    class Program
    {
        public static void Main(string[] args)
        {
            SerialPort port = new SerialPort("COM3",9600);
            port.Open();
            Thread.Sleep(2000);
            port.Write("a");
            Thread.Sleep(2000);
            port.Write("e");
            Thread.Sleep(2000);
            port.Write("h");
            Thread.Sleep(2000);
            string data = port.ReadExisting();
            Console.Write(data);
           
            Console.ReadKey();
        }
    }
}


Globalement même résultat qu'avec le Java, mais bon, je vais plutôt continuer avec cette méthode ;-).

Petit tutorial qui le fait en VB sur le Wiki de Microsoft.

Aucun commentaire:

Enregistrer un commentaire