Mise en place d'un application Symfony de stockage de mesures d'une sonde photovoltaïque avec un Arduino et extension ethernet

Mise en place d'un application Symfony de stockage de mesures d'une sonde photovoltaïque avec un Arduino et extension ethernet

Publié le 11/07/2016

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.7

2 - 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.