Le prototype de mesure de température, basé un microcontrôleur PIC16F628A, a exactement les mêmes fonctionnalités que celui de l’article Arduino : Prototype de sonde de température, Oregon 2.1 compatible, à consommation réduite.

Domoticz, le logiciel libre de domotique équipé d’une passerelle radio RFXCOM n’y voit d’ailleurs aucune différence.
La circuiterie est par contre nettement plus simple . La temporisation est mise en œuvre dans le PIC  en s’appuyant sur son watchdog, faible consommateur d’énergie.

Pendant les périodes d’inactivité, l’alimentation du module RF et de la sonde DS180B20 de température est coupée à l’aide d’un optocoupleur 4N35.

Le tableau ci-dessous compare la consommations dudit prototype à celui basé sur l’Arduino et ce dans un contexte de mesures transmises toutes les 45 secondes, une prise et transmission d’une mesure prenant environ une seconde et demie.

On peut voir que la version PIC basée sur un PIC16F628A se montre 13,5 fois moins énergivore que celle basée sur un Arduino.

Le programme C du prototype Arduino a été adapté pour être accepté par le compilateur XC8 de Microchip, version gratuite. Pour l’essentiel, les adaptations se sont portées sur les déclarations de données de type « byte » modifiées en type « unsigned char » et sur les opérations en virgule flottante grandes, consommatrices de mémoire, toutes remplacées par des opérations sur des entiers.

Voici le schéma du montage.

Le code du programme est présenté ci-dessous.

#include <xc.h>
#include <math.h>

#pragma config CP=OFF, WDTE=ON, PWRTE=ON , MCLRE=ON, FOSC=INTOSCIO
/*
CP                ON : impossible de relire
PWRTE            ON    Timer reset sur power on en service
WDTE            ON    Watch-dog en service
FOSC            LP    Oscillateur quartz basse vitesse
XT    Oscillateur quartz moyenne vitesse
HS    Oscillateur quartz grande vitesse
EXTRCIO        ??? Oscillateur RC sur RA7 fonct. IO RA6
EXTRCCLK    ??? Oscillateur RC sur RA7 CLKOUT sur RA6
EC    ??? fonct. IO sur RA6 et RA7
INTOSCIO    Oscillateur interne fonct. IO sur RA6 et RA7
INTOSCCLK    Oscillateur interne sortir sur RA6 et fonct. IO SUR RA7
MCLRE            OFF ? -> RA5 input
LVP_OFF            RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming
(programmation basse tension off)
LVP_ON             RB4/PGM pin has PGM function, low-voltage programming enabled
_BODEN_ON/off   BOD enabled (brown out reset )
*/
#define _XTAL_FREQ 4000000 // Internal oscillator
#define OPTIONALVAL 0x0F
#define INTERMASK _INTCON_INTE_MASK + _INTCON_GIE_MASK
#define TX_PIN RA2
#define TRIS_TX_PIN TRISA2
#define POWER_PIN RA1
#define TRIS_POWER_PIN TRISA1
#define TIME 512
#define TWOTIME TIME*2
#define SEND_HIGH() TX_PIN = 1
#define SEND_LOW() TX_PIN = 0
#define POWER_ON() POWER_PIN = 1
#define POWER_OFF()POWER_PIN = 0
#define Skip_ROM 0xCC
#define Convert_T 0x44
#define Read_scratchpad 0xBE
#define Port_18B20 PORTBbits.RB3
#define Tx_18B20 TRISB3 = 0
#define Rx_18B20 TRISB3 = 1

unsigned char OregonMessageBuffer[8] ={0xEA,0x4C,0x20,0xBA,0x00,0x35,0xD0,0x03};
unsigned char temp[9];
static unsigned short tempLow = 0x90;
static unsigned short tempHigh = 0xFC;

inline void sendZero(void)
{
SEND_HIGH();
__delay_us(TIME);
SEND_LOW();
__delay_us(TWOTIME);
SEND_HIGH();
__delay_us(TIME);
}

inline void sendOne(void)
{
SEND_LOW();
__delay_us(TIME);
SEND_HIGH();
__delay_us(TWOTIME);
SEND_LOW();
__delay_us(TIME);
}

inline void sendQuarterMSB(const unsigned char data)
{
((data) & (1 <<(4))) ? sendOne() : sendZero();
((data) & (1 <<(5))) ? sendOne() : sendZero();
((data) & (1 <<(6))) ? sendOne() : sendZero();
((data) & (1 <<(7))) ? sendOne() : sendZero();
}

inline void sendQuarterLSB(const unsigned char data)
{
((data) & (1 <<(0))) ? sendOne() : sendZero();
((data) & (1 <<(1))) ? sendOne() : sendZero();
((data) & (1 <<(2))) ? sendOne() : sendZero();
((data) & (1 <<(3))) ? sendOne() : sendZero();
}

void sendData(unsigned char *data, unsigned char size)
{
for(unsigned char i = 0; i < size; ++i)
{
sendQuarterLSB(data[i]);
sendQuarterMSB(data[i]);
}
}

inline void sendPreamble(void)
{
unsigned char PREAMBLE[]={0xFF,0xFF};
sendData(PREAMBLE, 2);
}

inline void sendPostamble(void)
{
sendQuarterLSB(0x00);
}

void sendOregon(unsigned char *data, unsigned char size)
{
sendPreamble();
sendData(data, size);
sendPostamble();
}

