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



Relevé ensoleillement (TSL260)
Luminosité (LDR)


Montage en test

Fonera + interface i2c/ds1621 + Max128 sur Labdec
Max128 + capteurs


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.091) - 0.101 ) ;	// t° = ( (25.12/275) x Vdiff) ) - 0.101
 
	sprintf(Humidity, "%2.1f", (float) (8/229 * channel[5]) - 10.262 ) ;			// h = 8/229V - 10.262
 
	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