Hem triat el sensor de temperatura i humitat RHT03 de l'empresa Maxdetect.

El connexionat del sensor és molt senzill. Té quatre potes, dues són per a l'alimentació, una és la que es connecta amb el microcontrolador i la restant no es connecta enlloc. L'esquema és el següent:

Com la mateixa pota serveix d'entrada i de sortida, hi ha moments que és el sensor qui envia senyal i en altres moments és el microcontrolador qui ho fa. Però en algun moment la pota queda sense connexió efectiva i, per això, cal posar-hi una resistència que manté la pota a 5 V si no està treballant.
El sensor pot llegir correctament valors de temperatura entre -40 °C i +80 °C així com humitats relatives entre 0 i 100 % amb una xifra decimal en tots dos casos. El sensor envia una cadena de 40 bits com la següent:
0000 0010 1000 1100 0000 0001 0101 1111 1110 1110
Els valors s'envien en l'ordre següent:
| Ordre | Número de bits | Contingut | Exemple | ||
| Bits | Decimal | Lectura | |||
| 1 | 16 | Humitat | 0000 0010 1000 1100 | 652 | 65,2 % |
| 2 | 16 | Temperatura | 0000 0001 0101 1111 | 351 | 35,1 °C |
| 1000 0000 0110 0101 | 101 | -10,1 °C | |||
| 3 | 8 | Suma de comprovació | 1110 1110 | ||
El primer bit de la temperatura no forma part del valor sinó que indica el signe. Quan la temperatura és positiva aquest bit és zero i si és negativa el bit és 1. El valor es calcula amb els altres 15 bits.
La suma de comprovació es calcula sumant els quatre bytes de la temperatura i la humitat. El resultat d'aquesta suma (prescindim de si hi ha un novè bit) és el valor que s'envia. Per exemple en el cas següent
0000 0010 1000 1100 0000 0001 0101 1111 1110 1110
seria:
| 0000 0010 | ||
| 1000 1100 | ||
| 0000 0001 | ||
| 0101 1111 | ||
| 1110 1110 |
La seqüència d'un enviament seria la de la figura seüent:

Abans i després de l'enviament, el microcontrolador i el sensor estan funcionant com a entrades i és la resistència qui manté l'estat a 5 V (tram en color blau).
Quan el microcontrolador vol llegir el sensor (color taronja) ha de posar-se en mode sortida i enviar un zero durant 1 ms o més. Tot seguit es posa a 1 i torna a passar a mode entrada per esperar que el sensor respongui.
El sensor envia primer un senyal de que inicia la transmissió de dades (color rosa) consistent en un 0 i un 1 de 80 μs cada un. Tots els temps que aquí s'indiquen són aproximats.
Després el sensor envia els 40 bits (color verd). Un bit a 0 està format per un valor 0 d'uns 50 μs seguit d'un valor 1 d'uns 26 a 28 μs. En canvi, un bit a 1 està format per un valor 0 d'uns 50 μs seguit d'un valor 1 d'uns 70 μs.
Un cop ha acabat la transmissió, el sensor es posa a 0 un moment i després torna a passar a mode recepció.
A continuació hi ha un programa de prova del sensor. El programa llegeix el valor del sensor (connectat a RA5) i mostra a una pantalla sèrie la humitat i la temperatura. També mostra la suma de comprovació. Per assegurar que la lectura es fa correctament s'ha optat per fer anar el rellotge del microcontrolador a una velocitat més alta durant el temps de fer la lectura del sensor i després es torna a la velocitat normal.
#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF #pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz #define Sens RA5 // Li assigna un nom a l'adreça del sensor
unsigned int Valor; // Variable de treball unsigned int Error; // Error de lectura unsigned int Lectura[3]; // Valor llegit // Lectura[0] és la suma de comprovació // Lectura[1] és la temperatura // Lectura[2] és la humitat char Digits[5]; // Vector amb el número dígit a dígit // Digits[0] és el decimal char Negatiu; // Guarda si el valor de la temperatura és negatiu o positiu
// Definició de les funcions que farem servir char Sensor(void); // Lectura del sensor. dona 0 si la suma de control és correcta void EnviaL(char Caracter); // Envia un caràcter void Esborra(void); // Esborra la pantalla i posa el cursor a l'inici void Cursor(char Filera, char Columna); // Posiciona el cursor (filera 1 a 2 i columna 1 a 32, segons pantalla)
void main (void) {
ANSEL = 0b00000001; // Configura AN0 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
TRISC = 0; // Tot el port C és de sortida
TRISB = 0; // Tot el port B és de sortida
TRISA = 0b11011111; // Tot el port A és d'entrada excepte, de moment, RA5
PORTA = 0b00100000; // RA5 a 1
T1CON = 0b00100001; // Configuració de Timer1
// Com a temporitzador basat en rellotge
// 10 - Factor d'escala de 4
// TMR1L s'incrementarà cada 2 us
TXSTAbits.BRGH = 1; // Configuració de velocitat
BAUDCTLbits.BRG16 = 0; // Paràmetre de velocitat de 8 bits
SPBRG = 25; // Velocitat de 9600 baud
TXSTAbits.SYNC = 0; // Comunicació asíncrona
TXSTAbits.TX9 = 0; // Comunicació de 8 bits
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
__delay_ms(1000); // Retard d'1 s
Esborra(); // Esborra la pantalla i posa el cursor a l'inici
while (1) { // Inici del bucle de programa
OSCCON = 0b01111000; // IRCF = 111, rellotge a 8 MHz
Error = Sensor();
OSCCON = 0b01101000; // IRCF = 110, rellotge a 4 MHz
if (Error == 0) { // Llegeix el sensor i si és correcte segueix
Valor = Lectura[2]; // Humitat
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Valor % 10;
Valor = Valor / 10;
}
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Digits[j] + '0'; // Li sumem el codi ASCII de 0
} // Això és la humitat multiplicada per 10
char mira0 = 1; // De moment, hem de mirar si el caràcter és zero
for (signed char j = 4; j > 1; j--){ // Els dos darrers zeros els mostrarem sempre
if(mira0 == 1) { // Encara toca comprovar sí és 0?
if(Digits[j] == '0') { // Mirem si el dígit és 0
Digits[j] = ' '; // Si ho és, hi posem un espai
} else {
mira0 = 0; // Si no ho és, ja no cal mirar-ne més
}
}
}
Cursor(1, 1); // Posició
for (signed char j = 4; j > 0; j--){ // 4 dígits (el 5è és el decimal
EnviaL(Digits[j]); // Número
}
EnviaL(','); // Coma
EnviaL(Digits[0]); // Número
EnviaL(' '); // Espai
EnviaL('%'); // Tant per cent
Valor = Lectura[1]; // Temperatura
if (Valor >= 32768) { // Si el bit més significatiu està activat és negatiu
Negatiu = 1; // És negatiu
Valor = Valor -32768; // Agafem el valor absolut
} else {
Negatiu = 0; // És positiu
}
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Valor % 10;
Valor = Valor / 10;
}
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Digits[j] + '0'; // Li sumem el codi ASCII de 0
} // Això és la temperatura multiplicada per 10
char mira0 = 1; // De moment, hem de mirar si el caràcter és zero
for (signed char j = 4; j > 1; j--){ // Els dos darrers zeros els mostrarem sempre
if(mira0 == 1) { // Encara toca comprovar sí és 0?
if(Digits[j] == '0') { // Mirem si el dígit és 0
Digits[j] = ' '; // Si ho és, hi posem un espai
} else {
mira0 = 0; // Si no ho és, ja no cal mirar-ne més
}
}
}
if (Negatiu) {
Digits[4] = '-'; // Si és negatiu, posem el -
}
Cursor(2, 1); // Posició
for (signed char j = 4; j > 0; j--){ // 4 dígits (el 5è és el decimal)
EnviaL(Digits[j]); // Número
}
EnviaL(','); // Coma
EnviaL(Digits[0]); // Número
EnviaL(' '); // Espai
EnviaL(0b11011111); // Graus
EnviaL('C'); // Lletra
Valor = Lectura[0]; // Suma de comprovació
for (signed char j = 0; j < 3; j++){ // 3 dígits (només és un byte)
Digits[j] = Valor % 10;
Valor = Valor / 10;
}
for (signed char j = 0; j < 3; j++){ // 3 dígits
Digits[j] = Digits[j] + '0'; // Li sumem el codi ASCII de 0
}
if (Digits[3] == '0') { // Mirem si el primer dígit és 0
Digits[3] = ' '; // Si ho és, hi posem un espai
if (Digits[2] == '0') { // I mirem si ho és el segon
Digits[2] = ' '; // Si ho és, hi posem un espai
} // Els darrer zero el mostrarem sempre
}
Cursor(1, 11); // Posició
for (signed char j = 2; j >= 0; j--){ // 3 dígits
EnviaL(Digits[j]); // Número
}
} else {
Cursor(1, 1); // Posició
EnviaL('E');
EnviaL('r');
EnviaL('r');
EnviaL('o');
EnviaL('r');
EnviaL(' ');
Error = Error + '0'; // Passa el valor a ASCII
EnviaL(Error); // Número
}
__delay_ms(1000); // Retard d'1 s
}
}
char Sensor(void) {
char Bytes[5]; // Vector per a guardar els cinc bytes que envia el sensor
char Temps; // Guarda el valor de TMR1L
for (signed char j = 0; j < 5; j++){ // 5 bytes
Bytes[j] = 0; // Posem el vector a zero
}
TRISA = 0b11011111; // Tot el port A és d'entrada excepte, de moment, RA5
PORTA = 0b00000000; // RA5 a 0
__delay_ms(2); // Retard de 2 ms
PORTA = 0b00100000; // RA5 a 1
__delay_us(15); // Retard de 15 us
// Un cop activat, esperem la resposta
TRISA = 0b11111111; // Tot el port A és d'entrada, inclosa RA5
// Esperem a rebre el pols d'inici
// Primer un L d'uns 80 us
// Per començar, s'ha de desactivar l'entrada
// i s'ha de reactivar al cap d'entre 70 i 90 us
while (Sens) // S'ha desactivat l'entrada?
; // No, doncs esperem
TMR1H = 0;
TMR1L = 0; // Sí, doncs comencem a comptar el temps
while ((!Sens) && (TMR1L < 45)) // Esperem que s'activi l'entrada
; // O passin més de 90 us
Temps = TMR1L; // Agafa el valor de TMR1L
TMR1H = 0;
TMR1L = 0; // Torna a començar a comptar el temps
if (Temps > 45) { // És més gran
return 1; // Error 1 - L inicial massa llarg
}
if (Temps < 35) { // Mirem que no sigui menor que 70 us
return 2; // Error 2 - L inicial massa curt
}
// L inicial ja està
while (Sens && (TMR1L < 45)) // Esperem a que es desactivi l'entrada
; // O passin més de 90 us
Temps = TMR1L; // Agafa el valor de TMR1L
TMR1H = 0;
TMR1L = 0; // Torna a començar a comptar el temps
if (Temps > 45) { // És més gran
return 3; // Error 3 - H inicial massa llarg
}
if (Temps < 35) { // Mirem que no sigui menor que 70 us
return 4; // Error 4 - H inicial massa curt
}
// H inicial ja està
// Ara hem de rebre els bits
TMR1H = 0;
TMR1L = 0; // Torna a començar a comptar el temps
for (signed char j = 0; j < 5; j++){ // 5 bytes
for (signed char k = 0; k < 8; k++){ // 8 bits a cada byte
Bytes[j] = 2 * Bytes[j]; // Rodem a l'esquerra el bit anterior
// per deixar lloc al nou
// Si no n'hi havia cap no canvia res
// ja que estava a zero
// Esperem a rebre un bit
// Primer un L d'uns 80 us
// Ara l'entrada està a zero
// S'ha d'activar al cap d'entre 10 i 90 us
while ((!Sens) && (TMR1L < 45)) // Esperem que s'activi l'entrada
; // O passin més de 90 us
Temps = TMR1L; // Agafa el valor de TMR1L
TMR1H = 0;
TMR1L = 0; // Torna a començar a comptar el temps
if (Temps > 45) { // És més gran
return 5; // Error 5 - Valor L del bit massa llarg
}
if (Temps < 5) { // Mirem que no sigui menor que 10 us
return 6; // Error 6 - Valor L del bit massa curt
}
// Ja tenim el valor L del bit. Ara esperem un H
while (Sens && (TMR1L < 40)) // Esperem a que es desactivi l'entrada
; // O passin més de 80 us
Temps = TMR1L; // Agafa el valor de TMR1L
TMR1H = 0;
TMR1L = 0; // Torna a començar a comptar el temps
if (Temps > 40) { // És més gran
return 7; // Error 7 - Valor H del bit massa llarg
}
if (Temps < 6) { // Mirem que no sigui menor que 12 us
return 8; // Error 8 - Valor H del bit massa curt
}
// Ja hem comprovat que no sigui massa curt ni massa llarg
// Si és més petit que 38 us és un pols curt, o sigui un 0
// Si és curt no cal fer res, el 0 ja hi és
// Si és més gran que 60 us és un pols llarg, o sigui un 1
if (Temps > 19) { // És més gran que 38 us
// Sí, doncs no és un pols curt
if (Temps > 30) { // És més gran que 60 us
// És un pols llarg
Bytes[j] = Bytes[j] + 1; // Entrem un 1
} else {
return 9; // Error 9 - Valor H del bit incorrecte
}
}
}
}
// Ja tenim els 5 bytes. Fem la suma de comprovació
// Cal sumar els quatre primers bytea
// Però sense portar-ne
Valor = 0; // Primer la posem a zero
for (signed char j = 0; j < 4; j++){ // 4 bytes
Valor = Valor + Bytes[j]; // Sumem un dels quatre elements
if (Valor > 255) { // Mirem si és més gran del que cap a un byte
// Si és més gran, només pot haver activat un bit del segon byte
// el que correspon a 256
Valor = Valor -256; // Restem les que en portàvem
}
}
if (Valor != Bytes[4]) { // No coincideixen
return 10; // Error 10 - Falla la suma de comprovació
}
Lectura[0] = Bytes[4]; // Suma de comprovació
Lectura[1] = 256 * Bytes[2] + Bytes[3]; // Temperatura
Lectura[2] = 256 * Bytes[0] + Bytes[1]; // Humitat
return 0; // Recepció correcta
}
void EnviaL(char Caracter) {
TXREG = Caracter; // Agafa el caràcter i l'envia
__delay_ms(1); // Donem temps
while (PIR1bits.TXIF == 0) // Esperem que s'acabi d'enviar
; // No fem res
}
void Esborra(void) {
EnviaL(254); // Caràcter de control
EnviaL(1); // Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
char Posicio = 0; // Variable per a calcular la posició
if (Filera == 2) {
Posicio = 64; // La primera columna de la segona fila és 64;
}
if (Columna > 0 && Columna < 33) { // Comprovem que sigui un valor raonable
Posicio = Posicio + Columna; // Sumem les adreces
Posicio = Posicio - 1; // Restem 1 perquè numera des de 0
}
Posicio = Posicio + 128; // Posa el bit de posicionat a 1
EnviaL(254); // Control de la posició del cursor
EnviaL(Posicio); // Canvia el cursor de lloc
}

Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.