Acquisition senseurs I2C Fonera par messages xPL
De MicElectroLinGenMet.
Sommaire |
Description
Suite au test d'une sonde DS1621 sur la Fonera grâce au driver i2c de l'excellent site de Lefinnois.
Voici une adaptation du programme C tournant sur la Fonera pour envoyer les données de capteurs par xPL.
En plus du thermométre Ds1621, j'ai rajouté un Max128 (Convertisseur Analogique/Numérique 8 canaux.)
Shéma du circuit Max128
Max128 à relier à l'interface i2c de la Fonera.
Capteurs "météo" reliés au Max128:
Canal 0 = Référence de tension U/2 Canal 1 = capteur lumière infra-rouge TSL260 Canal 2 = capteur lumière LDR Canal 3-4 = capteur de température LM35 Canal 5 = capteur humidite SY-HS-230 (10 - 90% => 580-2870mV)
Détection des circuits i2c
root@fonera:~# i2cdetect 0
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-0.
I will probe address range 0x03-0x77.
Continue? [Y/n]
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: XX XX XX XX XX XX XX XX XX XX XX XX XX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
20: XX XX XX XX XX XX XX XX 28 XX XX XX XX XX XX XX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 4f
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
70: XX XX XX XX XX XX XX XX
==> Ds1621 en 0x4F (0x9E/0x9F) ==> Max128 en 0x28 (0x50/0x51)
Programme
Ci-dessous source C du programme à compiler avec le SDK OpenWrt
Une fois compiler le programme est simplement lancé en crontab de la Fonera toutes les minutes.
Si mode debug activé, le programme affiche les valeurs lus sur les capteurs et les messages xPL envoyés.
root@fonera:~# ./xpl-senseurs
TempDS=29.6° Vref=2.57V(2571) LumIR=1533 LumLDR=3663 TempLM35=22.8°(245) Hum=33.8%(1079)
xpl-trig
{
hop=1
source=domos-fonera.i2cbus
target=*
}
sensor.basic
{
device=fonera_ds1621
type=temp
current=29.6
}
...
La Fonera "broadcaste" les messages sur le réseau chaque minute et si xpl-rrd (librairie Xpl-perl) tourne sur un Linux du réseau, celui-ci va automatiquement généré des graphes RRDTool avec les messages sensor.basic reçus.
Messages xPL reçus avec un xPL_Logger
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_ds1621' type='temp' current='29.6' }
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_max128' type='voltage' current='2.57' }
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_max128' type='lumir' current='1533' }
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_max128' type='luminosity' current='3663' }
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_max128' type='temp' current='22.8' }
09/08/25 14:47:52 xpl-trig {domos-fonera.i2cbus *} sensor.basic { device='fonera_max128' type='humidity' current='33.8' }
Exemples de graphes générés par xpl-rrd
Pour que xpl-rrd crée les graphes des sensors type='lumir' et type='luminosity' qui ne font partie du schéma sensors.basic, j'ai modifié les fichiers: sensor.basic.yaml et xpl-rrd de la librairie Xpl-perl.
- Types lumir et luminosity rajoutés.
$ cat /usr/share/perl5/xPL/schema/sensor.basic.yaml --- doc: http://wiki.xplproject.org.uk/index.php/Schema_-_SENSOR.BASIC types: xpl-trig: fields: - name: device required: 1 - name: type required: 1 validation: type: Set set: - battery - count - current - direction - distance - energy - fan - generic - humidity - input - pressure - speed - temp - uv - lumir - luminosity - voltage - volume - weight - name: current required: 1 - name: lowest - name: highest - name: units ...
- Pour prise en compte des message de type voltage, lumir et luminosity par xpl-rrd
$ cat /usr/bin/xpl-rrd
...
elsif ($msg->class eq "sensor" && $msg->class_type eq "basic" &&
$msg->device && $msg->type eq "voltage") {
my $dev = exists $map{$msg->device} ? $map{$msg->device} : $msg->device;
delete $state{$dev};
$state{$dev.'/voltage'} = join($c, $time, $msg->type, $msg->current, 1, 'GAUGE', -20, 20); # Rajout. Dan
}
elsif ($msg->class eq "sensor" && $msg->class_type eq "basic" &&
$msg->device && $msg->type eq "luminosity") {
my $dev = exists $map{$msg->device} ? $map{$msg->device} : $msg->device;
delete $state{$dev};
$state{$dev.'/luminosity'} = join($c, $time, $msg->type, $msg->current, 1, 'GAUGE', 0, 4100); # Rajout. Dan
}
elsif ($msg->class eq "sensor" && $msg->class_type eq "basic" &&
$msg->device && $msg->type eq "lumir") {
my $dev = exists $map{$msg->device} ? $map{$msg->device} : $msg->device;
delete $state{$dev};
$state{$dev.'/lumir'} = join($c, $time, $msg->type, $msg->current, 1, 'GAUGE', 0, 4100); # Rajout. Dan
}
...
Montage en test
Source
Archive source complète à copier dans répertoire package du SDK (Kamikaze 7.09): xpl-senseurs.tgz
// Aquisition senseurs i2C Fonera par messages xPL /* Lecture sondes DS1621 + Max128 sur interface I2C GPIO Fonera */ /* Envoi par message xPL la t° DS1621 et canaux CAN 0 à 6 MAX128 */ /* Inspiré de "Un bus i2c pour La Fonera" */ /* http://www.lefinnois.net/wp/index.php/2007/05/05/un-bus-i2c-pour-la-fonera/ */ /* Par domos domos78<at>free<point>fr - 24/8/2009 */ /* http://vesta.homelinux.net/Aquisition_senseurs_I2C_Fonera_par_messages_xPL */ /*--------------------------------------------------------------------------------------*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <syslog.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include "i2c-dev.h" // Fichier en locale. #define I2C_SLAVE 0x0703 // Change slave address #define DEVICE "/dev/i2c-0" #define ds1621_addr 0x9E >> 1 // Adr. i2c DS1621 (A0, A1, A2 à Vcc) #define max128_addr 0x50 >> 1 // Adr. i2c MAX128 #define BROADCASTIP "192.168.0.255" // Adr. de broadcast sur réseau local. #define HUBPORT 3865 // Port HUB xPL #define XPLMSGSOURCE "domos-fonera.i2cbus" #define XPLMSGTARGET "*" #define XPLMSGDEVICE1 "fonera_ds1621" #define XPLMSGDEVICE2 "fonera_max128" // Variables i2c static int i2c_fd ; // Variables socket int sockxpl ; struct sockaddr_in address ; int enabled = 1 ; // Flags mode debug activer à 1. int debug =0 ; /*------------------------------------------------------------------------------*/ /* Fonctions i2c */ /*------------------------------------------------------------------------------*/ void i2c_init() { if ((i2c_fd = open(DEVICE, O_RDWR)) < 0) { syslog(LOG_ERR, "Erreur ouverture port: %s (%d)\n", strerror(errno), errno); exit(EXIT_FAILURE); } } /*------------------------------------------------------------------------------*/ void SelectSlave(unsigned char slaveaddr) { if (ioctl(i2c_fd, I2C_SLAVE, slaveaddr) < 0) { syslog(LOG_ERR, "Erreur selection esclave i2c: %s (%d)\n", strerror(errno), errno); close ( i2c_fd ) ; exit(EXIT_FAILURE) ; } } /*------------------------------------------------------------------------------*/ /* Fonctions DS1621 */ /*------------------------------------------------------------------------------*/ int i2c_init_ds1621(unsigned char addr) { int ack; char buff[10] ; errno = 0 ; SelectSlave(addr) ; buff[0] = 0xAC ; buff[1] = 0x01 ; ack= write( i2c_fd, buff, 2 ); if ( ack< 0 ) return ack ; // ack < 0x00 si erreur et errno contient n°erreur ou message dans strerror(errno). /* start temperature conversion */ buff[0] = 0xEE ; write( i2c_fd, buff, 1 ); sleep(1) ; return 1 ; } /*------------------------------------------------------------------------------*/ double i2c_gettemp_ds1621(unsigned char addr) { int k, count, slope, temp; char buff[10] ; SelectSlave(addr) ; /* stop conversion */ errno = 0 ; buff[0] = 0x22 ; if ( write( i2c_fd, buff, 1 ) < 0 ) return 255 ; // Write retourne -1 et strerror(errno))='Remote I/O error' si adr. i2c non connectée. else { /* Temperature reading (1 Celsius degree precision) */ buff[0] = 0xAA ; write( i2c_fd, buff, 1 ); read(i2c_fd, buff, 1) ; temp = buff[0] ; /* Counter reading (fraction of degree) ) */ buff[0] = 0xA8 ; write( i2c_fd, buff, 1 ); read(i2c_fd, buff, 1) ; count = buff[0] ; /* slope reading */ buff[0] = 0xA9 ; write( i2c_fd, buff, 1 ); read(i2c_fd, buff, 1) ; slope = buff[0] ; k = temp; if (slope != 0) { k = temp*100-25+(100*(slope-count))/slope; } /* start temperature conversion */ buff[0] = 0xEE ; write( i2c_fd, buff, 1 ); return (float)k/100 ; } } /*------------------------------------------------------------------------------*/ /* Fonctions MAX128 */ /*------------------------------------------------------------------------------*/ int i2c_init_max128(int device, char addr) { int ack ; char buff[255]; errno = 0 ; SelectSlave(addr) ; buff[0] = 0x88 ; ack = write( device, buff, 1 ); return ack ; // ack < 0x00 si erreur et errno contient n°erreur ou message dans strerror(errno). } /*------------------------------------------------------------------------------*/ int i2c_read_max128(int device, char addr, int channel) { int ack ; char buff[255]; SelectSlave(addr) ; buff[0]=0x88 + (channel << 4) ; ack = write( device, buff, 1 ) ; // Init. max128 channel , 0 à vref (4.096V), pas de power down 0x1 000 10 00. usleep(10000) ; errno = 0 ; ack = read(device, buff, 2) ; if ( ack < 0 ) return ack ; // ack < 0x00 si erreur et errno contient n°erreur ou chaine strerror(errno). else return ( (unsigned char) buff[0] << 4 ) + ( (unsigned char) buff[1] >> 4 ) ; // 0xFA et 0x20 => 0xFA2 } /*------------------------------------------------------------------------------*/ /* Fonctions socket */ /*------------------------------------------------------------------------------*/ int sockxpl_init() { /* Create the UDP socket */ int socketudp ; if ((socketudp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { syslog(LOG_ERR, "Erreur ouverture socket: %s (%d)\n", strerror(errno), errno) ; exit(EXIT_FAILURE) ; } setsockopt(socketudp, SOL_SOCKET, SO_BROADCAST, &enabled, sizeof enabled) ; //Droit broadcast. /* Construct the server sockaddr_in structure */ memset(&address, 0, sizeof(address)); /* Clear struct */ address.sin_family = AF_INET; /* Internet/IP */ address.sin_addr.s_addr = inet_addr(BROADCASTIP); /* IP address */ address.sin_port = htons(HUBPORT); /* Hub port */ return socketudp ; } // Broadcast message xpl int sendxplmsg(char msg[]) { /* Send the msg to the server */ int msglen ; msglen = strlen(msg); if (debug) printf("%s\n", msg) ; if (sendto(sockxpl, msg, msglen, 0, (struct sockaddr *) &address, sizeof(address) ) != msglen) { syslog(LOG_ERR, "Erreur envoie message xPL: %s (%d)\n", strerror(errno), errno) ; return 0 ; } return 1 ; } // Broadcast message sensor.basic xpl int sendxplsensorbasicmsg(char msgtype[],char device[], char type[], char current[]) { char msg[1024] ; char msgheader[512] ; // Entête du message sprintf( msgheader, "%s\n{\nhop=1\nsource=%s\ntarget=%s\n}\nsensor.basic\n{\n", msgtype, XPLMSGSOURCE, XPLMSGTARGET) ; sprintf( msg, "%sdevice=%s\ntype=%s\ncurrent=%s\n}\n", msgheader, device, type, current) ; if ( sendxplmsg(msg) ) return 1 ; else return 0 ; } /*------------------------------------------------------------------------------*/ /* MAIN */ /*------------------------------------------------------------------------------*/ int main ( int argc, char ** argv ) { char tempDS[8] ; char Vref[8] ; char tempLM35[8] ; char lumIR[8] ; char lumLDR[8] ; char Humidity[8] ; int no_channel ; // Canaux Max128. int channel[8] ; openlog("xpl-senseurs", LOG_PID, LOG_USER) ; // Init i2c. i2c_init() ; // Init ds1621. if ( i2c_init_ds1621(ds1621_addr) < 0 ) { syslog(LOG_ERR, "Erreur init DS1621 addr 0x%x: '%s', Abandon programme !", ds1621_addr, strerror(errno)); close ( i2c_fd ); exit(1) ; } // Init max128. if ( i2c_init_max128(i2c_fd, max128_addr) < 0 ) { syslog(LOG_ERR, "Erreur init MAX128 addr 0x%x: '%s', Abandon programme !", max128_addr, strerror(errno)); close ( i2c_fd ); exit(1) ; } // Lecture temp. ds1621. sprintf(tempDS, "%2.1f", i2c_gettemp_ds1621(ds1621_addr) ) ; // Lecture canaux ADC max128 . for(no_channel=0; no_channel<6; no_channel++) channel[no_channel] = i2c_read_max128(i2c_fd, max128_addr, no_channel) ; close (i2c_fd); sprintf(Vref, "%2.2f", (float) channel[0]/1000 ) ; sprintf(lumIR, "%d", channel[1] ) ; sprintf(lumLDR, "%d", channel[2] ) ; sprintf(tempLM35, "%2.1f", (float) (channel[4] - channel[3]) * 0.093 ) ; sprintf(Humidity, "%2.1f", (float) channel[5]/3189*100 ) ; if (debug) printf("TempDS=%s° Vref=%sV(%d) LumIR=%s LumLDR=%s TempLM35=%s°(%d) Hum=%s%%(%d)\n", tempDS, Vref, channel[0], lumIR, lumLDR, tempLM35, channel[4] - channel[3], Humidity, channel[5] ) ; // Init socket. sockxpl = sockxpl_init() ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE1, "temp", tempDS) ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE2, "voltage", Vref) ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE2, "lumir", lumIR) ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE2, "luminosity", lumLDR) ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE2, "temp", tempLM35) ; sendxplsensorbasicmsg("xpl-stat", XPLMSGDEVICE2, "humidity", Humidity) ; close(sockxpl) ; exit(0) ; } /*--------------------------------------------------------------------------------------*/
25/8/2009
