Non satisfais des solutions web statique et vu que je cible plus particulièrement la domotique, il me fallait une solution permettant d'utiliser un client léger (tout est relatif) facile à intégré , portable et surtout une solution full duplex.
La seul façons d’atteindre mes objectifs à été d'utiliser les sockets et plus particulièrement les websocket presque normalisé et déjà implémenté dans safari, chrome et Firefox (pas réussi avec celui ci).
J'ai d'abord chercher les serveurs compatible websocket et j'ai décidé d'utilisé celui ci en php que j'ai pu remanier à ma sauce pour le rendre compatible avec l'arduino (sans le surchargé de la couche handshake des websocket).
Donc mon (mes) client(s) ce connecte au serveur via les websocket, l'arduino comme socket simple.
Pour la communication entre mon (mes) client(s) et l'arduino j'ai écris mon propre protocole simple et parfaitement adapté à la domotique inspiré du protocole plcbus(x10).
Il est simple à comprendre , il consiste à envoyer une trame du genre format hex 0x02|0x04|0x03|0x05|0x01|0x00|0x03 .
0x02: début de trame
0x04: t'aille des données ici 4 octet (0x03|0x05|0x01|0x00) donc taille maximum de 255 octet (256-la commande) pour l'envoie de donné sur le port série ou autre par exemple.
0x03: la commande par exemple analogWrite (256 possibilités de commandes)
0x05: le numéro de l'entrée correspondant
0x01: HIGH
0x00: ack non demandé (à l'inverse 0x01 ack demandé)
0x03 fin de trame (le fait de mettre un début et une fin de trame permet de vérifié ça validité, pas à 100% mais presque)
Le tout est envoyé au format Json vers le serveur ensuite parser par le serveur qui transmet la trame à l'arduino, l'arduino retourne la trame sous forma JSon directement au client (le support JSon est natif en javascript donc ...) .
Pour l'instant je n'en suis qu'au début du développement et des testes , il pourra y avoir encore des modifications mais je suis assez satisfait du résultat actuel.
un petit schéma pour visualiser tout ça.
Deux petite vidéos faites avec les moyens du bord, je tente de montré que les deux client (safari et chrome) sont bien synchrone, l'arduino retourne bien le statut ainsi que l’envoie de la valeur d'une photo résistance toute les 5 sec (analog 5) et l'appuis d'un bouton poussoir en local qui retourne en même temps le statut de la sortie arduino au client web (On vois bien tout l’intérêt des websocket et non celui d'une page web statique qui ne permet pas de modifié le statut d'une entré ou sortie: ma lampe est allumée ou éteinte ????).
La librairie avec 2 petit exemples de possibilités pour l'arduino.
http://only.dommel.be/docs/DDuino.zip
le serveur et l'exemple pour mes testes.
http://only.dommel.be/docs/DDuinowsoc.zip
Vous pouvez utiliser Mamp, Wamb, ... comme serveur web pour le faire fonctionné.
http://www.mamp.info/en/index.html
Pour le serveur il faut modifier cette ligne dans le fichier websocket.start.php avec vos propre valeurs (l'adresse du serveur, le port serveur , l'adresse de l'arduino)
$master = new WebSocket("192.168.1.100",9390, "192.168.1.109");
pour lancé le serveur il suffit soit via la console de taper la commande si dessous (en s'étant placé dans le répertoire), soit en lançant le fichier websocket.start.php via le navigateur.
php -q websocket.start.php
pour le client de teste il faudra également modifier cette ligne dans le fichier client.html avec vos propre paramètres (adresse du serveur et port).
<body onload="init('ws://192.168.1.100:9390')">
Côté arduino ajouter la librairie et mettre vos propre donnée (adresse arduino, ip serveur, port serveur, ...)
#include <DDuino.h>
Le code arduino est assez simple, vu que l'arduino agira comme client on tente une connexion, ici je fais une tentative toute les 5 sec pour mes testes.
if(!client.connected() && millis() > delay1)
{
client.connect();
if(client.connected())
{
Serial.println("conect ...");
}
else
{
Serial.println("disconet ...");
}
delay1 = millis()+5000;
}
On réceptionne les données clients, appel à une fonction action une fois toute les données reçues .
if(client.available())
{
uint8_t lengthData = client.available();
uint8_t data[lengthData];
int i = 0;
while(client.available() > 0)
{
data[i] = client.read();
i+=1;
}
action(data);
client.flush();
}
}
l'objet ce charge d’effectuer les traitements et de renvoyé des données ack (si demandé "1") ou relevé sonde, ... au clients web
void action(uint8_t* data)
{
String str = dduino.sendCmdJsonReturn(data);
if(str != "0")
{
client.print(str);
}
}
Note: mon protocole n'est pas uniquement valable que pour la communication avec un ethernet shield, il peux aussi être utiliser entre arduino vi un port série, i2c, ...
Une autre méthode aurait alors été utilisé pour l’envoie de données aux client(s) sendCmdDataReturn(uint8_t *data) qui retourne un tableau d'octet, il suffit d'adapter le code aux mode de communications.
Voir le code minimum pour faire fonctionné l'arduino via le web, c'est très light et on peux presque tout faire sans y touché.
#include <SPI.h>
#include <Ethernet.h>
#include <DDuino.h>
DDuino dduino = DDuino();
unsigned long delay1 = 0;
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x6D, 0x86 };
byte ip[] = { 192,168,1,109 };
byte server[] = { 192,168,1,100 };
Client client(server, 9390);
void setup()
{
Ethernet.begin(mac, ip, subnet);
client.connected();
Serial.begin(9600);
}
void loop()
{
if(client.available())
{
uint8_t lengthData = client.available();
uint8_t data[lengthData];
int i = 0;
while(client.available() > 0)
{
data[i] = client.read();
i+=1;
}
action(data);
client.flush();
}
}
void action(uint8_t* data)
{
String str = dduino.sendCmdJsonReturn(data);
if(str != "0")
{
client.print(str);
}
}
Le code web est assez simple en utilisant les fichiers Javascript fourni.
exemple de code html, un simple appel de méthode qui active une sortie numérique de l'arduino digitalWrite(8, 1, 1) active la sortie numérique 8 avec demande de ack, il n'y a rien de plus à mettre, il ne faut pas être un grand développeur pour l'utiliser.
<div><button id="dw8" onclick="digitalWrite(8, 1, 1)">on</button></div>
pour le retour de statut il suffit de modifier l'intérieure de la fonction appropriée dans le fichier response.js et agir selon les données reçues sur le code html via javascript.
function digitalWriteResp(pin, value, ack)
{
id="dw"+pin;
if(pin == 8)
{
if(value == 1)
{
$(id).innerHTML="off";
$(id).setAttribute('onclick', "digitalWrite("+pin+", 0, "+ack+")");
}
else
{
$(id).innerHTML="on";
$(id).setAttribute('onclick', "digitalWrite("+pin+", 1, "+ack+")");
}
}
}
Les commandes actuels +- testé, // fonctions javascript correspondantent
#define PIN_MODE 0x01 //pinMode(pin, value)
#define DIGITAL_WRITE 0x02 //digitalWrite(pin, value, ack)
#define DIGITAL_READ 0x03 //digitalRead(pin)
#define ANALOG_REFERENCE 0x04 //analogReference(value)
#define ANALOG_WRITE 0x05 //analogWrite(pin, value, ack)
#define ANALOG_READ 0x06 //analogRead(pin)
#define SHIFT_OUT 0x07 //shiftOut(latch, data, clock, bitOrder, value, ack)
#define SHIFT_IN 0x08 //shiftIn(dataPin, clockPin, bitOrder)
#define PULSE_IN 0x09 //pulseIn(pin)
#define TONE 0x0A //tone(pin, value, ack)
#define TONE_DURATION 0x0B //toneDuration(pin, value, duration, ack)
#define NO_TONE 0x0C //noTone(pin, ack)
#define SERIAL_WRITE 0x0D //serialWrite(chaine)
#define EEPROM_READ 0x0E // eepromRead(address_start, address_end)
#define EEPROM_WRITE 0x0F //eepromWrite(address, value, ack)
#define SERVO_WRITE 0x10 //servoWrite(item, value, ack)
#define SERVO_WRITEMS 0x11 //servoWriteMS(item, value, ack)
#define STEP 0x12 //stepperStep(item, value)
#define SETSPEED 0x13 //stepperSetSpeed(item, value)
#define SPI_PARAM 0x14 //spiParam(BitOrder, ClockDivider, DataMode)
#define SPI_TRANSFER 0x15 //spiTransfer(slave, data)