Objectifs
On souhaite réaliser une solution nous permettant de mesurer et stocker les informations fournis par une cellule photo-électrique.
Nous allons pour cela réaliser un montage pour connecter notre cellule au ports de communication de notre carte Arduino. Ce dernier dispose d'un carte d'extension ethernet permettant de créer un serveur web qui servira la page fournissant le résultat sous forme d'objet JSON.
Une application Symfony sera chargée de récolter le JSON fourni par l'Arduino via une commande déclenchée par un cron job. Une fois la mesure récoltée, elle sera stockée en base de données. Ici on développera notre app dans une VM de dev sur Debian et on déploiera dans un serveur LAMP sur un Raspberry-Pi. Ce dernier aura la puissance suffisante pour ce genre d'application peu gourmande en ressource.
Environnement et matériel nécessaire
Environnement
- Framework PHP : Symfony 2.8
- Framework / IDE Arduino : 1.6.7
- VM Dev : Debian 8
- Apache : 2.4
- PHP : 5.6
- MySQL : 5.5
Matériel
- - 1 Arduino UNO R3
- - 1 Neuftech Ethernet Lan Shield Module W5100 Micro SD pr Arduino 2009 UNO Mega 1280 2560
- - 1 cable USB A/B
- - 1 cable RJ45
- - 1 cellule photovoltaïque
- - 1 résistance 2,2 kOhms
1 - Installation de l'environnement de développement Arduino
Installation sur le poste de développement de l'IDE Arduino 1.6.7, télécharger l'application et la copier dans le dossier Applications :
Arduino 1.6.72 - Schéma de montage breadboard / Ethernet Arduino
Mise en place carte ethernet Arduino : on enfiche la carte d'extension sur l'Arduino.
On va connecter nos composants et la carte de la façon suivante :
- - [+] Arduino IN 5v > [-] cellule photovoltaïque
- - [+] photovoltaïque > [-] resistance 2,2 kOhms
- - [+] resistance 2,2 kOhms > GND
- - [+] cellule photovoltaïque > [-] Arduino IN A0
Ci-dessous la photo du montage.
3 - Branchement de l'Arduino et configuration de l'IDE
On relie l'Arduino au poste de développement par le cable USB.
On lance l'application Arduino.
Dans le menu "Outils", on sélectionne "Type de carte" > "Arduino/Genuino Uno".
Dans le menu "Outils", on sélectionne "Port" > "/dev/cu.usbmodem1451 (Arduino/Genuino Uno)".
4 - Script de lecture du capteur et serveur web sur l'Arduino
J'ai repris un script des tutoriaux du site d'Arduino et l'ai légèrement modifié pour parvenir à mes fins [source : https://www.arduino.cc/en/Tutorial/WebServer].
Le script de base permet de lire les valeurs des ports inputs et de les afficher sur une page web via le serveur web instancié dans la méthode setup().
La méthode loop() à été modifiée afin de retourner un objet JSON et de ne plus avoir la page en rechargement automatique.
Dans l'application Arduino on va créer un nouveau document et y coller le script ci-dessous :
#include
#include
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 0, 11);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/json");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println();
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 1; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("{\"analogInput\": ");
client.print(analogChannel);
client.print(",");
client.print("\"value\": ");
client.print(sensorReading);
}
client.println("}");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
On a choisi l'adresse IP : 192.168.0.11
On clique sur le bouton "Téléverser" et une fois que le téléchargement est terminé, on s'assure que notre Arduino est connecté au LAN :
ping 192.168.0.11
Remarque : En connectant l'Arduino aux ports ethernet de la Freebox : pas de communication et le voyant collision de la carte partait au rouge. J'ai du le connecter à un switch en aval pour que tout fonctionne normalement.
On va pouvoir tester dans un navigateur à l'adresse 192.168.0.11 et on va obtenir un objet JSON contenant deux propriétés : {"analogInput": 0,"value": 241}
La propriété "value" sera comprise entre 0 et 1000, ces valeurs varieront en fonction de la quantité de lumière fournie à la cellule.
5 - Application Symfony pour le stockage des mesures
On va maintenant pouvoir construire une application qui va se charger de la récolte des informations fournies par la sonde à intervalles réguliers et les stocker en BDD.
On part du principe que nous disposons d'un socle applicatif Symfony 2.8.x avec un bundle MainBundle contenant les modèles et la logique de notre app.
On dispose d'une entité Lightning qui va stocker nos mesures, elle aura pour attributs : id, date, lightAmount.
Le bundle va disposer uniquement d'un service et d'une commande. Cette dernière sera disponible à partir du shell et pourra être déclenchée via crontab. Le service va contenir la persistance de pour les objets Lightnings importés par la commande ArduinoImportCommand.
Je vais uniquement détailler la création du service et de la commande, vous pouvez vous référer au reste du site pour la création de bundle et la génération d'entité avec Symfony 2.
Création du service
On va créer un dossier "Service" à la racine de MainBundle et dans ce dernier, on va créer la class suivante :
nano src/AppTest/MainBundle/Service/LighteningLogic.php
<?php
namespace AppTest\MainBundle\Service;
use Doctrine\ORM\EntityManager;
use App\MainBundle\Entity\Lightning;
class LighteningLogic
{
private $em;
/**
* ArduinoImport constructor.
* @param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager) {
$this->em = $entityManager;
}
/**
* Calculate if day or night, save to db and push notifications
* @param $lightAmount
*/
public function saveLightning($lightAmount)
{
// save to db
$em = $this->em;
$lightning = new Lightning();
$lightning->setDate(new \DateTime());
$lightning->setLightAmount($lightAmount)
$em->persist($lightning);
$em->flush();
}
}
Création de la commande
On va créer un dossier "Commande" à la racine de MainBundle et dans ce dernier on va créer la class suivante :
nano src/AppTest/MainBundle/Command/ArduinoImportCommand.php
<?php
namespace AppTest\MainBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ArduinoImportCommand extends ContainerAwareCommand
{
protected function configure()
{
$this->setName('arduino:import')
->setDescription('Fetch arduino\'s data');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// retrieve data from arduino
$data = file_get_contents('http://192.168.0.11');
$data = json_decode($data);
$lightAmount = $data->value;
// save to db and push notification
$lightningLogicService = $this->getContainer()->get('plantmanager.lightning.logic');
$lightningLogicService-> saveLightning($lightAmount);
// send output cmd
$output->writeln($lightAmount);
}
}
Enregistrement du service
On enregistre notre service afin qu'il soit disponible dans le conteneur de service. Pour cela, on va éditer le fichier suivant comme suit :
nano src/AppTest/MainBundle/Resources/config/services.yml
services:
apptest.lightning.logic:
class: AppTest\MainBundle\Service\LightningLogic
arguments: [@doctrine.orm.entity_manager ]
Test de la commande
php app/console arduino:import
One doit recevoir en retour la valeur de la sonde.
Planification du cron job
On va éditer la liste des cron jobs pour y insérer le notre :
crontab -e
# m h dom mon dow command
0 19 * * * php /var/www/app_test/app/console arduino:import --env=prod 2>&1
Notre récolte des valeurs de la sonde auront à 19h tous les jours.