inline void setType(unsigned char *data, unsigned char* type)
{
data[0] = type[0];
data[1] = type[1];
}

inline void setChannel(unsigned char *data, unsigned char channel)
{
data[2] = channel;
}

inline void setId(unsigned char *data, unsigned char ID)
{
data[3] = ID;
}

void setBatteryLevel(unsigned char *data, unsigned char level)
{
if(!level) data[4] = 0x0C;
else data[4] = 0x00;
}

void setTemperature(unsigned char *data, int tempInt)
{
// Set temperature sign
if(tempInt < 0)
{
data[6] = 0x08;
tempInt *= -1;
}
else
{
data[6] = 0x00;
}
// Determine decimal and float part
int td = tempInt/100;
int tf = (tempInt-100*td)/10;
int tempFloat = tempInt-100*td – tf*10;
// Set temperature decimal part
data[5] = (td << 4);
data[5] |= tf;
// Set temperature float part
data[4] |= (tempFloat << 4);
}

int Sum(unsigned char count, const unsigned char* data)
{
int s = 0;
for(unsigned char i = 0; i<count;i++)
{
s += (data[i]&0xF0) >> 4;
s += (data[i]&0xF);
}
if(count > 127 )s += (data[count]& 0xF0) >> 4;
return s;
}

void calculateAndSetChecksum(unsigned char* data)
{
int s = ((Sum(6, data) + (data[6]&0xF) – 0xa) & 0xff);

data[6] |=  (s&0x0F) << 4;     data[7] =  (s&0xF0) >> 4;
}

char Reset_18B20(void) {
Tx_18B20; // output
Port_18B20 = 0; // set to low (0)
__delay_us(480); // 1 wire require time delay
Rx_18B20; // input
__delay_us(60); // 1 wire require time delay
if (Port_18B20 == 0) { // if there is a presence pluse
__delay_us(480);
return 0; // return 0 ( 1-wire is presence)
} else {
__delay_us(480);
return 1; // return 1 ( 1-wire is NOT presence)
}
}

void Write_18B20 (char Cmd){
unsigned char i;
Rx_18B20; // set pin# to input (1)
for(i = 0; i < 8; i++){
if((Cmd & (1<<i))!= 0) {
Tx_18B20; // set pin# to output (0)
Port_18B20 = 0; // set pin# to low (0)
__delay_us(1); // 1 wire require time delay
Rx_18B20; // set pin# to input (release the bus)
__delay_us(60); // 1 wire require time delay
} else {
Tx_18B20; // set pin# to output (0)
Port_18B20 = 0; // set pin# to low (0)
__delay_us(60); // 1 wire require time delay
Rx_18B20; // set pin# to input (release the bus)
}
}
}

char Read_18B20 (){
unsigned char i,result = 0;
Rx_18B20; // TRIS is input(1)
for(i = 0; i < 8; i++){
Tx_18B20; // TRIS is output(0)
Port_18B20 = 0; // generate low pluse for 2us
__delay_us(2);
Rx_18B20; // TRIS is input(1) release the bus
if(Port_18B20 != 0) result |= 1<<i;
__delay_us(60); // wait for recovery time
}
return result;
}

int getTemperature(){
unsigned char shift_reg, data_bit, sr_lsb, fb_bit, i, j;
tempLow = 0x90; // -55.000
tempHigh = 0xFC;
if(!Reset_18B20()){
Write_18B20(Skip_ROM);
Write_18B20(Convert_T);
__delay_ms(750);
Reset_18B20();
Write_18B20(Skip_ROM);
Write_18B20(Read_scratchpad);
for(i=0;i<9;i++) temp[i] = Read_18B20();
shift_reg=0;
for (i=0; i<8; i++){
for(j=0; j<8; j++){
data_bit = (temp[i]>>j)&0x01;
sr_lsb = shift_reg & 0x01;
fb_bit = (data_bit ^ sr_lsb) & 0x01;
shift_reg = shift_reg >> 1;
if (fb_bit)shift_reg = shift_reg ^ 0x8c;
}
}
if(temp[8] == shift_reg){
tempLow = temp[0];
tempHigh = temp[1];
return 1;
}
}
return 0;
}

void oneTime()
{
POWER_ON();
setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
CLRWDT();
if(getTemperature()== 0)return;
CLRWDT();
setTemperature(OregonMessageBuffer,(10*(tempHigh<<8 | tempLow))/16);
calculateAndSetChecksum(OregonMessageBuffer);
CLRWDT();
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
SEND_LOW();
__delay_us(TWOTIME*8);
CLRWDT();
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
SEND_LOW();
POWER_OFF();
}

void main(void) {
CMCON = _CMCON_CM0_MASK + _CMCON_CM1_MASK + _CMCON_CM2_MASK ; // comparator RA0 -> RA3 OFF pour utilisation IO
TRIS_TX_PIN = 0; // output module FR
TRIS_POWER_PIN = 0; // output 4N35
OPTION_REG = OPTIONALVAL;
TRISB=0;
unsigned char ID[] = {0xEA,0x4C};
setType(OregonMessageBuffer, ID);
setChannel(OregonMessageBuffer, 0x20);
setId(OregonMessageBuffer, 0xBA);
SEND_LOW();
while(1) {
oneTime();
SLEEP();SLEEP();SLEEP();SLEEP();SLEEP(); // +/- 10 sec.
SLEEP();SLEEP();SLEEP();SLEEP();SLEEP(); // +/- 10 sec.
}
return;
}