Generation signaux X10-RF avec un AVR

De MicElectroLinGenMet.

(Retour Accueil ou Liste des articles)

Sommaire

Description

Le système X10 a été développé en 1975. Ce système est le fruit des travaux effectués par la société écossaise Pico Electronics dans le but de pouvoir contrôler différents appareils électriques au sein d’une habitation.
(Source: Wikipedia)

Le système X10 peuvent être couplés avec des télécommandes radio qui donnent des ordres à des modules pilotés par courants porteurs par l'intermédiaire d'un convertisseur fréquence radio (433 MHz) en fréquence porteuse.


Cet article décrit la mise en oeuvre d'un Microcontroleurs Atmel AVR pour générer les signaux X10-RF.
Inspiré par la documentation sur le protocole X10-RF: X10_RF_Receiver.pdf


Voir aussi page: Generation signaux Chacon-RF avec un AVR

Shéma de base

Les composants principaux sont l'ATtiny13, microcontroleur AVR économique de 8 broches et un module émetteur à 433.92MHz, le TX-433-SAW, lui aussi trés abordable.

Ce montage de test ne fera rien d'autre qu'emettre une commande X10 ON suivi d'une X10 OFF toutes les minutes pour vérifier le fonctionnement. La réception a uniquement été testée sur un récepteur RFXCOM USB sous Linux avec le programme xpl-rfxcom de la librairie xpl-perl.


Programme de l'AVR

Source C à compiler avec avr-gcc avec ce Makefile.
L'ATtiny13 utilise dans ce programme sont oscillateur interne avec les "fuses" laissés aux valeurs par defaut. La précision est suffisante pout générer les tempo. necessaires.

Ce microcontroleur possédant 5 ports IO, des interruptions externes, timer, des CAN, il sera possible de brancher différents capteurs pour générer des messages X10 en conséquence (la LED n'étant là que pour visualiser l'émission X10-RF, il sera possible de l'enlever pour libérer un port).
Voir cette page Internet pour des exemples de programmation C de l'ATtiny13: http://avrbasiccode.wikispaces.com/


Source du programme main.c:

/*-----------------------------------------------------------------------------------------------*/
/*  Emetteur X10_RF ATtiny13 (17/6/2009)						*/
/* Par domos78 at free point fr								*/
/* Envoie une commande X10 ON suivi d'une X10 OFF toutes les minutes			*/
/* Utilisation de "util/delay.h" pour la tempo						*/
/* Description trame X10-RF: http://www.printcapture.com/files/X10_RF_Receiver.pdf	*/
 
#include <avr/io.h>
#include <avr/pgmspace.h>
 
// ATTiny13's internal oscillator defaults to 9.6 MHz. Prescaler set to divide by 8 (9.6/8=1.2MHz).
#define F_CPU 1200000UL
#include <util/delay.h>
 
#define X10PIN PB3		// Bit du port sur lequel est connectée la pin x10.
#define X10PORT PORTB		// Port de la sortie X10.
#define LEDPIN PB4		// Bit du port sur lequel est connectée la LED.
#define LEDPORT PORTB		// Port de la sortie LED.
 
#define ON  0x00
#define OFF 0x01
#define NBTRAMES 0x06
 
// Macros
#define setbit(PORT, BIT)	{ PORT |= _BV(BIT); }
#define clrbit(PORT, BIT)	{ PORT &= ~_BV(BIT); }
#define togbit(PORT, BIT)	{ PORT ^= _BV(BIT);  }
 
#define Led_Off()		{ setbit(LEDPORT, LEDPIN); }
#define Led_On()		{ clrbit(LEDPORT, LEDPIN); }
 
// --------------------------------------------------------------------------------------
// Tableaux en mémoire programme (PROGMEM) à relire avec fonction "pgm_read_byte"
// House codes: A à P
const unsigned char house_code[] PROGMEM = { 0x60, 0x70, 0x40, 0x50, 0x80, 0x90, 0xA0, 0xB0, 0xE0, 0xF0, 0xC0, 0xD0, 0x00, 0x10, 0X20, 0x30 } ;
 
// Unit codes: 1 à 8 (code 9 à 16, idem + mettre à '1' bit 2 house code).
const unsigned char unit_code[] PROGMEM = { 0x00, 0x10, 0x08, 0x18, 0x40, 0x50, 0x48, 0x58 } ;
 
// -------------------------------------- Fonctions X10 ---------------------------------
// Envoi entête:
void x10_sendpreamble(void)
{
	clrbit(X10PORT, X10PIN) ;		// X10PIN à 0.
	_delay_ms(40) ;
	setbit(X10PORT, X10PIN) ;		// X10PIN à 1.
	_delay_ms (9) ;
	clrbit(X10PORT, X10PIN) ;		// X10PIN à 0.
	_delay_ms(4) ;
	_delay_us(500) ;
}
 
// --------------------------------------------------------------------------------------
// Envoi bit:
void x10_sendbit(void)
{
	setbit(X10PORT, X10PIN) ;		// X10PIN à 1.
	_delay_us(560) ;
	clrbit(X10PORT, X10PIN) ;		// X10PIN à 0.
	_delay_us(560) ;
}
 
 
// --------------------------------------------------------------------------------------
// Envoi d'un octet X10 RF
void x10_sendbyte(unsigned char X10octet)
{
	unsigned char nobit, valbit;
	for (nobit=0; nobit<=7; nobit++)
	{
		valbit=X10octet & 0x80;
		X10octet=(X10octet << 1);
 
		x10_sendbit();				// On envoi un bit
		if ( valbit )				// Si bit à 1 pause + longue.
		{
			_delay_us(600) ;
			_delay_us(600) ;
		}
	}
}
 
// --------------------------------------------------------------------------------------
// Envoie trame X10, House Code / Unit Code / Action (ON/OFF)
void x10_sendcmd(unsigned char House, unsigned char Unit, unsigned char Action)
{
	unsigned char hc, uc ;
	unsigned char bcle ;
 
	hc = pgm_read_byte(house_code + House - 0x41) ;		// Lit code correspondant dans tableau house_code.
	if (Unit > 0x08) hc |= 0x04 ;				// Si Unit Code > 8 alors bit 2 House Code à 1.
 
	uc = pgm_read_byte(unit_code + (--Unit & 0x07) ) ;    	// Lit code correspondant dans tableau unit_code.
								// Bit 3 à 0 (doit être < 8).
 
	if (Action) uc |= 0x20 ;				// si Action = OFF, mise à '1' bit 5 unit code.
 
	// Envoie commande X10 hc/uc.
	for (bcle = 0; bcle < NBTRAMES; bcle++)			// Envoie la trame NBTRAMES X.
	{
		x10_sendpreamble() ;
		x10_sendbyte(hc) ;		// house code
		x10_sendbyte(~hc) ;		// ~house code
		x10_sendbyte(uc) ;		// unit code
		x10_sendbyte(~uc) ;		// ~unit code
		x10_sendbit() ;
	}
}
 
// --------------------------------------------------------------------------------------
// Pause de n secondes
void delay_s(unsigned short sec)
{
	sec*=10 ;
	while(sec > 0)
	{
		_delay_ms(100) ;		// _delay_ms pas précise pour valeur trop grande.
		--sec ;
	}
}
 
// --------------------------------------------------------------------------------------
// -------------------------------------- Main ------------------------------------------
int main(void) 
{
 
	unsigned char HouseCode = 'A' ;		// Commande A3 ON/OFF
	unsigned char UnitCode = 3 ;
 
	setbit(DDRB, X10PIN) ;			// X10PIN en sortie.
	setbit(DDRB, LEDPIN) ;			// LEDPIN en sortie.
 
	clrbit(X10PORT, X10PIN) ;		// X10PIN à 0.
 
	while (1) 
	{
		Led_On() ;				// Led ON.
 
		// Envoi commande ON
		x10_sendcmd(HouseCode, UnitCode, ON) ;	// Envoie commande X10 ON.
		delay_s(2) ;				// Pause 2s.
		// Envoi commande OFF
		x10_sendcmd(HouseCode, UnitCode, OFF) ;	// Envoie commande X10 OFF.
 
		Led_Off() ;				// Led OFF.
 
		delay_s(60) ;				// Pause 60s.
	}
}
 
/*-----------------------------------------------------------------------------------------------*/


Réception sous Linux

Le rfxcom est connecté sur un port USB. Il est vu comme périphérique usb-serial sous Linux.


Vue de la réception avec xpl-rfxcom:

$ xpl-rfxcom -i eth0 -B 4800 /dev/rfxcom
xpl-trig/x10.basic: bnz-rfxcom.vesta -> * - on a3
xpl-trig/x10.basic: bnz-rfxcom.vesta -> * - off a3


Messages xPL reçus avec xPL_Logger:

09/06/15 12:03:46 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a3' }                                                      
09/06/15 12:03:49 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a3' }                                             
09/06/15 12:03:58 xpl-stat {cdp1802-logger.C0A80004fuijjvts *} hbeat.app { interval='5' port='52232' remote-ip='192.168.0.4' version='1.1' }
09/06/15 12:04:02 xpl-cmnd {bnz-sender.vesta *} osd.basic { command='write' text='Mon Jun 15 12:04:01 CEST 2009' row='1' }                  
09/06/15 12:04:08 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='temp' current='22' }                              
09/06/15 12:04:08 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='humidity' current='55' string='comfortable' }     
09/06/15 12:04:08 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='battery' current='90' units='%' }                 
09/06/15 12:04:09 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='temp' current='22' }
09/06/15 12:04:09 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='battery' current='90' units='%' }
09/06/15 12:04:47 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='temp' current='22' }
09/06/15 12:04:47 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='humidity' current='55' string='comfortable' }
09/06/15 12:04:47 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='battery' current='90' units='%' }
09/06/15 12:04:51 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a3' }
09/06/15 12:04:53 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a3' }
09/06/15 12:05:03 xpl-cmnd {bnz-sender.vesta *} osd.basic { command='write' text='Mon Jun 15 12:05:02 CEST 2009' row='1' }
09/06/15 12:05:26 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='temp' current='22' }
09/06/15 12:05:26 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='humidity' current='55' string='comfortable' }
09/06/15 12:05:26 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thgr228n.ab' type='battery' current='90' units='%' }
09/06/15 12:05:27 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='temp' current='22' }
09/06/15 12:05:27 xpl-trig {bnz-rfxcom.vesta *} sensor.basic { device='thwr288a.de' type='battery' current='90' units='%' }
09/06/15 12:05:55 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a3' }
09/06/15 12:05:58 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a3' }


