diff --git a/Arduino/Arduino_GPS_Parser_GGA/Arduino_GPS_Parser_GGA.ino b/Arduino/Arduino_GPS_Parser_GGA/Arduino_GPS_Parser_GGA.ino new file mode 100644 index 00000000..a5a30ab5 --- /dev/null +++ b/Arduino/Arduino_GPS_Parser_GGA/Arduino_GPS_Parser_GGA.ino @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////// +// Simple GPS Sensor : parsing GGA NMEA sentence (GPGGA or GNGGA) +// On the Ublox 8 series GPGGA is replaced by GNGGA by default +// +// $GNGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 +// +// Copyright (C) 2018 Mamour Diop & Congduc Pham, University of Pau, France +///////////////////////////////////////////////////////////////////// + +//#define HARDWARE_SERIAL_GPS +#define SOFTWARE_SERIAL_GPS + +#ifdef HARDWARE_SERIAL_GPS + +/////////////////////////////// +// USING HARDWARE SERIAL +// +// On Arduino with only 1 serial port (TX/RX), it is necessary to perform the following actions +// as the serial port is use for both the GPS and the programming. To see GPS message: + +// be sure that serial monitor is not open +// connect only GPS_GND<->GND and GPS_TX<>RX +// after uploading the sketch, connect GPS_VCC<>3.3V +// then open Serial Monitor (set baud rate to 9600) +// do not connect GPS_TX +// +// you should see NMEA messages from the GPS module +// to understand NMEA message: http://aprs.gids.nl/nmea/ + +// if you are using an Arduino board with several serial port (such as the MEGA) +// then define here the one that is connected to the GPS module +//#define gps_serial Serial3 +#define gps_serial Serial + +#endif + +#ifdef SOFTWARE_SERIAL_GPS + +#include +// connect GPS_TX <> 4 and GPS_RX <> 3 +SoftwareSerial gps_serial(4,3); //rx,tx for Arduino side + +#endif + +char GPSBuffer[150]; +byte GPSIndex=0; +double gps_latitude = 0.0; +double gps_longitude = 0.0; +char lat_direction, lgt_direction; + +void parseNMEA(){ + char inByte; + bool detect_new_line=false; + + while (!detect_new_line) + { + if (gps_serial.available()) { + inByte = gps_serial.read(); + + //Serial.print(inByte); // Output exactly what we read from the GPS to debug + + if ((inByte =='$') || (GPSIndex >= 150)){ + GPSIndex = 0; + } + + if (inByte != '\r' && inByte != '\n'){ + GPSBuffer[GPSIndex++] = inByte; + } + + if (inByte == '\n'){ + GPSBuffer[GPSIndex++] = '\0'; + detect_new_line=true; + } + } + } + Serial.print("---->"); + Serial.println(GPSBuffer); + //Serial.flush(); +} + +bool isGGA(){ + return (GPSBuffer[1] == 'G' && (GPSBuffer[2] == 'P' || GPSBuffer[2] == 'N') && GPSBuffer[3] == 'G' && GPSBuffer[4] == 'G' && GPSBuffer[5] == 'A'); +} + +// GPGGA and GNGGA for the Ublox 8 series. Ex: +// no fix -> $GPGGA,,,,,,0,00,99.99,,,,,,*63 +// fix -> $GPGGA,171958.00,4903.61140,N,00203.95016,E,1,07,1.26,55.1,M,46.0,M,,*68 + +void decodeGGA(){ + int i, j; + char latitude[30], longitude[30]; + byte latIndex=0, lgtIndex=0; + + for (i=7, j=0; (i degrees. counted as is +// 02.78469 ==> minutes. divide by 60 before adding to degrees above +// Hence, the decimal equivalent is: 03 + (02.78469/60) = 3.046412 +// For longitude of 10601.6986, +// ==> 106+1.6986/60 = 106.02831 degrees +// Location is latitude or longitude +// In Southern hemisphere latitude should be negative +// In Western Hemisphere, longitude degrees should be negative +////////////////////////////////////////////////////////////////////////////////// + +double degree_location(double loc, char loc_direction){ + double degLoc = 0.0; + double degWhole= loc/100; //gives the whole degree part of latitude + double degDec = (degWhole - ((int)degWhole)) * 100 / 60; //gives fractional part of location + degLoc = (int)degWhole + degDec; //gives complete correct decimal form of location degrees + + if (loc_direction =='S' || loc_direction =='W') { + degLoc = (-1)*degLoc; + } + return degLoc; +} + +void setup(){ + Serial.begin(9600); + //if only 1 serial port then gps_serial is equivalent to Serial +#ifdef SOFTWARE_SERIAL_GPS + gps_serial.begin(9600); +#endif + Serial.println("GPS basic rogram"); +} + +void loop(){ + + bool get_fix = false; + + while (!get_fix){ + parseNMEA(); + if (isGGA()){ + decodeGGA(); + if (isValidLocation()){ + display_location(); + get_fix = true; + } + } + } +} diff --git a/Arduino/Arduino_LoRa_Demo_Sensor/Arduino_LoRa_Demo_Sensor.ino b/Arduino/Arduino_LoRa_Demo_Sensor/Arduino_LoRa_Demo_Sensor.ino index 7f671e05..5b945394 100644 --- a/Arduino/Arduino_LoRa_Demo_Sensor/Arduino_LoRa_Demo_Sensor.ino +++ b/Arduino/Arduino_LoRa_Demo_Sensor/Arduino_LoRa_Demo_Sensor.ino @@ -154,7 +154,7 @@ void setup() delay(500); // initialization of the demo sensor - sensor_demo_Init(); + sensor_Init(); } char *ftoa(char *a, double f, int precision) @@ -182,7 +182,7 @@ void loop(void) if (millis() > nextTransmissionTime) { - sensor_value = sensor_demo_getValue(); + sensor_value = sensor_getValue(); Serial.print(F("(Sensor value is ")); Serial.println(sensor_value); diff --git a/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.cpp b/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.cpp index 246de5b8..b2ef6b5d 100644 --- a/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.cpp +++ b/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.cpp @@ -11,10 +11,10 @@ char nomenclature_str[4]="TC"; // ADD HERE SOME INITIALIZATION CODE // HERE WE JUST DECLARE VALUE_PIN_READ AS INPUT PIN -void sensor_demo_Init() { +void sensor_Init() { // for the temperature sensor - pinMode(VALUE_PIN_READ, INPUT); + pinMode(PIN_READ, INPUT); pinMode(PIN_POWER, OUTPUT); } /////////////////////////////////////////////////////////////////// @@ -23,7 +23,7 @@ void sensor_demo_Init() { // CHANGE HERE THE WAY YOU READ A VALUE FROM YOUR SPECIFIC SENSOR // HERE IT IS AN EXAMPLE WITH THE LM35DZ SIMPLE ANALOG TEMPERATURE SENSOR -double sensor_demo_getValue() { +double sensor_getValue() { //power the sensor digitalWrite(PIN_POWER, HIGH); @@ -31,7 +31,7 @@ double sensor_demo_getValue() { delay(500); //read the raw sensor value - int value = analogRead(VALUE_PIN_READ); + int value = analogRead(PIN_READ); //power down the sensor digitalWrite(PIN_POWER, LOW); diff --git a/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.h b/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.h index c539a96b..fcaed87f 100644 --- a/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.h +++ b/Arduino/Arduino_LoRa_Demo_Sensor/my_demo_sensor_code.h @@ -8,14 +8,14 @@ #endif extern char nomenclature_str[4]; -void sensor_demo_Init(); -double sensor_demo_getValue(); +void sensor_Init(); +double sensor_getValue(); /////////////////////////////////////////////////////////////////// // CHANGE HERE THE PIN TO READ AND POWER SENSOR // IF YOU USE A COMPLETELY DIFFERENT SENSOR, YOU MAY NOT NEED THIS LINE -#define VALUE_PIN_READ A0 +#define PIN_READ A0 #define PIN_POWER 9 /////////////////////////////////////////////////////////////////// diff --git a/Arduino/Arduino_LoRa_Simple_BeaconCollar/AES-128_V10.cpp b/Arduino/Arduino_LoRa_Generic_DHT/AES-128_V10.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Simple_BeaconCollar/AES-128_V10.cpp rename to Arduino/Arduino_LoRa_Generic_DHT/AES-128_V10.cpp diff --git a/Arduino/Arduino_LoRa_Simple_BeaconCollar/AES-128_V10.h b/Arduino/Arduino_LoRa_Generic_DHT/AES-128_V10.h similarity index 100% rename from Arduino/Arduino_LoRa_Simple_BeaconCollar/AES-128_V10.h rename to Arduino/Arduino_LoRa_Generic_DHT/AES-128_V10.h diff --git a/Arduino/Arduino_LoRa_SoilHum/Arduino_LoRa_SoilHum.ino b/Arduino/Arduino_LoRa_Generic_DHT/Arduino_LoRa_Generic_DHT.ino similarity index 62% rename from Arduino/Arduino_LoRa_SoilHum/Arduino_LoRa_SoilHum.ino rename to Arduino/Arduino_LoRa_Generic_DHT/Arduino_LoRa_Generic_DHT.ino index 016535dd..63bb6565 100644 --- a/Arduino/Arduino_LoRa_SoilHum/Arduino_LoRa_SoilHum.ino +++ b/Arduino/Arduino_LoRa_Generic_DHT/Arduino_LoRa_Generic_DHT.ino @@ -1,8 +1,8 @@ /* - * Soil humidity sensor - * extended version with AES and custom Carrier Sense features + * DHT sensor + * extended version with AES * - * Copyright (C) 2017 Congduc Pham, University of Pau, France + * Copyright (C) 2018 Congduc Pham, University of Pau, France * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,26 +18,15 @@ * along with the program. If not, see . * ***************************************************************************** - * last update: May 15th, 2017 for the WaterSense project by C. Pham - * - * Sensor 1 is connected to digital 9 for power and A0 for output value - * Sensor 2 is connected to digital 8 for power and A1 for output value + * last update: Feb 17th, 2018 by C. Pham */ - -// IMPORTANT -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// add here some specific board define statements if you want to implement user-defined specific settings -// A/ LoRa radio node from IoTMCU: https://www.tindie.com/products/IOTMCU/lora-radio-node-v10/ -//#define IOTMCU_LORA_RADIO_NODE -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - #include // Include the SX1272 #include "SX1272.h" // Include sensors #include "Sensor.h" -#include "rawAnalog.h" -//#include "DS18B20.h" +#include "DHT22_Humidity.h" +#include "DHT22_Temperature.h" // IMPORTANT /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,23 +47,10 @@ #ifdef ETSI_EUROPE_REGULATION #define MAX_DBM 14 -// previous way for setting output power -// char powerLevel='M'; -#elif defined SENEGAL_REGULATION -#define MAX_DBM 10 -// previous way for setting output power -// 'H' is actually 6dBm, so better to use the new way to set output power -// char powerLevel='H'; -#elif defined FCC_US_REGULATION -#define MAX_DBM 14 #endif #ifdef BAND868 -#ifdef SENEGAL_REGULATION -const uint32_t DEFAULT_CHANNEL=CH_04_868; -#else const uint32_t DEFAULT_CHANNEL=CH_10_868; -#endif #elif defined BAND900 const uint32_t DEFAULT_CHANNEL=CH_05_900; #elif defined BAND433 @@ -93,36 +69,46 @@ const uint32_t DEFAULT_CHANNEL=CH_00_433; // COMMENT OR UNCOMMENT TO CHANGE FEATURES. // ONLY IF YOU KNOW WHAT YOU ARE DOING!!! OTHERWISE LEAVE AS IT IS #define WITH_EEPROM -#define WITH_APPKEY +#define SERIAL_MONITOR +//#define WITH_APPKEY +//if you are low on program memory, comment STRING_LIB to save about 2K +//#define STRING_LIB #define LOW_POWER #define LOW_POWER_HIBERNATE -#define WITH_AES -//#define WITH_ACK +//#define WITH_AES +//#define OLED /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // CHANGE HERE THE LORA MODE, NODE ADDRESS -#define LORAMODE 1 -// you need to change the node address for each sensor in the same organization/farm -// node address starts at 2 and ends at 255 -#define node_addr 2 +#define LORAMODE 4 +uint16_t node_addr=7; ////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// -// CHANGE HERE THE THINGSPEAK FIELD BETWEEN 1 AND 8 -#define field_index 1 +// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION +uint16_t idlePeriodInMin = 1; /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// -// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION -unsigned int idlePeriodInMin = 60; -/////////////////////////////////////////////////////////////////// +// COMMENT THIS LINE IF YOU WANT TO DYNAMICALLY SET THE NODE'S ADDR +// OR SOME OTHER PARAMETERS BY REMOTE RADIO COMMANDS (WITH_RCVW) +// LEAVE THIS LINE UNCOMMENTED IF YOU WANT TO USE THE DEFAULT VALUE +// AND CONFIGURE YOUR DEVICE BY CHANGING MANUALLY THESE VALUES IN +// THE SKETCH. +// +// ONCE YOU HAVE FLASHED A BOARD WITHOUT FORCE_DEFAULT_VALUE, YOU +// WILL BE ABLE TO DYNAMICALLY CONFIGURE IT AND SAVE THIS CONFIGU- +// RATION INTO EEPROM. ON RESET, THE BOARD WILL USE THE SAVED CON- +// FIGURATION. -// SENSORS DEFINITION -////////////////////////////////////////////////////////////////// -// CHANGE HERE THE NUMBER OF SENSORS, SOME CAN BE NOT CONNECTED -const int number_of_sensors = 1; -////////////////////////////////////////////////////////////////// +// IF YOU WANT TO REINITIALIZE A BOARD, YOU HAVE TO FIRST FLASH IT +// WITH FORCE_DEFAULT_VALUE, WAIT FOR ABOUT 10s SO THAT IT CAN BOOT +// AND FLASH IT AGAIN WITHOUT FORCE_DEFAULT_VALUE. THE BOARD WILL +// THEN USE THE DEFAULT CONFIGURATION UNTIL NEXT CONFIGURATION. + +#define FORCE_DEFAULT_VALUE +/////////////////////////////////////////////////////////////////// #ifdef WITH_APPKEY /////////////////////////////////////////////////////////////////// @@ -132,27 +118,53 @@ uint8_t my_appKey[4]={5, 6, 7, 8}; /////////////////////////////////////////////////////////////////// #endif -// we wrapped Serial.println to support the Arduino Zero or M0 -#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 -#define PRINTLN SerialUSB.println("") -#define PRINT_CSTSTR(fmt,param) SerialUSB.print(F(param)) -#define PRINT_STR(fmt,param) SerialUSB.print(param) -#define PRINT_VALUE(fmt,param) SerialUSB.print(param) -#define PRINT_HEX(fmt,param) SerialUSB.print(param,HEX) -#define FLUSHOUTPUT SerialUSB.flush(); -#else +/////////////////////////////////////////////////////////////////// +// IF YOU SEND A LONG STRING, INCREASE THE SIZE OF MESSAGE +uint8_t message[80]; +/////////////////////////////////////////////////////////////////// + +// SENSORS DEFINITION +////////////////////////////////////////////////////////////////// +// CHANGE HERE THE NUMBER OF SENSORS, SOME CAN BE NOT CONNECTED +const int number_of_sensors = 2; +////////////////////////////////////////////////////////////////// + +// array containing sensors pointers +Sensor* sensor_ptrs[number_of_sensors]; + +#ifdef SERIAL_MONITOR #define PRINTLN Serial.println("") #define PRINT_CSTSTR(fmt,param) Serial.print(F(param)) #define PRINT_STR(fmt,param) Serial.print(param) #define PRINT_VALUE(fmt,param) Serial.print(param) #define PRINT_HEX(fmt,param) Serial.print(param,HEX) #define FLUSHOUTPUT Serial.flush(); +#else +#define PRINTLN +#define PRINT_CSTSTR(fmt,param) +#define PRINT_STR(fmt,param) +#define PRINT_VALUE(fmt,param) +#define PRINT_HEX(fmt,param) +#define FLUSHOUTPUT #endif #ifdef WITH_EEPROM #include #endif +#ifdef OLED +#define OLED_PWR_PIN 8 +#include +// the OLED used +#if defined ARDUINO_Heltec_WIFI_LoRa_32 || defined ARDUINO_WIFI_LoRa_32 || defined HELTEC_LORA +U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); +#else +//reset is not used, but don't know what the lib will do with that pin, so better to indicate a pn that you do not use +U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ A5, /* data=*/ A4, /* reset=*/ 9); +#endif +char oled_msg[20]; +#endif + #define DEFAULT_DEST_ADDR 1 #ifdef WITH_ACK @@ -182,18 +194,11 @@ uint8_t my_appKey[4]={5, 6, 7, 8}; #include SnoozeTimer timer; SnoozeBlock sleep_config(timer); -#else +#else // for all other boards based on ATMega168, ATMega328P, ATMega32U4, ATMega2560, ATMega256RFR2, ATSAMD21G18A #define LOW_POWER_PERIOD 8 // you need the LowPower library from RocketScream // https://github.com/rocketscream/Low-Power #include "LowPower.h" - -#ifdef __SAMD21G18A__ -// use the RTC library -#include "RTCZero.h" -/* Create an rtc object */ -RTCZero rtc; -#endif #endif unsigned int nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD; #endif @@ -224,114 +229,66 @@ unsigned char Direction = 0x00; #endif unsigned long nextTransmissionTime=0L; -uint8_t message[50]; int loraMode=LORAMODE; + #ifdef WITH_EEPROM struct sx1272config { uint8_t flag1; uint8_t flag2; uint8_t seq; + uint16_t addr; + uint16_t idle_period; + uint8_t overwrite; // can add other fields such as LoRa mode,... }; sx1272config my_sx1272config; #endif -// array containing sensors pointers -Sensor* sensor_ptrs[number_of_sensors]; - void setup() { int e; #ifdef LOW_POWER bool low_power_status = IS_LOWPOWER; -#ifdef __SAMD21G18A__ - rtc.begin(); -#endif #else bool low_power_status = IS_NOT_LOWPOWER; #endif - + ////////////////////////////////////////////////////////////////// // ADD YOUR SENSORS HERE -// Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger=-1) - sensor_ptrs[0] = new rawAnalog("SM1", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A0, (uint8_t) 9); - //sensor_ptrs[1] = new rawAnalog("SM2", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A1, (uint8_t) 8); - //sensor_ptrs[2] = new DS18B20("DS", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 4, (uint8_t) 7); - //sensor_ptrs[2] = new rawAnalog("SM3", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7); - - sensor_ptrs[0]->set_n_sample(10); - //sensor_ptrs[1]->set_n_sample(10); - //sensor_ptrs[2]->set_n_sample(10); - +// Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger=-1) + sensor_ptrs[0] = new DHT22_Temperature("TC", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, A0, A1); + sensor_ptrs[1] = new DHT22_Humidity("HU", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, A0, A1); +////////////////////////////////////////////////////////////////// + delay(3000); // Open serial communications and wait for port to open: -#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 - SerialUSB.begin(38400); -#else Serial.begin(38400); -#endif - // Print a start message - PRINT_CSTSTR("%s","LoRa soil moisture sensor, extended version\n"); -#ifdef ARDUINO_AVR_PRO - PRINT_CSTSTR("%s","Arduino Pro Mini detected\n"); -#endif -#ifdef ARDUINO_AVR_NANO - PRINT_CSTSTR("%s","Arduino Nano detected\n"); -#endif -#ifdef ARDUINO_AVR_MINI - PRINT_CSTSTR("%s","Arduino Mini/Nexus detected\n"); -#endif -#ifdef ARDUINO_AVR_MEGA2560 - PRINT_CSTSTR("%s","Arduino Mega2560 detected\n"); +#ifdef OLED_PWR_PIN + pinMode(OLED_PWR_PIN, OUTPUT); + digitalWrite(OLED_PWR_PIN, HIGH); #endif -#ifdef ARDUINO_SAM_DUE - PRINT_CSTSTR("%s","Arduino Due detected\n"); -#endif -#ifdef __MK66FX1M0__ - PRINT_CSTSTR("%s","Teensy36 MK66FX1M0 detected\n"); -#endif -#ifdef __MK64FX512__ - PRINT_CSTSTR("%s","Teensy35 MK64FX512 detected\n"); -#endif -#ifdef __MK20DX256__ - PRINT_CSTSTR("%s","Teensy31/32 MK20DX256 detected\n"); -#endif -#ifdef __MKL26Z64__ - PRINT_CSTSTR("%s","TeensyLC MKL26Z64 detected\n"); -#endif -#if defined ARDUINO_SAMD_ZERO && not defined ARDUINO_SAMD_FEATHER_M0 - PRINT_CSTSTR("%s","Arduino M0/Zero detected\n"); -#endif -#ifdef ARDUINO_AVR_FEATHER32U4 - PRINT_CSTSTR("%s","Adafruit Feather32U4 detected\n"); -#endif -#ifdef ARDUINO_SAMD_FEATHER_M0 - PRINT_CSTSTR("%s","Adafruit FeatherM0 detected\n"); + +#ifdef OLED + u8x8.begin(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 1, "Arduino LoRa DHT"); #endif -// See http://www.nongnu.org/avr-libc/user-manual/using_tools.html -// for the list of define from the AVR compiler + // Print a start message + PRINT_CSTSTR("%s","LoRa DHT node\n"); + +#ifdef ARDUINO_AVR_PRO + PRINT_CSTSTR("%s","Arduino Pro Mini\n"); +#endif #ifdef __AVR_ATmega328P__ - PRINT_CSTSTR("%s","ATmega328P detected\n"); -#endif -#ifdef __AVR_ATmega32U4__ - PRINT_CSTSTR("%s","ATmega32U4 detected\n"); -#endif -#ifdef __AVR_ATmega2560__ - PRINT_CSTSTR("%s","ATmega2560 detected\n"); + PRINT_CSTSTR("%s","ATmega328P\n"); #endif -#ifdef __SAMD21G18A__ - PRINT_CSTSTR("%s","SAMD21G18A ARM Cortex-M0 detected\n"); -#endif -#ifdef __SAM3X8E__ - PRINT_CSTSTR("%s","SAM3X8E ARM Cortex-M3 detected\n"); -#endif // Power ON the module sx1272.ON(); @@ -341,29 +298,72 @@ void setup() EEPROM.get(0, my_sx1272config); // found a valid config? - if (my_sx1272config.flag1==0x12 && my_sx1272config.flag2==0x34) { - PRINT_CSTSTR("%s","Get back previous sx1272 config\n"); + if (my_sx1272config.flag1==0x12 && my_sx1272config.flag2==0x35) { + PRINT_CSTSTR("%s","EEPROM\n"); // set sequence number for SX1272 library sx1272._packetNumber=my_sx1272config.seq; - PRINT_CSTSTR("%s","Using packet sequence number of "); + PRINT_CSTSTR("%s","Using seq of "); PRINT_VALUE("%d", sx1272._packetNumber); PRINTLN; + +#ifdef FORCE_DEFAULT_VALUE + PRINT_CSTSTR("%s","Forced default\n"); + my_sx1272config.flag1=0x12; + my_sx1272config.flag2=0x35; + my_sx1272config.seq=sx1272._packetNumber; + my_sx1272config.addr=node_addr; + my_sx1272config.idle_period=idlePeriodInMin; + my_sx1272config.overwrite=0; + EEPROM.put(0, my_sx1272config); +#else + // get back the node_addr + if (my_sx1272config.addr!=0 && my_sx1272config.overwrite==1) { + + PRINT_CSTSTR("%s","Used stored addr\n"); + node_addr=my_sx1272config.addr; + } + else + PRINT_CSTSTR("%s","Stored addr is null\n"); + + // get back the idle period + //if (my_sx1272config.idle_period!=0 && my_sx1272config.overwrite==1) { + // + // PRINT_CSTSTR("%s","Used stored idle period\n"); + // idlePeriodInMin=my_sx1272config.idle_period; + //} + //else + // PRINT_CSTSTR("%s","Stored idle period is null\n"); +#endif + +#ifdef WITH_AES + DevAddr[3] = (unsigned char)node_addr; +#endif + PRINT_CSTSTR("%s","node addr "); + PRINT_VALUE("%d", node_addr); + PRINTLN; + + PRINT_CSTSTR("%s","idle period of "); + PRINT_VALUE("%d", idlePeriodInMin); + PRINTLN; } else { // otherwise, write config and start over my_sx1272config.flag1=0x12; - my_sx1272config.flag2=0x34; + my_sx1272config.flag2=0x35; my_sx1272config.seq=sx1272._packetNumber; + my_sx1272config.addr=node_addr; + my_sx1272config.idle_period=idlePeriodInMin; + my_sx1272config.overwrite=0; } #endif // Set transmission mode and print the result e = sx1272.setMode(loraMode); - PRINT_CSTSTR("%s","Setting Mode: state "); + PRINT_CSTSTR("%s","Set mode: state "); PRINT_VALUE("%d", e); PRINTLN; - + // enable carrier sense sx1272._enableCarrierSense=true; #ifdef LOW_POWER @@ -374,7 +374,7 @@ void setup() // Select frequency channel e = sx1272.setChannel(DEFAULT_CHANNEL); - PRINT_CSTSTR("%s","Setting Channel: state "); + PRINT_CSTSTR("%s","Channel: state "); PRINT_VALUE("%d", e); PRINTLN; @@ -393,80 +393,118 @@ void setup() e = sx1272.setPowerDBM((uint8_t)MAX_DBM); - PRINT_CSTSTR("%s","Setting Power: state "); + PRINT_CSTSTR("%s","Power: state "); PRINT_VALUE("%d", e); PRINTLN; // Set the node address and print the result e = sx1272.setNodeAddress(node_addr); - PRINT_CSTSTR("%s","Setting node addr: state "); + PRINT_CSTSTR("%s","node addr: state "); PRINT_VALUE("%d", e); PRINTLN; // Print a success message - PRINT_CSTSTR("%s","SX1272 successfully configured\n"); - + PRINT_CSTSTR("%s","SX1272 configured\n"); + + //printf_begin(); delay(500); } +#ifndef STRING_LIB + +char *ftoa(char *a, double f, int precision) +{ + long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000}; + + char *ret = a; + long heiltal = (long)f; + itoa(heiltal, a, 10); + while (*a != '\0') a++; + *a++ = '.'; + long desimal = abs((long)((f - heiltal) * p[precision])); + if (desimal < p[precision-1]) { + *a++ = '0'; + } + itoa(desimal, a, 10); + return ret; +} +#endif + void loop(void) { long startSend; long endSend; uint8_t app_key_offset=0; int e; - + bool valid_r_mic=true; + bool trigger_sensor=false; + uint8_t r_size; + #ifndef LOW_POWER // 600000+random(15,60)*1000 if (millis() > nextTransmissionTime) { #endif - -#ifdef WITH_APPKEY + +#if defined WITH_APPKEY && not defined LORAWAN app_key_offset = sizeof(my_appKey); // set the app key in the payload memcpy(message,my_appKey,app_key_offset); #endif - - uint8_t r_size; - - char final_str[40] = "\\!"; - + + strcpy((char*)message+app_key_offset, "\\!"); + // main loop for sensors, actually, you don't have to edit anything here // just add a predefined sensor if needed or provide a new sensor class instance for a handle a new physical sensor for (int i=0; iget_is_connected() || sensor_ptrs[i]->has_fake_data()) { - - int value = (int)sensor_ptrs[i]->get_value(); - - PRINT_CSTSTR("%s","SM"); - PRINT_VALUE("%d", i+1); - PRINT_CSTSTR("%s", " on 10 sample is "); - PRINT_VALUE("%d", value); - PRINTLN; - + +#ifdef STRING_LIB if (i==0) { - sprintf(final_str, "%s%s/%d", final_str, sensor_ptrs[i]->get_nomenclature(), value); + r_size=sprintf((char*)message+app_key_offset, "%s%s/%s", (char*)message+app_key_offset, sensor_ptrs[i]->get_nomenclature(), String(sensor_ptrs[i]->get_value()).c_str()); } else { - sprintf(final_str, "%s/%s/%d", final_str, sensor_ptrs[i]->get_nomenclature(), value); + r_size=sprintf((char*)message+app_key_offset, "%s/%s/%s", (char*)message+app_key_offset, sensor_ptrs[i]->get_nomenclature(), String(sensor_ptrs[i]->get_value()).c_str()); } +#else + char float_str[10]; + ftoa(float_str, sensor_ptrs[i]->get_value(), 2); + + if (i==0) { + r_size=sprintf((char*)message+app_key_offset, "%s%s/%s", (char*)message+app_key_offset, sensor_ptrs[i]->get_nomenclature(), float_str); + } + else { + r_size=sprintf((char*)message+app_key_offset, "%s/%s/%s", (char*)message+app_key_offset, sensor_ptrs[i]->get_nomenclature(), float_str); + } +#endif } } - - r_size=sprintf((char*)message+app_key_offset, final_str); - PRINT_CSTSTR("%s","Sending "); + // for testing + //strcpy((char*)message+app_key_offset, "hello"); + //r_size=5; + + PRINTLN; + PRINT_CSTSTR("%s","Payload "); PRINT_STR("%s",(char*)(message+app_key_offset)); PRINTLN; - - PRINT_CSTSTR("%s","Real payload size is "); + +#ifdef OLED + u8x8.clearLine(3); + // don't show the '\!' characters + sprintf(oled_msg, "%s", (char*)(message+app_key_offset+2)); + u8x8.drawString(0, 3, oled_msg); +#endif + + PRINT_CSTSTR("%s","Size is "); PRINT_VALUE("%d", r_size); PRINTLN; int pl=r_size+app_key_offset; + + uint8_t p_type=PKT_TYPE_DATA; -#ifdef WITH_AES +#if defined WITH_AES // PRINT_STR("%s",(char*)(message+app_key_offset)); PRINTLN; @@ -479,9 +517,11 @@ void loop(void) } PRINTLN; - PRINT_CSTSTR("%s","Encrypting\n"); + //PRINT_CSTSTR("%s","Encrypting\n"); PRINT_CSTSTR("%s","encrypted payload\n"); + Encrypt_Payload((unsigned char*)message, pl, Frame_Counter_Up, Direction); + //Print encrypted message for(int i = 0; i < pl; i++) { @@ -494,7 +534,7 @@ void loop(void) // with encryption, we use for the payload a LoRaWAN packet format to reuse available LoRaWAN encryption libraries // - unsigned char LORAWAN_Data[128]; + unsigned char LORAWAN_Data[80]; unsigned char LORAWAN_Package_Length; unsigned char MIC[4]; //Unconfirmed data up @@ -507,17 +547,13 @@ void loop(void) //Build the Radio Package, LoRaWAN format //See LoRaWAN specification LORAWAN_Data[0] = Mac_Header; - LORAWAN_Data[1] = DevAddr[3]; LORAWAN_Data[2] = DevAddr[2]; LORAWAN_Data[3] = DevAddr[1]; LORAWAN_Data[4] = DevAddr[0]; - LORAWAN_Data[5] = Frame_Control; - LORAWAN_Data[6] = (Frame_Counter_Up & 0x00FF); LORAWAN_Data[7] = ((Frame_Counter_Up >> 8) & 0x00FF); - LORAWAN_Data[8] = Frame_Port; //Set Current package length @@ -533,7 +569,7 @@ void loop(void) //Add data Lenth to package length LORAWAN_Package_Length = LORAWAN_Package_Length + r_size + app_key_offset; - PRINT_CSTSTR("%s","calculate MIC with NwkSKey\n"); + //PRINT_CSTSTR("%s","calculate MIC with NwkSKey\n"); //Calculate MIC Calculate_MIC(LORAWAN_Data, MIC, LORAWAN_Package_Length, Frame_Counter_Up, Direction); @@ -546,9 +582,10 @@ void loop(void) //Add MIC length to package length LORAWAN_Package_Length = LORAWAN_Package_Length + 4; - PRINT_CSTSTR("%s","transmitted LoRaWAN-like packet:\n"); - PRINT_CSTSTR("%s","MHDR[1] | DevAddr[4] | FCtrl[1] | FCnt[2] | FPort[1] | EncryptedPayload | MIC[4]\n"); - //Print transmitted data + //PRINT_CSTSTR("%s","LoRaWAN-like packet:\n"); + //PRINT_CSTSTR("%s","encapsulated LoRaWAN for encryption\n"); + PRINT_CSTSTR("%s","MHDR[1] | DevAddr[4] | FCtrl[1] | FCnt[2] | FPort[1] | Payload | MIC[4]\n"); + // Print transmitted data for(int i = 0; i < LORAWAN_Package_Length; i++) { if (LORAWAN_Data[i]<16) @@ -562,25 +599,9 @@ void loop(void) memcpy(message,LORAWAN_Data,LORAWAN_Package_Length); pl = LORAWAN_Package_Length; -#ifdef LORAWAN - PRINT_CSTSTR("%s","end-device uses native LoRaWAN packet format\n"); - // indicate to SX1272 lib that raw mode at transmission is required to avoid our own packet header - sx1272._rawFormat=true; -#else - PRINT_CSTSTR("%s","end-device uses encapsulated LoRaWAN packet format only for encryption\n"); -#endif - // in any case, we increment Frame_Counter_Up - // even if the transmission will not succeed + // we increment Frame_Counter_Up even if the transmission will not succeed Frame_Counter_Up++; -#endif - sx1272.CarrierSense(); - - startSend=millis(); - - uint8_t p_type=PKT_TYPE_DATA; - -#ifdef WITH_AES // indicate that payload is encrypted p_type = p_type | PKT_FLAG_DATA_ENCRYPTED; #endif @@ -591,36 +612,13 @@ void loop(void) #endif sx1272.setPacketType(p_type); - - // Send message to the gateway and print the result - // with the app key if this feature is enabled -#ifdef WITH_ACK - int n_retry=NB_RETRIES; - - do { - e = sx1272.sendPacketTimeoutACK(DEFAULT_DEST_ADDR, message, pl); - if (e==3) - PRINT_CSTSTR("%s","No ACK"); - - n_retry--; - - if (n_retry) - PRINT_CSTSTR("%s","Retry"); - else - PRINT_CSTSTR("%s","Abort"); - - } while (e && n_retry); -#else + sx1272.CarrierSense(); + + startSend=millis(); + // Send message to the gateway and print the result e = sx1272.sendPacketTimeout(DEFAULT_DEST_ADDR, message, pl); -#endif - endSend=millis(); - -#ifdef LORAWAN - // switch back to normal behavior - sx1272._rawFormat=false; -#endif #ifdef WITH_EEPROM // save packet number for next packet in case of reboot @@ -628,63 +626,56 @@ void loop(void) EEPROM.put(0, my_sx1272config); #endif - PRINT_CSTSTR("%s","LoRa pkt size "); + PRINT_CSTSTR("%s","pkt size "); PRINT_VALUE("%d", pl); PRINTLN; - PRINT_CSTSTR("%s","LoRa pkt seq "); + PRINT_CSTSTR("%s","pkt seq "); PRINT_VALUE("%d", sx1272.packet_sent.packnum); PRINTLN; - PRINT_CSTSTR("%s","LoRa Sent in "); + PRINT_CSTSTR("%s","Sent in "); PRINT_VALUE("%ld", endSend-startSend); PRINTLN; - PRINT_CSTSTR("%s","LoRa Sent w/CAD in "); + PRINT_CSTSTR("%s","Sent w/CAD in "); PRINT_VALUE("%ld", endSend-sx1272._startDoCad); PRINTLN; - PRINT_CSTSTR("%s","Packet sent, state "); + PRINT_CSTSTR("%s","Sent, state "); PRINT_VALUE("%d", e); PRINTLN; +#if defined OLED + u8x8.clearLine(4); + sprintf(oled_msg, "LoRa Sent: %d", e); + u8x8.drawString(0, 4, oled_msg); +#endif + #ifdef LOW_POWER - PRINT_CSTSTR("%s","Switch to power saving mode\n"); - + PRINT_CSTSTR("%s","To sleep mode for "); + PRINT_VALUE("%d", idlePeriodInMin); + PRINT_CSTSTR("%s","mins"); + PRINTLN; + e = sx1272.setSleepMode(); +#if defined OLED + u8x8.clearLine(5); + u8x8.drawString(0, 5, "Sleep..."); +#endif + + PRINT_CSTSTR("%s","radio sleep: "); + if (!e) - PRINT_CSTSTR("%s","Successfully switch LoRa module in sleep mode\n"); + PRINT_CSTSTR("%s","ok\n"); else - PRINT_CSTSTR("%s","Could not switch LoRa module in sleep mode\n"); + PRINT_CSTSTR("%s","err\n"); FLUSHOUTPUT delay(50); -#ifdef __SAMD21G18A__ - // For Arduino M0 or Zero we use the built-in RTC - rtc.setTime(17, 0, 0); - rtc.setDate(1, 1, 2000); - rtc.setAlarmTime(17, idlePeriodInMin, 0); - // for testing with 20s - //rtc.setAlarmTime(17, 0, 20); - rtc.enableAlarm(rtc.MATCH_HHMMSS); - //rtc.attachInterrupt(alarmMatch); - rtc.standbyMode(); - - LowPower.standby(); - - PRINT_CSTSTR("%s","SAMD21G18A wakes up from standby\n"); - FLUSHOUTPUT -#else - nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD + random(2,4); - -#if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ - // warning, setTimer accepts value from 1ms to 65535ms max - timer.setTimer(LOW_POWER_PERIOD*1000 + random(1,5)*1000);// milliseconds - - nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD; -#endif + nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD + random(1,2); for (int i=0; i= MIN_INTERVAL right away. Note that this assignment wraps around, + // but so will the subtraction. + _lastreadtime = -MIN_INTERVAL; + DEBUG_PRINT("Max clock cycles: "); DEBUG_PRINTLN(_maxcycles, DEC); +} + +//boolean S == Scale. True == Fahrenheit; False == Celcius +float DHT::readTemperature(bool S, bool force) { + float f = NAN; + + if (read(force)) { + switch (_type) { + case DHT11: + f = data[2]; + if(S) { + f = convertCtoF(f); + } + break; + case DHT22: + case DHT21: + f = data[2] & 0x7F; + f *= 256; + f += data[3]; + f *= 0.1; + if (data[2] & 0x80) { + f *= -1; + } + if(S) { + f = convertCtoF(f); + } + break; + } + } + return f; +} + +float DHT::convertCtoF(float c) { + return c * 1.8 + 32; +} + +float DHT::convertFtoC(float f) { + return (f - 32) * 0.55555; +} + +float DHT::readHumidity(bool force) { + float f = NAN; + if (read()) { + switch (_type) { + case DHT11: + f = data[0]; + break; + case DHT22: + case DHT21: + f = data[0]; + f *= 256; + f += data[1]; + f *= 0.1; + break; + } + } + return f; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHT::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) { + // Using both Rothfusz and Steadman's equations + // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml + float hi; + + if (!isFahrenheit) + temperature = convertCtoF(temperature); + + hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094)); + + if (hi > 79) { + hi = -42.379 + + 2.04901523 * temperature + + 10.14333127 * percentHumidity + + -0.22475541 * temperature*percentHumidity + + -0.00683783 * pow(temperature, 2) + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(temperature, 2) * percentHumidity + + 0.00085282 * temperature*pow(percentHumidity, 2) + + -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); + + if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) + hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); + + else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) + hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); + } + + return isFahrenheit ? hi : convertFtoC(hi); +} + +boolean DHT::read(bool force) { + // Check if sensor was read less than two seconds ago and return early + // to use last reading. + uint32_t currenttime = millis(); + if (!force && ((currenttime - _lastreadtime) < 2000)) { + return _lastresult; // return last correct measurement + } + _lastreadtime = currenttime; + + // Reset 40 bits of received data to zero. + data[0] = data[1] = data[2] = data[3] = data[4] = 0; + + // Send start signal. See DHT datasheet for full signal diagram: + // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf + + // Go into high impedence state to let pull-up raise data line level and + // start the reading process. + digitalWrite(_pin, HIGH); + delay(250); + + // First set data line low for 20 milliseconds. + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + delay(20); + + uint32_t cycles[80]; + { + // Turn off interrupts temporarily because the next sections are timing critical + // and we don't want any interruptions. + InterruptLock lock; + + // End the start signal by setting data line high for 40 microseconds. + digitalWrite(_pin, HIGH); + delayMicroseconds(40); + + // Now start reading the data line to get the value from the DHT sensor. + pinMode(_pin, INPUT_PULLUP); + delayMicroseconds(10); // Delay a bit to let sensor pull data line low. + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if (expectPulse(LOW) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); + _lastresult = false; + return _lastresult; + } + if (expectPulse(HIGH) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse.")); + _lastresult = false; + return _lastresult; + } + + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed all + // the pulses are read into a array and then examined in a later step. + for (int i=0; i<80; i+=2) { + cycles[i] = expectPulse(LOW); + cycles[i+1] = expectPulse(HIGH); + } + } // Timing critical code is now complete. + + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i=0; i<40; ++i) { + uint32_t lowCycles = cycles[2*i]; + uint32_t highCycles = cycles[2*i+1]; + if ((lowCycles == 0) || (highCycles == 0)) { + DEBUG_PRINTLN(F("Timeout waiting for pulse.")); + _lastresult = false; + return _lastresult; + } + data[i/8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + data[i/8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } + + DEBUG_PRINTLN(F("Received:")); + DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); + DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); + + // Check we read 40 bits and that the checksum matches. + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + _lastresult = true; + return _lastresult; + } + else { + DEBUG_PRINTLN(F("Checksum failure!")); + _lastresult = false; + return _lastresult; + } +} + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function (which is only available +// in the very latest IDE versions): +// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c +uint32_t DHT::expectPulse(bool level) { + uint32_t count = 0; + // On AVR platforms use direct GPIO port access as it's much faster and better + // for catching pulses that are 10's of microseconds in length: + #ifdef __AVR + uint8_t portState = level ? _bit : 0; + while ((*portInputRegister(_port) & _bit) == portState) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + // Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266 + // right now, perhaps bugs in direct port access functions?). + #else + while (digitalRead(_pin) == level) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + #endif + + return count; +} diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT.h new file mode 100644 index 00000000..d81f6dbc --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT.h @@ -0,0 +1,75 @@ +/* DHT library + +MIT license +written by Adafruit Industries +*/ +#ifndef DHT_H +#define DHT_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + + +// Uncomment to enable printing out nice debug messages. +//#define DHT_DEBUG + +// Define where debug output will be printed. +#define DEBUG_PRINTER Serial + +// Setup debug printing macros. +#ifdef DHT_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +// Define types of sensors. +#define DHT11 11 +#define DHT22 22 +#define DHT21 21 +#define AM2301 21 + + +class DHT { + public: + DHT(uint8_t pin, uint8_t type, uint8_t count=6); + void begin(void); + float readTemperature(bool S=false, bool force=false); + float convertCtoF(float); + float convertFtoC(float); + float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true); + float readHumidity(bool force=false); + boolean read(bool force=false); + + private: + uint8_t data[5]; + uint8_t _pin, _type; + #ifdef __AVR + // Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask + // for the digital pin connected to the DHT. Other platforms will use digitalRead. + uint8_t _bit, _port; + #endif + uint32_t _lastreadtime, _maxcycles; + bool _lastresult; + + uint32_t expectPulse(bool level); + +}; + +class InterruptLock { + public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.cpp new file mode 100644 index 00000000..9574229b --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.cpp @@ -0,0 +1,237 @@ +/* + DHT22.cpp - DHT22 sensor library + Developed by Ben Adams - 2011 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Humidity and Temperature Sensor DHT22 info found at +http://www.sparkfun.com/products/10167 + +Version 0.5: 15 Jan 2012 by Craig Ringer +- Updated to build against Arduino 1.0 +- Made accessors inline in the header so they can be optimized away + +Version 0.4: 24-Jan-2011 by Ben Adams +Added return code constants to keywords.txt +Returns DHT_ERROR_CHECKSUM on check sum mismatch + +Version 0.3: 17-Jan-2011 by Ben Adams +This version reads data +Needs check sum code added at the end of readData + +Version 0.2: 16-Jan-2011 by Ben Adams +Changed coding style to match other Arduino libraries. +This version will not read data either! + +Version 0.1: 10-Jan-2011 by Ben Adams nethoncho AT gmail.com +First Version is a skeleton. This version will not read data! +Code adapted from the following sources: +The Arduino OneWire lib +http://sheepdogguides.com/arduino/ar3ne1humDHT11.htm + +*/ + +#include "DHT22.h" +#include +#include + +extern "C" { +#include +#include +#include +} + +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*(base+1)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*(base+2)) &= ~(mask)) +//#define DIRECT_WRITE_HIGH(base, mask) ((*(base+2)) |= (mask)) + +// This should be 40, but the sensor is adding an extra bit at the start +#define DHT22_DATA_BIT_COUNT 41 + +DHT22::DHT22(uint8_t pin) +{ + _bitmask = digitalPinToBitMask(pin); + _baseReg = portInputRegister(digitalPinToPort(pin)); + _lastReadTime = millis(); + _lastHumidity = DHT22_ERROR_VALUE; + _lastTemperature = DHT22_ERROR_VALUE; +} + +// +// Read the 40 bit data stream from the DHT 22 +// Store the results in private member data to be read by public member functions +// +DHT22_ERROR_t DHT22::readData() +{ + uint8_t bitmask = _bitmask; + volatile uint8_t *reg asm("r30") = _baseReg; + uint8_t retryCount; + uint8_t bitTimes[DHT22_DATA_BIT_COUNT]; + int currentHumidity; + int currentTemperature; + uint8_t checkSum, csPart1, csPart2, csPart3, csPart4; + unsigned long currentTime; + int i; + + currentHumidity = 0; + currentTemperature = 0; + checkSum = 0; + currentTime = millis(); + for(i = 0; i < DHT22_DATA_BIT_COUNT; i++) + { + bitTimes[i] = 0; + } + + if(currentTime - _lastReadTime < 2000) + { + // Caller needs to wait 2 seconds between each call to readData + return DHT_ERROR_TOOQUICK; + } + _lastReadTime = currentTime; + + // Pin needs to start HIGH, wait until it is HIGH with a timeout + cli(); + DIRECT_MODE_INPUT(reg, bitmask); + sei(); + retryCount = 0; + do + { + if (retryCount > 125) + { + return DHT_BUS_HUNG; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Send the activate pulse + cli(); + DIRECT_WRITE_LOW(reg, bitmask); + DIRECT_MODE_OUTPUT(reg, bitmask); // Output Low + sei(); + delayMicroseconds(1100); // 1.1 ms + cli(); + DIRECT_MODE_INPUT(reg, bitmask); // Switch back to input so pin can float + sei(); + // Find the start of the ACK Pulse + retryCount = 0; + do + { + if (retryCount > 25) //(Spec is 20 to 40 us, 25*2 == 50 us) + { + return DHT_ERROR_NOT_PRESENT; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Find the end of the ACK Pulse + retryCount = 0; + do + { + if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us) + { + return DHT_ERROR_ACK_TOO_LONG; + } + retryCount++; + delayMicroseconds(2); + } while(DIRECT_READ(reg, bitmask)); + // Read the 40 bit data stream + for(i = 0; i < DHT22_DATA_BIT_COUNT; i++) + { + // Find the start of the sync pulse + retryCount = 0; + do + { + if (retryCount > 35) //(Spec is 50 us, 35*2 == 70 us) + { + return DHT_ERROR_SYNC_TIMEOUT; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Measure the width of the data pulse + retryCount = 0; + do + { + if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us) + { + return DHT_ERROR_DATA_TIMEOUT; + } + retryCount++; + delayMicroseconds(2); + } while(DIRECT_READ(reg, bitmask)); + bitTimes[i] = retryCount; + } + // Now bitTimes have the number of retries (us *2) + // that were needed to find the end of each data bit + // Spec: 0 is 26 to 28 us + // Spec: 1 is 70 us + // bitTimes[x] <= 11 is a 0 + // bitTimes[x] > 11 is a 1 + // Note: the bits are offset by one from the data sheet, not sure why + for(i = 0; i < 16; i++) + { + if(bitTimes[i + 1] > 11) + { + currentHumidity |= (1 << (15 - i)); + } + } + for(i = 0; i < 16; i++) + { + if(bitTimes[i + 17] > 11) + { + currentTemperature |= (1 << (15 - i)); + } + } + for(i = 0; i < 8; i++) + { + if(bitTimes[i + 33] > 11) + { + checkSum |= (1 << (7 - i)); + } + } + + _lastHumidity = currentHumidity & 0x7FFF; + if(currentTemperature & 0x8000) + { + // Below zero, non standard way of encoding negative numbers! + // Convert to native negative format. + _lastTemperature = -currentTemperature & 0x7FFF; + } + else + { + _lastTemperature = currentTemperature; + } + + csPart1 = currentHumidity >> 8; + csPart2 = currentHumidity & 0xFF; + csPart3 = currentTemperature >> 8; + csPart4 = currentTemperature & 0xFF; + if(checkSum == ((csPart1 + csPart2 + csPart3 + csPart4) & 0xFF)) + { + return DHT_ERROR_NONE; + } + return DHT_ERROR_CHECKSUM; +} + +// +// This is used when the millis clock rolls over to zero +// +void DHT22::clockReset() +{ + _lastReadTime = millis(); +} diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.h new file mode 100644 index 00000000..bddebc74 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22.h @@ -0,0 +1,76 @@ +#ifndef _DHT22_H_ +#define _DHT22_H_ + +#include + +#define DHT22_ERROR_VALUE -995 + +typedef enum +{ + DHT_ERROR_NONE = 0, + DHT_BUS_HUNG, + DHT_ERROR_NOT_PRESENT, + DHT_ERROR_ACK_TOO_LONG, + DHT_ERROR_SYNC_TIMEOUT, + DHT_ERROR_DATA_TIMEOUT, + DHT_ERROR_CHECKSUM, + DHT_ERROR_TOOQUICK +} DHT22_ERROR_t; + +class DHT22 +{ + private: + uint8_t _bitmask; + volatile uint8_t *_baseReg; + unsigned long _lastReadTime; + short int _lastHumidity; + short int _lastTemperature; + + public: + DHT22(uint8_t pin); + DHT22_ERROR_t readData(); + short int getHumidityInt(); + short int getTemperatureCInt(); + void clockReset(); +#if !defined(DHT22_NO_FLOAT) + float getHumidity(); + float getTemperatureC(); +#endif +}; + +// Report the humidity in .1 percent increments, such that 635 means 63.5% relative humidity +// +// Converts from the internal integer format on demand, so you might want +// to cache the result. +inline short int DHT22::getHumidityInt() +{ + return _lastHumidity; +} + +// Get the temperature in decidegrees C, such that 326 means 32.6 degrees C. +// The temperature may be negative, so be careful when handling the fractional part. +inline short int DHT22::getTemperatureCInt() +{ + return _lastTemperature; +} + +#if !defined(DHT22_NO_FLOAT) +// Return the percentage relative humidity in decimal form +inline float DHT22::getHumidity() +{ + return float(_lastHumidity)/10; +} +#endif + +#if !defined(DHT22_NO_FLOAT) +// Return the percentage relative humidity in decimal form +// +// Converts from the internal integer format on demand, so you might want +// to cache the result. +inline float DHT22::getTemperatureC() +{ + return float(_lastTemperature)/10; +} +#endif //DHT22_SUPPORT_FLOAT + +#endif /*_DHT22_H_*/ diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.cpp new file mode 100644 index 00000000..ac6033eb --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +* +* modified by Congduc Pham, University of Pau, France +*/ + +#include "DHT22_Humidity.h" + +DHT22_Humidity::DHT22_Humidity(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power){ + if (get_is_connected()){ + // start library DHT + // dht = new DHT22(get_pin_read()); + dht = new DHT(get_pin_read(), DHT22); + + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(2000); + } +} + +void DHT22_Humidity::update_data() +{ + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + dht->begin(); + + // wait + delay(get_warmup_time()); + + double h = dht->readHumidity(); + + if (isnan(h)) + set_data((double)-1.0); + else + set_data(h); + + // recover errorCode to know if there was an error or not + //errorCode = dht->readData(); + + //if(errorCode == DHT_ERROR_NONE){ + // no error + //set_data((double)dht->getHumidity()); + //} + //else { + // set_data((double)-1.0); + //} + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(15, 90)); + } +} + +double DHT22_Humidity::get_value() +{ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.h new file mode 100644 index 00000000..fe1f932a --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Humidity.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +*/ + +#ifndef DHT22_HUMIDITY_H +#define DHT22_HUMIDITY_H + +#include "Sensor.h" +//#include "DHT22.h" +#include "DHT.h" + +class DHT22_Humidity : public Sensor { + public: + DHT22_Humidity(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power); + double get_value(); + void update_data(); + + private: + DHT* dht = NULL; + //DHT22* dht = NULL; + //DHT22_ERROR_t errorCode; + uint8_t _dht_type; + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.cpp new file mode 100644 index 00000000..07aed9e6 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +* +* modified by Congduc Pham, University of Pau, France +*/ + +#include "DHT22_Temperature.h" + +DHT22_Temperature::DHT22_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature,is_analog, is_connected, is_low_power, pin_read, pin_power){ + if (get_is_connected()){ + // start library DHT + // dht = new DHT22(get_pin_read()); + dht = new DHT(get_pin_read(), DHT22); + + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(2000); + } +} + +void DHT22_Temperature::update_data() +{ + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + dht->begin(); + + // wait + delay(get_warmup_time()); + + double t = dht->readTemperature(); + + if (isnan(t)) + set_data((double)-1.0); + else + set_data(t); + + // recover errorCode to know if there was an error or not + //errorCode = dht->readData(); + + //if(errorCode == DHT_ERROR_NONE){ + // no error + //set_data((double)dht->getTemperatureC()); + //} + //else { + // set_data((double)-1.0); + //} + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(-20, 40)); + } + +} + +double DHT22_Temperature::get_value(){ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.h new file mode 100644 index 00000000..e8e2e35c --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/DHT22_Temperature.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +*/ + +#ifndef DHT22_TEMPERATURE_H +#define DHT22_TEMPERATURE_H + +#include "Sensor.h" +//#include "DHT22.h" +#include "DHT.h" + +class DHT22_Temperature : public Sensor { + public: + DHT22_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power); + double get_value(); + void update_data(); + + private: + DHT* dht = NULL; + //DHT22* dht = NULL; + //DHT22_ERROR_t errorCode; + uint8_t _dht_type; + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/DS18B20.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/DS18B20.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/DS18B20.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/DS18B20.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/DS18B20.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/DS18B20.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/DS18B20.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/DS18B20.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/HCSR04.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/HCSR04.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/HCSR04.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/HCSR04.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/HCSR04.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/HCSR04.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/HCSR04.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/HCSR04.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/HRLV.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/HRLV.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/HRLV.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/HRLV.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/HRLV.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/HRLV.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/HRLV.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/HRLV.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/LM35.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/LM35.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/LM35.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/LM35.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/LM35.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/LM35.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/LM35.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/LM35.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/LeafWetness.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/LeafWetness.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/LeafWetness.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/LeafWetness.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/LeafWetness.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/LeafWetness.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/LeafWetness.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/LeafWetness.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/SHT_Humidity.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Humidity.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/SHT_Humidity.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Humidity.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/SHT_Humidity.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Humidity.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/SHT_Humidity.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Humidity.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/SHT_Temperature.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Temperature.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/SHT_Temperature.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Temperature.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/SHT_Temperature.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Temperature.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/SHT_Temperature.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/SHT_Temperature.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/Sensirion.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensirion.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/Sensirion.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/Sensirion.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/Sensirion.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensirion.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/Sensirion.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/Sensirion.h diff --git a/Arduino/Arduino_LoRa_SoilHum/Sensor.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.cpp similarity index 93% rename from Arduino/Arduino_LoRa_SoilHum/Sensor.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.cpp index 6c8a9467..a013a12b 100644 --- a/Arduino/Arduino_LoRa_SoilHum/Sensor.cpp +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.cpp @@ -25,9 +25,9 @@ Sensor::Sensor(char* nomenclature, bool is_analog, bool is_connected, bool is_lo set_pin_power(pin_power); set_pin_trigger(pin_trigger); set_data(0); - set_wait_time(0); + set_warmup_time(0); set_fake_data(false); - set_n_sample(1); + set_n_sample(5); /*if(_pin_power != -1){ set_power_set("LOW"); @@ -74,8 +74,8 @@ double Sensor::get_data(){ return _data; } -unsigned long Sensor::get_wait_time(){ - return _wait_time; +uint16_t Sensor::get_warmup_time(){ + return _warmup_time; } bool Sensor::has_fake_data(){ @@ -132,8 +132,8 @@ void Sensor::set_data(double d) { _data = -1.0; } -void Sensor::set_wait_time(unsigned long i){ - _wait_time = i; +void Sensor::set_warmup_time(uint16_t t){ + _warmup_time = t; } void Sensor::set_fake_data(bool b) { diff --git a/Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.h similarity index 87% rename from Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.h index 41ff6b82..07f076f6 100644 --- a/Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.h +++ b/Arduino/Arduino_LoRa_Generic_MultiSensors/Sensor.h @@ -2,6 +2,8 @@ * Copyright (C) 2016 Nicolas Bertuol, University of Pau, France * * nicolas.bertuol@etud.univ-pau.fr +* +* Modified by C. Pham, University of Pau, France */ #ifndef SENSOR_H @@ -20,7 +22,7 @@ #define IS_LOWPOWER true #define IS_NOT_LOWPOWER false -#define MAX_NOMENCLATURE_LENGTH 4 +#define MAX_NOMENCLATURE_LENGTH 5 #if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_MINI || defined __MK20DX256__ || defined __MKL26Z64__ || defined __SAMD21G18A__ // these boards work in 3.3V @@ -50,8 +52,8 @@ class Sensor { uint8_t get_pin_power(); int get_pin_trigger(); //char* get_power_set(); - double get_data(); - unsigned long get_wait_time(); + double get_data(); + uint16_t get_warmup_time(); bool has_fake_data(); bool has_pin_trigger(); uint8_t get_n_sample(); @@ -65,8 +67,8 @@ class Sensor { void set_pin_power(uint8_t u); void set_pin_trigger(int u); //void set_power_set(char* c); - void set_data(double d); - void set_wait_time(unsigned long i); + void set_data(double d); + void set_warmup_time(uint16_t t); void set_fake_data(bool b); void set_n_sample(uint8_t n); @@ -83,8 +85,9 @@ class Sensor { int _pin_trigger; bool _with_fake_data; //char* _power_set = NULL; - double _data; - unsigned long _wait_time; + double _data; + // delay in ms before reading data, sensor is powered + uint16_t _warmup_time; uint8_t _n_sample; }; diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/TMP36.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/TMP36.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/TMP36.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/TMP36.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/TMP36.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/TMP36.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/TMP36.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/TMP36.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/rawAnalog.cpp b/Arduino/Arduino_LoRa_Generic_MultiSensors/rawAnalog.cpp similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/rawAnalog.cpp rename to Arduino/Arduino_LoRa_Generic_MultiSensors/rawAnalog.cpp diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/rawAnalog.h b/Arduino/Arduino_LoRa_Generic_MultiSensors/rawAnalog.h similarity index 100% rename from Arduino/Arduino_LoRa_Generic_Sensor/rawAnalog.h rename to Arduino/Arduino_LoRa_Generic_MultiSensors/rawAnalog.h diff --git a/Arduino/Arduino_LoRa_Generic_Sensor/Adafruit_Sensor.h b/Arduino/Arduino_LoRa_Generic_Sensor/Adafruit_Sensor.h deleted file mode 100644 index 7742afc1..00000000 --- a/Arduino/Arduino_LoRa_Generic_Sensor/Adafruit_Sensor.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -* Copyright (C) 2008 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software< /span> -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and - * extended sensor support to include color, voltage and current */ - -#ifndef _ADAFRUIT_SENSOR_H -#define _ADAFRUIT_SENSOR_H - -#if ARDUINO >= 100 - #include "Arduino.h" - #include "Print.h" -#else - #include "WProgram.h" -#endif - -/* Intentionally modeled after sensors.h in the Android API: - * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h */ - -/* Constants */ -#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ -#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ -#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ -#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) -#define SENSORS_MAGFIELD_EARTH_MAX (60.0F) /**< Maximum magnetic field on Earth's surface */ -#define SENSORS_MAGFIELD_EARTH_MIN (30.0F) /**< Minimum magnetic field on Earth's surface */ -#define SENSORS_PRESSURE_SEALEVELHPA (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ -#define SENSORS_DPS_TO_RADS (0.017453293F) /**< Degrees/s to rad/s multiplier */ -#define SENSORS_GAUSS_TO_MICROTESLA (100) /**< Gauss to micro-Tesla multiplier */ - -/** Sensor types */ -typedef enum -{ - SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ - SENSOR_TYPE_MAGNETIC_FIELD = (2), - SENSOR_TYPE_ORIENTATION = (3), - SENSOR_TYPE_GYROSCOPE = (4), - SENSOR_TYPE_LIGHT = (5), - SENSOR_TYPE_PRESSURE = (6), - SENSOR_TYPE_PROXIMITY = (8), - SENSOR_TYPE_GRAVITY = (9), - SENSOR_TYPE_LINEAR_ACCELERATION = (10), /**< Acceleration not including gravity */ - SENSOR_TYPE_ROTATION_VECTOR = (11), - SENSOR_TYPE_RELATIVE_HUMIDITY = (12), - SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), - SENSOR_TYPE_VOLTAGE = (15), - SENSOR_TYPE_CURRENT = (16), - SENSOR_TYPE_COLOR = (17) -} sensors_type_t; - -/** struct sensors_vec_s is used to return a vector in a common format. */ -typedef struct { - union { - float v[3]; - struct { - float x; - float y; - float z; - }; - /* Orientation sensors */ - struct { - float roll; /**< Rotation around the longitudinal axis (the plane body, 'X axis'). Roll is positive and increasing when moving downward. -90°<=roll<=90° */ - float pitch; /**< Rotation around the lateral axis (the wing span, 'Y axis'). Pitch is positive and increasing when moving upwards. -180°<=pitch<=180°) */ - float heading; /**< Angle between the longitudinal axis (the plane body) and magnetic north, measured clockwise when viewing from the top of the device. 0-359° */ - }; - }; - int8_t status; - uint8_t reserved[3]; -} sensors_vec_t; - -/** struct sensors_color_s is used to return color data in a common format. */ -typedef struct { - union { - float c[3]; - /* RGB color space */ - struct { - float r; /**< Red component */ - float g; /**< Green component */ - float b; /**< Blue component */ - }; - }; - uint32_t rgba; /**< 24-bit RGBA value */ -} sensors_color_t; - -/* Sensor event (36 bytes) */ -/** struct sensor_event_s is used to provide a single sensor event in a common format. */ -typedef struct -{ - int32_t version; /**< must be sizeof(struct sensors_event_t) */ - int32_t sensor_id; /**< unique sensor identifier */ - int32_t type; /**< sensor type */ - int32_t reserved0; /**< reserved */ - int32_t timestamp; /**< time is in milliseconds */ - union - { - float data[4]; - sensors_vec_t acceleration; /**< acceleration values are in meter per second per second (m/s^2) */ - sensors_vec_t magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ - sensors_vec_t orientation; /**< orientation values are in degrees */ - sensors_vec_t gyro; /**< gyroscope values are in rad/s */ - float temperature; /**< temperature is in degrees centigrade (Celsius) */ - float distance; /**< distance in centimeters */ - float light; /**< light in SI lux units */ - float pressure; /**< pressure in hectopascal (hPa) */ - float relative_humidity; /**< relative humidity in percent */ - float current; /**< current in milliamps (mA) */ - float voltage; /**< voltage in volts (V) */ - sensors_color_t color; /**< color in RGB component values */ - }; -} sensors_event_t; - -/* Sensor details (40 bytes) */ -/** struct sensor_s is used to describe basic information about a specific sensor. */ -typedef struct -{ - char name[12]; /**< sensor name */ - int32_t version; /**< version of the hardware + driver */ - int32_t sensor_id; /**< unique sensor identifier */ - int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */ - float max_value; /**< maximum value of this sensor's value in SI units */ - float min_value; /**< minimum value of this sensor's value in SI units */ - float resolution; /**< smallest difference between two values reported by this sensor */ - int32_t min_delay; /**< min delay in microseconds between events. zero = not a constant rate */ -} sensor_t; - -class Adafruit_Sensor { - public: - // Constructor(s) - Adafruit_Sensor() {} - virtual ~Adafruit_Sensor() {} - - // These must be defined by the subclass - virtual void enableAutoRange(bool enabled) {}; - virtual bool getEvent(sensors_event_t*) = 0; - virtual void getSensor(sensor_t*) = 0; - - private: - bool _autoRange; -}; - -#endif diff --git a/Arduino/Arduino_LoRa_Simple_SoilHum/Arduino_LoRa_Simple_SoilHum.ino b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Arduino_LoRa_Generic_Simple_MultiSensors.ino similarity index 58% rename from Arduino/Arduino_LoRa_Simple_SoilHum/Arduino_LoRa_Simple_SoilHum.ino rename to Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Arduino_LoRa_Generic_Simple_MultiSensors.ino index d86a76a7..a606f709 100644 --- a/Arduino/Arduino_LoRa_Simple_SoilHum/Arduino_LoRa_Simple_SoilHum.ino +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Arduino_LoRa_Generic_Simple_MultiSensors.ino @@ -1,7 +1,7 @@ /* - * Soil humidity sensor + * Demonstration of generic sensors with the LoRa gateway * - * Copyright (C) 2017 Congduc Pham, University of Pau, France + * Copyright (C) 2016-2018 Congduc Pham, University of Pau, France * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,25 +17,36 @@ * along with the program. If not, see . * ***************************************************************************** - * last update: March 27th, 2017 for the WaterSense project by C. Pham + * Modified by Nicolas Bertuol (May 2016), University of Pau, France. + * first version of generic sensor + * nicolas.bertuol@etud.univ-pau.fr * - * Sensor 1 is connected to digital 9 for power and A0 for output value - * Sensor 2 is connected to digital 8 for power and A1 for output value + * last update: September 18th, 2018 by C. Pham */ - // IMPORTANT +// IMPORTANT /////////////////////////////////////////////////////////////////////////////////////////////////////////// // add here some specific board define statements if you want to implement user-defined specific settings // A/ LoRa radio node from IoTMCU: https://www.tindie.com/products/IOTMCU/lora-radio-node-v10/ //#define IOTMCU_LORA_RADIO_NODE /////////////////////////////////////////////////////////////////////////////////////////////////////////// - + #include // Include the SX1272 #include "SX1272.h" // Include sensors #include "Sensor.h" -#include "rawAnalog.h" +#include "LM35.h" +#include "TMP36.h" +#include "DHT22_Humidity.h" +#include "DHT22_Temperature.h" +#include "SHT_Humidity.h" +#include "SHT_Temperature.h" +#include "DS18B20.h" +//#include "rawAnalog.h" +//#include "LeafWetness.h" +//#include "HCSR04.h" +//#include "HRLV.h" // IMPORTANT /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -90,9 +101,12 @@ const uint32_t DEFAULT_CHANNEL=CH_00_433; // ONLY IF YOU KNOW WHAT YOU ARE DOING!!! OTHERWISE LEAVE AS IT IS #define WITH_EEPROM #define WITH_APPKEY +#define FLOAT_TEMP #define LOW_POWER #define LOW_POWER_HIBERNATE //#define WITH_ACK +//this will enable a receive window after every transmission +//#define WITH_RCVW /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// @@ -109,26 +123,33 @@ const uint32_t DEFAULT_CHANNEL=CH_00_433; /////////////////////////////////////////////////////////////////// // CHANGE HERE THE LORA MODE, NODE ADDRESS #define LORAMODE 1 -// you need to change the node address for each sensor in the same organization/farm -// node address starts at 2 and ends at 255 -#define node_addr 2 +uint8_t node_addr=6; ////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// -// CHANGE HERE THE THINGSPEAK FIELD BETWEEN 1 AND 8 -#define field_index 1 +// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION +unsigned int idlePeriodInMin = 10; /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// -// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION -unsigned int idlePeriodInMin = 60; -/////////////////////////////////////////////////////////////////// +// COMMENT THIS LINE IF YOU WANT TO DYNAMICALLY SET THE NODE'S ADDR +// OR SOME OTHER PARAMETERS BY REMOTE RADIO COMMANDS (WITH_RCVW) +// LEAVE THIS LINE UNCOMMENTED IF YOU WANT TO USE THE DEFAULT VALUE +// AND CONFIGURE YOUR DEVICE BY CHANGING MANUALLY THESE VALUES IN +// THE SKETCH. +// +// ONCE YOU HAVE FLASHED A BOARD WITHOUT FORCE_DEFAULT_VALUE, YOU +// WILL BE ABLE TO DYNAMICALLY CONFIGURE IT AND SAVE THIS CONFIGU- +// RATION INTO EEPROM. ON RESET, THE BOARD WILL USE THE SAVED CON- +// FIGURATION. -// SENSORS DEFINITION -////////////////////////////////////////////////////////////////// -// CHANGE HERE THE NUMBER OF SENSORS, SOME CAN BE NOT CONNECTED -const int number_of_sensors = 2; -////////////////////////////////////////////////////////////////// +// IF YOU WANT TO REINITIALIZE A BOARD, YOU HAVE TO FIRST FLASH IT +// WITH FORCE_DEFAULT_VALUE, WAIT FOR ABOUT 10s SO THAT IT CAN BOOT +// AND FLASH IT AGAIN WITHOUT FORCE_DEFAULT_VALUE. THE BOARD WILL +// THEN USE THE DEFAULT CONFIGURATION UNTIL NEXT CONFIGURATION. + +#define FORCE_DEFAULT_VALUE +/////////////////////////////////////////////////////////////////// #ifdef WITH_APPKEY /////////////////////////////////////////////////////////////////// @@ -146,7 +167,7 @@ uint8_t my_appKey[4]={5, 6, 7, 8}; #define PRINT_VALUE(fmt,param) SerialUSB.print(param) #define FLUSHOUTPUT SerialUSB.flush(); #else -#define PRINTLN Serial.println("") +#define PRINTLN Serial.println("") #define PRINT_CSTSTR(fmt,param) Serial.print(F(param)) #define PRINT_STR(fmt,param) Serial.print(param) #define PRINT_VALUE(fmt,param) Serial.print(param) @@ -163,21 +184,6 @@ uint8_t my_appKey[4]={5, 6, 7, 8}; #define NB_RETRIES 2 #endif -#if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_MINI || defined ARDUINO_SAM_DUE || defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ || defined __SAMD21G18A__ - // if you have a Pro Mini running at 5V, then change here - // these boards work in 3.3V - // Nexus board from Ideetron is a Mini - // __MK66FX1M0__ is for Teensy36 - // __MK64FX512__ is for Teensy35 - // __MK20DX256__ is for Teensy31/32 - // __MKL26Z64__ is for TeensyLC - // __SAMD21G18A__ is for Zero/M0 and FeatherM0 (Cortex-M0) - #define TEMP_SCALE 3300.0 -#else // ARDUINO_AVR_NANO || defined ARDUINO_AVR_UNO || defined ARDUINO_AVR_MEGA2560 - // also for all other boards, so change here if required. - #define TEMP_SCALE 5000.0 -#endif - #ifdef LOW_POWER // this is for the Teensy36, Teensy35, Teensy31/32 & TeensyLC // need v6 of Snooze library @@ -212,17 +218,68 @@ struct sx1272config { uint8_t flag1; uint8_t flag2; uint8_t seq; + uint8_t addr; + unsigned int idle_period; + uint8_t overwrite; // can add other fields such as LoRa mode,... }; sx1272config my_sx1272config; #endif +#ifdef WITH_RCVW + +// will wait for 5s before opening the rcv window +#define DELAY_BEFORE_RCVW 5000 + +long getCmdValue(int &i, char* strBuff=NULL) { + + char seqStr[7]="******"; + + int j=0; + // character '#' will indicate end of cmd value + while ((char)message[i]!='#' && (i < strlen((char*)message)) && jset_n_sample(10); - sensor_ptrs[1]->set_n_sample(10); - //sensor_ptrs[2]->set_n_sample(10); + sensor_ptrs[0] = new LM35("LM35", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A0, (uint8_t) 9 /*no pin trigger*/); + sensor_ptrs[1] = new TMP36("TMP36", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A1, (uint8_t) 8 /*no pin trigger*/); + sensor_ptrs[2] = new DHT22_Temperature("TC1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); + sensor_ptrs[3] = new DHT22_Humidity("HU1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); + //for SHT, pin_trigger will be the clock pin + sensor_ptrs[4] = new SHT_Temperature("TC2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); + sensor_ptrs[5] = new SHT_Humidity("HU2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); + sensor_ptrs[6] = new DS18B20("DS", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 3, (uint8_t) 4 /*no pin trigger*/); + //sensor_ptrs[3] = new rawAnalog("SH", IS_ANALOG, IS_NOT_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7); + //sensor_ptrs[4] = new LeafWetness("lw", IS_ANALOG, IS_NOT_CONNECTED, low_power_status, (uint8_t) A3, (uint8_t) 6); + //sensor_ptrs[5] = new HRLV("DIS_", IS_ANALOG, IS_NOT_CONNECTED, low_power_status, (uint8_t) A3, (uint8_t) 6); + //sensor_ptrs[6] = new DS18B20("DS", IS_NOT_ANALOG, IS_NOT_CONNECTED, low_power_status, (uint8_t) 4, (uint8_t) 5); + //sensor_ptrs[7] = new HCSR04("DIS", IS_NOT_ANALOG, IS_NOT_CONNECTED, low_power_status, (uint8_t) 39, (uint8_t) 41, (uint8_t) 40); // for the MEGA + + // for non connected sensors, indicate whether you want some fake data, for test purposes for instance + //sensor_ptrs[3]->set_fake_data(true); + //sensor_ptrs[4]->set_fake_data(true); + +////////////////////////////////////////////////////////////////// + delay(3000); // Open serial communications and wait for port to open: #if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 SerialUSB.begin(38400); #else Serial.begin(38400); -#endif +#endif // Print a start message - PRINT_CSTSTR("%s","Simple LoRa soil moisture sensor\n"); + PRINT_CSTSTR("%s","Generic LoRa sensor\n"); #ifdef ARDUINO_AVR_PRO PRINT_CSTSTR("%s","Arduino Pro Mini detected\n"); @@ -319,7 +389,7 @@ void setup() EEPROM.get(0, my_sx1272config); // found a valid config? - if (my_sx1272config.flag1==0x12 && my_sx1272config.flag2==0x34) { + if (my_sx1272config.flag1==0x12 && my_sx1272config.flag2==0x35) { PRINT_CSTSTR("%s","Get back previous sx1272 config\n"); // set sequence number for SX1272 library @@ -327,12 +397,52 @@ void setup() PRINT_CSTSTR("%s","Using packet sequence number of "); PRINT_VALUE("%d", sx1272._packetNumber); PRINTLN; + +#ifdef FORCE_DEFAULT_VALUE + PRINT_CSTSTR("%s","Forced to use default parameters\n"); + my_sx1272config.flag1=0x12; + my_sx1272config.flag2=0x35; + my_sx1272config.seq=sx1272._packetNumber; + my_sx1272config.addr=node_addr; + my_sx1272config.idle_period=idlePeriodInMin; + my_sx1272config.overwrite=0; + EEPROM.put(0, my_sx1272config); +#else + // get back the node_addr + if (my_sx1272config.addr!=0 && my_sx1272config.overwrite==1) { + + PRINT_CSTSTR("%s","Used stored address\n"); + node_addr=my_sx1272config.addr; + } + else + PRINT_CSTSTR("%s","Stored node addr is null\n"); + + // get back the idle period + if (my_sx1272config.idle_period!=0 && my_sx1272config.overwrite==1) { + + PRINT_CSTSTR("%s","Used stored idle period\n"); + idlePeriodInMin=my_sx1272config.idle_period; + } + else + PRINT_CSTSTR("%s","Stored idle period is null\n"); +#endif + + PRINT_CSTSTR("%s","Using node addr of "); + PRINT_VALUE("%d", node_addr); + PRINTLN; + + PRINT_STR("%s","Using idle period of "); + PRINT_VALUE("%d", idlePeriodInMin); + PRINTLN; } else { // otherwise, write config and start over my_sx1272config.flag1=0x12; - my_sx1272config.flag2=0x34; + my_sx1272config.flag2=0x35; my_sx1272config.seq=sx1272._packetNumber; + my_sx1272config.addr=node_addr; + my_sx1272config.idle_period=idlePeriodInMin; + my_sx1272config.overwrite=0; } #endif @@ -348,8 +458,8 @@ void setup() // TODO: with low power, when setting the radio module in sleep mode // there seem to be some issue with RSSI reading sx1272._RSSIonSend=false; -#endif - +#endif + // Select frequency channel e = sx1272.setChannel(DEFAULT_CHANNEL); PRINT_CSTSTR("%s","Setting Channel: state "); @@ -367,8 +477,8 @@ void setup() #endif // previous way for setting output power - // e = sx1272.setPower(powerLevel); - + // e = sx1272.setPower(powerLevel); + e = sx1272.setPowerDBM((uint8_t)MAX_DBM); PRINT_CSTSTR("%s","Setting Power: state "); PRINT_VALUE("%d", e); @@ -381,7 +491,7 @@ void setup() PRINTLN; // Print a success message - PRINT_CSTSTR("%s","SX1272 successfully configured\n"); + PRINT_STR("%s","SX1272 successfully configured\n"); delay(500); } @@ -405,30 +515,32 @@ void loop(void) #endif uint8_t r_size; - + char final_str[80] = "\\!"; + char aux[6] = ""; + + //this is how we can wake up some sensors in advance in case they need a longer warmup time + //digitalWrite(sensor_ptrs[4]->get_pin_power(),HIGH); // main loop for sensors, actually, you don't have to edit anything here // just add a predefined sensor if needed or provide a new sensor class instance for a handle a new physical sensor for (int i=0; iget_is_connected() || sensor_ptrs[i]->has_fake_data()) { - - int value = (int)sensor_ptrs[i]->get_value(); - - PRINT_CSTSTR("%s","SM"); - PRINT_VALUE("%d", i+1); - PRINT_CSTSTR("%s", " on 10 sample is "); - PRINT_VALUE("%d", value); - PRINTLN; - + + ftoa(aux, sensor_ptrs[i]->get_value(), 2); + if (i==0) { - sprintf(final_str, "%s%s/%d", final_str, sensor_ptrs[i]->get_nomenclature(), value); + //sprintf(final_str, "%s%s/%s", final_str, nomenclatures_array[i], aux); + sprintf(final_str, "%s%s/%s", final_str, sensor_ptrs[i]->get_nomenclature(), aux); + } else { - sprintf(final_str, "%s/%s/%d", final_str, sensor_ptrs[i]->get_nomenclature(), value); + sprintf(final_str, "%s/%s/%s", final_str, sensor_ptrs[i]->get_nomenclature(), aux); } } + //else + // strcpy(aux,""); } r_size=sprintf((char*)message+app_key_offset, final_str); @@ -447,13 +559,14 @@ void loop(void) startSend=millis(); + uint8_t p_type=PKT_TYPE_DATA; + #ifdef WITH_APPKEY // indicate that we have an appkey - sx1272.setPacketType(PKT_TYPE_DATA | PKT_FLAG_DATA_WAPPKEY); -#else - // just a simple data packet - sx1272.setPacketType(PKT_TYPE_DATA); + p_type = p_type | PKT_FLAG_DATA_WAPPKEY; #endif + + sx1272.setPacketType(p_type); // Send message to the gateway and print the result // with the app key if this feature is enabled @@ -471,12 +584,13 @@ void loop(void) if (n_retry) PRINT_CSTSTR("%s","Retry"); else - PRINT_CSTSTR("%s","Abort"); + PRINT_CSTSTR("%s","Abort"); } while (e && n_retry); #else e = sx1272.sendPacketTimeout(DEFAULT_DEST_ADDR, message, pl); -#endif +#endif + endSend=millis(); #ifdef WITH_EEPROM @@ -484,10 +598,6 @@ void loop(void) my_sx1272config.seq=sx1272._packetNumber; EEPROM.put(0, my_sx1272config); #endif - - PRINT_CSTSTR("%s","LoRa pkt size "); - PRINT_VALUE("%d", pl); - PRINTLN; PRINT_CSTSTR("%s","LoRa pkt seq "); PRINT_VALUE("%d", sx1272.packet_sent.packnum); @@ -505,10 +615,135 @@ void loop(void) PRINT_VALUE("%d", e); PRINTLN; - PRINT_CSTSTR("%s","Remaining ToA is "); - PRINT_VALUE("%d", sx1272.getRemainingToA()); +#ifdef WITH_RCVW + PRINT_CSTSTR("%s","Wait for "); + PRINT_VALUE("%d", DELAY_BEFORE_RCVW-1000); PRINTLN; - + //wait a bit + delay(DELAY_BEFORE_RCVW-1000); + + PRINT_CSTSTR("%s","Wait for incoming packet\n"); + // wait for incoming packets + e = sx1272.receivePacketTimeout(10000); + + if (!e) { + int i=0; + int cmdValue; + uint8_t tmp_length; + + sx1272.getSNR(); + sx1272.getRSSIpacket(); + + tmp_length=sx1272._payloadlength; + + sprintf((char*)message, "^p%d,%d,%d,%d,%d,%d,%d\n", + sx1272.packet_received.dst, + sx1272.packet_received.type, + sx1272.packet_received.src, + sx1272.packet_received.packnum, + tmp_length, + sx1272._SNR, + sx1272._RSSIpacket); + + PRINT_STR("%s",(char*)message); + + for ( ; i 255) + cmdValue = 255; + // cannot set addr lower than 2 since 0 is broadcast and 1 is for gateway + if (cmdValue < 2) + cmdValue = node_addr; + // set node addr + node_addr=cmdValue; + + PRINT_CSTSTR("%s","Set LoRa node addr to "); + PRINT_VALUE("%d", node_addr); + PRINTLN; + // Set the node address and print the result + e = sx1272.setNodeAddress(node_addr); + PRINT_CSTSTR("%s","Setting LoRa node addr: state "); + PRINT_VALUE("%d",e); + PRINTLN; + +#ifdef WITH_EEPROM + // save new node_addr in case of reboot + my_sx1272config.addr=node_addr; + my_sx1272config.overwrite=1; + EEPROM.put(0, my_sx1272config); +#endif + + break; + + // set the time between 2 transmissions, /@I10# to set to 10 minutes for instance + case 'I': + + i++; + cmdValue=getCmdValue(i); + + // cannot set addr lower than 1 minute + if (cmdValue < 1) + cmdValue = idlePeriodInMin; + // idlePeriodInMin + idlePeriodInMin=cmdValue; + + PRINT_CSTSTR("%s","Set duty-cycle to "); + PRINT_VALUE("%d", idlePeriodInMin); + PRINTLN; + +#ifdef WITH_EEPROM + // save new node_addr in case of reboot + my_sx1272config.idle_period=idlePeriodInMin; + my_sx1272config.overwrite=1; + EEPROM.put(0, my_sx1272config); +#endif + + break; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // add here new commands + // + + // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + default: + + PRINT_CSTSTR("%s","Unrecognized cmd\n"); + break; + } + } + } + else + PRINT_CSTSTR("%s","No packet\n"); +#endif + #ifdef LOW_POWER PRINT_CSTSTR("%s","Switch to power saving mode\n"); @@ -533,11 +768,11 @@ void loop(void) //rtc.attachInterrupt(alarmMatch); rtc.standbyMode(); - LowPower.standby(); - + LowPower.standby(); + PRINT_CSTSTR("%s","SAMD21G18A wakes up from standby\n"); FLUSHOUTPUT -#else +#else nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD + random(2,4); #if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ @@ -561,14 +796,13 @@ void loop(void) //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF, // TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART3_OFF, // USART2_OFF, USART1_OFF, USART0_OFF, TWI_OFF); -#elif defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ +#elif defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ // Teensy31/32 & TeensyLC #ifdef LOW_POWER_HIBERNATE Snooze.hibernate(sleep_config); #else Snooze.deepSleep(sleep_config); -#endif - +#endif #else // use the delay function delay(LOW_POWER_PERIOD*1000); @@ -579,8 +813,8 @@ void loop(void) } delay(50); -#endif - +#endif + #else PRINT_VALUE("%ld", nextTransmissionTime); PRINTLN; @@ -591,6 +825,4 @@ void loop(void) PRINTLN; } #endif - - delay(50); } diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.cpp new file mode 100644 index 00000000..86ad91c4 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.cpp @@ -0,0 +1,259 @@ +/* DHT library + +MIT license +written by Adafruit Industries +*/ + +#include "DHT.h" + +#define MIN_INTERVAL 2000 + +DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) { + _pin = pin; + _type = type; + #ifdef __AVR + _bit = digitalPinToBitMask(pin); + _port = digitalPinToPort(pin); + #endif + _maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for + // reading pulses from DHT sensor. + // Note that count is now ignored as the DHT reading algorithm adjusts itself + // basd on the speed of the processor. +} + +void DHT::begin(void) { + // set up the pins! + pinMode(_pin, INPUT_PULLUP); + // Using this value makes sure that millis() - lastreadtime will be + // >= MIN_INTERVAL right away. Note that this assignment wraps around, + // but so will the subtraction. + _lastreadtime = -MIN_INTERVAL; + DEBUG_PRINT("Max clock cycles: "); DEBUG_PRINTLN(_maxcycles, DEC); +} + +//boolean S == Scale. True == Fahrenheit; False == Celcius +float DHT::readTemperature(bool S, bool force) { + float f = NAN; + + if (read(force)) { + switch (_type) { + case DHT11: + f = data[2]; + if(S) { + f = convertCtoF(f); + } + break; + case DHT22: + case DHT21: + f = data[2] & 0x7F; + f *= 256; + f += data[3]; + f *= 0.1; + if (data[2] & 0x80) { + f *= -1; + } + if(S) { + f = convertCtoF(f); + } + break; + } + } + return f; +} + +float DHT::convertCtoF(float c) { + return c * 1.8 + 32; +} + +float DHT::convertFtoC(float f) { + return (f - 32) * 0.55555; +} + +float DHT::readHumidity(bool force) { + float f = NAN; + if (read()) { + switch (_type) { + case DHT11: + f = data[0]; + break; + case DHT22: + case DHT21: + f = data[0]; + f *= 256; + f += data[1]; + f *= 0.1; + break; + } + } + return f; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHT::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) { + // Using both Rothfusz and Steadman's equations + // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml + float hi; + + if (!isFahrenheit) + temperature = convertCtoF(temperature); + + hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094)); + + if (hi > 79) { + hi = -42.379 + + 2.04901523 * temperature + + 10.14333127 * percentHumidity + + -0.22475541 * temperature*percentHumidity + + -0.00683783 * pow(temperature, 2) + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(temperature, 2) * percentHumidity + + 0.00085282 * temperature*pow(percentHumidity, 2) + + -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); + + if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) + hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); + + else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) + hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); + } + + return isFahrenheit ? hi : convertFtoC(hi); +} + +boolean DHT::read(bool force) { + // Check if sensor was read less than two seconds ago and return early + // to use last reading. + uint32_t currenttime = millis(); + if (!force && ((currenttime - _lastreadtime) < 2000)) { + return _lastresult; // return last correct measurement + } + _lastreadtime = currenttime; + + // Reset 40 bits of received data to zero. + data[0] = data[1] = data[2] = data[3] = data[4] = 0; + + // Send start signal. See DHT datasheet for full signal diagram: + // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf + + // Go into high impedence state to let pull-up raise data line level and + // start the reading process. + digitalWrite(_pin, HIGH); + delay(250); + + // First set data line low for 20 milliseconds. + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + delay(20); + + uint32_t cycles[80]; + { + // Turn off interrupts temporarily because the next sections are timing critical + // and we don't want any interruptions. + InterruptLock lock; + + // End the start signal by setting data line high for 40 microseconds. + digitalWrite(_pin, HIGH); + delayMicroseconds(40); + + // Now start reading the data line to get the value from the DHT sensor. + pinMode(_pin, INPUT_PULLUP); + delayMicroseconds(10); // Delay a bit to let sensor pull data line low. + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if (expectPulse(LOW) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); + _lastresult = false; + return _lastresult; + } + if (expectPulse(HIGH) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse.")); + _lastresult = false; + return _lastresult; + } + + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed all + // the pulses are read into a array and then examined in a later step. + for (int i=0; i<80; i+=2) { + cycles[i] = expectPulse(LOW); + cycles[i+1] = expectPulse(HIGH); + } + } // Timing critical code is now complete. + + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i=0; i<40; ++i) { + uint32_t lowCycles = cycles[2*i]; + uint32_t highCycles = cycles[2*i+1]; + if ((lowCycles == 0) || (highCycles == 0)) { + DEBUG_PRINTLN(F("Timeout waiting for pulse.")); + _lastresult = false; + return _lastresult; + } + data[i/8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + data[i/8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } + + DEBUG_PRINTLN(F("Received:")); + DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); + DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); + + // Check we read 40 bits and that the checksum matches. + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + _lastresult = true; + return _lastresult; + } + else { + DEBUG_PRINTLN(F("Checksum failure!")); + _lastresult = false; + return _lastresult; + } +} + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function (which is only available +// in the very latest IDE versions): +// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c +uint32_t DHT::expectPulse(bool level) { + uint32_t count = 0; + // On AVR platforms use direct GPIO port access as it's much faster and better + // for catching pulses that are 10's of microseconds in length: + #ifdef __AVR + uint8_t portState = level ? _bit : 0; + while ((*portInputRegister(_port) & _bit) == portState) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + // Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266 + // right now, perhaps bugs in direct port access functions?). + #else + while (digitalRead(_pin) == level) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + #endif + + return count; +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.h new file mode 100644 index 00000000..d81f6dbc --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT.h @@ -0,0 +1,75 @@ +/* DHT library + +MIT license +written by Adafruit Industries +*/ +#ifndef DHT_H +#define DHT_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + + +// Uncomment to enable printing out nice debug messages. +//#define DHT_DEBUG + +// Define where debug output will be printed. +#define DEBUG_PRINTER Serial + +// Setup debug printing macros. +#ifdef DHT_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +// Define types of sensors. +#define DHT11 11 +#define DHT22 22 +#define DHT21 21 +#define AM2301 21 + + +class DHT { + public: + DHT(uint8_t pin, uint8_t type, uint8_t count=6); + void begin(void); + float readTemperature(bool S=false, bool force=false); + float convertCtoF(float); + float convertFtoC(float); + float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true); + float readHumidity(bool force=false); + boolean read(bool force=false); + + private: + uint8_t data[5]; + uint8_t _pin, _type; + #ifdef __AVR + // Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask + // for the digital pin connected to the DHT. Other platforms will use digitalRead. + uint8_t _bit, _port; + #endif + uint32_t _lastreadtime, _maxcycles; + bool _lastresult; + + uint32_t expectPulse(bool level); + +}; + +class InterruptLock { + public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.cpp new file mode 100644 index 00000000..9574229b --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.cpp @@ -0,0 +1,237 @@ +/* + DHT22.cpp - DHT22 sensor library + Developed by Ben Adams - 2011 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Humidity and Temperature Sensor DHT22 info found at +http://www.sparkfun.com/products/10167 + +Version 0.5: 15 Jan 2012 by Craig Ringer +- Updated to build against Arduino 1.0 +- Made accessors inline in the header so they can be optimized away + +Version 0.4: 24-Jan-2011 by Ben Adams +Added return code constants to keywords.txt +Returns DHT_ERROR_CHECKSUM on check sum mismatch + +Version 0.3: 17-Jan-2011 by Ben Adams +This version reads data +Needs check sum code added at the end of readData + +Version 0.2: 16-Jan-2011 by Ben Adams +Changed coding style to match other Arduino libraries. +This version will not read data either! + +Version 0.1: 10-Jan-2011 by Ben Adams nethoncho AT gmail.com +First Version is a skeleton. This version will not read data! +Code adapted from the following sources: +The Arduino OneWire lib +http://sheepdogguides.com/arduino/ar3ne1humDHT11.htm + +*/ + +#include "DHT22.h" +#include +#include + +extern "C" { +#include +#include +#include +} + +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*(base+1)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*(base+2)) &= ~(mask)) +//#define DIRECT_WRITE_HIGH(base, mask) ((*(base+2)) |= (mask)) + +// This should be 40, but the sensor is adding an extra bit at the start +#define DHT22_DATA_BIT_COUNT 41 + +DHT22::DHT22(uint8_t pin) +{ + _bitmask = digitalPinToBitMask(pin); + _baseReg = portInputRegister(digitalPinToPort(pin)); + _lastReadTime = millis(); + _lastHumidity = DHT22_ERROR_VALUE; + _lastTemperature = DHT22_ERROR_VALUE; +} + +// +// Read the 40 bit data stream from the DHT 22 +// Store the results in private member data to be read by public member functions +// +DHT22_ERROR_t DHT22::readData() +{ + uint8_t bitmask = _bitmask; + volatile uint8_t *reg asm("r30") = _baseReg; + uint8_t retryCount; + uint8_t bitTimes[DHT22_DATA_BIT_COUNT]; + int currentHumidity; + int currentTemperature; + uint8_t checkSum, csPart1, csPart2, csPart3, csPart4; + unsigned long currentTime; + int i; + + currentHumidity = 0; + currentTemperature = 0; + checkSum = 0; + currentTime = millis(); + for(i = 0; i < DHT22_DATA_BIT_COUNT; i++) + { + bitTimes[i] = 0; + } + + if(currentTime - _lastReadTime < 2000) + { + // Caller needs to wait 2 seconds between each call to readData + return DHT_ERROR_TOOQUICK; + } + _lastReadTime = currentTime; + + // Pin needs to start HIGH, wait until it is HIGH with a timeout + cli(); + DIRECT_MODE_INPUT(reg, bitmask); + sei(); + retryCount = 0; + do + { + if (retryCount > 125) + { + return DHT_BUS_HUNG; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Send the activate pulse + cli(); + DIRECT_WRITE_LOW(reg, bitmask); + DIRECT_MODE_OUTPUT(reg, bitmask); // Output Low + sei(); + delayMicroseconds(1100); // 1.1 ms + cli(); + DIRECT_MODE_INPUT(reg, bitmask); // Switch back to input so pin can float + sei(); + // Find the start of the ACK Pulse + retryCount = 0; + do + { + if (retryCount > 25) //(Spec is 20 to 40 us, 25*2 == 50 us) + { + return DHT_ERROR_NOT_PRESENT; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Find the end of the ACK Pulse + retryCount = 0; + do + { + if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us) + { + return DHT_ERROR_ACK_TOO_LONG; + } + retryCount++; + delayMicroseconds(2); + } while(DIRECT_READ(reg, bitmask)); + // Read the 40 bit data stream + for(i = 0; i < DHT22_DATA_BIT_COUNT; i++) + { + // Find the start of the sync pulse + retryCount = 0; + do + { + if (retryCount > 35) //(Spec is 50 us, 35*2 == 70 us) + { + return DHT_ERROR_SYNC_TIMEOUT; + } + retryCount++; + delayMicroseconds(2); + } while(!DIRECT_READ(reg, bitmask)); + // Measure the width of the data pulse + retryCount = 0; + do + { + if (retryCount > 50) //(Spec is 80 us, 50*2 == 100 us) + { + return DHT_ERROR_DATA_TIMEOUT; + } + retryCount++; + delayMicroseconds(2); + } while(DIRECT_READ(reg, bitmask)); + bitTimes[i] = retryCount; + } + // Now bitTimes have the number of retries (us *2) + // that were needed to find the end of each data bit + // Spec: 0 is 26 to 28 us + // Spec: 1 is 70 us + // bitTimes[x] <= 11 is a 0 + // bitTimes[x] > 11 is a 1 + // Note: the bits are offset by one from the data sheet, not sure why + for(i = 0; i < 16; i++) + { + if(bitTimes[i + 1] > 11) + { + currentHumidity |= (1 << (15 - i)); + } + } + for(i = 0; i < 16; i++) + { + if(bitTimes[i + 17] > 11) + { + currentTemperature |= (1 << (15 - i)); + } + } + for(i = 0; i < 8; i++) + { + if(bitTimes[i + 33] > 11) + { + checkSum |= (1 << (7 - i)); + } + } + + _lastHumidity = currentHumidity & 0x7FFF; + if(currentTemperature & 0x8000) + { + // Below zero, non standard way of encoding negative numbers! + // Convert to native negative format. + _lastTemperature = -currentTemperature & 0x7FFF; + } + else + { + _lastTemperature = currentTemperature; + } + + csPart1 = currentHumidity >> 8; + csPart2 = currentHumidity & 0xFF; + csPart3 = currentTemperature >> 8; + csPart4 = currentTemperature & 0xFF; + if(checkSum == ((csPart1 + csPart2 + csPart3 + csPart4) & 0xFF)) + { + return DHT_ERROR_NONE; + } + return DHT_ERROR_CHECKSUM; +} + +// +// This is used when the millis clock rolls over to zero +// +void DHT22::clockReset() +{ + _lastReadTime = millis(); +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.h new file mode 100644 index 00000000..bddebc74 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22.h @@ -0,0 +1,76 @@ +#ifndef _DHT22_H_ +#define _DHT22_H_ + +#include + +#define DHT22_ERROR_VALUE -995 + +typedef enum +{ + DHT_ERROR_NONE = 0, + DHT_BUS_HUNG, + DHT_ERROR_NOT_PRESENT, + DHT_ERROR_ACK_TOO_LONG, + DHT_ERROR_SYNC_TIMEOUT, + DHT_ERROR_DATA_TIMEOUT, + DHT_ERROR_CHECKSUM, + DHT_ERROR_TOOQUICK +} DHT22_ERROR_t; + +class DHT22 +{ + private: + uint8_t _bitmask; + volatile uint8_t *_baseReg; + unsigned long _lastReadTime; + short int _lastHumidity; + short int _lastTemperature; + + public: + DHT22(uint8_t pin); + DHT22_ERROR_t readData(); + short int getHumidityInt(); + short int getTemperatureCInt(); + void clockReset(); +#if !defined(DHT22_NO_FLOAT) + float getHumidity(); + float getTemperatureC(); +#endif +}; + +// Report the humidity in .1 percent increments, such that 635 means 63.5% relative humidity +// +// Converts from the internal integer format on demand, so you might want +// to cache the result. +inline short int DHT22::getHumidityInt() +{ + return _lastHumidity; +} + +// Get the temperature in decidegrees C, such that 326 means 32.6 degrees C. +// The temperature may be negative, so be careful when handling the fractional part. +inline short int DHT22::getTemperatureCInt() +{ + return _lastTemperature; +} + +#if !defined(DHT22_NO_FLOAT) +// Return the percentage relative humidity in decimal form +inline float DHT22::getHumidity() +{ + return float(_lastHumidity)/10; +} +#endif + +#if !defined(DHT22_NO_FLOAT) +// Return the percentage relative humidity in decimal form +// +// Converts from the internal integer format on demand, so you might want +// to cache the result. +inline float DHT22::getTemperatureC() +{ + return float(_lastTemperature)/10; +} +#endif //DHT22_SUPPORT_FLOAT + +#endif /*_DHT22_H_*/ diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.cpp new file mode 100644 index 00000000..ac6033eb --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +* +* modified by Congduc Pham, University of Pau, France +*/ + +#include "DHT22_Humidity.h" + +DHT22_Humidity::DHT22_Humidity(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power){ + if (get_is_connected()){ + // start library DHT + // dht = new DHT22(get_pin_read()); + dht = new DHT(get_pin_read(), DHT22); + + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(2000); + } +} + +void DHT22_Humidity::update_data() +{ + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + dht->begin(); + + // wait + delay(get_warmup_time()); + + double h = dht->readHumidity(); + + if (isnan(h)) + set_data((double)-1.0); + else + set_data(h); + + // recover errorCode to know if there was an error or not + //errorCode = dht->readData(); + + //if(errorCode == DHT_ERROR_NONE){ + // no error + //set_data((double)dht->getHumidity()); + //} + //else { + // set_data((double)-1.0); + //} + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(15, 90)); + } +} + +double DHT22_Humidity::get_value() +{ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.h new file mode 100644 index 00000000..fe1f932a --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Humidity.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +*/ + +#ifndef DHT22_HUMIDITY_H +#define DHT22_HUMIDITY_H + +#include "Sensor.h" +//#include "DHT22.h" +#include "DHT.h" + +class DHT22_Humidity : public Sensor { + public: + DHT22_Humidity(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power); + double get_value(); + void update_data(); + + private: + DHT* dht = NULL; + //DHT22* dht = NULL; + //DHT22_ERROR_t errorCode; + uint8_t _dht_type; + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.cpp new file mode 100644 index 00000000..07aed9e6 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +* +* modified by Congduc Pham, University of Pau, France +*/ + +#include "DHT22_Temperature.h" + +DHT22_Temperature::DHT22_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature,is_analog, is_connected, is_low_power, pin_read, pin_power){ + if (get_is_connected()){ + // start library DHT + // dht = new DHT22(get_pin_read()); + dht = new DHT(get_pin_read(), DHT22); + + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(2000); + } +} + +void DHT22_Temperature::update_data() +{ + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + dht->begin(); + + // wait + delay(get_warmup_time()); + + double t = dht->readTemperature(); + + if (isnan(t)) + set_data((double)-1.0); + else + set_data(t); + + // recover errorCode to know if there was an error or not + //errorCode = dht->readData(); + + //if(errorCode == DHT_ERROR_NONE){ + // no error + //set_data((double)dht->getTemperatureC()); + //} + //else { + // set_data((double)-1.0); + //} + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(-20, 40)); + } + +} + +double DHT22_Temperature::get_value(){ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.h new file mode 100644 index 00000000..e8e2e35c --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DHT22_Temperature.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) 2016 Nicolas Bertuol, University of Pau, France +* +* nicolas.bertuol@etud.univ-pau.fr +*/ + +#ifndef DHT22_TEMPERATURE_H +#define DHT22_TEMPERATURE_H + +#include "Sensor.h" +//#include "DHT22.h" +#include "DHT.h" + +class DHT22_Temperature : public Sensor { + public: + DHT22_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power); + double get_value(); + void update_data(); + + private: + DHT* dht = NULL; + //DHT22* dht = NULL; + //DHT22_ERROR_t errorCode; + uint8_t _dht_type; + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_SoilHum/DS18B20.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.cpp similarity index 66% rename from Arduino/Arduino_LoRa_SoilHum/DS18B20.cpp rename to Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.cpp index 7016537f..223b5958 100644 --- a/Arduino/Arduino_LoRa_SoilHum/DS18B20.cpp +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.cpp @@ -11,6 +11,9 @@ DS18B20::DS18B20(char* nomenclature, bool is_analog, bool is_connected, bool is_ // start OneWire ds = new OneWire(get_pin_read()); + // Pass our oneWire reference to Dallas Temperature + sensors = new DallasTemperature(ds); + // to power the DS18B20 pinMode(get_pin_power(),OUTPUT); @@ -19,11 +22,13 @@ DS18B20::DS18B20(char* nomenclature, bool is_analog, bool is_connected, bool is_ else digitalWrite(get_pin_power(),HIGH); - set_wait_time(1000); + set_warmup_time(1000); } } -void DS18B20::update_data() + + //First version DS18B20::update_data() +/*void DS18B20::update_data() { double temp; @@ -79,6 +84,43 @@ void DS18B20::update_data() if (has_fake_data()) set_data((double)random(-20, 40)); } +} */ + +// New version based on DallasTemperature library +void DS18B20::update_data() +{ + double temp=-1.0; + + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + // wait + delay(get_warmup_time()); + + // Start up the library + sensors->begin(); + + // call sensors.requestTemperatures() to issue a global temperature + // request to all devices on the bus + sensors->requestTemperatures(); // Send the command to get temperature readings + temp = sensors->getTempCByIndex(0); // Why "byIndex"? + // You can have more than one DS18B20 on the same bus. + // 0 refers to the first IC on the wire + //delay(1000); + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + + set_data(temp); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(-20, 40)); + } } double DS18B20::get_value() diff --git a/Arduino/Arduino_LoRa_SoilHum/DS18B20.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.h similarity index 86% rename from Arduino/Arduino_LoRa_SoilHum/DS18B20.h rename to Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.h index e91d7cc6..4459dac7 100644 --- a/Arduino/Arduino_LoRa_SoilHum/DS18B20.h +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/DS18B20.h @@ -9,6 +9,7 @@ #include "Sensor.h" #include "OneWire.h" +#include "DallasTemperature.h" #define DS18B20_addr 0x28 @@ -20,6 +21,7 @@ class DS18B20 : public Sensor { private: OneWire* ds = NULL; + DallasTemperature* sensors = NULL; }; #endif diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/HCSR04.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/HCSR04.cpp new file mode 100644 index 00000000..a5ac34cf --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/HCSR04.cpp @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2016 Congduc Pham, University of Pau, France +* +* Congduc.Pham@univ-pau.fr +* +*/ + +#include "HCSR04.h" + +HCSR04::HCSR04(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power, int pin_trigger):Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger){ + if (get_is_connected()){ + + pinMode(get_pin_read(), INPUT); + pinMode(get_pin_power(),OUTPUT); + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + if (has_pin_trigger()) + pinMode(get_pin_trigger(),OUTPUT); + + set_warmup_time(500); + } +} + +void HCSR04::update_data() +{ + if (get_is_connected()) { + + long echo; + double aux_distance = 0; + double distance = 0; + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + // wait + delay(get_warmup_time()); + + for(int i=0; imeasure(&t, &h); + } + + if (ret == S_Meas_Rdy) + set_data((double)h); + else + set_data((double)-100.0); + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(15, 90)); + } +} + +double SHT_Humidity::get_value() +{ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Humidity.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Humidity.h new file mode 100644 index 00000000..c7661e1d --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Humidity.h @@ -0,0 +1,25 @@ +/* +* Copyright (C) 2018 C. Pham University of Pau, France +* +* Congduc.Pham@univ-pau.fr +*/ + +#ifndef SHT_HUMIDITY_H +#define SHT_HUMIDITY_H + +#include "Sensor.h" +#include "Sensirion.h" + +//uncomment if you use a SHT2x +//#define SHT2x + +class SHT_Humidity : public Sensor { + public: + SHT_Humidity(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power, uint8_t pin_trigger); + double get_value(); + void update_data(); + private: + Sensirion* sht = NULL; +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.cpp new file mode 100644 index 00000000..b682e202 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2018 C. Pham, University of Pau, France +* +* Congduc.Pham@univ-pau.fr +* +*/ + +#include "SHT_Temperature.h" + +SHT_Temperature::SHT_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power, uint8_t pin_trigger):Sensor(nomenclature,is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger){ + if (get_is_connected()){ + +#ifdef SHT2x + sht = new Sensirion(get_pin_read(), get_pin_trigger(), 0x40); +#else + sht = new Sensirion(get_pin_read(), get_pin_trigger()); +#endif + + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(1000); + } +} + +void SHT_Temperature::update_data() +{ + if (get_is_connected()) { + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + // wait + delay(get_warmup_time()); + + float t; + float h; + int ret; + long mTime; + long endTime; + + mTime=millis(); + endTime=mTime+1000; + + while ( (ret != S_Meas_Rdy) && (millis()measure(&t, &h); + } + + if (ret == S_Meas_Rdy) + set_data((double)t); + else + set_data((double)-100.0); + + if (get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + } + else { + // if not connected, set a random value (for testing) + if (has_fake_data()) + set_data((double)random(-20, 40)); + } + +} + +double SHT_Temperature::get_value(){ + update_data(); + return get_data(); +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.h new file mode 100644 index 00000000..8f46d693 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/SHT_Temperature.h @@ -0,0 +1,26 @@ +/* +* Copyright (C) 2018 C. Pham University of Pau, France +* +* Congduc.Pham@univ-pau.fr +*/ + +#ifndef SHT_TEMPERATURE_H +#define SHT_TEMPERATURE_H + +#include "Sensor.h" +#include "Sensirion.h" + +//uncomment if you use a SHT2x +//#define SHT2x + +class SHT_Temperature : public Sensor { + public: + SHT_Temperature(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power, uint8_t pin_trigger); + double get_value(); + double get_value(float *data = NULL, float *data1 = NULL, float *data2 = NULL); + void update_data(); + private: + Sensirion* sht = NULL; +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.cpp new file mode 100644 index 00000000..b37f962c --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.cpp @@ -0,0 +1,706 @@ +/* ========================================================================== */ +/* Sensirion.cpp - Library for Sensirion SHT1x & SHT7x family temperature */ +/* and humidity sensors */ +/* Created by Markus Schatzl, November 28, 2008 */ +/* Released into the public domain */ +/* */ +/* Revised (v1.1) by Carl Jackson, August 4, 2010 */ +/* Rewritten (v2.0) by Carl Jackson, December 10, 2010 */ +/* Rewritten (v3.01) by Thierry Couquillou, June 3, 2016 */ +/* See README.txt file for details */ +/* ========================================================================== */ + + +/****************************************************************************** + * Includes + ******************************************************************************/ + +extern "C" +{ + // AVR LibC Includes + #include + #include + //#include + + // Wiring Core Includes + #if (ARDUINO < 100) + #include + #endif +} + +#if (ARDUINO >= 100) + #include +#endif + +#include "Sensirion.h" + + +/****************************************************************************** + * Definitions + ******************************************************************************/ + +#define PULSE_step _step = ((error>=0)?_step+1:0) // Next step if no error (>=0)else Step 0 +#define WAIT_step _step = ((error>0)?_step+1:((error<0)?0:_step)) // Wait if 0. Next step if no error (>0) else Step 0 + + +// Sensirion command definitions: adr comm r/w +const uint8_t MEAS_TEMP = 0x03; // 000 0001 1 - Trigger T measurement hold master +const uint8_t MEAS_HUMI = 0x05; // 000 0010 1 - Trigger RH measurement hold master +// const uint8_t MEAS_TEMP_NH = 0x13; // 000 1001 1 - Trigger T measurement no hold master +// const uint8_t MEAS_HUMI_NH = 0x15; // 000 1010 1 - Trigger RH measurement no hold master +const uint8_t STAT_REG_W = 0x06; // 000 0011 0 - Write user register +const uint8_t STAT_REG_R = 0x07; // 000 0011 1 - Read user register +const uint8_t SOFT_RESET = 0x1e; // 000 1111 0 - Soft reset + +// Status register writable bits +const uint8_t SR_MASK = 0x07; + +// getByte flags +const bool noACK = false; +const bool ACK = true; + + + +/****************************************************************************** + * Constructors + ******************************************************************************/ + +Sensirion::Sensirion(uint8_t dataPin, uint8_t clockPin, uint8_t address, bool noholdmaster) +{ + // Initialize private storage for library functions + _pinData = dataPin; + _pinClock = clockPin; + + _shtaddress = address<<1; // Address depends of SHT1x/2x/3x. LSb is reserved for R/W + _shtnohold = (noholdmaster!=0)<<4; + _sht1x = (address==0x00); // SHT1x address = 0x00 + _sht2x = (address==0x40); // SHT2x address = 0x40 + _sht3x = (address==0x44 || address==0x45); // SHT3x address = 0x44 or 0x45 + + if(_sht1x) // SHT1x have 3 first bits of command used by address and = 0 + _shtprefix = 0x00; + else // SHT2x/3x have 3 first bits of command always = 1 + _shtprefix = 0xE0; + + _time = millis(); + _stat_reg = 0x00; // Sensor status register default state + + + // Return sensor to default state + resetConnection(); // Reset communication link with sensor + + startTransmission(); + putByte(SOFT_RESET); // Send soft reset command + stopTransmission(); +} + + +/****************************************************************************** + * User functions + ******************************************************************************/ + +// S_Meas_Wait = 0; // Wait for sensor cooling down +// S_Temp_Req = 1; // Temperature request (pulse) +// S_Temp_Wait = 2; // Wait for temperature measurement +// S_Humi_Req = 3; // Humidity request (pulse) means temperature measurement was successfull +// S_Humi_Wait = 4; // Wait for humidity measurement +// S_Calc_Run = 5; // calculation in progress +// S_Meas_Rdy = 6; // Temperature measurement was successfull (pulse) means all measurement successfull + +// All-in-one : measure(&temperature, &humidity, &dewpoint, temp0, &humi0 [=equivalent humidity@temp0]) +// All parameters are optionals eg: measure(&temperature) if only need temp. Non blocking. Return S_Meas_Rdy every data refresh. +int8_t Sensirion::measure(float *temp, float *humi, float *dew, float temp0, float *humi0) +{ + int8_t error = 0; + + if (!_sht1x && !_sht2x && !_sht3x) + return S_Err_Param; + + if (_pinData==_pinClock) + return S_Err_Param; + + switch(_step) + { + case 0: + if(((millis()-_time)>3000) || ((millis()-_time)>1000 && (_sht2x || _sht3x))) // Wait 3s for SHT1x, 1s for SHT2x/3x to cool down + _step++; + break; + + case 1: // Temperature request (pulse) + case 3: // Humidity request (pulse) + _time = millis(); // Reset time for time-out and delay + error = meas(_step>>1); // Request measurement 0=Temp. 1=Humi. + PULSE_step; // Next step if no error (>=0)else Step 0 + break; + + case 2: // Wait for temperature measurement + case 4: // Wait for humidity measurement + error = measRdy((_step-1)>>1); // Read measurement if available 0=Temp. 1=Humi. + if(((millis()-_time)>500) || ((millis()-_time)>100 && (_sht2x||_sht3x))) + error = S_Err_TO; + WAIT_step; // Wait if 0. Next step if no error (>0) else Step 0 + break; + + case 5: + _time = millis(); // Reset time for time-out and delay + if (temp) + { + *temp = calcTemp(_meas[TEMP]); // Temperature calculation + if (humi) + { + *humi = calcHumi(_meas[HUMI], *temp); // Humidity calculation + /* + if (dew) + { + *dew = calcDewpoint(*humi, *temp); // Dewpoint calculation + if (humi0) + { + *humi0 = calcHumi(*dew, temp0); // Equivalent humidity at temp0 (usefull for hum/temp close control loop + } + } + */ + } + } + _step = (error>=0)?_step+1:0; // Next step if no error else step 0 + break; + + case 6: + _step = 0; // Last step. Just one pulse. Measurement is available + break; + + default: + _step = 0; + } + + return (error<0)?error:_step; +} + +// Initiate measurement. +int8_t Sensirion::meas(uint8_t cmd) // Step 1 & 3 +{ + int8_t error = 0; + +// pinMode(4, OUTPUT); // Trig scope +// digitalWrite(4, HIGH); // Trig scope +// PULSE_SHORT; // Trig scope +// digitalWrite(4, LOW); // Trig scope + + if(_sht1x) + { + readSR(&_stat_reg); // Update sensor status register state + _crc = bitrev(_stat_reg & SR_MASK); // Initialize CRC calculation + } + + startTransmission(); + + if(!_sht1x) + { + if (error = putByte(_shtaddress+0)) // +0 = write bit. + { + stopTransmission(); + return error; + } + } + + if (cmd == TEMP) + cmd = _shtprefix | _shtnohold | MEAS_TEMP; + else + cmd = _shtprefix | _shtnohold | MEAS_HUMI; + + if (error = putByte(cmd)) + { + stopTransmission(); + return error; + } + + if(_sht1x) + calcCRC(cmd, &_crc); // Include command byte in CRC calculation + + if(!_sht1x) + { + if(_shtnohold) + { + PULSE_LONG; + stopTransmission(); + } + else + { + startTransmission(); + if (error = putByte(_shtaddress+1)) // +1 = read bit. Should return ACK + { + stopTransmission(); + return error; + } + + pinMode(_pinClock, INPUT_PULLUP); // Return clock line to input mode pullup resistor 20kohm + PULSE_SHORT; + } + } + + return 0; +} + +// Check if non-blocking measurement has completed +// Non-zero return indicates complete (with or without error) +int8_t Sensirion::measRdy(uint8_t cmd) // Step 2 & 4 +{ + int8_t error = 0; + + if(_sht1x) + { + if (digitalRead(_pinData)) // If _pinData HIGH measurement not ready ... + return 0; // ... will retry next time + } + else if(!_shtnohold) + { + if (!digitalRead(_pinClock)) // If _pinClk LOW measurement not ready ... + return 0; // ... will retry next time + } + else // Need to poll to know if ready + { + startTransmission(); + if (putByte(_shtaddress+1)) // +1 = read bit. return !=0 -> ACK not received (measurement not ready) + { + stopTransmission(); // No ACK. Stop transmission and ... + return 0; // ... will retry next time + } + } + +// pinMode(4, OUTPUT); // Trig scope +// digitalWrite(4, HIGH); // Trig scope +// PULSE_SHORT; // Trig scope +// digitalWrite(4, LOW); // Trig scope + + error = getResult(cmd); // return 1 if OK, <0 = Error. Only possible error is S_Err_CRC + + stopTransmission(); + + return error; // return 1 = OK, 0 = Retry next time, <0 = Error. +} + +// Get measurement result from sensor (plus CRC, if enabled) +int8_t Sensirion::getResult(uint8_t cmd) +{ + uint8_t val; + + if(!_sht1x) + _crc = 0; // Initialize CRC calculation + + val = getByte(ACK); + calcCRC(val, &_crc); + _meas[cmd] = val; + val = getByte(ACK); + calcCRC(val, &_crc); + _meas[cmd] = (_meas[cmd] << 8) | val; + val = getByte(noACK); + + if(_sht1x) + val = bitrev(val); + + if (val != _crc) + return S_Err_CRC; + + return 1; +} + +// Write status register +int8_t Sensirion::writeSR(uint8_t value) +{ + int8_t error = 0; + + value &= SR_MASK; // Mask off unwritable bits + _stat_reg = value; // Save local copy + + startTransmission(); + + if(!_sht1x) + { + if (error = putByte(_shtaddress+0)) // +0 = write bit. + { + stopTransmission(); + return error; + } + } + + if (error = putByte(_shtprefix | STAT_REG_W)) + { + stopTransmission(); + return error; + } + + error = putByte(value); + + stopTransmission(); + return error; +} + +// Read status register +int8_t Sensirion::readSR(uint8_t *result) +{ + uint8_t val; + int8_t error = 0; + +// pinMode(4, OUTPUT); // Trig scope +// digitalWrite(4, HIGH); // Trig scope +// PULSE_SHORT; // Trig scope +// digitalWrite(4, LOW); // Trig scope + + if(_sht1x) + _crc = bitrev(_stat_reg & SR_MASK); // Initialize CRC calculation + else + _crc = 0; // Initialize CRC calculation + + startTransmission(); + + if(!_sht1x) + { + if (error = putByte(_shtaddress+1)) // +1 = read bit. + { + stopTransmission(); + return error; + } + } + + if (error = putByte(_shtprefix | STAT_REG_R)) + { + stopTransmission(); + return error; + } + + calcCRC(_shtprefix | STAT_REG_R, &_crc); // Include command byte in CRC calculation + + _stat_reg = getByte(ACK); // Update sensor status register state + *result = _stat_reg; // Return sensor status register state + + calcCRC(*result, &_crc); + + val = getByte(noACK); + + if(_sht1x) + val = bitrev(val); + + if (val != _crc) + error = S_Err_CRC; + + stopTransmission(); + + return error; +} + +// Public reset function +// Note: Soft reset returns sensor status register to default values +int8_t Sensirion::reset(void) +{ + int8_t error = 0; + + _stat_reg = 0x00; // Sensor status register default state + resetConnection(); // Reset communication link with sensor + + startTransmission(); + + error = putByte(SOFT_RESET); // Send soft reset command & return status + + stopTransmission(); + + return error; +} + + +/****************************************************************************** + * Sensirion data communication + ******************************************************************************/ + +// Write byte to sensor and check for acknowledge +int8_t Sensirion::putByte(uint8_t value) +{ + uint8_t mask; + int8_t i; + int8_t error = 0; + + pinMode(_pinClock, OUTPUT); // Set clock line to output mode + pinMode(_pinData, OUTPUT); // Set data line to output mode + + mask = 0x80; // Bit mask to transmit MSB first + for (i = 8; i > 0; i--) + { + digitalWrite(_pinData, value & mask); + PULSE_SHORT; + digitalWrite(_pinClock, HIGH); // Generate clock pulse + PULSE_LONG; + digitalWrite(_pinClock, LOW); + PULSE_SHORT; + mask >>= 1; // Shift mask for next data bit + } + + pinMode(_pinData, INPUT_PULLUP); // Return data line to input mode pullup resistor 20kohm + PULSE_SHORT; + + digitalWrite(_pinClock, HIGH); // Clock #9 for ACK + PULSE_LONG; + if (digitalRead(_pinData)) // Verify ACK ('0') received from sensor + error = S_Err_NoACK; + PULSE_SHORT; + digitalWrite(_pinClock, LOW); // Finish with clock in low state + PULSE_SHORT; + + return error; +} + +// Read byte from sensor and send acknowledge if "ack" is true +uint8_t Sensirion::getByte(bool ack) +{ + int8_t i; + uint8_t result = 0; + + pinMode(_pinClock, OUTPUT); // Set clock line to output mode + + for (i = 8; i > 0; i--) + { + result <<= 1; // Shift received bits towards MSB + digitalWrite(_pinClock, HIGH); // Generate clock pulse + PULSE_SHORT; + result |= digitalRead(_pinData); // Merge next bit into LSB position + digitalWrite(_pinClock, LOW); + PULSE_SHORT; + } + pinMode(_pinData, OUTPUT); + digitalWrite(_pinData, !ack); // Assert ACK ('0') if ack == 1 + PULSE_SHORT; + digitalWrite(_pinClock, HIGH); // Clock #9 for ACK / noACK + PULSE_LONG; + digitalWrite(_pinClock, LOW); // Finish with clock in low state + PULSE_SHORT; + + pinMode(_pinData, INPUT_PULLUP); // Return data line to input mode pullup resistor 20kohm + PULSE_SHORT; + + return result; +} + + +/****************************************************************************** + * Sensirion signaling + ******************************************************************************/ + +// Generate Sensirion-specific transmission start sequence +// This is where Sensirion SHT1x does not conform to the I2C standard and is +// the main reason why the AVR TWI hardware support can not be used. +// ___ ___ +// SCK : ___| |___| |______ +// _____ ________ +// DATA: |_______| +// +// SHT2x/3x follow I2C standard. +// _________ +// SCK : |______ +// _____ +// DATA: |__________ + + +void Sensirion::startTransmission(void) +{ + if(_sht1x) + { + digitalWrite(_pinData, HIGH); // Set data register high before turning on + pinMode(_pinData, OUTPUT); // output driver (avoid possible low pulse) + digitalWrite(_pinClock, LOW); // Set clk register low before turning on (avoid possible high pulse) + pinMode(_pinClock, OUTPUT); // Clock turned to output on 1st transmission + + PULSE_SHORT; // Start transmission sequence + digitalWrite(_pinClock, HIGH); + PULSE_SHORT; + digitalWrite(_pinData, LOW); + PULSE_SHORT; + digitalWrite(_pinClock, LOW); + PULSE_LONG; + digitalWrite(_pinClock, HIGH); + PULSE_SHORT; + digitalWrite(_pinData, HIGH); + PULSE_SHORT; + digitalWrite(_pinClock, LOW); + PULSE_SHORT; + } + else + { + digitalWrite(_pinData, HIGH); // Set data register high before turning on + pinMode(_pinData, OUTPUT); // output driver (avoid possible low pulse) + digitalWrite(_pinClock, HIGH); // Set clk register high before turning on + pinMode(_pinClock, OUTPUT); // output driver (avoid possible low pulse) + + PULSE_SHORT; // Start transmission sequence + digitalWrite(_pinData, LOW); + PULSE_SHORT; + digitalWrite(_pinClock, LOW); + PULSE_SHORT; + } +} + +void Sensirion::stopTransmission(void) +{ + if(!_sht1x) + { + digitalWrite(_pinData, LOW); // Set data register low before turning on + pinMode(_pinData, OUTPUT); // output driver (avoid possible high pulse) + digitalWrite(_pinClock, LOW); // Set clk register low before turning on + pinMode(_pinClock, OUTPUT); // output driver (avoid possible high pulse) + + PULSE_SHORT; // Stop transmission sequence + digitalWrite(_pinClock, HIGH); + PULSE_SHORT; + digitalWrite(_pinData, HIGH); + PULSE_SHORT; + + pinMode(_pinData, INPUT_PULLUP); // Return clock line to input mode pullup resistor 20kohm + pinMode(_pinClock, INPUT_PULLUP); // Return clock line to input mode pullup resistor 20kohm + PULSE_SHORT; + } +} + +// Communication link reset +// At least 9 SCK cycles with DATA=1, followed by transmission start sequence +// _ _ _ _ _ _ _ _ _ ___ ___ +// SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |______ +// ______________________________________________________ ________ +// DATA: |_______| + +void Sensirion::resetConnection(void) +{ + int8_t i; + + if (_sht1x) + { + digitalWrite(_pinData, HIGH); // Set data register high before turning on + pinMode(_pinData, OUTPUT); // output driver (avoid possible low pulse) + PULSE_LONG; + for (i = 0; i < 9; i++) + { + digitalWrite(_pinClock, HIGH); + PULSE_LONG; + digitalWrite(_pinClock, LOW); + PULSE_LONG; + } + + startTransmission(); + } +} + + +/****************************************************************************** + * Helper Functions + ******************************************************************************/ + +// Calculates temperature in degrees C from raw sensor data +float Sensirion::calcTemp(uint16_t rawData) +{ + if (_sht1x) + { + if (_stat_reg & LOW_RES) + return -40.1 + 0.04 * rawData; + else + return -40.1 + 0.01 * rawData; + } + else + return -46.85 + 175.72 / 65536.0 * rawData; +} + + +// const float C1 = -4.0000; // for V3 sensors +// const float C2l = 0.6480; // for V3 sensors, 8-bit precision +// const float C3l = -7.2000E-4; // for V3 sensors, 8-bit precision +// const float C2h = 0.0405; // for V3 sensors, 12-bit precision +// const float C3h = -2.8000E-6; // for V3 sensors, 12-bit precision + +// const float C1 = -2.0468; // for V4 and V5 sensors +// const float C2l = 0.5872; // for V4 and V5 sensors, 8-bit precision +// const float C3l = -4.0845E-4; // for V4 and V5 sensors, 8-bit precision +// const float C2h = 0.0367; // for V4 and V5 sensors, 12-bit precision +// const float C3h = -1.5955E-6; // for V4 and V5 sensors, 12-bit precision + +// Calculates relative humidity from raw sensor data (with temperature compensation) +float Sensirion::calcHumi(uint16_t rawData, float temp) +{ + float humi; + + if (_sht1x) + { + if (_stat_reg & LOW_RES) + { + // for V3 sensors +// humi = -4.0000 + 0.6480 * rawData + -7.2000E-4 * rawData * rawData; +// humi = (temp - 25.0) * (0.01 + 0.00128 * rawData) + humi; + + // for V4 and V5 sensors + humi = -2.0468 + 0.5872 * rawData + -4.0845E-4 * rawData * rawData; + humi = (temp - 25.0) * (0.01 + 0.00128 * rawData) + humi; + } + else + { + // for V3 sensors +// humi = -4.0000 + 0.0405 * rawData + -2.8000E-6 * rawData * rawData; +// humi = (temp - 25.0) * (0.01 + 0.00008 * rawData) + humi; + + // for V4 and V5 sensors + humi = -2.0468 + 0.0367 * rawData + -1.5955E-6 * rawData * rawData; + humi = (temp - 25.0) * (0.01 + 0.00008 * rawData) + humi; + } + } + else + humi = -6.0 + 125.0 / 65536.0 * rawData; + + if (humi > 100.0) humi = 100.0; + if (humi < 0.1) humi = 0.1; + + return humi; +} + +/* +// RH: =100*(EXP((17.625*TD)/(243.04+TD))/EXP((17.625*T)/(243.04+T))) +// TD: =243.04*(LN(RH/100)+((17.625*T)/(243.04+T)))/(17.625-LN(RH/100)-((17.625*T)/(243.04+T))) +// T: =243.04*(((17.625*TD)/(243.04+TD))-LN(RH/100))/(17.625+LN(RH/100)-((17.625*TD)/(243.04+TD))) + +// Calculates dew point in degrees C from humidity +float Sensirion::calcDewpoint(float humi, float temp) +{ + float k; + k = log(humi/100) + (17.625 * temp) / (243.04 + temp); + return 243.04 * k / (17.625 - k); +} + +// Calculates humidity in degrees C from dew point +float Sensirion::calcHumi(float dewpoint, float temp) +{ + float k1,k2; + k1 = exp((17.625*dewpoint)/(243.04+dewpoint)); + k2 = exp((17.625*temp)/(243.04+temp)); + return 100*k1/k2; +} + +*/ + +// Calculate CRC for a single byte +void Sensirion::calcCRC(uint8_t value, uint8_t *crc) +{ + const uint8_t POLY = 0x31; // Polynomial: x**8 + x**5 + x**4 + 1 + int8_t i; + *crc ^= value; + for (i = 8; i > 0; i--) + { + if (*crc & 0x80) + *crc = (*crc << 1) ^ POLY; + else + *crc = (*crc << 1); + } +} + +// Bit-reverse a byte (for CRC calculations) +uint8_t Sensirion::bitrev(uint8_t value) +{ + int8_t i; + uint8_t result = 0; + for (i = 8; i > 0; i--) + { + result = (result << 1) | (value & 0x01); + value >>= 1; + } + return result; +} diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.h new file mode 100644 index 00000000..94fe767a --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensirion.h @@ -0,0 +1,90 @@ +/* ========================================================================== */ +/* Sensirion.h - Library for Sensirion SHT1x & SHT7x family temperature */ +/* and humidity sensors */ +/* Created by Markus Schatzl, November 28, 2008 */ +/* Released into the public domain */ +/* */ +/* Revised (v1.1) by Carl Jackson, August 4, 2010 */ +/* Rewritten (v2.0) by Carl Jackson, December 10, 2010 */ +/* Rewritten (v3.01) by Thierry Couquillou, June 3, 2016 */ +/* See README.txt file for details */ +/* ========================================================================== */ + + +#ifndef Sensirion_h +#define Sensirion_h + +#include + + +// Clock pulse timing macros +// Lengthening these may assist communication over long wires +#define PULSE_LONG delayMicroseconds(30) +#define PULSE_SHORT delayMicroseconds(15) + +// User constants DO NOT CHANGE +const uint8_t TEMP = 0; +const uint8_t HUMI = 1; + +// Status register bit definitions +const uint8_t LOW_RES = 0x01; // 12-bit Temp / 8-bit RH (vs. 14 / 12) +const uint8_t NORELOAD = 0x02; // No reload of calibrarion data +const uint8_t HEAT_ON = 0x04; // Built-in heater on +const uint8_t BATT_LOW = 0x40; // VDD < 2.47V + +// Function return code definitions +const int8_t S_Err_TO = -4; // Timeout +const int8_t S_Err_CRC = -3; // CRC failure +const int8_t S_Err_NoACK = -2; // ACK expected but not received +const int8_t S_Err_Param = -1; // Parameter error in function call +const int8_t S_Meas_Wait = 0; // Wait for sensor cooling down +const int8_t S_Temp_Req = 1; // Temperature request (pulse) +const int8_t S_Temp_Wait = 2; // Wait for temperature measurement +const int8_t S_Humi_Req = 3; // Humidity request (pulse) means temperature measurement was successfull +const int8_t S_Humi_Wait = 4; // Wait for humidity measurement +const int8_t S_Calc_Run = 5; // Calculation in progress +const int8_t S_Meas_Rdy = 6; // All measurement was successfull (pulse) + + +class Sensirion +{ + private: + uint8_t _pinData; // Pin interface + uint8_t _pinClock; + uint8_t _step; // Step of read cycle 0/1/2/3/4/5 + uint8_t _stat_reg; // Local copy of status register + uint8_t _shtaddress; // Address of SHTxx module + uint8_t _shtnohold; // Hold Master mode for SHT2x/3x module + uint8_t _sht1x; // With Address we can know if it's SHT1x/2x/3x + uint8_t _sht2x; + uint8_t _sht3x; + uint8_t _shtprefix; // prefix for SHT2x and SHT3x command + uint32_t _time; // For delay and Time out calculation + uint16_t _meas[2]; // meas[0] = Temperature, meas[1] = Humidity + uint8_t _crc; + + int8_t getResult(uint8_t cmd); + int8_t meas(uint8_t cmd); + int8_t measRdy(uint8_t cmd); + int8_t putByte(uint8_t value); + uint8_t getByte(bool ack); + uint8_t bitrev(uint8_t value); + void startTransmission(void); + void stopTransmission(void); + void resetConnection(void); + void calcCRC(uint8_t value, uint8_t *crc); + + public: + Sensirion(uint8_t dataPin, uint8_t clockPin, uint8_t address = 0x00, bool noholdmaster = false); + + int8_t measure(float *temp = NULL, float *humi = NULL, float *dew = NULL, float temp0 = 0, float *humi0 = NULL); + int8_t writeSR(uint8_t value); + int8_t readSR(uint8_t *result); + int8_t reset(void); + float calcTemp(uint16_t rawData); + float calcHumi(uint16_t rawData, float temp); + //float calcHumi(float dewpoint, float temp); + //float calcDewpoint(float humi, float temp); +}; + +#endif // #ifndef Sensirion_h diff --git a/Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.cpp similarity index 93% rename from Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.cpp rename to Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.cpp index 6c8a9467..a013a12b 100644 --- a/Arduino/Arduino_LoRa_Simple_SoilHum/Sensor.cpp +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.cpp @@ -25,9 +25,9 @@ Sensor::Sensor(char* nomenclature, bool is_analog, bool is_connected, bool is_lo set_pin_power(pin_power); set_pin_trigger(pin_trigger); set_data(0); - set_wait_time(0); + set_warmup_time(0); set_fake_data(false); - set_n_sample(1); + set_n_sample(5); /*if(_pin_power != -1){ set_power_set("LOW"); @@ -74,8 +74,8 @@ double Sensor::get_data(){ return _data; } -unsigned long Sensor::get_wait_time(){ - return _wait_time; +uint16_t Sensor::get_warmup_time(){ + return _warmup_time; } bool Sensor::has_fake_data(){ @@ -132,8 +132,8 @@ void Sensor::set_data(double d) { _data = -1.0; } -void Sensor::set_wait_time(unsigned long i){ - _wait_time = i; +void Sensor::set_warmup_time(uint16_t t){ + _warmup_time = t; } void Sensor::set_fake_data(bool b) { diff --git a/Arduino/Arduino_LoRa_SoilHum/Sensor.h b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.h similarity index 87% rename from Arduino/Arduino_LoRa_SoilHum/Sensor.h rename to Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.h index 41ff6b82..07f076f6 100644 --- a/Arduino/Arduino_LoRa_SoilHum/Sensor.h +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/Sensor.h @@ -2,6 +2,8 @@ * Copyright (C) 2016 Nicolas Bertuol, University of Pau, France * * nicolas.bertuol@etud.univ-pau.fr +* +* Modified by C. Pham, University of Pau, France */ #ifndef SENSOR_H @@ -20,7 +22,7 @@ #define IS_LOWPOWER true #define IS_NOT_LOWPOWER false -#define MAX_NOMENCLATURE_LENGTH 4 +#define MAX_NOMENCLATURE_LENGTH 5 #if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_MINI || defined __MK20DX256__ || defined __MKL26Z64__ || defined __SAMD21G18A__ // these boards work in 3.3V @@ -50,8 +52,8 @@ class Sensor { uint8_t get_pin_power(); int get_pin_trigger(); //char* get_power_set(); - double get_data(); - unsigned long get_wait_time(); + double get_data(); + uint16_t get_warmup_time(); bool has_fake_data(); bool has_pin_trigger(); uint8_t get_n_sample(); @@ -65,8 +67,8 @@ class Sensor { void set_pin_power(uint8_t u); void set_pin_trigger(int u); //void set_power_set(char* c); - void set_data(double d); - void set_wait_time(unsigned long i); + void set_data(double d); + void set_warmup_time(uint16_t t); void set_fake_data(bool b); void set_n_sample(uint8_t n); @@ -83,8 +85,9 @@ class Sensor { int _pin_trigger; bool _with_fake_data; //char* _power_set = NULL; - double _data; - unsigned long _wait_time; + double _data; + // delay in ms before reading data, sensor is powered + uint16_t _warmup_time; uint8_t _n_sample; }; diff --git a/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/TMP36.cpp b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/TMP36.cpp new file mode 100644 index 00000000..9faa45b6 --- /dev/null +++ b/Arduino/Arduino_LoRa_Generic_Simple_MultiSensors/TMP36.cpp @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2018 C. Pham, University of Pau, France +* +* Congduc.Pham@univ-pau.fr +* +*/ + +#include "TMP36.h" + +TMP36::TMP36(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power){ + if (get_is_connected()){ + + pinMode(get_pin_read(), INPUT); + pinMode(get_pin_power(),OUTPUT); + + if(get_is_low_power()) + digitalWrite(get_pin_power(),LOW); + else + digitalWrite(get_pin_power(),HIGH); + + set_warmup_time(500); + } +} + +void TMP36::update_data() +{ + if (get_is_connected()) { + + double aux_temp = 0; + double temperature = 0; + + // if we use a digital pin to power the sensor... + if (get_is_low_power()) + digitalWrite(get_pin_power(),HIGH); + + // wait + delay(get_warmup_time()); + + for(int i=0; i. - * - ***************************************************************************** - */ - -// IMPORTANT -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// add here some specific board define statements if you want to implement user-defined specific settings -// A/ LoRa radio node from IoTMCU: https://www.tindie.com/products/IOTMCU/lora-radio-node-v10/ -//#define IOTMCU_LORA_RADIO_NODE -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#include -// Include the SX1272 -#include "SX1272.h" - -// IMPORTANT -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// please uncomment only 1 choice -// -#define ETSI_EUROPE_REGULATION -//#define FCC_US_REGULATION -//#define SENEGAL_REGULATION -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// IMPORTANT -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// please uncomment only 1 choice -#define BAND868 -//#define BAND900 -//#define BAND433 -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef ETSI_EUROPE_REGULATION -#define MAX_DBM 14 -// previous way for setting output power -// char powerLevel='M'; -#elif defined SENEGAL_REGULATION -#define MAX_DBM 10 -// previous way for setting output power -// 'H' is actually 6dBm, so better to use the new way to set output power -// char powerLevel='H'; -#elif defined FCC_US_REGULATION -#define MAX_DBM 14 -#endif - -#ifdef BAND868 -#ifdef SENEGAL_REGULATION -const uint32_t DEFAULT_CHANNEL=CH_04_868; -#else -const uint32_t DEFAULT_CHANNEL=CH_10_868; -#endif -#elif defined BAND900 -const uint32_t DEFAULT_CHANNEL=CH_05_900; -#elif defined BAND433 -const uint32_t DEFAULT_CHANNEL=CH_00_433; -#endif - -// IMPORTANT -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// uncomment if your radio is an HopeRF RFM92W, HopeRF RFM95W, Modtronix inAir9B, NiceRF1276 -// or you known from the circuit diagram that output use the PABOOST line instead of the RFO line -#define PABOOST -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////// -// COMMENT OR UNCOMMENT TO CHANGE FEATURES. -// ONLY IF YOU KNOW WHAT YOU ARE DOING!!! OTHERWISE LEAVE AS IT IS -#define WITH_APPKEY -#define LOW_POWER -#define LOW_POWER_HIBERNATE -//#define WITH_AES -//#define LORAWAN -//#define TO_LORAWAN_GW -//#define WITH_ACK -/////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////// -// ADD HERE OTHER PLATFORMS THAT DO NOT LOW POWER -#if defined ARDUINO_SAM_DUE -#undef LOW_POWER -#endif -/////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////// -// CHANGE HERE THE LORA MODE, NODE ADDRESS -#define LORAMODE 1 -#define node_addr 14 -////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////// -// CHANGE HERE THE THINGSPEAK FIELD BETWEEN 1 AND 4 -#define field_index 1 -/////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////// -// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION -unsigned int idlePeriodInMin = 1; -/////////////////////////////////////////////////////////////////// - -#ifdef WITH_APPKEY -/////////////////////////////////////////////////////////////////// -// CHANGE HERE THE APPKEY, BUT IF GW CHECKS FOR APPKEY, MUST BE -// IN THE APPKEY LIST MAINTAINED BY GW. -uint8_t my_appKey[4]={5, 6, 7, 8}; -/////////////////////////////////////////////////////////////////// -#endif - -// we wrapped Serial.println to support the Arduino Zero or M0 -#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 -#define PRINTLN SerialUSB.println("") -#define PRINT_CSTSTR(fmt,param) SerialUSB.print(F(param)) -#define PRINT_STR(fmt,param) SerialUSB.print(param) -#define PRINT_VALUE(fmt,param) SerialUSB.print(param) -#define PRINT_HEX(fmt,param) SerialUSB.print(param,HEX) -#define FLUSHOUTPUT SerialUSB.flush(); -#else -#define PRINTLN Serial.println("") -#define PRINT_CSTSTR(fmt,param) Serial.print(F(param)) -#define PRINT_STR(fmt,param) Serial.print(param) -#define PRINT_VALUE(fmt,param) Serial.print(param) -#define PRINT_HEX(fmt,param) Serial.print(param,HEX) -#define FLUSHOUTPUT Serial.flush(); -#endif - -#define DEFAULT_DEST_ADDR 1 - -#ifdef WITH_ACK -#define NB_RETRIES 2 -#endif - -#ifdef LOW_POWER -// this is for the Teensy36, Teensy35, Teensy31/32 & TeensyLC -// need v6 of Snooze library -#if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ -#define LOW_POWER_PERIOD 60 -#include -SnoozeTimer timer; -SnoozeBlock sleep_config(timer); -#else -#define LOW_POWER_PERIOD 8 -// you need the LowPower library from RocketScream -// https://github.com/rocketscream/Low-Power -#include "LowPower.h" - -#ifdef __SAMD21G18A__ -// use the RTC library -#include "RTCZero.h" -/* Create an rtc object */ -RTCZero rtc; -#endif -#endif -unsigned int nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD; -#endif - -#ifdef WITH_AES - -#include "AES-128_V10.h" -#include "Encrypt_V31.h" - -unsigned char AppSkey[16] = { - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C -}; - -unsigned char NwkSkey[16] = { - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C -}; - -unsigned char DevAddr[4] = { - 0x00, 0x00, 0x00, node_addr -}; - -uint16_t Frame_Counter_Up = 0x0000; -// we use the same convention than for LoRaWAN as we will use the same AES convention -// See LoRaWAN specifications -unsigned char Direction = 0x00; -#endif - -unsigned long nextTransmissionTime=0L; -uint8_t message[100]; - -#ifdef TO_LORAWAN_GW -int loraMode=1; -#else -int loraMode=LORAMODE; -#endif - -uint16_t beaconCounter=0; -long randCounter=1234L; - -void setup() -{ - int e; - - randomSeed(randCounter); - -#ifdef LOW_POWER -#ifdef __SAMD21G18A__ - rtc.begin(); -#endif -#endif - - delay(3000); - // Open serial communications and wait for port to open: -#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 - SerialUSB.begin(38400); -#else - Serial.begin(38400); -#endif - // Print a start message - PRINT_CSTSTR("%s","Simple beacon system\n"); - -#ifdef ARDUINO_AVR_PRO - PRINT_CSTSTR("%s","Arduino Pro Mini detected\n"); -#endif -#ifdef ARDUINO_AVR_NANO - PRINT_CSTSTR("%s","Arduino Nano detected\n"); -#endif -#ifdef ARDUINO_AVR_MINI - PRINT_CSTSTR("%s","Arduino MINI/Nexus detected\n"); -#endif -#ifdef ARDUINO_AVR_MEGA2560 - PRINT_CSTSTR("%s","Arduino Mega2560 detected\n"); -#endif -#ifdef ARDUINO_SAM_DUE - PRINT_CSTSTR("%s","Arduino Due detected\n"); -#endif -#ifdef __MK66FX1M0__ - PRINT_CSTSTR("%s","Teensy36 MK66FX1M0 detected\n"); -#endif -#ifdef __MK64FX512__ - PRINT_CSTSTR("%s","Teensy35 MK64FX512 detected\n"); -#endif -#ifdef __MK20DX256__ - PRINT_CSTSTR("%s","Teensy31/32 MK20DX256 detected\n"); -#endif -#ifdef __MKL26Z64__ - PRINT_CSTSTR("%s","TeensyLC MKL26Z64 detected\n"); -#endif -#if defined ARDUINO_SAMD_ZERO && not defined ARDUINO_SAMD_FEATHER_M0 - PRINT_CSTSTR("%s","Arduino M0/Zero detected\n"); -#endif -#ifdef ARDUINO_AVR_FEATHER32U4 - PRINT_CSTSTR("%s","Adafruit Feather32U4 detected\n"); -#endif -#ifdef ARDUINO_SAMD_FEATHER_M0 - PRINT_CSTSTR("%s","Adafruit FeatherM0 detected\n"); -#endif - -// See http://www.nongnu.org/avr-libc/user-manual/using_tools.html -// for the list of define from the AVR compiler - -#ifdef __AVR_ATmega328P__ - PRINT_CSTSTR("%s","ATmega328P detected\n"); -#endif -#ifdef __AVR_ATmega32U4__ - PRINT_CSTSTR("%s","ATmega32U4 detected\n"); -#endif -#ifdef __AVR_ATmega2560__ - PRINT_CSTSTR("%s","ATmega2560 detected\n"); -#endif -#ifdef __SAMD21G18A__ - PRINT_CSTSTR("%s","SAMD21G18A ARM Cortex-M0+ detected\n"); -#endif -#ifdef __SAM3X8E__ - PRINT_CSTSTR("%s","SAM3X8E ARM Cortex-M3 detected\n"); -#endif - - // Power ON the module - sx1272.ON(); - - // Set transmission mode and print the result - e = sx1272.setMode(loraMode); - PRINT_CSTSTR("%s","Setting Mode: state "); - PRINT_VALUE("%d", e); - PRINTLN; - - // enable carrier sense - sx1272._enableCarrierSense=true; -#ifdef LOW_POWER - // TODO: with low power, when setting the radio module in sleep mode - // there seem to be some issue with RSSI reading - sx1272._RSSIonSend=false; -#endif - - // Select frequency channel - e = sx1272.setChannel(DEFAULT_CHANNEL); - PRINT_CSTSTR("%s","Setting Channel: state "); - PRINT_VALUE("%d", e); - PRINTLN; - - // Select amplifier line; PABOOST or RFO -#ifdef PABOOST - sx1272._needPABOOST=true; - // previous way for setting output power - // powerLevel='x'; -#else - // previous way for setting output power - // powerLevel='M'; -#endif - - // previous way for setting output power - // e = sx1272.setPower(powerLevel); - - e = sx1272.setPowerDBM((uint8_t)MAX_DBM); - PRINT_CSTSTR("%s","Setting Power: state "); - PRINT_VALUE("%d", e); - PRINTLN; - - // Set the node address and print the result - e = sx1272.setNodeAddress(node_addr); - PRINT_CSTSTR("%s","Setting node addr: state "); - PRINT_VALUE("%d", e); - PRINTLN; - -#ifdef TO_LORAWAN_GW - e = sx1272.setSyncWord(0x34); - PRINT_CSTSTR("%s","Set sync word to 0x34 state "); - PRINT_VALUE("%d", e); - PRINTLN; -#endif - - // Print a success message - PRINT_CSTSTR("%s","SX1272 successfully configured\n"); - - delay(500); -} - -void loop(void) -{ - long startSend; - long endSend; - uint8_t app_key_offset=0; - int e; - -#ifndef LOW_POWER - // 600000+random(15,60)*1000 - if (millis() > nextTransmissionTime) { -#endif - -#if defined WITH_APPKEY && not defined WITH_AES - app_key_offset = sizeof(my_appKey); - // set the app key in the payload - memcpy(message,my_appKey,app_key_offset); -#endif - - uint8_t r_size; - - //r_size=sprintf((char*)message+app_key_offset,"\\!SRC/%d/BC/%d/RC/%ld",node_addr, beaconCounter, randCounter); - r_size=sprintf((char*)message+app_key_offset,"\\!BC/%d", beaconCounter); - - beaconCounter++; - randCounter=random(0, 65536); - - PRINT_CSTSTR("%s","Sending "); - PRINT_STR("%s",(char*)(message+app_key_offset)); - PRINTLN; - - PRINT_CSTSTR("%s","Real payload size is "); - PRINT_VALUE("%d", r_size); - PRINTLN; - - int pl=r_size+app_key_offset; - -#ifdef WITH_AES - // if encryption then we DO NOT use appkey - // - PRINT_STR("%s",(char*)message); - PRINTLN; - PRINT_CSTSTR("%s","plain payload hex\n"); - for (int i=0; i> 8) & 0x00FF); - - LORAWAN_Data[8] = Frame_Port; - - //Set Current package length - LORAWAN_Package_Length = 9; - - //Load Data - for(int i = 0; i < r_size; i++) - { - // see that we don't take the appkey, just the encrypted data that starts that message[app_key_offset] - LORAWAN_Data[LORAWAN_Package_Length + i] = message[i]; - } - - //Add data Lenth to package length - LORAWAN_Package_Length = LORAWAN_Package_Length + r_size; - - PRINT_CSTSTR("%s","calculate MIC with NwkSKey\n"); - //Calculate MIC - Calculate_MIC(LORAWAN_Data, MIC, LORAWAN_Package_Length, Frame_Counter_Up, Direction); - - //Load MIC in package - for(int i=0; i < 4; i++) - { - LORAWAN_Data[i + LORAWAN_Package_Length] = MIC[i]; - } - - //Add MIC length to package length - LORAWAN_Package_Length = LORAWAN_Package_Length + 4; - - PRINT_CSTSTR("%s","transmitted LoRaWAN-like packet:\n"); - PRINT_CSTSTR("%s","MHDR[1] | DevAddr[4] | FCtrl[1] | FCnt[2] | FPort[1] | EncryptedPayload | MIC[4]\n"); - //Print transmitted data - for(int i = 0; i < LORAWAN_Package_Length; i++) - { - if (LORAWAN_Data[i]<16) - PRINT_CSTSTR("%s","0"); - PRINT_HEX("%X", LORAWAN_Data[i]); - PRINT_CSTSTR("%s"," "); - } - PRINTLN; - - // copy back to message - memcpy(message,LORAWAN_Data,LORAWAN_Package_Length); - pl = LORAWAN_Package_Length; - -#ifdef LORAWAN - PRINT_CSTSTR("%s","end-device uses native LoRaWAN packet format\n"); - // indicate to SX1272 lib that raw mode at transmission is required to avoid our own packet header - sx1272._rawFormat=true; -#else - PRINT_CSTSTR("%s","end-device uses encapsulated LoRaWAN packet format only for encryption\n"); -#endif - // in any case, we increment Frame_Counter_Up - // even if the transmission will not succeed - Frame_Counter_Up++; -#endif - - sx1272.CarrierSense(); - - startSend=millis(); - -#ifdef WITH_AES - // indicate that payload is encrypted - // DO NOT take into account appkey - sx1272.setPacketType(PKT_TYPE_DATA | PKT_FLAG_DATA_ENCRYPTED); -#else -#ifdef WITH_APPKEY - // indicate that we have an appkey - sx1272.setPacketType(PKT_TYPE_DATA | PKT_FLAG_DATA_WAPPKEY); -#else - // just a simple data packet - sx1272.setPacketType(PKT_TYPE_DATA); -#endif -#endif - - // Send message to the gateway and print the result - // with the app key if this feature is enabled -#ifdef WITH_ACK - int n_retry=NB_RETRIES; - - do { - e = sx1272.sendPacketTimeoutACK(DEFAULT_DEST_ADDR, message, pl); - - if (e==3) - PRINT_CSTSTR("%s","No ACK"); - - n_retry--; - - if (n_retry) - PRINT_CSTSTR("%s","Retry"); - else - PRINT_CSTSTR("%s","Abort"); - - } while (e && n_retry); -#else - e = sx1272.sendPacketTimeout(DEFAULT_DEST_ADDR, message, pl); -#endif - endSend=millis(); - -#ifdef LORAWAN - // switch back to normal behavior - sx1272._rawFormat=false; -#endif - - PRINT_CSTSTR("%s","LoRa pkt size "); - PRINT_VALUE("%d", pl); - PRINTLN; - - PRINT_CSTSTR("%s","LoRa pkt seq "); - PRINT_VALUE("%d", sx1272.packet_sent.packnum); - PRINTLN; - - PRINT_CSTSTR("%s","LoRa Sent in "); - PRINT_VALUE("%ld", endSend-startSend); - PRINTLN; - - PRINT_CSTSTR("%s","LoRa Sent w/CAD in "); - PRINT_VALUE("%ld", endSend-sx1272._startDoCad); - PRINTLN; - - PRINT_CSTSTR("%s","Packet sent, state "); - PRINT_VALUE("%d", e); - PRINTLN; - -#ifdef LOW_POWER - PRINT_CSTSTR("%s","Switch to power saving mode\n"); - - e = sx1272.setSleepMode(); - - if (!e) - PRINT_CSTSTR("%s","Successfully switch LoRa module in sleep mode\n"); - else - PRINT_CSTSTR("%s","Could not switch LoRa module in sleep mode\n"); - - FLUSHOUTPUT - delay(50); - -#ifdef __SAMD21G18A__ - // For Arduino M0 or Zero we use the built-in RTC - rtc.setTime(17, 0, 0); - rtc.setDate(1, 1, 2000); - rtc.setAlarmTime(17, idlePeriodInMin, 0); - // for testing with 20s - //rtc.setAlarmTime(17, 0, 20); - rtc.enableAlarm(rtc.MATCH_HHMMSS); - //rtc.attachInterrupt(alarmMatch); - rtc.standbyMode(); - - LowPower.standby(); - - PRINT_CSTSTR("%s","SAMD21G18A wakes up from standby\n"); - FLUSHOUTPUT -#else - nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD + random(2,4); - -#if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ - // warning, setTimer accepts value from 1ms to 65535ms max - timer.setTimer(LOW_POWER_PERIOD*1000 + random(1,5)*1000);// milliseconds - - nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD; -#endif - - //nCycle = 2; - - for (int i=0; i. * ***************************************************************************** - * last update: Oct 18th, 2018 by C. Pham + * last update: Nov 2nd, 2018 by C. Pham + * + * This version uses the same structure than the Arduino_LoRa_Demo_Sensor where + * the sensor-related code is in a separate file */ // IMPORTANT @@ -30,6 +33,7 @@ #include // Include the SX1272 #include "SX1272.h" +#include "my_DHT_sensor_code.h" //uncomment if you want to disable WiFi on ESP8266 boards //#include @@ -44,7 +48,7 @@ /////////////////////////////////////////////////////////////////// // CHANGE HERE THE NODE ADDRESS -#define node_addr 8 +#define node_addr 6 ////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// @@ -65,7 +69,6 @@ unsigned int idlePeriodInMin = 10; //#define SENEGAL_REGULATION /////////////////////////////////////////////////////////////////////////////////////////////////////////// -// IMPORTANT /////////////////////////////////////////////////////////////////////////////////////////////////////////// // please uncomment only 1 choice #define BAND868 @@ -129,18 +132,6 @@ const uint32_t DEFAULT_CHANNEL=CH_00_433; #define field_index 3 /////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////// -// CHANGE HERE THE READ PIN AND THE POWER PIN FOR THE TEMP. SENSOR -#define TEMP_PIN_READ A0 -// use digital 9 to power the temperature sensor if needed -// but on most ESP8266 boards pin 9 can not be used, so use pin 2 instead -#if defined ARDUINO_ESP8266_ESP01 || defined ARDUINO_ESP8266_NODEMCU || defined IOTMCU_LORA_RADIO_NODE -#define TEMP_PIN_POWER 2 -#else -#define TEMP_PIN_POWER 9 -#endif -/////////////////////////////////////////////////////////////////// - #ifdef WITH_APPKEY /////////////////////////////////////////////////////////////////// // CHANGE HERE THE APPKEY, BUT IF GW CHECKS FOR APPKEY, MUST BE @@ -179,21 +170,6 @@ uint8_t message[50]; #define NB_RETRIES 2 #endif -#if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_MINI || defined ARDUINO_SAM_DUE || defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ || defined __SAMD21G18A__ - // if you have a Pro Mini running at 5V, then change here - // these boards work in 3.3V - // Nexus board from Ideetron is a Mini - // __MK66FX1M0__ is for Teensy36 - // __MK64FX512__ is for Teensy35 - // __MK20DX256__ is for Teensy31/32 - // __MKL26Z64__ is for TeensyLC - // __SAMD21G18A__ is for Zero/M0 and FeatherM0 (Cortex-M0) - #define TEMP_SCALE 3300.0 -#else // ARDUINO_AVR_NANO || defined ARDUINO_AVR_UNO || defined ARDUINO_AVR_MEGA2560 - // also for all other boards, so change here if required. - #define TEMP_SCALE 5000.0 -#endif - #ifdef LOW_POWER // this is for the Teensy36, Teensy35, Teensy31/32 & TeensyLC // need v6 of Snooze library @@ -247,17 +223,15 @@ void setup() //WiFi.forceSleepBegin(); //delay(1); - // for the temperature sensor - pinMode(TEMP_PIN_READ, INPUT); - // and to power the temperature sensor - pinMode(TEMP_PIN_POWER,OUTPUT); + // initialization of the temperature sensor + sensor_Init(); #ifdef LOW_POWER #ifdef __SAMD21G18A__ rtc.begin(); #endif #else - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(DHT_PIN_POWER,HIGH); #endif delay(3000); @@ -452,43 +426,18 @@ void loop(void) #endif #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(PIN_POWER,HIGH); // security? delay(200); #endif - - temp = 0.0; - int value; - - for (int i=0; i<5; i++) { - // change here how the temperature should be computed depending on your sensor type - // - value = analogRead(TEMP_PIN_READ); - - //LM35DZ - //the LM35DZ needs at least 4v as supply voltage - //can be used on 5v board - temp += (value*TEMP_SCALE/1024.0)/10; - - //TMP36 - //the TMP36 can work with supply voltage of 2.7v-5.5v - //can be used on 3.3v board - //we use a 0.95 factor when powering with less than 3.3v, e.g. 3.1v in the average for instance - //this setting is for 2 AA batteries - //temp += ((value*0.95*TEMP_SCALE/1024.0)-500)/10; - - PRINT_CSTSTR("%s","Reading "); - PRINT_VALUE("%d", value); - PRINTLN; - delay(100); - } + temp = sensor_getValue(); + #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,LOW); + digitalWrite(PIN_POWER,LOW); #endif - PRINT_CSTSTR("%s","Mean temp is "); - temp = temp/5; + PRINT_CSTSTR("%s","Temp is "); PRINT_VALUE("%f", temp); PRINTLN; @@ -502,13 +451,13 @@ void loop(void) // the recommended format if now \!TC/22.5 #ifdef STRING_LIB - r_size=sprintf((char*)message+app_key_offset,"\\!#%d#TC/%s",field_index,String(temp).c_str()); + r_size=sprintf((char*)message+app_key_offset,"\\!#%d#%s/%s",field_index,nomenclature_str,String(temp).c_str()); #else char float_str[10]; ftoa(float_str,temp,2); // this is for testing, uncomment if you just want to test, without a real temp sensor plugged //strcpy(float_str, "21.55567"); - r_size=sprintf((char*)message+app_key_offset,"\\!#%d#TC/%s",field_index,float_str); + r_size=sprintf((char*)message+app_key_offset,"\\!#%d#%s/%s",field_index,nomenclature_str,float_str); #endif PRINT_CSTSTR("%s","Sending "); diff --git a/Arduino/Arduino_LoRa_Simple_DHT/DHT.cpp b/Arduino/Arduino_LoRa_Simple_DHT/DHT.cpp new file mode 100644 index 00000000..86ad91c4 --- /dev/null +++ b/Arduino/Arduino_LoRa_Simple_DHT/DHT.cpp @@ -0,0 +1,259 @@ +/* DHT library + +MIT license +written by Adafruit Industries +*/ + +#include "DHT.h" + +#define MIN_INTERVAL 2000 + +DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) { + _pin = pin; + _type = type; + #ifdef __AVR + _bit = digitalPinToBitMask(pin); + _port = digitalPinToPort(pin); + #endif + _maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for + // reading pulses from DHT sensor. + // Note that count is now ignored as the DHT reading algorithm adjusts itself + // basd on the speed of the processor. +} + +void DHT::begin(void) { + // set up the pins! + pinMode(_pin, INPUT_PULLUP); + // Using this value makes sure that millis() - lastreadtime will be + // >= MIN_INTERVAL right away. Note that this assignment wraps around, + // but so will the subtraction. + _lastreadtime = -MIN_INTERVAL; + DEBUG_PRINT("Max clock cycles: "); DEBUG_PRINTLN(_maxcycles, DEC); +} + +//boolean S == Scale. True == Fahrenheit; False == Celcius +float DHT::readTemperature(bool S, bool force) { + float f = NAN; + + if (read(force)) { + switch (_type) { + case DHT11: + f = data[2]; + if(S) { + f = convertCtoF(f); + } + break; + case DHT22: + case DHT21: + f = data[2] & 0x7F; + f *= 256; + f += data[3]; + f *= 0.1; + if (data[2] & 0x80) { + f *= -1; + } + if(S) { + f = convertCtoF(f); + } + break; + } + } + return f; +} + +float DHT::convertCtoF(float c) { + return c * 1.8 + 32; +} + +float DHT::convertFtoC(float f) { + return (f - 32) * 0.55555; +} + +float DHT::readHumidity(bool force) { + float f = NAN; + if (read()) { + switch (_type) { + case DHT11: + f = data[0]; + break; + case DHT22: + case DHT21: + f = data[0]; + f *= 256; + f += data[1]; + f *= 0.1; + break; + } + } + return f; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHT::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) { + // Using both Rothfusz and Steadman's equations + // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml + float hi; + + if (!isFahrenheit) + temperature = convertCtoF(temperature); + + hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094)); + + if (hi > 79) { + hi = -42.379 + + 2.04901523 * temperature + + 10.14333127 * percentHumidity + + -0.22475541 * temperature*percentHumidity + + -0.00683783 * pow(temperature, 2) + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(temperature, 2) * percentHumidity + + 0.00085282 * temperature*pow(percentHumidity, 2) + + -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); + + if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) + hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); + + else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) + hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); + } + + return isFahrenheit ? hi : convertFtoC(hi); +} + +boolean DHT::read(bool force) { + // Check if sensor was read less than two seconds ago and return early + // to use last reading. + uint32_t currenttime = millis(); + if (!force && ((currenttime - _lastreadtime) < 2000)) { + return _lastresult; // return last correct measurement + } + _lastreadtime = currenttime; + + // Reset 40 bits of received data to zero. + data[0] = data[1] = data[2] = data[3] = data[4] = 0; + + // Send start signal. See DHT datasheet for full signal diagram: + // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf + + // Go into high impedence state to let pull-up raise data line level and + // start the reading process. + digitalWrite(_pin, HIGH); + delay(250); + + // First set data line low for 20 milliseconds. + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + delay(20); + + uint32_t cycles[80]; + { + // Turn off interrupts temporarily because the next sections are timing critical + // and we don't want any interruptions. + InterruptLock lock; + + // End the start signal by setting data line high for 40 microseconds. + digitalWrite(_pin, HIGH); + delayMicroseconds(40); + + // Now start reading the data line to get the value from the DHT sensor. + pinMode(_pin, INPUT_PULLUP); + delayMicroseconds(10); // Delay a bit to let sensor pull data line low. + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if (expectPulse(LOW) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); + _lastresult = false; + return _lastresult; + } + if (expectPulse(HIGH) == 0) { + DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse.")); + _lastresult = false; + return _lastresult; + } + + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed all + // the pulses are read into a array and then examined in a later step. + for (int i=0; i<80; i+=2) { + cycles[i] = expectPulse(LOW); + cycles[i+1] = expectPulse(HIGH); + } + } // Timing critical code is now complete. + + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i=0; i<40; ++i) { + uint32_t lowCycles = cycles[2*i]; + uint32_t highCycles = cycles[2*i+1]; + if ((lowCycles == 0) || (highCycles == 0)) { + DEBUG_PRINTLN(F("Timeout waiting for pulse.")); + _lastresult = false; + return _lastresult; + } + data[i/8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + data[i/8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } + + DEBUG_PRINTLN(F("Received:")); + DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); + DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); + DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); + + // Check we read 40 bits and that the checksum matches. + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + _lastresult = true; + return _lastresult; + } + else { + DEBUG_PRINTLN(F("Checksum failure!")); + _lastresult = false; + return _lastresult; + } +} + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function (which is only available +// in the very latest IDE versions): +// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c +uint32_t DHT::expectPulse(bool level) { + uint32_t count = 0; + // On AVR platforms use direct GPIO port access as it's much faster and better + // for catching pulses that are 10's of microseconds in length: + #ifdef __AVR + uint8_t portState = level ? _bit : 0; + while ((*portInputRegister(_port) & _bit) == portState) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + // Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266 + // right now, perhaps bugs in direct port access functions?). + #else + while (digitalRead(_pin) == level) { + if (count++ >= _maxcycles) { + return 0; // Exceeded timeout, fail. + } + } + #endif + + return count; +} diff --git a/Arduino/Arduino_LoRa_Simple_DHT/DHT.h b/Arduino/Arduino_LoRa_Simple_DHT/DHT.h new file mode 100644 index 00000000..d81f6dbc --- /dev/null +++ b/Arduino/Arduino_LoRa_Simple_DHT/DHT.h @@ -0,0 +1,75 @@ +/* DHT library + +MIT license +written by Adafruit Industries +*/ +#ifndef DHT_H +#define DHT_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + + +// Uncomment to enable printing out nice debug messages. +//#define DHT_DEBUG + +// Define where debug output will be printed. +#define DEBUG_PRINTER Serial + +// Setup debug printing macros. +#ifdef DHT_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +// Define types of sensors. +#define DHT11 11 +#define DHT22 22 +#define DHT21 21 +#define AM2301 21 + + +class DHT { + public: + DHT(uint8_t pin, uint8_t type, uint8_t count=6); + void begin(void); + float readTemperature(bool S=false, bool force=false); + float convertCtoF(float); + float convertFtoC(float); + float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true); + float readHumidity(bool force=false); + boolean read(bool force=false); + + private: + uint8_t data[5]; + uint8_t _pin, _type; + #ifdef __AVR + // Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask + // for the digital pin connected to the DHT. Other platforms will use digitalRead. + uint8_t _bit, _port; + #endif + uint32_t _lastreadtime, _maxcycles; + bool _lastresult; + + uint32_t expectPulse(bool level); + +}; + +class InterruptLock { + public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } + +}; + +#endif diff --git a/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.cpp b/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.cpp new file mode 100644 index 00000000..0c64c70f --- /dev/null +++ b/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.cpp @@ -0,0 +1,48 @@ +#include "my_DHT_sensor_code.h" + +/////////////////////////////////////////////////////////////////// +// CHANGE HERE THE NOMEMCLATURE, HERE TC WOULD MEAN TEMPERATURE IN CELCIUS FOR INSTANCE +// USE A MAXIMUM OF 3 CHARACTERS + +char nomenclature_str[4]="TC"; +////////////////////////////////////////////////////////////////// + +DHT dht(PIN_READ, DHTTYPE); + +/////////////////////////////////////////////////////////////////// +// ADD HERE SOME INITIALIZATION CODE +// HERE WE JUST DECLARE VALUE_PIN_READ AS INPUT PIN + +void sensor_Init() { + + // for the temperature sensor + pinMode(PIN_READ, INPUT); + pinMode(PIN_POWER, OUTPUT); + dht.begin(); +} +/////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////// +// CHANGE HERE THE WAY YOU READ A VALUE FROM YOUR SPECIFIC SENSOR +// HERE IT IS AN EXAMPLE WITH THE DHT22 + +double sensor_getValue() { + + //read the raw sensor value + //float h = dht.readHumidity(); + float t = dht.readTemperature(); + + if (isnan(t)) { + Serial.println("Failed to read from DHT sensor!"); + } + else { + Serial.print("Temperature: "); + Serial.println(t); + //Serial.print(" degrees Celcius Humidity: "); + //Serial.print(h); + //Serial.println("%"); + } + + return t; +} +/////////////////////////////////////////////////////////////////// diff --git a/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.h b/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.h new file mode 100644 index 00000000..44b69764 --- /dev/null +++ b/Arduino/Arduino_LoRa_Simple_DHT/my_DHT_sensor_code.h @@ -0,0 +1,31 @@ +#ifndef MY_DHT_SENSOR_CODE +#define MY_DHT_SENSOR_CODE + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include "DHT.h" + +extern char nomenclature_str[4]; +void sensor_Init(); +double sensor_getValue(); + +/////////////////////////////////////////////////////////////////// +// CHANGE HERE THE READ PIN AND THE POWER PIN FOR THE TEMP. SENSOR +#define PIN_READ A0 +// use digital 9 to power the temperature sensor if needed +// but on most ESP8266 boards pin 9 can not be used, so use pin 2 instead +#if defined ARDUINO_ESP8266_ESP01 || defined ARDUINO_ESP8266_NODEMCU || defined IOTMCU_LORA_RADIO_NODE +#define PIN_POWER 2 +#else +#define PIN_POWER 9 +#endif +/////////////////////////////////////////////////////////////////// + +//#define DHTTYPE DHT11 // DHT 11 +#define DHTTYPE DHT22 // DHT 22 (AM2302 & AM2305) + +#endif diff --git a/Arduino/Arduino_LoRa_Simple_SoilHum/rawAnalog.cpp b/Arduino/Arduino_LoRa_Simple_SoilHum/rawAnalog.cpp deleted file mode 100644 index c9b5bef9..00000000 --- a/Arduino/Arduino_LoRa_Simple_SoilHum/rawAnalog.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (C) 2016 Congduc Pham, University of Pau, France -* -* Congduc.Pham@univ-pau.fr -*/ - - -#include "rawAnalog.h" - -rawAnalog::rawAnalog(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power):Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power){ - if (get_is_connected()){ - - pinMode(get_pin_read(), INPUT); - pinMode(get_pin_power(),OUTPUT); - - if(get_is_low_power()) - digitalWrite(get_pin_power(),LOW); - else - digitalWrite(get_pin_power(),HIGH); - - set_wait_time(100); - } -} - -void rawAnalog::update_data() -{ - if (get_is_connected()) { - - int aux_value = 0; - int value = 0; - - // if we use a digital pin to power the sensor... - if (get_is_low_power()) - digitalWrite(get_pin_power(),HIGH); - - // wait - delay(get_wait_time()); - - for(int i=0; i. * ***************************************************************************** - * last update: Oct 18th, 2018 by C. Pham + * last update: Nov 2nd, 2018 by C. Pham * * This version uses the same structure than the Arduino_LoRa_Demo_Sensor where * the sensor-related code is in a separate file @@ -224,14 +224,14 @@ void setup() //delay(1); // initialization of the temperature sensor - sensor_temp_Init(); + sensor_Init(); #ifdef LOW_POWER #ifdef __SAMD21G18A__ rtc.begin(); #endif #else - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(PIN_POWER,HIGH); #endif delay(3000); @@ -426,7 +426,7 @@ void loop(void) #endif #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(PIN_POWER,HIGH); // security? delay(200); #endif @@ -434,12 +434,12 @@ void loop(void) temp = 0.0; for (int i=0; i<5; i++) { - temp += sensor_temp_getValue(); + temp += sensor_getValue(); delay(100); } #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,LOW); + digitalWrite(PIN_POWER,LOW); #endif PRINT_CSTSTR("%s","Mean temp is "); diff --git a/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.cpp b/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.cpp index 7cdf4179..9f15ac30 100644 --- a/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.cpp +++ b/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.cpp @@ -11,11 +11,11 @@ char nomenclature_str[4]="TC"; // ADD HERE SOME INITIALIZATION CODE // HERE WE JUST DECLARE VALUE_PIN_READ AS INPUT PIN -void sensor_temp_Init() { +void sensor_Init() { // for the temperature sensor - pinMode(TEMP_PIN_READ, INPUT); - pinMode(TEMP_PIN_POWER, OUTPUT); + pinMode(PIN_READ, INPUT); + pinMode(PIN_POWER, OUTPUT); } /////////////////////////////////////////////////////////////////// @@ -23,10 +23,10 @@ void sensor_temp_Init() { // CHANGE HERE THE WAY YOU READ A VALUE FROM YOUR SPECIFIC SENSOR // HERE IT IS AN EXAMPLE WITH THE LM35DZ SIMPLE ANALOG TEMPERATURE SENSOR -double sensor_temp_getValue() { +double sensor_getValue() { //read the raw sensor value - int value = analogRead(TEMP_PIN_READ); + int value = analogRead(PIN_READ); Serial.print(F("Reading ")); Serial.println(value); diff --git a/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.h b/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.h index e82320a1..482224d3 100644 --- a/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.h +++ b/Arduino/Arduino_LoRa_Simple_temp/my_temp_sensor_code.h @@ -8,18 +8,18 @@ #endif extern char nomenclature_str[4]; -void sensor_temp_Init(); -double sensor_temp_getValue(); +void sensor_Init(); +double sensor_getValue(); /////////////////////////////////////////////////////////////////// // CHANGE HERE THE READ PIN AND THE POWER PIN FOR THE TEMP. SENSOR -#define TEMP_PIN_READ A0 +#define PIN_READ A0 // use digital 9 to power the temperature sensor if needed // but on most ESP8266 boards pin 9 can not be used, so use pin 2 instead #if defined ARDUINO_ESP8266_ESP01 || defined ARDUINO_ESP8266_NODEMCU || defined IOTMCU_LORA_RADIO_NODE -#define TEMP_PIN_POWER 2 +#define PIN_POWER 2 #else -#define TEMP_PIN_POWER 9 +#define PIN_POWER 9 #endif /////////////////////////////////////////////////////////////////// diff --git a/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.cpp b/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.cpp deleted file mode 100755 index b1efb1c8..00000000 --- a/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/****************************************************************************************** -* Copyright 2015, 2016 Ideetron B.V. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -******************************************************************************************/ -/****************************************************************************************** -* -* File: AES-128_V10.cpp -* Author: Gerben den Hartog -* Compagny: Ideetron B.V. -* Website: http://www.ideetron.nl/LoRa -* E-mail: info@ideetron.nl -******************************************************************************************/ -/**************************************************************************************** -* -* Created on: 20-10-2015 -* Supported Hardware: ID150119-02 Nexus board with RFM95 -* -* Firmware Version 1.0 -* First version -****************************************************************************************/ - -#include "AES-128_V10.h" - -/* -******************************************************************************************** -* Global Variables -******************************************************************************************** -*/ - -unsigned char State[4][4]; - -unsigned char S_Table[16][16] = { - {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, - {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, - {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, - {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, - {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, - {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, - {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, - {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, - {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, - {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, - {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, - {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, - {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, - {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, - {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, - {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} -}; - -/* -***************************************************************************************** -* Description : Function for encrypting data using AES-128 -* -* Arguments : *Data Data to encrypt is a 16 byte long arry -* *Key Key to encrypt data with is a 16 byte long arry -***************************************************************************************** -*/ -void AES_Encrypt(unsigned char *Data, unsigned char *Key) -{ - unsigned char i; - unsigned char Row,Collum; - unsigned char Round = 0x00; - unsigned char Round_Key[16]; - - //Copy input to State arry - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = Data[Row + (4*Collum)]; - } - } - - //Copy key to round key - for(i = 0; i < 16; i++) - { - Round_Key[i] = Key[i]; - } - - //Add round key - AES_Add_Round_Key(Round_Key); - - //Preform 9 full rounds - for(Round = 1; Round < 10; Round++) - { - //Preform Byte substitution with S table - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); - } - } - - //Preform Row Shift - AES_Shift_Rows(); - - //Mix Collums - AES_Mix_Collums(); - - //Calculate new round key - AES_Calculate_Round_Key(Round,Round_Key); - - //Add round key - AES_Add_Round_Key(Round_Key); - } - - //Last round whitout mix collums - //Preform Byte substitution with S table - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); - } - } - - //Shift rows - AES_Shift_Rows(); - - //Calculate new round key - AES_Calculate_Round_Key(Round,Round_Key); - - //Add round Key - AES_Add_Round_Key(Round_Key); - - //Copy the State into the data array - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - Data[Row + (4*Collum)] = State[Row][Collum]; - } - } - -} - -/* -***************************************************************************************** -* Description : Function that add's the round key for the current round -* -* Arguments : *Round_Key 16 byte long array holding the Round Key -***************************************************************************************** -*/ -void AES_Add_Round_Key(unsigned char *Round_Key) -{ - unsigned char Row,Collum; - - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; - } - } -} - -/* -***************************************************************************************** -* Description : Function that substitutes a byte with a byte from the S_Table -* -* Arguments : Byte The byte that will be substituted -* -* Return : The return is the found byte in the S_Table -***************************************************************************************** -*/ -unsigned char AES_Sub_Byte(unsigned char Byte) -{ - unsigned char S_Row,S_Collum; - unsigned char S_Byte; - - //Split byte up in Row and Collum - S_Row = ((Byte >> 4) & 0x0F); - S_Collum = (Byte & 0x0F); - - //Find the correct byte in the S_Table - S_Byte = S_Table[S_Row][S_Collum]; - - return S_Byte; -} - -/* -***************************************************************************************** -* Description : Function that preforms the shift row operation described in the AES standard -***************************************************************************************** -*/ -void AES_Shift_Rows() -{ - unsigned char Buffer; - - //Row 0 doesn't change - - //Shift Row 1 one left - //Store firt byte in buffer - Buffer = State[1][0]; - //Shift all bytes - State[1][0] = State[1][1]; - State[1][1] = State[1][2]; - State[1][2] = State[1][3]; - State[1][3] = Buffer; - - //Shift row 2 two left - Buffer = State[2][0]; - State[2][0] = State[2][2]; - State[2][2] = Buffer; - Buffer = State[2][1]; - State[2][1] = State[2][3]; - State[2][3] = Buffer; - - //Shift row 3 three left - Buffer = State[3][3]; - State[3][3] = State[3][2]; - State[3][2] = State[3][1]; - State[3][1] = State[3][0]; - State[3][0] = Buffer; -} - -/* -***************************************************************************************** -* Description : Function that preforms the Mix Collums operation described in the AES standard -***************************************************************************************** -*/ -void AES_Mix_Collums() -{ - unsigned char Row,Collum; - unsigned char a[4], b[4]; - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - a[Row] = State[Row][Collum]; - b[Row] = (State[Row][Collum] << 1); - - if((State[Row][Collum] & 0x80) == 0x80) - { - b[Row] = b[Row] ^ 0x1B; - } - } - State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; - State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; - State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; - State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; - } -} - -/* -***************************************************************************************** -* Description : Function that calculaties the round key for the current round -* -* Arguments : Round Number of current Round -* *Round_Key 16 byte long array holding the Round Key -***************************************************************************************** -*/ -void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) -{ - unsigned char i,j; - unsigned char b; - unsigned char Temp[4]; - unsigned char Buffer; - unsigned char Rcon; - - //Calculate first Temp - //Copy laste byte from previous key - for(i = 0; i < 4; i++) - { - Temp[i] = Round_Key[i+12]; - } - - //Rotate Temp - Buffer = Temp[0]; - Temp[0] = Temp[1]; - Temp[1] = Temp[2]; - Temp[2] = Temp[3]; - Temp[3] = Buffer; - - //Substitute Temp - for(i = 0; i < 4; i++) - { - Temp[i] = AES_Sub_Byte(Temp[i]); - } - - //Calculate Rcon - Rcon = 0x01; - while(Round != 1) - { - b = Rcon & 0x80; - Rcon = Rcon << 1; - if(b == 0x80) - { - Rcon = Rcon ^ 0x1b; - } - Round--; - } - - //XOR Rcon - Temp[0] = Temp[0] ^ Rcon; - - //Calculate new key - for(i = 0; i < 4; i++) - { - for(j = 0; j < 4; j++) - { - Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; - Temp[j] = Round_Key[j + (4*i)]; - } - } -} diff --git a/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.h b/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.h deleted file mode 100755 index da1e04ac..00000000 --- a/Arduino/Arduino_LoRa_SoilHum/AES-128_V10.h +++ /dev/null @@ -1,51 +0,0 @@ -/****************************************************************************************** -* Copyright 2015, 2016 Ideetron B.V. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -******************************************************************************************/ -/****************************************************************************************** -* -* File: AES-128_V10.h -* Author: Gerben den Hartog -* Compagny: Ideetron B.V. -* Website: http://www.ideetron.nl/LoRa -* E-mail: info@ideetron.nl -******************************************************************************************/ -/**************************************************************************************** -* -* Created on: 20-10-2015 -* Supported Hardware: ID150119-02 Nexus board with RFM95 -* -* Firmware Version 1.0 -* First version -****************************************************************************************/ - -#ifndef AES128_V10_H -#define AES128_V10_H - -/* -******************************************************************************************** -* FUNCTION PORTOTYPES -******************************************************************************************** -*/ - -void AES_Encrypt(unsigned char *Data, unsigned char *Key); -void AES_Add_Round_Key(unsigned char *Round_Key); -unsigned char AES_Sub_Byte(unsigned char Byte); -void AES_Shift_Rows(); -void AES_Mix_Collums(); -void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); -void Send_State(); - -#endif diff --git a/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.cpp b/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.cpp deleted file mode 100755 index 4bd26269..00000000 --- a/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/****************************************************************************************** -* Copyright 2015, 2016 Ideetron B.V. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -******************************************************************************************/ -/****************************************************************************************** -* -* File: Encrypt_V31.cpp -* Author: Gerben den Hartog -* Compagny: Ideetron B.V. -* Website: http://www.ideetron.nl/LoRa -* E-mail: info@ideetron.nl -******************************************************************************************/ -/**************************************************************************************** -* -* Created on: 04-02-2016 -* Supported Hardware: ID150119-02 Nexus board with RFM95 -* -* Firmware Version 1.0 -* First version -* -* Firmware Version 2.0 -* Works the same is 1.0 using own AES encryption -* -* Firmware Version 3.0 -* Included direction in MIC calculation and encryption -* -* Firmware Version 3.1 -* Now using AppSkey in Encrypt Payload function -****************************************************************************************/ - -/* -***************************************************************************************** -* INCLUDE FILES -***************************************************************************************** -*/ - -#include "Encrypt_V31.h" -#include "AES-128_V10.h" - -/* -***************************************************************************************** -* INCLUDE GLOBAL VARIABLES -***************************************************************************************** -*/ - -extern unsigned char NwkSkey[16]; -extern unsigned char AppSkey[16]; -extern unsigned char DevAddr[4]; - -void Encrypt_Payload(unsigned char *Data, unsigned char Data_Length, unsigned int Frame_Counter, unsigned char Direction) -{ - unsigned char i = 0x00; - unsigned char j; - unsigned char Number_of_Blocks = 0x00; - unsigned char Incomplete_Block_Size = 0x00; - - unsigned char Block_A[16]; - - //Calculate number of blocks - Number_of_Blocks = Data_Length / 16; - Incomplete_Block_Size = Data_Length % 16; - if(Incomplete_Block_Size != 0) - { - Number_of_Blocks++; - } - - for(i = 1; i <= Number_of_Blocks; i++) - { - Block_A[0] = 0x01; - Block_A[1] = 0x00; - Block_A[2] = 0x00; - Block_A[3] = 0x00; - Block_A[4] = 0x00; - - Block_A[5] = Direction; - - Block_A[6] = DevAddr[3]; - Block_A[7] = DevAddr[2]; - Block_A[8] = DevAddr[1]; - Block_A[9] = DevAddr[0]; - - Block_A[10] = (Frame_Counter & 0x00FF); - Block_A[11] = ((Frame_Counter >> 8) & 0x00FF); - - Block_A[12] = 0x00; //Frame counter upper Bytes - Block_A[13] = 0x00; - - Block_A[14] = 0x00; - - Block_A[15] = i; - - //Calculate S - AES_Encrypt(Block_A,AppSkey); - - //Check for last block - if(i != Number_of_Blocks) - { - for(j = 0; j < 16; j++) - { - *Data = *Data ^ Block_A[j]; - Data++; - } - } - else - { - if(Incomplete_Block_Size == 0) - { - Incomplete_Block_Size = 16; - } - for(j = 0; j < Incomplete_Block_Size; j++) - { - *Data = *Data ^ Block_A[j]; - Data++; - } - } - } -} - -void Calculate_MIC(unsigned char *Data, unsigned char *Final_MIC, unsigned char Data_Length, unsigned int Frame_Counter, unsigned char Direction) -{ - unsigned char i; - unsigned char Block_B[16]; - unsigned char Key_K1[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned char Key_K2[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - //unsigned char Data_Copy[16]; - - unsigned char Old_Data[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned char New_Data[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - unsigned char Number_of_Blocks = 0x00; - unsigned char Incomplete_Block_Size = 0x00; - unsigned char Block_Counter = 0x01; - - //Create Block_B - Block_B[0] = 0x49; - Block_B[1] = 0x00; - Block_B[2] = 0x00; - Block_B[3] = 0x00; - Block_B[4] = 0x00; - - Block_B[5] = Direction; - - Block_B[6] = DevAddr[3]; - Block_B[7] = DevAddr[2]; - Block_B[8] = DevAddr[1]; - Block_B[9] = DevAddr[0]; - - Block_B[10] = (Frame_Counter & 0x00FF); - Block_B[11] = ((Frame_Counter >> 8) & 0x00FF); - - Block_B[12] = 0x00; //Frame counter upper bytes - Block_B[13] = 0x00; - - Block_B[14] = 0x00; - Block_B[15] = Data_Length; - - //Calculate number of Blocks and blocksize of last block - Number_of_Blocks = Data_Length / 16; - Incomplete_Block_Size = Data_Length % 16; - - if(Incomplete_Block_Size != 0) - { - Number_of_Blocks++; - } - - Generate_Keys(Key_K1, Key_K2); - - //Preform Calculation on Block B0 - - //Preform AES encryption - AES_Encrypt(Block_B,NwkSkey); - - //Copy Block_B to Old_Data - for(i = 0; i < 16; i++) - { - Old_Data[i] = Block_B[i]; - } - - //Preform full calculating until n-1 messsage blocks - while(Block_Counter < Number_of_Blocks) - { - //Copy data into array - for(i = 0; i < 16; i++) - { - New_Data[i] = *Data; - Data++; - } - - //Preform XOR with old data - XOR(New_Data,Old_Data); - - //Preform AES encryption - AES_Encrypt(New_Data,NwkSkey); - - //Copy New_Data to Old_Data - for(i = 0; i < 16; i++) - { - Old_Data[i] = New_Data[i]; - } - - //Raise Block counter - Block_Counter++; - } - - //Perform calculation on last block - //Check if Datalength is a multiple of 16 - if(Incomplete_Block_Size == 0) - { - //Copy last data into array - for(i = 0; i < 16; i++) - { - New_Data[i] = *Data; - Data++; - } - - //Preform XOR with Key 1 - XOR(New_Data,Key_K1); - - //Preform XOR with old data - XOR(New_Data,Old_Data); - - //Preform last AES routine - AES_Encrypt(New_Data,NwkSkey); - } - else - { - //Copy the remaining data and fill the rest - for(i = 0; i < 16; i++) - { - if(i < Incomplete_Block_Size) - { - New_Data[i] = *Data; - Data++; - } - if(i == Incomplete_Block_Size) - { - New_Data[i] = 0x80; - } - if(i > Incomplete_Block_Size) - { - New_Data[i] = 0x00; - } - } - - //Preform XOR with Key 2 - XOR(New_Data,Key_K2); - - //Preform XOR with Old data - XOR(New_Data,Old_Data); - - //Preform last AES routine - AES_Encrypt(New_Data,NwkSkey); - } - - Final_MIC[0] = New_Data[0]; - Final_MIC[1] = New_Data[1]; - Final_MIC[2] = New_Data[2]; - Final_MIC[3] = New_Data[3]; -} - -void Generate_Keys(unsigned char *K1, unsigned char *K2) -{ - unsigned char i; - unsigned char MSB_Key; - - //Encrypt the zeros in K1 with the NwkSkey - AES_Encrypt(K1,NwkSkey); - - //Create K1 - //Check if MSB is 1 - if((K1[0] & 0x80) == 0x80) - { - MSB_Key = 1; - } - else - { - MSB_Key = 0; - } - - //Shift K1 one bit left - Shift_Left(K1); - - //if MSB was 1 - if(MSB_Key == 1) - { - K1[15] = K1[15] ^ 0x87; - } - - //Copy K1 to K2 - for( i = 0; i < 16; i++) - { - K2[i] = K1[i]; - } - - //Check if MSB is 1 - if((K2[0] & 0x80) == 0x80) - { - MSB_Key = 1; - } - else - { - MSB_Key = 0; - } - - //Shift K2 one bit left - Shift_Left(K2); - - //Check if MSB was 1 - if(MSB_Key == 1) - { - K2[15] = K2[15] ^ 0x87; - } -} - -void Shift_Left(unsigned char *Data) -{ - unsigned char i; - unsigned char Overflow = 0; - //unsigned char High_Byte, Low_Byte; - - for(i = 0; i < 16; i++) - { - //Check for overflow on next byte except for the last byte - if(i < 15) - { - //Check if upper bit is one - if((Data[i+1] & 0x80) == 0x80) - { - Overflow = 1; - } - else - { - Overflow = 0; - } - } - else - { - Overflow = 0; - } - - //Shift one left - Data[i] = (Data[i] << 1) + Overflow; - } -} - -void XOR(unsigned char *New_Data,unsigned char *Old_Data) -{ - unsigned char i; - - for(i = 0; i < 16; i++) - { - New_Data[i] = New_Data[i] ^ Old_Data[i]; - } -} - diff --git a/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.h b/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.h deleted file mode 100755 index b03572db..00000000 --- a/Arduino/Arduino_LoRa_SoilHum/Encrypt_V31.h +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************************** -* Copyright 2015, 2016 Ideetron B.V. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -******************************************************************************************/ -/****************************************************************************************** -* -* File: Encrypt_V31.h -* Author: Gerben den Hartog -* Compagny: Ideetron B.V. -* Website: http://www.ideetron.nl/LoRa -* E-mail: info@ideetron.nl -******************************************************************************************/ -/**************************************************************************************** -* -* Created on: 04-02-2016 -* Supported Hardware: ID150119-02 Nexus board with RFM95 -* -* Firmware Version 2.0 -* First version -* -* Firmware Version 2.0 -* Works the same is 1.0 using own AES encryption -* -* Firmware Version 3.0 -* Included direction in MIC calculation and encryption -* -* Firmware Version 3.1 -* Now using AppSkey in Encrypt Payload function -****************************************************************************************/ - -#ifndef ENCRYPT_V31_H -#define ENCRYPT_V31_H - -/* -***************************************************************************************** -* FUNCTION PROTOTYPES -***************************************************************************************** -*/ - -void Calculate_MIC(unsigned char *Data, unsigned char *Final_MIC, unsigned char Data_Length, unsigned int Frame_Counter, unsigned char Direction); -void Encrypt_Payload(unsigned char *Data, unsigned char Data_Length, unsigned int Frame_Counter, unsigned char Direction); -void Generate_Keys(unsigned char *K1, unsigned char *K2); -void Shift_Left(unsigned char *Data); -void XOR(unsigned char *New_Data,unsigned char *Old_Data); - -#endif diff --git a/Arduino/Arduino_LoRa_SoilHum/rawAnalog.h b/Arduino/Arduino_LoRa_SoilHum/rawAnalog.h deleted file mode 100644 index 0bc75349..00000000 --- a/Arduino/Arduino_LoRa_SoilHum/rawAnalog.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -* Copyright (C) 2016 Congduc Pham, University of Pau, France -* -* Congduc.Pham@univ-pau.fr -*/ - -#ifndef RAWANALOG_H -#define RAWANALOG_H -#include "Sensor.h" - -#define RAWANALOG_SCALE _BOARD_MVOLT_SCALE - -class rawAnalog : public Sensor { - public: - rawAnalog(char* nomenclature, bool is_analog, bool is_connected, bool is_low_power, uint8_t pin_read, uint8_t pin_power); - void update_data(); - double get_value(); -}; - -#endif diff --git a/Arduino/Arduino_LoRa_temp/Arduino_LoRa_temp.ino b/Arduino/Arduino_LoRa_temp/Arduino_LoRa_temp.ino index 0349e139..69ef68e7 100644 --- a/Arduino/Arduino_LoRa_temp/Arduino_LoRa_temp.ino +++ b/Arduino/Arduino_LoRa_temp/Arduino_LoRa_temp.ino @@ -18,7 +18,7 @@ * along with the program. If not, see . * ***************************************************************************** - * last update: Oct 18th, 2018 by C. Pham + * last update: Nov 2nd, 2018 by C. Pham * * This version uses the same structure than the Arduino_LoRa_Demo_Sensor where * the sensor-related code is in a separate file @@ -299,14 +299,14 @@ void setup() int e; // initialization of the temperature sensor - sensor_temp_Init(); + sensor_Init(); #ifdef LOW_POWER #ifdef __SAMD21G18A__ rtc.begin(); #endif #else - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(PIN_POWER,HIGH); #endif delay(3000); @@ -536,7 +536,7 @@ void loop(void) #endif #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,HIGH); + digitalWrite(PIN_POWER,HIGH); // security? delay(200); #endif @@ -548,7 +548,7 @@ void loop(void) // for (int i=0; i<5; i++) { - temp += sensor_temp_getValue(); + temp += sensor_getValue(); delay(100); } @@ -557,7 +557,7 @@ void loop(void) // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef LOW_POWER - digitalWrite(TEMP_PIN_POWER,LOW); + digitalWrite(PIN_POWER,LOW); #endif PRINT_CSTSTR("%s","Mean temp is "); diff --git a/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.cpp b/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.cpp index a882016b..4ec1d86a 100644 --- a/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.cpp +++ b/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.cpp @@ -11,22 +11,22 @@ char nomenclature_str[4]="TC"; // ADD HERE SOME INITIALIZATION CODE // HERE WE JUST DECLARE VALUE_PIN_READ AS INPUT PIN -void sensor_temp_Init() { +void sensor_Init() { // for the temperature sensor - pinMode(TEMP_PIN_READ, INPUT); - pinMode(TEMP_PIN_POWER, OUTPUT); + pinMode(PIN_READ, INPUT); + pinMode(PIN_POWER, OUTPUT); } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // CHANGE HERE THE WAY YOU READ A VALUE FROM YOUR SPECIFIC SENSOR -// HERE IT IS AN EXAMPLE WITH THE LM35DZ SIMPLE ANALOG TEMPERATURE SENSOR +// HERE IT IS AN EXAMPLE WITH THE TMP36 SIMPLE ANALOG TEMPERATURE SENSOR -double sensor_temp_getValue() { +double sensor_getValue() { //read the raw sensor value - int value = analogRead(TEMP_PIN_READ); + int value = analogRead(PIN_READ); Serial.print(F("Reading ")); Serial.println(value); diff --git a/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.h b/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.h index e82320a1..482224d3 100644 --- a/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.h +++ b/Arduino/Arduino_LoRa_temp/my_temp_sensor_code.h @@ -8,18 +8,18 @@ #endif extern char nomenclature_str[4]; -void sensor_temp_Init(); -double sensor_temp_getValue(); +void sensor_Init(); +double sensor_getValue(); /////////////////////////////////////////////////////////////////// // CHANGE HERE THE READ PIN AND THE POWER PIN FOR THE TEMP. SENSOR -#define TEMP_PIN_READ A0 +#define PIN_READ A0 // use digital 9 to power the temperature sensor if needed // but on most ESP8266 boards pin 9 can not be used, so use pin 2 instead #if defined ARDUINO_ESP8266_ESP01 || defined ARDUINO_ESP8266_NODEMCU || defined IOTMCU_LORA_RADIO_NODE -#define TEMP_PIN_POWER 2 +#define PIN_POWER 2 #else -#define TEMP_PIN_POWER 9 +#define PIN_POWER 9 #endif /////////////////////////////////////////////////////////////////// diff --git a/Arduino/README.md b/Arduino/README.md index e0eb7234..adb9ff3d 100644 --- a/Arduino/README.md +++ b/Arduino/README.md @@ -1,7 +1,7 @@ Arduino example sketches ======================== -This folder contains sketches for Arduino (and compatible) boards. The example sketches will show how simple, yet effective, low-cost LoRa IoT device can be programmed. For instance, they show how LoRa radio modules are configured and how a device can send sensed data to a gateway. They actually serve as template for future developments. On the Uno, Pro Mini, Mini, Nano, Teensy the mapping is as follows: +This folder contains sketches for Arduino (and compatible) boards. The example sketches, in increasing level of complexity, will show how simple, yet effective, low-cost LoRa IoT device can be programmed. For instance, they show how LoRa radio modules are configured and how a device can send sensed data to a gateway. They actually serve as template for future developments. On the Uno, Pro Mini, Mini, Nano, Teensy the mapping is as follows: ``` Arduino Radio module @@ -17,70 +17,50 @@ On the MEGA, the SPI pin are as follows: 50 (MISO), 51 (MOSI), 52 (SCK). Startin All the examples uses so-called LoRa mode 1 (BW125, CR45, SF12) at 865.2 Mhz (`CH_10_868`) to work with the default gateway configuration. -**`Arduino_LoRa_Demo_Sensor`** is a simple demo sketch for training purpose. The main program, i.e. `Arduino_LoRa_Demo_Sensor` can be left unchanged by the students. They just have to add/modify code in `my_demo_sensor_code.h` and `my_demo_sensor_code.cpp` to adapt the code for a given physical sensor. The provided example reads from either an LM35DZ or a TMP36 analog temperature sensor. +**`Arduino_LoRa_Demo_Sensor`** is a very simple demo sketch for training purpose. The main program, i.e. `Arduino_LoRa_Demo_Sensor` can be left unchanged by the students. They just have to add/modify code in `my_demo_sensor_code.h` and `my_demo_sensor_code.cpp` to adapt the code for a given physical sensor. The provided example reads from either an LM35DZ or a TMP36 analog temperature sensor. -**`Arduino_LoRa_Ping_Pong`** shows a simple ping-pong communication between a LoRa device and a gateway by requesting an acknowlegment for data messages sent to the gateway. This example can serve as a simple range test as the device displays back the SNR of the received packet on the gateway. +**`Arduino_LoRa_Simple_temp`** uses the same simple structure than `Arduino_LoRa_Demo_Sensor` where `my_temp_sensor_code.cpp` contains the code to read values from the physical sensor (which is still either an LM35DZ or a TMP36 analog temperature sensor). Additionally, this example illustrates how to implement periodic sensing with low-power mode to run on battery for years. -**`Arduino_LoRa_Ping_Pong_LCD`** is an extended version that uses an OLED display to show in real-time the results of the range test. The pictures below show our simple range tester built from an Heltec ESP32 WiFi LoRa with OLED display. Plenty of information on this nice board are available from https://robotzero.one/heltec-wifi-lora-32/ and https://github.com/darthm0e/esp32-101 to name a few. +**`Arduino_LoRa_Simple_DHT`** shows how a more elaborated digital sensor such as the DHT22 (also known as AM2302) can be used. Code for DHT sensor is provided by the DHT library by Adafruit. This example therefore shows how you can use libraries provided by third-parties which is most likely the approach that you will use if you need to support a new physical sensor. Note that the DHT code can also be used for the AM2305 sensor. One advantage of the AM2305 is that it usually comes in an outdoor casing which make it suitable for outdoor and real-world deployment scenarios. Note that as it is a very simple example, only one physical measure is provided. In the example, it is the temperature even if the DHT22 sensor can provide both temperature and humidity. -![](https://github.com/CongducPham/LowCostLoRaGw/blob/master/images/pingpong.png) - -We also use a regular Arduino Pro Mini or Arduino Nano with an I2C 0.9inch OLED display (such as [this one]( https://fr.aliexpress.com/item/1pcs-0-96-blue-0-96-inch-OLED-module-New-128X64-OLED-LCD-LED-Display-Module/32643950109.html?spm=a2g0s.9042311.0.0.LbMu7r)). You can connect SDA and SCL to pin A4 and A5 respectively. Of course, connect your LoRa module as usual. For range test prior to deploying the sensor nodes, it is recommended to build the Ping-Pong tester with the case that you will use for your final end-devices because the impact of the case on transmission quality is important (see [this antenna presentation](https://www.youtube.com/watch?v=AhFy4-kForA&feature=youtu.be) from our colleague Fabien Ferrero). On the left part you can see an Arduino Nano using the Modtronix inAir9 radio module with the external antenna. On the right part, it is an Arduino Pro Mini with the integrated antenna provided by a simple PCB (see below the `Arduino_LoRa_GPS` description). - -![](https://github.com/CongducPham/LowCostLoRaGw/blob/master/images/pingpong_1.png) - -**`Arduino_LoRa_Simple_temp`** illustrates how a simple LoRa device with temperature data can be flashed to an Arduino board. The example illustrates in a simple manner how to implement most of the features of a real IoT device: periodic sensing, transmission to gateway, duty-cycle and low-power mode to run on battery for months. - -**`Arduino_LoRa_temp`** illustrates a more complex example with AES encryption and the possibility to send LoRaWAN packet. It can also open a receive window after every transmission to wait for downlink message coming from the gateway (uncomment `#define WTH_RCVW`). The template shows for instance how an '/@Ax#' command from the gateway can be parsed to set the node's address to 'x'. It can serve as a template for a more complex LoRa IoT device. +**`Arduino_LoRa_temp`** ends the simple temperature example serie. It illustrates a more complex example with AES encryption and the possibility to send LoRaWAN packet. It can also open a receive window after every transmission to wait for downlink message coming from the gateway (to do so, uncomment `#define WTH_RCVW`). The template shows for instance how an '/@Ax#' command from the gateway can be parsed to set the node's address to 'x'. It can serve as a template for a more complex LoRa IoT device with actuation capability on downlink packets from the gateway. -**`Arduino_LoRa_Simple_BeaconCollar`** is a simple beacon system to build a collar device intended for Cattle Rustling applications. The device will periodically send a beacon message with a sequence number to track at the gateway the beacon's RSSI and beacon losses to trigger alarms. Look at this [dedicated tutorial](https://github.com/CongducPham/tutorials/blob/master/Low-cost-LoRa-Collar.pdf) to build such as system. +**`Arduino_GPS_Parser_GGA`** is a very simple GPS example used to introduce the usage of GPS sensor modules. GPS modules are very popular sensors as geolocalization capabilities are quite attractive in IoT applications. Besides, these modules are becoming less and less expansive as a GPS module can be bought for about 5€ now (for instance the well-known UBlox 6M/7M/M8N modules). The program constantly reads data from the GPS serial (`gps_serial`) and will try to decode GPGGA messages. This example can served as a basis for a tracking device that parses in a very light-weight manner the GPGGA NMEA message. Once a fix is obtained, the GPGGA message will provide localization data and the program will convert the latitude and longitude into decimal degree that can directly be copied/pasted into GoogleMap for instance. -**`Arduino_LoRa_GPS`** is a more elaborated GPS beacon system where a GPS module (UBlox 6M/7M/M8N) is used to get the coordinates of the collar device. The device will periodically send a beacon message with a sequence number and the GPS coordinates in the following format `\!BC/0/LAT/43.31408/LGT/-0.36362/FXT/4172`. This example can served as a basis for a tracking device that parses in a very light-weight manner the GPGGA NMEA message. Look at this [dedicated tutorial](https://github.com/CongducPham/tutorials/blob/master/Low-cost-LoRa-Collar.pdf) to build such as system. In the tutorial, the GPS device can use an open source PCB board designed by Fabien Ferrero from LEAT laboratory, University of Nice, France, to easily integrate an Arduino Pro Mini and an RFM95W radio module. The PCB has an integrated antenna to avoid external fragile part. The PCB Gerber layout file can be obtained from [https://github.com/FabienFerrero/UCA_Board](https://github.com/FabienFerrero/UCA_Board). Here is a [1-click order link](https://www.pcbway.com/project/shareproject/W48634ASK5_UCA_reverse.html) on PCBWAY.com. The latest PCB version with more holes for more connectivity options can be ordered from [here](https://www.pcbway.com/project/shareproject/UCA_Board.html). +**`Arduino_LoRa_GPS`** is a more elaborated GPS system that we use to build a localization system for cattle collars. The GPS library is also stored in an external file (`gps_light.cpp` and `gps_light.h`). The device will periodically send a beacon message with a sequence number and the GPS coordinates in the following format `\!BC/0/LAT/43.31408/LGT/-0.36362/FXT/4172`. Look at this [dedicated tutorial](https://github.com/CongducPham/tutorials/blob/master/Low-cost-LoRa-Collar.pdf) to build such a system. In the tutorial, the GPS device can use an open source PCB board designed by Fabien Ferrero from LEAT laboratory, University of Nice, France, to easily integrate an Arduino Pro Mini and an RFM95W radio module. The PCB has an integrated antenna to avoid external fragile part. The PCB Gerber layout file can be obtained from [https://github.com/FabienFerrero/UCA_Board](https://github.com/FabienFerrero/UCA_Board). Here is a [1-click order link](https://www.pcbway.com/project/shareproject/W48634ASK5_UCA_reverse.html) on PCBWAY.com. The latest PCB version with more holes for more connectivity options can be ordered from [here](https://www.pcbway.com/project/shareproject/UCA_Board.html). -This example also shows how a dedicated "cloud" task, `CloudGpsFile.py`, is implemented and enabled in `clouds.json` to process GPS messages from remote GPS devices and to compute their distance to the gateway. More details are provided in this [dedicated tutorial](https://github.com/CongducPham/tutorials/blob/master/Low-cost-LoRa-Collar.pdf). +On the gateway side, this example also shows how a dedicated "cloud" task, `CloudGpsFile.py`, is implemented and enabled in `clouds.json` to process GPS messages from remote GPS devices and to compute their distance to the gateway. More details are provided in this [dedicated tutorial](https://github.com/CongducPham/tutorials/blob/master/Low-cost-LoRa-Collar.pdf). -**`Arduino_LoRa_Generic_Sensor`** is a very generic sensor template where a large variety of new physical sensors can be added. All physical sensors must be derived from a base Sensor class (defined in `Sensor.cpp` and `Sensor.h`) and should provide a `get_value()` and `get_nomenclature()` function. All the periodic task loop with duty-cycle low-power management is already there as in previous examples. Some predefined physical sensors are also already defined: +**`Arduino_LoRa_Generic_DHT`** shows a more "generic" approach to handle physical sensors. All physical sensors must be derived from a base `Sensor` class (defined in `Sensor.cpp` and `Sensor.h`) and should provide a `get_value()` and `get_nomenclature()` function. Some predefined physical sensors are already defined, but we are demonstrating only the DHT22 in this example: - very simple LM35DZ analog temperature sensor - very simple TMP36 analog temperature sensor - digital DHT22 (AM2302) temperature and humidity sensor (work also with the AM2305) -- digital SHT1x and SHT2x temperature and humidity sensor +- digital SHT1x and SHT2x temperature and humidity sensor from Sensirion - digital DS18B20 temperature sensor - ultra-sonic HC-SR04 distance sensor - Davies Leaf Wetness sensor - general raw analog sensor -You just have to define the number of physical sensors you want to consider: +All the periodic task loop with duty-cycle low-power management is already there as in previous examples. In this `Arduino_LoRa_Generic_DHT` example, we show how the DHT22 sensor is handled by declaring 2 logical sensors: one for the temperature and one for the humidity. Again, the DHT code is the one from Adafruit and it is wrapped in our own generic sensor library. + +To use the template, you just have to define the number of logical sensors you want to consider: // SENSORS DEFINITION ////////////////////////////////////////////////////////////////// // CHANGE HERE THE NUMBER OF SENSORS, SOME CAN BE NOT CONNECTED - const int number_of_sensors = 7; + const int number_of_sensors = 2; ////////////////////////////////////////////////////////////////// - -Add the sensors: + +And add the logical sensors. For each physical sensor, you have to indicate a nomemclature string, whether it is analog or not, whether it is connected to your board or not, whether its power pin is regulated by low-power management or not, the read pin, the power pin and an optional trigger pin. ////////////////////////////////////////////////////////////////// // ADD YOUR SENSORS HERE // Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger=-1) - sensor_ptrs[0] = new LM35("LM35", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A0, (uint8_t) 9 /*no pin trigger*/); - sensor_ptrs[1] = new TMP36("TMP36", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A1, (uint8_t) 8 /*no pin trigger*/); - sensor_ptrs[2] = new DHT22_Temperature("TC1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); - sensor_ptrs[3] = new DHT22_Humidity("HU1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); - //for SHT, pin_trigger will be the clock pin - sensor_ptrs[4] = new SHT_Temperature("TC2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); - sensor_ptrs[5] = new SHT_Humidity("HU2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); - sensor_ptrs[6] = new DS18B20("DS", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 3, (uint8_t) 4 /*no pin trigger*/); - - // for non connected sensors, indicate whether you want some fake data, for test purposes for instance - //sensor_ptrs[3]->set_fake_data(true); - //sensor_ptrs[4]->set_fake_data(true); - - ////////////////////////////////////////////////////////////////// - -For each physical sensor, you have to indicate a nomemclature string, whether it is analog or not, whether it is connected to your board or not, whether its power pin is regulated by low-power management or not, the read pin, the power pin and an optional trigger pin (see the HCSR04 sensor). See how sensors that are not connected can provide emulated data for test purposes. + sensor_ptrs[0] = new DHT22_Temperature("TC", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, A0, A1); + sensor_ptrs[1] = new DHT22_Humidity("HU", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, A0, A1); -The control loop that is periodically executed is as follows: +Then the control loop that is periodically executed in the template is as follows: char final_str[80] = "\\!"; char aux[6] = ""; @@ -100,13 +80,55 @@ The control loop that is periodically executed is as follows: } } -With the declared sensors, the transmitted data string can be as follows: +With the DHT example, the transmitted data string can be as follows: + + \!TC/27.79/HU/56.50 + +You can look at the provided examples to see how you can write a specific sensor class for a specific/new physical sensor. The previous simple temperature example `Arduino_LoRa_Simple_temp` can be obtained from the generic example by simply using a single sensor declaration with the LM35 class (i.e. as `sensor_ptrs[0]`). + +**`Arduino_LoRa_Generic_MultiSensor`** is an extension of the previous example to handle 7 logical sensors. + + // SENSORS DEFINITION + ////////////////////////////////////////////////////////////////// + // CHANGE HERE THE NUMBER OF SENSORS, SOME CAN BE NOT CONNECTED + const int number_of_sensors = 7; + ////////////////////////////////////////////////////////////////// + +With the following sensors: - \!LM35/27.71/TMP36/27.2/TC1/27.79/HU1/56.50/TC2/28.63/HU2/50.49/DS/27.93 + ////////////////////////////////////////////////////////////////// + // ADD YOUR SENSORS HERE + // Sensor(nomenclature, is_analog, is_connected, is_low_power, pin_read, pin_power, pin_trigger=-1) + sensor_ptrs[0] = new LM35("LM35", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A0, (uint8_t) 9 /*no pin trigger*/); + sensor_ptrs[1] = new TMP36("TMP36", IS_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A1, (uint8_t) 8 /*no pin trigger*/); + sensor_ptrs[2] = new DHT22_Temperature("TC1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); + sensor_ptrs[3] = new DHT22_Humidity("HU1", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) A2, (uint8_t) 7 /*no pin trigger*/); + //for SHT, pin_trigger will be the clock pin + sensor_ptrs[4] = new SHT_Temperature("TC2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); + sensor_ptrs[5] = new SHT_Humidity("HU2", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 2, (uint8_t) 6, (uint8_t) 5); + sensor_ptrs[6] = new DS18B20("DS", IS_NOT_ANALOG, IS_CONNECTED, low_power_status, (uint8_t) 3, (uint8_t) 4 /*no pin trigger*/); -You can look at the provided examples to see how you can write a specific sensor class for a specific physical sensor. The previous simple temperature example `Arduino_LoRa_Simple_temp` can be obtained from the generic example by simply using a single sensor declaration with the LM35 class (i.e. `sensor_ptrs[0]`). + // for non connected sensors, indicate whether you want some fake data, for test purposes for instance + //sensor_ptrs[3]->set_fake_data(true); + //sensor_ptrs[4]->set_fake_data(true); + + ////////////////////////////////////////////////////////////////// + +Compared to the previous example, you can see how sensors that are not connected can provide emulated data for test purposes. With the declared sensors, the transmitted data string can be as follows: + + \!LM35/27.71/TMP36/27.2/TC1/27.79/HU1/56.50/TC2/28.63/HU2/50.49/DS/27.93 + +This generic multi-sensors example drives 5 types of temperature and humidity sensors (LM35DZ, TMP36, DHT22, SHT10, DS18B20) on the same node. You can see our [ThingSpeak channel here](https://thingspeak.com/channels/66583) that shows Sensor 3 data. + +**`Arduino_LoRa_Ping_Pong`** shows a simple ping-pong communication between a LoRa device and a gateway by requesting an acknowlegment for data messages sent to the gateway. This example can serve as a simple range test as the device displays back the SNR of the received packet on the gateway. + +**`Arduino_LoRa_Ping_Pong_LCD`** is an extended version that uses an OLED display to show in real-time the results of the range test. The pictures below show our simple range tester built from an Heltec ESP32 WiFi LoRa with OLED display. Plenty of information on this nice board are available from https://robotzero.one/heltec-wifi-lora-32/ and https://github.com/darthm0e/esp32-101 to name a few. + +![](https://github.com/CongducPham/LowCostLoRaGw/blob/master/images/pingpong.png) + +We also use a regular Arduino Pro Mini or Arduino Nano with an I2C 0.9inch OLED display (such as [this one]( https://fr.aliexpress.com/item/1pcs-0-96-blue-0-96-inch-OLED-module-New-128X64-OLED-LCD-LED-Display-Module/32643950109.html?spm=a2g0s.9042311.0.0.LbMu7r)). You can connect SDA and SCL to pin A4 and A5 respectively. Of course, connect your LoRa module as usual. For range test prior to deploying the sensor nodes, it is recommended to build the Ping-Pong tester with the case that you will use for your final end-devices because the impact of the case on transmission quality is important (see [this antenna presentation](https://www.youtube.com/watch?v=AhFy4-kForA&feature=youtu.be) from our colleague Fabien Ferrero). On the left part you can see an Arduino Nano using the Modtronix inAir9 radio module with the external antenna. On the right part, it is an Arduino Pro Mini with the integrated antenna provided by a simple PCB (see below the `Arduino_LoRa_GPS` description). -The generic example drives 5 types of temperature and humidity sensors (LM35DZ, TMP36, DHT22, SHT10, DS18B20) on the same node. You can see our [ThingSpeak channel here](https://thingspeak.com/channels/66583) that shows Sensor 3 data. +![](https://github.com/CongducPham/LowCostLoRaGw/blob/master/images/pingpong_1.png) **`Arduino_LoRa_InteractiveDevice`** is a tool that turns an Arduino board to an interactive device where a user can interactively enter data to be sent to the gateway. There are also many parameters that can dynamically be configured. This example can serve for test and debug purposes as well. @@ -410,5 +432,5 @@ Our library uses the following 4-byte header: `dst(1B)` `ptype(1B)` `src(1B)` `s flags <-> seq ``` -These variables will be set using the `RHDatagram.h` functions: `setHeaderTo`, `setHeaderfrom`, `setHeaderId`, `setHeaderFlags`. The **`Arduino_LoRa_Radiohead_Example`** provides an example sending "hello" using so-called LoRa mode 1 (BW125, CR45, SF12) on 865.2Mhz. PA_BOOST is activated by default and the DI0 pin is connected to Arduino digital pin 2. +These variables will be set using the `RHDatagram.h` functions: `setHeaderTo`, `setHeaderfrom`, `setHeaderId`, `setHeaderFlags`. The **`Arduino_LoRa_Radiohead_Example`** provides an example sending "hello" using so-called LoRa mode 1 (BW125, CR45, SF12) on 865.2Mhz. PA_BOOST is activated by default and the DIO0 pin is connected to Arduino digital pin 2.