/*
   RadioHead attiny85 DHT22 Weather Station

   2021 Stefan Ostermann based on several source from the internetz.

   Sources:
   https://git.cryhost.de/crycode/attiny85-radiohead-dht22-weather-sensor/-/tree/master
*/



#include <Arduino.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/delay.h>

// RadioHead
#include <RH_ASK.h>


// DHT22 lib
// not using the current adafruit dht library, because it's too memory intensive
#include "dht22.h"

// Configuration, see file config.h.example and rename to config.h. Project specific, do not add to git!
#include "config.h"

#include "sensor433.h"



// global variables:

// watchdog flag
volatile boolean f_wdt = 1;

// temp / humid toggle
volatile uint8_t counter = 0;

dht22 dht;

uint8_t rh_buf[RH_BUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

RH_ASK rh_driver(RH_SPEED, RH_RX_PIN, RH_TX_PIN, RH_PTT_PIN);
// end global variables.



// prototypes:
void rh_send(uint8_t rh_buf[], uint8_t len);

void setup_watchdog(int ii);

void system_sleep();

void gotoSleep(void);

int getVcc();




void setup()
{
  // setup the LED pin
  pinMode(TRANSISTOR_BASE_PIN, OUTPUT);

  pinMode(SOIL_PIN, INPUT);

  // init the DHT22
  if (USE_DHT_SENSOR)
  {
    dht_init(&dht, DHT_PIN);
  }

  uint8_t num_blink = 3;

  if (!rh_driver.init())
  {
    num_blink = 5;
  }

  blink(num_blink);

  // send start message
  rh_buf[0] = MSG_START;
  rh_send(rh_buf,1);

  setup_watchdog(WATCHDOG_WAKEUP);
}

// main loop
void loop()
{

  if (f_wdt)
  {
    f_wdt = 0;
    // turn on the LED and the transistor:
    digitalWrite(TRANSISTOR_BASE_PIN, HIGH);

    // temperature and humidity
    float t = 0; //float is 32bit = 4 byte
    float h = 0;
    int soil = 0;
    long battery = 0;

    // wait for everything the transistor switched on:
    delay(500);

    if (counter % 4 == 0)
    {
      rh_buf[0] = MSG_BAT;
      battery = getVcc();
      memcpy(&rh_buf[1], &battery, 4);
      rh_send(rh_buf,RH_BUF_LEN);
    }
    else if (USE_DHT_SENSOR)
    {
      if (dht_read_data(&dht, &t, &h) == 1)
      {
        rh_buf[0] = MSG_TEMPHUM;
        memcpy(&rh_buf[1], &t, 4);
        memcpy(&rh_buf[5], &h, 4);
        soil = analogRead(SOIL_PIN);
        memcpy(&rh_buf[9], &soil, 2);

        rh_send(rh_buf,RH_BUF_LEN);
      }
      else
      {
        // error message
        blink(4);
        digitalWrite(TRANSISTOR_BASE_PIN, HIGH);
        delay(5);
        rh_buf[0] = MSG_ERR;
        rh_send(rh_buf,1);
      }
    }
    else
    {
      rh_buf[0] = MSG_HUM;
      soil = analogRead(SOIL_PIN);
      memcpy(&rh_buf[1], &soil, 2);

      rh_send(rh_buf,RH_BUF_LEN);
    }
    counter++;

    // turn off the LED and transistor:
    digitalWrite(TRANSISTOR_BASE_PIN, LOW);
    // deep sleep
    for (uint8_t i = 0; i < WATCHDOG_SLEEP_FURTHER; i++)
    {
      system_sleep();
      // if one of the first loops sleep less long for easyier debugging:
      if (counter<10) {
        break;
      }
    }
  }
}

// function to send RadioHead messages from the buffer
void rh_send(uint8_t rh_buf[],uint8_t len)
{
#ifdef DEVICE_ID
rh_buf[0] = add_device_id(rh_buf[0], DEVICE_ID);
#endif

  rh_driver.send((uint8_t *)rh_buf, len);
  rh_driver.waitPacketSent();
}

ISR(WDT_vect)
{
  f_wdt = 1; // set global flag
}

/* set system into the sleep state
   system wakes up when watchdog is timed out
*/
void system_sleep()
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  sleep_mode(); // System sleeps here

  sleep_disable(); // System continues execution here when watchdog timed out
}

/* 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
   6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
*/
void setup_watchdog(int ii)
{

  byte bb;
  int ww;
  if (ii > 9)
    ii = 9;
  bb = ii & 7;
  if (ii > 7)
    bb |= (1 << 5);
  bb |= (1 << WDCE);
  ww = bb;

  MCUSR &= ~(1 << WDRF);
  // start timed sequence
  WDTCR |= (1 << WDCE) | (1 << WDE);
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}

/**
   From https://github.com/cano64/ArduinoSystemStatus/blob/master/SystemStatus.cpp
*/
int getVcc()
{
  //reads internal 1V1 reference against VCC
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)
  ADMUX = _BV(MUX5) | _BV(MUX0); // For ATtiny84
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
  ADMUX = _BV(MUX3) | _BV(MUX2); // For ATtiny85/45
#elif defined(__AVR_ATmega1284P__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // For ATmega1284
#else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // For ATmega328
#endif
  delay(2);            // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC))
    ;
  uint8_t low = ADCL;
  unsigned int val = (ADCH << 8) | low;
  //discard previous result
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC))
    ;
  low = ADCL;
  val = (ADCH << 8) | low;

  return ((long)1024 * 1100) / val;
}