mardi 28 août 2012

Téléinformation 4/5 (Base de données)

Et enfin ,la partie sauvegarde/affichage.
Ceci est possible sur un site hébergé ou bien sur un NAS en local.
Les pré-requis sont d'avoir une base de donnée (MySQL pour ma part) et PHP.

Commençons par la base de données.
J'ai donc une base de données définie comme cela :

Voici le script SQL pour créer la base :

CREATE TABLE IF NOT EXISTS `teleinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `temps` datetime DEFAULT NULL,
  `hchp` bigint(9) DEFAULT NULL,
  `hchc` bigint(9) DEFAULT NULL,
  `ptec` text CHARACTER SET latin1 COLLATE latin1_general_ci,
  `papp` int(5) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7317 ;


Une fois la table créée, il faut avoir les fichiers php qui vont bien pour que la vera puisse insérer les élément dans le table.
Comme vu dans le poste précédent, la vera appel le fichier update.php qui est le suivant :

<?php
// Connexion MySql et requète.
$serveur="xxxxxx";
$login="xxxxxx";
$base="xxxxxx";
$pass="xxxxxx";
$table="teleinfo";

if(!isset($_GET["HCHP"])) {
    $hchp= "NULL";
}else {
    $hchp= $_GET["HCHP"];
}

if(!isset($_GET["HCHC"])) {
    $hchc= "NULL";
}else {
    $hchc= $_GET["HCHC"];
}

if(!isset($_GET["PTEC"])) {
    $ptec= "NULL";
}elseif ($_GET["PTEC"] == "HP..") {
    $ptec= "HP";
}elseif ($_GET["PTEC"] == "HC..") {
    $ptec= "HC";
}

if(!isset($_GET["PAPP"])) {
    $papp= "NULL";
}else {
    $papp= $_GET["PAPP"];
}

$con = mysql_connect($serveur, $login, $pass);
if (!$con)
  {
  die('Could not connect: ' . mysql_error());
  }

mysql_select_db($base) or die("Erreur de connexion a la base de donnees $base");
mysql_query("SET NAMES 'utf8'");

$requete = mysql_query("INSERT INTO $table (temps,hchp,hchc,ptec,papp) VALUES(NOW(),$hchp,$hchc,'$ptec',$papp)");
?>


Enfin il faut un la vera envera à intervalle régulier dans le base de données les valeurs issues de la carte arduino contenant le compteur EDF HP, HC, la tranche horaire, la puissance instantannée consommée. La date y sera ajoutée lors de l'insertion dans la base.

Le prochain message sera sur l'affichage.

Téléinformation 3/5 (VERA)

Passons maintenant à la vera.
Dans Apps > Develop Apps > Edit Startup Lua j'ai rajouté une fonction pour me permettre d'extraire les valeurs des champs XML.
Voici la fonction en question à rajouter telle quelle:

function extractElement(tag, xml, default)
    local pattern = "<"..tag..">(.*)</"..tag..">"
    local result = (xml:match(pattern) or default)
    return result
end



Maintenant j'ai créé un virtual container nommé EDF. Noter son ID pour la scéne de mise à jour.

Et enfin, je créé une scène qui, à intervalle régulier, va me récupérer les infos de mon arduino et me les afficher dans mon virtual container ainsi que m'envoyer les info sur mon site WEB pour l'affichage de l'historique

 Ma scène se compose exclusivement d'un code LUUP qui se déclenche à intervalle régulier toutes les 10 min mais peut être modifié à volonté suivant la précision que l'on souhaite.
Nous verrons dans le prochain message la mise en oeuvre de la partie WEB.

luup.log("Update VContainer EDF teleinfo")
local SID_Vc = "urn:upnp-org:serviceId:VContainer1"

local lul_cmd = "http://192.168.1.202/"
local code, res = luup.inet.wget(lul_cmd,10, "" ,"")
if (code == 0) then
  local hchp= extractElement("HCHP", res, "N/A")
  luup.variable_set(SID_Vc, "VariableName1","HCHP (KW)", VMOD_CONT_EDF)
  luup.variable_set(SID_Vc, "Variable1",math.floor(hchp/1000),VMOD_CONT_EDF)

  local hchc= extractElement("HCHC", res, "N/A")
  luup.variable_set(SID_Vc, "VariableName2","HCHC (KW)", VMOD_CONT_EDF)
  luup.variable_set(SID_Vc, "Variable2",math.floor(hchc/1000),VMOD_CONT_EDF)

  local ptec= extractElement("PTEC", res, "N/A")
  luup.variable_set(SID_Vc, "VariableName3","PTEC", VMOD_CONT_EDF)
  luup.variable_set(SID_Vc, "Variable3",ptec,VMOD_CONT_EDF)

  local papp= extractElement("PAPP", res, "N/A")
  luup.variable_set(SID_Vc, "VariableName4","PAPP (W)", VMOD_CONT_EDF)
  luup.variable_set(SID_Vc, "Variable4",papp,VMOD_CONT_EDF)

   luup.log("Save EDF to my web site")
   local lul_update = 'http://www.mywebsite.com/EDF/update.php?HCHP=' .. hchp .. '&HCHC=' .. hchc .. '&PTEC=' .. ptec .. '&PAPP=' .. papp .. ''
   luup.inet.wget(lul_update,6, "" ,"")
   return true
else
   luup.log("EDF teleinfo ERROR : " .. code)
   return false
end


Ce qui donne le device suivant :

Téléinformation 2/5 (Arduino SW)


#include <SoftwareSerial.h>
#include <SPI.h>
#include <Ethernet.h>

SoftwareSerial cptSerial(2,3);

//#define DebugL1        //envoie seulement pour le débug sur l'USB
//#define DebugL2        //envoie tout sur l'USB

//*****************************************************************************************
  byte inByte = 0 ;        // caractère entrant téléinfo
  char buffteleinfo[21] = "";
  byte bufflen = 0;
 
// declarations Teleinfo
  byte isousc = 0;        // Intensité souscrite, A
  unsigned int papp = 0;  // Puissance apparente, VA

  char ptec[4] = "";      // Période Tarifaire en cours, 4 alphanumériques (HC.. ou HP..)

  unsigned int iinst = 0;      // Monophasé - Intensité Instantanée, A  (intensité efficace instantanée)
   
  unsigned long hchc = 0;
  unsigned long hchp = 0;

#define debtrame 0x02
#define debligne 0x0A
#define finligne 0x0D


byte mac[] = {0x54,0x55,0x58,0x10,0x00,0x26};    // MAC address of Arduino
IPAddress ip(192,168,1, 202);                    // IP address of Arduino

EthernetServer server(80);

// ************** initialisation *******************************
void setup()
{
 // initialisation du port 0-1 lecture Téléinfo
    Serial.begin(1200);
    cptSerial.begin(1200);    // le mode 7 bit parité pair se fait plus loin

 // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

#ifdef DebugL1
    Serial.print("-- Teleinfo Arduino UNO --");
#endif
}

// ************** boucle principale *******************************

void loop()                     // Programme en boucle
{

  read_teleinfo();
  read_web();

}

///////////////////////////////////////////////////////////////////
// Calcul Checksum teleinfo
///////////////////////////////////////////////////////////////////
char chksum(char *buff, int len)
{
  int i;
  char sum = 0;
    for (i=1; i<(len-2); i++) sum = sum + buff[i];
    sum = (sum & 0x3F) + 0x20;
    return(sum);
}

///////////////////////////////////////////////////////////////////
// Analyse de la ligne de Teleinfo
///////////////////////////////////////////////////////////////////
void traitbuf_cpt(char *buff, int len)
{

  if (strncmp("HCHP ", &buff[1] , 5)==0){
      hchp = atol(&buff[6]);
  #ifdef DebugL1
      Serial.print("- Index Heures Pleines: "); Serial.println(hchp,DEC);
  #endif       
  }

  else if (strncmp("HCHC ", &buff[1] , 5)==0){
      hchc = atol(&buff[6]);
  #ifdef DebugL1
      Serial.print("- Index Heures Creuses: ");Serial.println(hchc,DEC);
  #endif       
  }

  else if (strncmp("IINST ", &buff[1] , 6)==0){
      iinst = atol(&buff[7]);
  #ifdef DebugL1
      Serial.print("- I iNStantannee : "); Serial.println(iinst,DEC);    // valeur à l'ampère près donc à 240W près => pas terrible
  #endif    
  }

  else if (strncmp("PAPP ", &buff[1] , 5)==0){
      papp = atol(&buff[6]);
  #ifdef DebugL1
      Serial.print("- Puissance apparente : ");Serial.println(papp,DEC);
  #endif       
  }

  else if (strncmp("PTEC ", &buff[1] , 5)==0){
        strncpy(ptec, &buff[6], 4);
        ptec[4]='\0';
  #ifdef DebugL1
        Serial.print("- Periode Tarifaire En Cours : "); Serial.println(ptec);
  #endif       
  }
    
}

///////////////////////////////////////////////////////////////////
// Lecture trame teleinfo (ligne par ligne)
///////////////////////////////////////////////////////////////////
void read_teleinfo()
{
  // si une donnée est dispo sur le port série
  if (cptSerial.available() > 0)
  {
  // recupère le caractère dispo
    inByte = (cptSerial.read()& 0x7F);      // le 0X7F pour avoir en 7 bits au lieu de 8

#ifdef DebugL2   
    Serial.print(char(inByte));  // echo des trames sur l'USB (choisir entre les messages ou l'echo des trames)
#endif

    if (inByte == debtrame) bufflen = 0; // test le début de trame
    if (inByte == debligne) // test si c'est le caractère de début de ligne
    {
      bufflen = 0;
    } 
    buffteleinfo[bufflen] = inByte;
    bufflen++;
    if (bufflen > 21)bufflen=0;       // longueur max du buffer (21 pour lire trame ADCO)
    if (inByte == finligne && bufflen > 5) // si Fin de ligne trouvée
    {
      if (chksum(buffteleinfo,bufflen-1)== buffteleinfo[bufflen-2]) // Test du Checksum
      {
        traitbuf_cpt(buffteleinfo,bufflen-1); // ChekSum OK => Analyse de la Trame
      }
    }
  }
}

///////////////////////////////////////////////////////////////////
// Création page Web client
///////////////////////////////////////////////////////////////////
void read_web()
{
    // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    #ifdef DebugL1   
      Serial.println("new request");
    #endif
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        #ifdef DebugL1
          Serial.write(c);
        #endif
        // 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.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
          client.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
      client.println("<ARDUINO>");
          client.print("<HCHC>");
          client.print(hchc,DEC);
          client.print("</HCHC>");
          client.print("<HCHP>");
          client.print(hchp,DEC);
          client.print("</HCHP>");
          client.print("<IINST>");
          client.print(iinst,DEC);
          client.print("</IINST>");   
          client.print("<PAPP>");
          client.print(papp,DEC);
          client.print("</PAPP>");
          client.print("<PTEC>");
          client.print(ptec);
          client.print("</PTEC>");
          client.println("</ARDUINO>");
          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();
    #ifdef DebugL1
      Serial.println("client disonnected");
    #endif
  }
}

Téléinformation 1/5 (Arduino HW)

C'est parti pour le tuto sur la téléinformation couplée à la micasaverde VERA.


Pour la partie hardware, j'ai donc une arduino UNO, une ethernet shield W5100 et une carte supplémentaire dont voici le schéma. J'ai pas trouvé plus simple comme schéma et ça fonctionne parfaitement.
Le schéma viens de là : http://www.chaleurterre.com/forum/viewtopic.php?p=51607#51607

Pour l'optocoupleur, j'ai mis ça : http://fr.farnell.com/jsp/search/productdetail.jsp?SKU=1045384
0.5€ HT le circuit + 2 résistances, c'est pas la ruine.

Ensuite il reste le code de l'arduino, la config de la vera et le site WEB pour avoir l'historique

vendredi 24 août 2012

Présentation du projet 2/2

 Petit message détaillant un peu plus le projet :
J'ai donc 3 cartes arduino : 2 UNO (RFID (à gauche) et téléinformation (à droite)) et 1 MEGA pour les Input/Output (au milieu)
Chaque carte arduino est couplée à une carte ethernet W5100. La carte officielle que je trouve beaucoup plus simple à programmée et plus fiable et plus performante. Des tests préalables sur carte à base de chipset enc28j60 ne m'ont pas mis en confiance. J'avais des blocages de la carte ethernet. Donc changement de stratégie et utilisation de la carte ethernet officielle.

En dessous, j'ai une carte avec 8 relais. Me manque pour le moment ma carte bi-relai pour le fil-pilot. Et au dessus (non visible sur le photo) un capteur de température one-wire DS18B20 pour vérifier la température dans le coffret.

J'ai en plus 2 alim sur rail DIN. une pour les arduino et 1 pour le routeur wifi configué en bridge wifi.

Présentation du projet 1/2


Le but de ce blog est de présenter ma solution domotique à moindre coût pour réaliser des fonctions de :
  • Commande de lampe ou autre à base de relais
  • Réception de commande par contact
  • Réception de commande par RFID
  • Mesure de la consommation électrique
  • Relevé de température par bus 1-Wire
  • Commande du fil pilote pour la gestion du chauffage (Normal, éco, hors gel, veille)
Mon installation est faite à base de carte arduino et est interfacée avec un box domotique micasaverde VERA3 pour la partie intelligence. Des plugins spécifiques y ont été développés pour pouvoir gérer les différentes fonctions.

En parallèle, un site web avec base de données MySQL stocke les données des mesures afin d'avoir l'historique.