Code : Tout sélectionner
/*
* connectingStuff, Oregon Scientific v2.1 Emitter
* http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
*
* Copyright (C) 2013 olivier.lebrun@gmail.com
*
* sketch unique pour sonde ds18b20 ou DHT11/22
* choix de la périodicité de transmission
* remontée niveau de batterie
*
* ajout d'au capteur pir ou reed ou tilt
*
* V2 par vil1driver
* [Tuto diy] Fabriquer sonde radio Oregon a 5€ pour les nuls V2
* http://easydomoticz.com/forum/viewtopic.php?t=1956
*
* V2.1 Par Jojo 12/2016
* Suppression des routines de capteurs supplémentaires
* pour alléger le code, utilisation simplement en thermomètre RF 433
*
*/
/************************************************************
emplacement des PIN de la puce ATtiny85
+-------+
Ain0 D5 PB5 1|* |8 VCC
Ain3 D3 PB3 2| |7 PB2 D2 Ain1
Ain2 D4 PB4 3| |6 PB1 D1 pwm1
GND 4| |5 PB0 D0 pwm0
+-------+
cablage a realiser
+-------+
1|* |8 (+)
2| |7 Data Sonde
TX 433 3| |6 Data Sonde
(-) 4| |5 Data Sonde
+-------+
*/
//**************** Confuguration *******************//
// Thermomètre modifié par Jojo d'après le sketch
// "V2 par vil1driver"
// V2.1 jojo -- 10/1206
// J'ai enlevé la possibilité de rajouter des Switch A et Switch B
// car je l'utilise simplement en thermomètre
// V 2.1 jojo -- 12/2016
// Ajout du canal dans les paramètres pour choisir plus facilement
// V 2.2 jojo -- 01/2017
// En cas d'erreur du DS18B20 n'émet rien ou émet -127 (erreur) au choix
// V 2.3 jojo -- 01/2017
// Modification de l'accès au DS18B20 par DallasTemperature
// plutot que par le scratchpad qui faisait 1 erreur de mesure sur 2
#define NODE_ID 0xCC // Identifiant unique de votre sonde (hexadecimal)
#define NODE_ID2 0xaC // Identifiant unique de votre sonde (hexadecimal)
#define NODE_ID3 0xbC // Identifiant unique de votre sonde (hexadecimal)
#define LOW_BATTERY_LEVEL 2600 // Voltage minumum (mV) avant d'indiquer batterie faible
// #define LOW_BATTERY_LEVEL 3900 // Voltage minumum (mV) avant d'indiquer batterie faible
#define WDT_COUNT 1 // Nombre de cycles entre chaque mesure (1 cycles = 8 secondes, 5x8 = 40s)
// Canal d'émission: 3 canaux possibles:
// canal 1: 0x10 canal 2: 0x20 canal 3: 0x30
const byte canal = 0x20;
// decommenter la ligne qui corresponds a votre sonde
#define DS18B20
//#define DHT11
//#define DHT22
// si une mesure est identique a la precedente, elle ne sera pas transmise
// on economise ainsi la batterie
// decommentez la ligne suivante si vous souhaitez transmettre chaque mesure
#define ALWAYS_SEND
#define TX_PIN 3 // pin 3 // data transmetteur
//**********************************************************//
//*************** Fin de configuration ******************//
//**********************************************************//
// Chargement des librairies
#include <avr/sleep.h> // Sleep Modes
#include <avr/wdt.h> // Watchdog timer
#include <avr/interrupt.h>
#ifdef DS18B20
#include "OneWire.h"
#include <DallasTemperature.h>
// Capteur de température DS18B20 Connecté pin 0
// (avec résistance de rappel de 4,7k au +)
#define ONE_WIRE_BUS 0
#define ONE_WIRE_BUS 1
#define ONE_WIRE_BUS 2
uint32_t delayMS;
// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
#else
#include "dht.h"
dht DHT;
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
volatile float lastTemp = 0.0;
volatile int count = 0;
boolean lowBattery = false;
const unsigned long TIME = 512;
const unsigned long TWOTIME = TIME*2;
#define SENDHIGH() digitalWrite(TX_PIN, HIGH)
#define SENDLOW() digitalWrite(TX_PIN, LOW)
// Buffer for Oregon message
#ifdef DS18B20
byte OregonMessageBuffer[8];
#else
byte OregonMessageBuffer[9];
#endif
/**
* \brief Send logical "0" over RF
* \details azero bit be represented by an off-to-on transition
* \ of the RF signal at the middle of a clock period.
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first
*/
inline void sendZero(void)
{
SENDHIGH();
delayMicroseconds(TIME);
SENDLOW();
delayMicroseconds(TWOTIME);
SENDHIGH();
delayMicroseconds(TIME);
}
/**
* \brief Send logical "1" over RF
* \details a one bit be represented by an on-to-off transition
* \ of the RF signal at the middle of a clock period.
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first
*/
inline void sendOne(void)
{
SENDLOW();
delayMicroseconds(TIME);
SENDHIGH();
delayMicroseconds(TWOTIME);
SENDLOW();
delayMicroseconds(TIME);
}
/**
* Send a bits quarter (4 bits = MSB from 8 bits value) over RF
*
* @param data Source data to process and sent
*/
/**
* \brief Send a bits quarter (4 bits = MSB from 8 bits value) over RF
* \param data Data to send
*/
inline void sendQuarterMSB(const byte data)
{
(bitRead(data, 4)) ? sendOne() : sendZero();
(bitRead(data, 5)) ? sendOne() : sendZero();
(bitRead(data, 6)) ? sendOne() : sendZero();
(bitRead(data, 7)) ? sendOne() : sendZero();
}
/**
* \brief Send a bits quarter (4 bits = LSB from 8 bits value) over RF
* \param data Data to send
*/
inline void sendQuarterLSB(const byte data)
{
(bitRead(data, 0)) ? sendOne() : sendZero();
(bitRead(data, 1)) ? sendOne() : sendZero();
(bitRead(data, 2)) ? sendOne() : sendZero();
(bitRead(data, 3)) ? sendOne() : sendZero();
}
/******************************************************************/
/******************************************************************/
/******************************************************************/
/**
* \brief Send a buffer over RF
* \param data Data to send
* \param size size of data to send
*/
void sendData(byte *data, byte size)
{
for(byte i = 0; i < size; ++i)
{
sendQuarterLSB(data[i]);
sendQuarterMSB(data[i]);
}
}
/**
* \brief Send an Oregon message
* \param data The Oregon message
*/
void sendOregon(byte *data, byte size)
{
sendPreamble();
//sendSync();
sendData(data, size);
sendPostamble();
}
/**
* \brief Send preamble
* \details The preamble consists of 16 "1" bits
*/
inline void sendPreamble(void)
{
byte PREAMBLE[]={0xFF,0xFF};
sendData(PREAMBLE, 2);
}
/**
* \brief Send postamble
* \details The postamble consists of 8 "0" bits
*/
inline void sendPostamble(void)
{
#ifdef DS18B20
sendQuarterLSB(0x00);
#else
byte POSTAMBLE[]={0x00};
sendData(POSTAMBLE, 1);
#endif
}
/**
* \brief Send sync nibble
* \details The sync is 0xA. It is not use in this version since the sync nibble
* \ is include in the Oregon message to send.
*/
inline void sendSync(void)
{
sendQuarterLSB(0xA);
}
/******************************************************************/
/******************************************************************/
/******************************************************************/
/**
* \brief Set the sensor type
* \param data Oregon message
* \param type Sensor type
*/
inline void setType(byte *data, byte* type)
{
data[0] = type[0];
data[1] = type[1];
}
/**
* \brief Set the sensor channel
* \param data Oregon message
* \param channel Sensor channel (0x10, 0x20, 0x30)
*/
inline void setChannel(byte *data, byte channel)
{
data[2] = channel;
}
/**
* \brief Set the sensor ID
* \param data Oregon message
* \param ID Sensor unique ID
*/
inline void setId(byte *data, byte ID)
{
data[3] = ID;
}
/**
* \brief Set the sensor battery level
* \param data Oregon message
* \param level Battery level (0 = low, 1 = high)
*/
void setBatteryLevel(byte *data, byte level)
{
if(!level) data[4] = 0x0C;
else data[4] = 0x00;
}
/**
* \brief Set the sensor temperature
* \param data Oregon message
* \param temp the temperature
*/
void setTemperature(byte *data, float temp)
{
// Set temperature sign
if(temp < 0)
{
data[6] = 0x08;
temp *= -1;
}
else
{
data[6] = 0x00;
}
// Determine decimal and float part
int tempInt = (int)temp;
int td = (int)(tempInt / 10);
int tf = (int)round((float)((float)tempInt/10 - (float)td) * 10);
int tempFloat = (int)round((float)(temp - (float)tempInt) * 10);
// Set temperature decimal part
data[5] = (td << 4);
data[5] |= tf;
// Set temperature float part
data[4] |= (tempFloat << 4);
}
/**
* \brief Set the sensor humidity
* \param data Oregon message
* \param hum the humidity
*/
void setHumidity(byte* data, byte hum)
{
data[7] = (hum/10);
data[6] |= (hum - data[7]*10) << 4;
}
/**
* \brief Sum data for checksum
* \param count number of bit to sum
* \param data Oregon message
*/
int Sum(byte count, const byte* data)
{
int s = 0;
for(byte i = 0; i<count;i++)
{
s += (data[i]&0xF0) >> 4;
s += (data[i]&0xF);
}
if(int(count) != count)
s += (data[count]&0xF0) >> 4;
return s;
}
/**
* \brief Calculate checksum
* \param data Oregon message
*/
void calculateAndSetChecksum(byte* data)
{
#ifdef DS18B20
int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);
data[6] |= (s&0x0F) << 4; data[7] = (s&0xF0) >> 4;
#else
data[8] = ((Sum(8, data) - 0xa) & 0xFF);
#endif
}
/******************************************************************/
/******************************************************************/
boolean getTemperature(float *temp){
// Fonction récupérant la température
// Retourne true si tout va bien, ou false en cas d'erreur
#ifdef DS18B20
// ------------------------------------------------------------
// En cas d'erreur avec le DS18B20 on peut soit ne rien émettre
// soit émettre "-127°" (erreur classique du DS18B20) pour dire
// qu'il ya une erreur
// ------------------------------------------------------------
// Pour ne rien émettre, faire juste
// return false; // Retourne une erreur
// ------------------------------------------------------------
// Pour retourner "-127°"
// *temp = 127; // Retourne -127 comme température
// return true; // Emet cette température
// ------------------------------------------------------------
// Delay between measurements.
delay(delayMS);
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
sensors.requestTemperatures(); // Send the command to get temperatures
// On stocke tout de suite la température pour l'envoyer ensuite via rf433
*temp = sensors.getTempCByIndex(0);
// Pas d'erreur
return true;
#else
#ifdef DHT11
//delay(2000);
int chk = DHT.read11(DATA_PIN);
if (chk == DHTLIB_OK) { // Ok
// Pas d'erreur
*temp = DHT.temperature;
return true;
}
else
{
return false;
}
#else
#ifdef DHT22
//delay(2000);
int chk = DHT.read22(DATA_PIN);
if (chk == DHTLIB_OK) { // Ok
// Pas d'erreur
*temp = DHT.temperature;
return true;
}
else
{
return false;
}
#endif
#endif
#endif
}
/******************************************************************/
void setup()
{
CLKPR = (1<<CLKPCE);
CLKPR = B00000000; // set the fuses to 8mhz clock-speed.
#if defined(DS18B20) || defined(DHT11) || defined(DHT22)
// initialisation des cycles de reveil
setup_watchdog(9);
#endif
pinMode(TX_PIN, OUTPUT); // sortie transmetteur
SENDLOW();
#ifdef DS18B20
// Create the Oregon message for a temperature only sensor (TNHN132N)
byte ID[] = {0xEA,0x4C};
#else
// Create the Oregon message for a temperature/humidity sensor (THGR2228N)
byte ID[] = {0x1A,0x2D};
#endif
setType(OregonMessageBuffer, ID);
setChannel(OregonMessageBuffer, canal);
setId(OregonMessageBuffer, NODE_ID);
setId(OregonMessageBuffer, NODE_ID2);
setId(OregonMessageBuffer, NODE_ID3);
// On scrute la première fois le DS 18B20 à blanc car sinon la première mesure
// donnerait un résultat faux (85 °c)
// Sans doute un problème d'initialisation ?
float temp;
getTemperature(&temp);
delay (2000);
}
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_mode(); // Go to sleep
sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
}
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {
byte bb;
int ww;
if (ii > 9 ) ii=9;
bb=ii & 7;
if (ii > 7) bb|= (1<<5);
bb|= (1<<WDCE);
ww=bb;
MCUSR &= ~(1<<WDRF);
// start timed sequence
WDTCR |= (1<<WDCE) | (1<<WDE);
// set new watchdog timeout value
WDTCR = bb;
WDTCR |= _BV(WDIE);
}
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
//wake up
count--;
}
//reads internal 1V1 reference against VCC
//return number 0 .. 1023
int analogReadInternal() {
ADMUX = _BV(MUX3) | _BV(MUX2); // For ATtiny85
delay(5); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
uint8_t low = ADCL;
return (ADCH << 8) | low;
}
//calculate VCC based on internal referrence
//return voltage in mV
int readVCC() {
return ((uint32_t)1024 * (uint32_t)1100) / analogReadInternal();
// Start up the library DS18B20
sensors.begin();
}
void loop()
{
#if defined(DS18B20) || defined(DHT11) || defined(DHT22)
if (count <= 0) { // on attend que le nombre de cycle soit atteint
count=WDT_COUNT; // reset counter
// Get Temperature, humidity and battery level from sensors
float temp;
if (getTemperature(&temp)) {
// we need round temp to one decimal...
int a = round(temp * 10);
temp = a / 10.0;
// if temp has changed
if (temp != lastTemp) {
#ifndef ALWAYS_SEND
// save temp
lastTemp = temp;
#endif
// Get the battery state
int vcc = readVCC();
lowBattery = vcc < LOW_BATTERY_LEVEL;
// Set Battery Level
setBatteryLevel(OregonMessageBuffer, !lowBattery); // 0=low, 1=high
// Set Temperature
setTemperature(OregonMessageBuffer, temp);
#ifndef DS18B20
// Set Humidity
setHumidity(OregonMessageBuffer, DHT.humidity);
#endif
// Calculate the checksum
calculateAndSetChecksum(OregonMessageBuffer);
// Send the Message over RF
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Send a "pause"
SENDLOW();
delayMicroseconds(TWOTIME*8);
// Send a copie of the first message. The v2.1 protocol send the message two time
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
SENDLOW();
}
}
}
#endif
system_sleep();
}