Photos du montage

Montage sur plaque d'essais
ATtiny13 et module HF 433.92MHz



Exemple pratique

Il suffira de rajouter deux composants passifs et un ILS au montage précédent pour réaliser un détecteur d'ouverture X10-RF. Quant au programme (X10-RF_ILS_attiny13.c), une fonction gérant la détection d'ouverture ou de fermeture par interruption a été rajoutée à la version de base.

En résumé, à chaque changement d'état de l'ILS, le programme enverra un ordre X10-RF 'A3 ON' pour une ouverture et 'A3 OFF' pour une fermeture de porte par exemple.
En dehors de ces interruptions la boucle principale enverra le statut en cours de l'ILS avec la commande 'A4 ON' ou 'A4 OFF' par intervalle régulier, 60s dans le source pour les tests (15 ou 30mn dans la pratique).

Restera à améliorer la consommation sur pile en forçant le micro-contrôleur en veille par exemple si le mode "heartbeat' (message 'A4 ON/OFF') n'est pas necessaire.

Le programmateur utilisant aussi la broche PB1 (INT0), j'ai été obligé d'enlever temporairement le condensateur de 220nF sinon la programmation ne fonctionnait pas correctement.

Messages xPL reçus avec xPL_Logger:

09/06/24 00:05:11 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a4' }	=> HeartBeat
09/06/24 00:06:10 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a4' }
09/06/24 00:06:46 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a3' }		=> Ouverture porte.
09/06/24 00:07:12 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a4' }
09/06/24 00:08:12 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='on' device='a4' }
09/06/24 00:09:04 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a3' }	=> Fermeture porte.
09/06/24 00:09:12 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a4' }
09/06/24 00:10:12 xpl-trig {bnz-rfxcom.vesta *} x10.basic { command='off' device='a4' }


Test avec un ILS sous verre



14/06/2009