Skip to content

Instantly share code, notes, and snippets.

@Sharangovich
Last active February 17, 2024 14:28
Show Gist options
  • Save Sharangovich/9509c91b43614f820755c956dcdd66ff to your computer and use it in GitHub Desktop.
Save Sharangovich/9509c91b43614f820755c956dcdd66ff to your computer and use it in GitHub Desktop.
/*
Original Author: Klusjesman
Tested with STK500 + ATMega328P
GCC-AVR compiler
Modified by supersjimmie:
Code and libraries made compatible with Arduino and ESP8266
Tested with Arduino IDE v1.6.5 and 1.6.9
For ESP8266 tested with ESP8266 core for Arduino v 2.1.0 and 2.2.0 Stable
(See https://github.com/esp8266/Arduino/ )
*/
/*
CC11xx pins ESP pins Arduino pins Description
1 - VCC VCC VCC 3v3
2 - GND GND GND Ground
3 - MOSI 13=D7 Pin 11 Data input to CC11xx
4 - SCK 14=D5 Pin 13 Clock pin
5 - MISO/GDO1 12=D6 Pin 12 Data output from CC11xx / serial clock from CC11xx
6 - GDO2 04=D2 Pin 2? Serial data to CC11xx
7 - GDO0 ? Pin ? output as a symbol of receiving or sending data
8 - CSN 15=D8 Pin 10 Chip select / (SPI_SS)
*/
#include <SPI.h>
#include "IthoCC1101.h"
#include "IthoPacket.h"
#include <HTU21D.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#define RF_VCC_PIN 3
#define SENSOR_VCC_PIN 4
#define GREEN_LED_PIN 8
#define AMBER_LED_PIN 9
// Pro Mini version is 3V3 8MHz!!!
/*
Calculater power consumption during awake period with measurments
Devider MHz Uptime(s) mAs
clock_div_2 4 0.226 0.4718
clock_div_4 2 0.283 > 0.4213 <
clock_div_8 1 0.459 0.52425
clock_div_16 0.5 0.751 0.75805
clock_div_32 0.25 1.355 1.532515
*/
#define CLOCK_DIV 4 // setup works well at divider 16 (0.5MHz). Also on 32 and 64 but Itho lib needs adjustmets to delays.
#define CLOCK_DIV_PRESCALE clock_div_4
// #define CLOCK_ADJUSTED_BAUD 300 * CLOCK_DIV // for divider 64 clock 0.125MHz
// #define CLOCK_ADJUSTED_BAUD 600 * CLOCK_DIV // for divider 32 clock 0.25MHz
// #define CLOCK_ADJUSTED_BAUD 1200 * CLOCK_DIV // for divider 16 clock 0.5MHz
// #define CLOCK_ADJUSTED_BAUD 2400 * CLOCK_DIV // for divider 8 clock 1MHz
#define CLOCK_ADJUSTED_BAUD 4800 * CLOCK_DIV // for divider 4 clock 2MHz
// #define CLOCK_ADJUSTED_BAUD 9600 * CLOCK_DIV // for divider 2 clock 4MHz
#define DEFAULT_TARGET_SLEEP_CYCLES 1
#define TIMER_SLEEP_CYCLES 75 // should be around 10 minutes (75 cycles of 8 seconds)
// #define TIMER_SLEEP_CYCLES 150 // should be around 10 minutes (150 cycles of 4 seconds)
// #define TIMER_SLEEP_CYCLES 300 // should be around 10 minutes (300 cycles of 2 seconds)
#define MAX_HUMIDITY 70
#define MAX_VALID_TEMPERATURE 45
#define MIN_VALID_TEMPERATURE 5
#define SHORT_DELAY_MICROSECONDS 3200 / CLOCK_DIV
#define LONG_DELAY_MICROSECONDS 360000 / CLOCK_DIV
#define RF_DELAY_MICROSECONDS 100000 / CLOCK_DIV
IthoCC1101 rf;
HTU21D sensor;
const uint8_t RFTid[] = {106, 170, 106, 101, 154, 107, 154, 86}; // my ID, magic number
volatile uint16_t sleepCycles = 0; // Sleep cycle counter
volatile uint16_t targetSleepCycles = DEFAULT_TARGET_SLEEP_CYCLES; // Number of sleep cycles to run, default 1
void setup(void) {
// slow clock to divide by N
clock_prescale_set(CLOCK_DIV_PRESCALE); // This setup works at divider 16 (1MHz clock)
Serial.begin(CLOCK_ADJUSTED_BAUD);
Serial.println("Setup start");
digitalWrite(RF_VCC_PIN, LOW);
digitalWrite(SENSOR_VCC_PIN, LOW);
pinMode(RF_VCC_PIN, OUTPUT); // RF_VCC_PIN powers RF module when necessary
pinMode(SENSOR_VCC_PIN, OUTPUT); // SENSOR_VCC_PIN powers temperature and humidity sensor HTU21D when necessary
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(AMBER_LED_PIN, OUTPUT);
// Register as a new RFT, follow user manual for the Itho fan.
// In most cases: power down, wait 15 sec, power on, bind/register RFT within 2 minutes.
//
// Serial.println("Register command start");
// RFSwitchOn();
// rf.init();
// sendRegister();
// RFSwitchOff();
// Serial.println("Register command sent");
Serial.println("Setup done");
}
void RFSwitchOn(void) {
Serial.println("RF on");
digitalWrite(RF_VCC_PIN, HIGH);
delayMicroseconds(RF_DELAY_MICROSECONDS);
rf.init();
Serial.println("RF init done");
}
void RFSwitchOff(void) {
digitalWrite(RF_VCC_PIN, LOW);
Serial.println("RF off");
}
void SensorSwitchOn(void) {
Serial.println("Sensor on");
digitalWrite(SENSOR_VCC_PIN, HIGH);
sensor.begin();
Serial.println("Sensor init done");
}
void SensorSwitchOff(void) {
digitalWrite(SENSOR_VCC_PIN, LOW);
Serial.println("Sensor off");
}
void GreenLedBlink(void) {
digitalWrite(GREEN_LED_PIN, HIGH);
delayMicroseconds(SHORT_DELAY_MICROSECONDS);
digitalWrite(GREEN_LED_PIN, LOW);
}
void ErrorBlink(void) {
digitalWrite(AMBER_LED_PIN, HIGH);
delayMicroseconds(SHORT_DELAY_MICROSECONDS);
digitalWrite(AMBER_LED_PIN, LOW);
delayMicroseconds(LONG_DELAY_MICROSECONDS);
digitalWrite(AMBER_LED_PIN, HIGH);
delayMicroseconds(SHORT_DELAY_MICROSECONDS);
digitalWrite(AMBER_LED_PIN, LOW);
}
void loop(void) {
// Serial.println(millis ());
// Serial.println(lastSleep);
// Serial.println(WAIT_TIME);
// if (millis () - lastSleep <= WAIT_TIME / CLOCK_DIV)
// {
// lastSleep = millis ();
// noInterrupts ();
// byte old_ADCSRA = ADCSRA;
// // disable ADC
// ADCSRA = 0;
// // pin change interrupt (example for D0)
// PCMSK2 |= bit (PCINT16); // want pin 0
// PCIFR |= bit (PCIF2); // clear any outstanding interrupts
// PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7
// set_sleep_mode (SLEEP_MODE_PWR_DOWN);
// power_adc_disable();
// power_spi_disable();
// power_timer0_disable();
// power_timer1_disable();
// power_timer2_disable();
// power_twi_disable();
// UCSR0B &= ~bit (RXEN0); // disable receiver
// UCSR0B &= ~bit (TXEN0); // disable transmitter
// sleep_enable();
// digitalWrite (AWAKE_LED, LOW);
// interrupts ();
// sleep_cpu ();
// digitalWrite (AWAKE_LED, HIGH);
// sleep_disable();
// power_all_enable();
// ADCSRA = old_ADCSRA;
// PCICR &= ~bit (PCIE2); // disable pin change interrupts for D0 to D7
// UCSR0B |= bit (RXEN0); // enable receiver
// UCSR0B |= bit (TXEN0); // enable transmitter
// } // end of time to sleep
SensorSwitchOn();
/*
RESOLUTION_RH12_T14 = 0, 12 bit for RH and 14 bit for temperature
RESOLUTION_RH8_T12 = 1, 8 bit for RH and 12 bit for temperature
RESOLUTION_RH10_T13 = 2, 10 bit for RH and 13 bit for temperature
RESOLUTION_RH11_T11 = 3 11 bit for RH and 11 bit for temperature
HTU21D_DELAY_T[] = {50, 13, 25, 7};
HTU21D_DELAY_H[] = {16, 3, 5, 8};
*/
sensor.setResolution(RESOLUTION_RH11_T11); // lower resolution for quicker response
GreenLedBlink();
if(sensor.measure()) {
float temperature = sensor.getTemperature();
float humidity = sensor.getHumidity();
SensorSwitchOff();
// Printing to Serial takes extra 1s at div 32!!!
// Serial.print("Temperature (°C): ");
Serial.println(temperature);
// Serial.print("Humidity (%RH): ");
Serial.println(humidity);
// Sensor when not ready gives invalid temperature (-46.85 or a value greater than 100)
// Only react if the temperature is in the reasonable indoor range.
if (humidity > MAX_HUMIDITY && temperature > MIN_VALID_TEMPERATURE && temperature < MAX_VALID_TEMPERATURE) {
RFSwitchOn();
sendTimer();
targetSleepCycles = TIMER_SLEEP_CYCLES;
RFSwitchOff();
GreenLedBlink();
} else if (temperature < MIN_VALID_TEMPERATURE || temperature > MAX_VALID_TEMPERATURE) {
ErrorBlink();
} else {
GreenLedBlink();
}
} else {
ErrorBlink();
}
SensorSwitchOff();
// Sleep
static byte prevADCSRA = ADCSRA;
ADCSRA = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
// Without this Serial doesn't work
// UCSR0B &= ~bit (RXEN0); // disable receiver
// UCSR0B &= ~bit (TXEN0); // disable transmitter
sleep_enable();
while (sleepCycles < targetSleepCycles) { // 1 cycle lasts 8 seconds
// Turn of Brown Out Detection (low voltage). This is automatically re-enabled upon timer interrupt
sleep_bod_disable();
// Ensure we can wake up again by first disabling interrupts (temporarily) so
// the wakeISR does not run before we are asleep and then prevent interrupts,
// and then defining the ISR (Interrupt Service Routine) to run when poked awake by the timer
noInterrupts();
/* Clear the reset flag. */
MCUSR &= ~(1 << WDRF);
/* In order to change WDE or the prescaler, we need to
* set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCSR |= (1 << WDCE) | (1 << WDE);
/* set new watchdog timeout prescaler value */
WDTCSR = 1 << WDP0 | 1 << WDP3; // 8.0 seconds
// WDTCSR = 1 << WDP3; // 4.0 seconds
/*
time const Prescaler-Bits
WDP0 WDP1 WDP2 WDP3
16 ms WDTO_15MS 0 0 0 0
32 ms WDTO_30MS 1 0 0 0
64 ms WDTO_60MS 0 1 0 0
0,125s WDTO_120MS 1 1 0 0
0,25s WDTO_250MS 0 0 1 0
0,5s WDTO_500MS 1 0 1 0
1,0s WDTO_1S 0 1 1 0
2,0s WDTO_2S 1 1 1 0
4,0s WDTO_4S 0 0 0 1
8,0s WDTO_8S 1 0 0 1
*/
/* Enable the WD interrupt (note no reset). */
WDTCSR |= _BV(WDIE);
// Send a message just to show we are about to sleep
Serial.print("Sleep cycle ");
Serial.print(sleepCycles + 1);
Serial.print(" of ");
Serial.println(targetSleepCycles);
Serial.flush();
// Allow interrupts now
interrupts();
blinkInSleep();
// And enter sleep mode as set above
sleep_cpu();
}
// Prevent sleep mode, so we don't enter it again, except deliberately, by code
sleep_disable();
power_all_enable();
// Without this Serial doesn't work
// UCSR0B |= bit (RXEN0); // enable receiver
// UCSR0B |= bit (TXEN0); // enable transmitter
// Re-enable ADC if it was previously running
ADCSRA = prevADCSRA;
// Wakes up at this point when timer wakes up µC
Serial.println("Awake");
// Reset sleep counter
sleepCycles = 0;
targetSleepCycles = DEFAULT_TARGET_SLEEP_CYCLES;
}
// When WatchDog timer causes µC to wake it comes here
ISR (WDT_vect) {
// Turn off watchdog, we don't want it to do anything (like resetting this sketch)
wdt_disable();
// Increment the WDT interrupt count
sleepCycles++;
// Now we continue running the main Loop() just after we went to sleep
}
void blinkInSleep(void) {
power_all_enable();
// Assumed that sleep cycles number is bigger, then the timer is running
if (targetSleepCycles > DEFAULT_TARGET_SLEEP_CYCLES) {
digitalWrite(AMBER_LED_PIN, HIGH);
delayMicroseconds(SHORT_DELAY_MICROSECONDS);
digitalWrite(AMBER_LED_PIN, LOW);
}
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
}
/////////////////////////////
/////////////////////////////
void sendRegister() {
Serial.println("sending join...");
rf.sendCommand(IthoJoin);
Serial.println("sending join done.");
}
void sendStandbySpeed() {
Serial.println("sending standby...");
rf.sendCommand(IthoStandby);
Serial.println("sending standby done.");
}
void sendLowSpeed() {
Serial.println("sending low...");
rf.sendCommand(IthoLow);
Serial.println("sending low done.");
}
void sendMediumSpeed() {
Serial.println("sending medium...");
rf.sendCommand(IthoMedium);
Serial.println("sending medium done.");
}
void sendHighSpeed() {
Serial.println("sending high...");
rf.sendCommand(IthoHigh);
Serial.println("sending high done.");
}
void sendFullSpeed() {
Serial.println("sending FullSpeed...");
rf.sendCommand(IthoFull);
Serial.println("sending FullSpeed done.");
}
void sendTimer() {
Serial.println("sending timer...");
rf.sendCommand(IthoTimer1);
Serial.println("sending timer done.");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment