// **********************************************************************************
// Sample RFM69 sender/node sketch for DS18B20
// Periodically wakes up, reads the sensor, and goes back to sleep
// Compensates sleep time for the conversion time which can take up to 700ms on this sensor
//
//                  ************************************
//                  **** IMPORTANT NOTES ON DS18B20 ****
//                  ************************************
//
// DS18B20 requires a pullup on the data pin (2.2K-10K)
// Adjust DS18B20 read resolution based on required accuracy and conversion time
// Radio & MCU are WDT slept during conversion but DS18B20 still uses ~1mA during conversion
// Conversion times: ~95ms @9bit, ~180ms @10bit, 370ms @11bit, ~650ms @12bit)
// **********************************************************************************
// Copyright Felix Rusu 2020, http://www.LowPowerLab.com/contact
// **********************************************************************************
// License
// **********************************************************************************
// 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 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 General Public        
// License for more details.                              
//                                                        
// Licence can be viewed at                               
// http://www.gnu.org/licenses/gpl-3.0.txt
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
// **********************************************************************************
//*********************************************************************************************
#include <RFM69.h>      //https://www.github.com/lowpowerlab/rfm69
#include <RFM69_ATC.h>  //included in RFM69
#include <LowPower.h>   //https://www.github.com/lowpowerlab/lowpower
#include <OneWire.h>    //https://github.com/PaulStoffregen/OneWire
//*********************************************************************************************
#define DALLAS_PIN        7
#define DALLAS_RESOLUTION 12
#define TRANSMITPERIOD    5000  //read and send data every this many ms (WDT sleep)
//*********************************************************************************************
#define NODEID           789
#define NETWORKID        100
#define GATEWAYID        1   //as a rule of thumb the gateway ID should always be 1
#define FREQUENCY        RF69_915MHZ  //match the RFM69 version! Others: RF69_433MHZ, RF69_868MHZ
//#define FREQUENCY_EXACT 916000000
#define ENCRYPTKEY       "sampleEncryptKey" //same 16 characters on all nodes, comment this line to disable encryption
#define IS_RFM69HW_HCW   //uncomment only for RFM69HW/HCW! Leave out if you have RFM69W/CW!
//*********************************************************************************************
#define ENABLE_ATC       //comment out this line to disable AUTO TRANSMISSION CONTROL
#define ATC_RSSI         -90
//*********************************************************************************************
#define SERIAL_BAUD      115200 //comment out to turn off serial output
#ifdef SERIAL_BAUD
  #define DEBUG(input)   Serial.print(input)
  #define DEBUGln(input) Serial.println(input)
  #define DEBUGHEX(x)    Serial.print(x, HEX)
  #define DEBUGflush()   Serial.flush()
#else
  #define DEBUG(input)
  #define DEBUGln(input)
  #define DEBUGflush()
  #define DEBUGHEX(x)
#endif

#define LED_HIGH digitalWrite(LED_BUILTIN, HIGH)
#define LED_LOW digitalWrite(LED_BUILTIN, LOW)
//*********************************************************************************************
#ifdef ENABLE_ATC
  RFM69_ATC radio;
#else
  RFM69 radio;
#endif

int HighByte, LowByte, TReading, SignBit, Tc_100, Fh_100, Whole, Fract;
char buff[61]; //max packet size is 61 with encryption enabled
char Fstr[10];
byte buffLen;
float fahrenheit, celsius;
uint32_t time=0, now=0, LASTPACKETTIME=-1;
uint16_t conversionTime=0;

void setup() {
#ifdef SERIAL_BAUD
  Serial.begin(SERIAL_BAUD);
#endif
  pinMode(LED_BUILTIN, OUTPUT);

  radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW_HCW
  radio.setHighPower(); //make sure to call this for RFM69_HW/HCW
#endif
#ifdef FREQUENCY_EXACT
  radio.setFrequency(FREQUENCY_EXACT); //set frequency to some custom frequency
#endif
#ifdef ENCRYPTKEY
  radio.encrypt(ENCRYPTKEY);
#endif

#ifdef ENABLE_ATC
  radio.enableAutoPower(ATC_RSSI);
#endif
  radio.sleep();
  sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
  DEBUGln(buff);
}

void loop() {
  fahrenheit = 0;
  celsius = 0;
  getTemp(DALLAS_PIN, DALLAS_RESOLUTION);

  if (fahrenheit !=0) {
    dtostrf(fahrenheit, 3,2, Fstr);
    sprintf(buff, "F:%s", Fstr);
    buffLen = strlen(buff);

    LED_HIGH;
    DEBUG("Sending '"); DEBUG(buff); DEBUG("' .. ");
    if (radio.sendWithRetry(GATEWAYID, buff, buffLen))
      DEBUG("ok!");
    else DEBUG("nok...");
    radio.sleep();
    DEBUGln();
    LED_LOW;
  }
  else DEBUGln("Nothing to send: fahrenheit=0");

  DEBUGflush();
  LowPower.longPowerDown(TRANSMITPERIOD - conversionTime);
  DEBUGln("WAKEUP");
}

void getTemp(byte dataPin, byte resolution) {
  byte dsaddr[8]; // Device identifier
  OneWire myds(dataPin);
  getFirstDSAddr(myds,dsaddr);
  conversionTime=0;

  DEBUG(F("DallasAddress:"));
  for (int j=0;j<8;j++) {
    if (dsaddr[j] < 16) DEBUG('0');
    DEBUGHEX(dsaddr[j]);
  }
  DEBUGln();
  getdstemp(myds, dsaddr, resolution);
}

void getFirstDSAddr(OneWire myds, byte firstAddr[]){
  byte i;
  byte present = 0;
  byte addr[8];
  float celsius, fahrenheit;
  int length = 8;

  //DEBUGln("Looking for 1-Wire devices...");
  while(myds.search(addr)) {
    for( i = 0; i < 8; i++) firstAddr[i]=addr[i];
    if (OneWire::crc8( addr, 7) != addr[7]) {
        DEBUGln("FAIL:INVALID CRC");
        return;
    }
    return;
  }
}

float getdstemp(OneWire myds, byte addr[8], byte resolution) {
  byte present = 0;
  byte data[12];
  byte type_s;
  uint32_t starttime=millis();
  uint32_t sleeptime=0;

  switch (addr[0]) {
    case 0x10:
      //DEBUGln(F("  Chip = DS18S20"));  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      //DEBUGln(F("  Chip = DS18B20"));
      type_s = 0;
      break;
    case 0x22:
      //DEBUGln(F("  Chip = DS1822"));
      type_s = 0;
      break;
    default:
      DEBUGln(F("Device is not a DS18x20 family device."));
  }

  // Set reading resolution
  byte resbyte = 0x1F;
  if (resolution == 12) resbyte = 0x7F;
  else if (resolution == 11) resbyte = 0x5F;
  else if (resolution == 10) resbyte = 0x3F;

  // Set configuration
  myds.reset();
  myds.select(addr);
  myds.write(0x4E);         // Write scratchpad
  myds.write(0);            // TL
  myds.write(0);            // TH
  myds.write(resbyte);         // Configuration Register
  myds.write(0x48);         // Copy Scratchpad
  myds.reset();
  myds.select(addr);
  myds.write(0x44,1);         // start conversion, with parasite power on at the end

  // Sleep while waiting for conversion to complete
  while (!myds.read()) {
    LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);
    sleeptime+=15;
  }

  conversionTime = millis() - starttime+sleeptime;
  DEBUG("Conversion took: "); DEBUG(conversionTime); DEBUGln(" ms");

  present = myds.reset();
  myds.select(addr);    
  myds.write(0xBE);         // Read Scratchpad

  DEBUG("Raw Scratchpad Data: ");
  for (byte i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = myds.read();
    DEBUGHEX(data[i]);
  }
  DEBUGln();

  // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    } else {
      byte cfg = (data[4] & 0x60);
      // default is 12 bit resolution: ~650 ms conversion time
      if (cfg == 0x00) raw = raw << 3;  // 9 bit resolution: ~95ms
      else if (cfg == 0x20) raw = raw << 2; // 10 bit res: ~180ms
      else if (cfg == 0x40) raw = raw << 1; // 11 bit res: ~370ms
    }
  }

  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  return conversionTime;
}
