#include <LowPower.h>
/* Dépendance pour le bus 1-Wire */
#include <OneWire.h>
// Configuration Oregon
#define MODE_0 0 // Temperature only [THN132N]
#define MODE_1 1 // Temperature + Humidity [THGR2228N]
#define MODE_2 2 // Temperature + Humidity + Baro() [BTHR918N]
#define PIN_ORP A0
#define PIN_PH A1
#define MODE MODE_1
const byte TX_PIN = 9;
/* Broche du bus 1-Wire */
const byte BROCHE_ONEWIRE = 2;
const byte DONE_PIN = 5;
/* Code de retour de la fonction getTemperature() */
enum DS18B20_RCODES {
READ_OK, // Lecture ok
NO_SENSOR_FOUND, // Pas de capteur
INVALID_ADDRESS, // Adresse reçue invalide
INVALID_SENSOR // Capteur invalide (pas un DS18B20)
};
/** ********************************************************** */
/** ************ Fonction de préparation Oregon ************** */
/** ********************************************************** */
#define SEND_HIGH() digitalWrite(TX_PIN, HIGH)
#define SEND_LOW() digitalWrite(TX_PIN, LOW)
const unsigned long TIME = 512;
const unsigned long TWOTIME = TIME*2;
// Buffer for Oregon message
#if MODE == MODE_0
byte OregonMessageBuffer[8];
#elif MODE == MODE_1
byte OregonMessageBuffer[9];
#elif MODE == MODE_2
byte OregonMessageBuffer[11];
#else
#error mode unknown
#endif
/* Création de l'objet OneWire pour manipuler le bus 1-Wire */
OneWire ds(BROCHE_ONEWIRE);
/**
* \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){
SEND_HIGH();
delayMicroseconds(TIME);
SEND_LOW();
delayMicroseconds(TWOTIME);
SEND_HIGH();
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){
SEND_LOW();
delayMicroseconds(TIME);
SEND_HIGH();
delayMicroseconds(TWOTIME);
SEND_LOW();
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){
#if MODE == MODE_0
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;
}
/**
* Fonction de lecture de la température via un capteur DS18B20.
*/
byte getTemperature(float *temperature, byte reset_search) {
byte data[9], addr[8];
// data[] : Données lues depuis le scratchpad
// addr[] : Adresse du module 1-Wire détecté
/* Reset le bus 1-Wire ci nécessaire (requis pour la lecture du premier capteur) */
if (reset_search) {
ds.reset_search();
}
/* Recherche le prochain capteur 1-Wire disponible */
if (!ds.search(addr)) {
// Pas de capteur
Serial.print(F("pas de capteur"));
Serial.println();
return NO_SENSOR_FOUND;
}
/* Vérifie que l'adresse a été correctement reçue */
if (OneWire::crc8(addr, 7) != addr[7]) {
// Adresse invalide
Serial.print(F("mauvaise addr"));
Serial.print(OneWire::crc8(addr, 7), 2);
Serial.print(addr[7], 2);
Serial.println();
return INVALID_ADDRESS;
}
/* Vérifie qu'il s'agit bien d'un DS18B20 */
if (addr[0] != 0x28) {
// Mauvais type de capteur
Serial.print(F("mauvais sensor"));
Serial.print(addr[0], 2);
Serial.println();
return INVALID_SENSOR;
}
/* Reset le bus 1-Wire et sélectionne le capteur */
ds.reset();
ds.select(addr);
/* Lance une prise de mesure de température et attend la fin de la mesure */
ds.write(0x44, 1);
delay(800);
/* Reset le bus 1-Wire, sélectionne le capteur et envoie une demande de lecture du scratchpad */
ds.reset();
ds.select(addr);
ds.write(0xBE);
/* Lecture du scratchpad */
for (byte i = 0; i < 9; i++) {
data[i] = ds.read();
}
/* Calcul de la température en degré Celsius */
*temperature = (int16_t) ((data[1] << 8) | data[0]) * 0.0625;
// Pas d'erreur
return READ_OK;
}
/**
* \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 Set the sensor temperature
* \param data Oregon message
* \param temp the temperature
*/
void setPressure(byte *data, float pres){
if ((pres > 850) && (pres < 1100)) {
data[8] = (int)round(pres) - 856;
data[9] = 0xC0;
}
}
/**
* \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){
#if MODE == MODE_0
int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);
data[6] |= (s&0x0F) << 4; data[7] = (s&0xF0) >> 4;
#elif MODE == MODE_1
data[8] = ((Sum(8, data) - 0xa) & 0xFF);
#else
data[10] = ((Sum(10, data) - 0xa) & 0xFF);
#endif
}
float getRedox() {
float value, valORP;
int buf[10],temp;
unsigned long int avgValue;
//value = (float)analogRead(PIN_ORP); // raw value between 0 and 1023
//Serial.print("ORP brute: ");Serial.println(value);
//value = map(value,0,1023,0,5000); // mV
/*valORP = (2500-value)/1.037;
Serial.print("ORP: ");Serial.println(valORP);*/
for(int i=0;i<10;i++)
{
buf[i]=analogRead(PIN_ORP); delay(30);
}
for(int i=0;i<9;i++)
{
for(int j=i+1;j<10;j++) {
if(buf[i]>buf[j]) {
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
avgValue=0;
for(int i=2;i<8;i++) {
avgValue+=buf[i];
}
value=(float)avgValue/6.0;
return value;
}
float getPH() {
float value, valPH;
int buf[10],temp;
unsigned long int avgValue;
//value = (float)analogRead(PIN_PH); // raw value between 0 and 1023
/*Serial.print("PH brut: ");Serial.println(value);
value = map(value,0,1023,0,5000); // mV
valPH = (2500-value)/1.037;
Serial.print("PH: ");Serial.println(valPH);
return valPH;*/
for(int i=0;i<10;i++)
{
buf[i]=analogRead(PIN_PH); delay(30);
}
for(int i=0;i<9;i++)
{
for(int j=i+1;j<10;j++) {
if(buf[i]>buf[j]) {
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
avgValue=0;
for(int i=2;i<8;i++) {
avgValue+=buf[i];
}
value=(float)avgValue/6.0;
return value;
}
/** Fonction setup() **/
void setup() {
pinMode(DONE_PIN, OUTPUT);
pinMode(TX_PIN, OUTPUT);
Serial.begin(9600);
Serial.println("\n[Oregon V2.1 encoder]");
SEND_LOW();
#if MODE == MODE_0
// Create the Oregon message for a temperature only sensor (THN132N)
byte ID[] = {0xEA,0x4C};
#elif MODE == MODE_1
// Create the Oregon message for a temperature/humidity sensor (THGR2228N)
byte ID[] = {0x1A,0x2D};
#else
// Create the Oregon message for a temperature/humidity/barometer sensor (BTHR918N)
byte ID[] = {0x5A, 0x6D};
#endif
setType(OregonMessageBuffer, ID);
setChannel(OregonMessageBuffer, 0x20);
setId(OregonMessageBuffer, 0xCB);
}
/** Fonction loop() **/
void loop() {
float temperature, temperatureCumul = 0.0f;
float humidity = 59;
float pressure = 998;
int bat = 1;
int loopNb = 10;
int l = 0;
float redox, redoxCumul = 0.0f;
float ph, phCumul = 0.0f;
while(l<loopNb) {
/* Lit la température ambiante à ~1Hz */
if (getTemperature(&temperature, true) != READ_OK) {
//Serial.println(F("Erreur de lecture du capteur"));
//return;
bat = 0;
}
/* Affiche la température */
Serial.print(F("Temperature : "));
Serial.print(temperature, 2);
Serial.write('C');
Serial.println();
//temperatureCumul += temperature;
//if (l==loopNb-1) {
setId(OregonMessageBuffer, 0xCB);
//setTemperature(OregonMessageBuffer, temperatureCumul / loopNb);
setTemperature(OregonMessageBuffer, temperature);
setBatteryLevel(OregonMessageBuffer, bat); // 0 : low, 1 : high
#if MODE != MODE_0
// Set Humidity
setHumidity(OregonMessageBuffer, l);
#endif
#if MODE == MODE_2
// Set Pressure
setPressure(OregonMessageBuffer, pressure);
#endif
// Calculate the checksum
calculateAndSetChecksum(OregonMessageBuffer);
// Show the Oregon Message
Serial.println("Oregon -> ");
for (byte i = 0; i < sizeof(OregonMessageBuffer); ++i) {
Serial.print(OregonMessageBuffer[i] >> 4, HEX);
Serial.print(OregonMessageBuffer[i] & 0x0F, HEX);
}
Serial.println();
// Send the Message over RF
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Send a "pause"
SEND_LOW();
delayMicroseconds(TWOTIME*8);
// Send a copie of the first message. The v2.1 protocol send the
// message two time
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Wait for 30 seconds before send a new message
SEND_LOW();
//}
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
redox = getRedox();
/* Affiche la température */
Serial.print(F("redox raw value : "));
Serial.print(redox, 2);
Serial.println();
redoxCumul += redox;
//if (l==loopNb-1) {
// we send redox in temperature now
setId(OregonMessageBuffer, 0xCA);
setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
float fakeTemperatureRedox = redoxCumul/(l+1);
if (fakeTemperatureRedox>999) fakeTemperatureRedox=999;
fakeTemperatureRedox /= 10;
setTemperature(OregonMessageBuffer, fakeTemperatureRedox);
#if MODE != MODE_0
// Set Humidity
if (redox>999) redox = 999;
setHumidity(OregonMessageBuffer, redox / 10);
#endif
// Calculate the checksum
calculateAndSetChecksum(OregonMessageBuffer);
// Send the Message over RF
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Send a "pause"
SEND_LOW();
delayMicroseconds(TWOTIME*8);
// Send a copie of the first message. The v2.1 protocol send the
// message two time
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Wait for 30 seconds before send a new message
SEND_LOW();
//}
//LowPower.sleep(1000);
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
ph = getPH();
phCumul += ph;
/* Affiche la température */
Serial.print(F("ph raw value : "));
Serial.print(ph, 2);
Serial.println();
//if (l==loopNb-1) {
// we send ph in temperature now
setId(OregonMessageBuffer, 0xC9);
setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
float fakeTemperaturePH = phCumul / (l+1);
if (fakeTemperaturePH>999) fakeTemperaturePH=999-fakeTemperaturePH;
fakeTemperaturePH /= 10;
setTemperature(OregonMessageBuffer, fakeTemperaturePH);
#if MODE != MODE_0
// Set Humidity
if (ph>999) ph = 999;
setHumidity(OregonMessageBuffer, ph / 10);
#endif
// Calculate the checksum
calculateAndSetChecksum(OregonMessageBuffer);
// Send the Message over RF
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Send a "pause"
SEND_LOW();
delayMicroseconds(TWOTIME*8);
// Send a copie of the first message. The v2.1 protocol send the
// message two time
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
// Wait for 30 seconds before send a new message
SEND_LOW();
//}
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
l++;
}
digitalWrite(DONE_PIN, HIGH);
//LowPower.idle(SLEEP_1S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
//LowPower.idle(SLEEP_1S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
delay(5000);
digitalWrite(DONE_PIN, LOW);
//delay(10000);
//for(int i = 0; i < 2; i++)
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF);
}