#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include "Wire.h"
/*
https://github.com/SpenceKonde/ATTinyCore
To load boatloder and software, use NODEMCU as ISP, The board is wired to the ATTiny85 as follows:
NODEMCU 3.3V -> ATTINY85 PIN 8
NODEMCU GND -> ATTINY85 PIN 4
NODEMCU D5 -> ATTINY85 PIN 7 (This is also SLC, a pull up resistor should be added)
NODEMCU D6 -> ATTINY85 PIN 6
NODEMCU D7 -> ATTINY85 PIN 5 (this is also SDA, a pull up resistor should be added)
NODEMCU D8 -> ATTINY85 PIN 1 (Reset, use for programing Tiny, disconnect for running)
NODEMCU's esp8266:
D5, GPIO14, SPI:HSCLK -> also use for I2C Tiny SDA test
D6, GPIO12, SPI:HMISO -> also SPI MISO
D7, GPIO13, SPI:HMOSI -> also use for I2C Tiny SCL test
D8, GPIO15, SPI:HCS -> also SPI SS
D1, GPIO5, SCL
D2, GPIO4, SDA
Using ATTinycore's Wire for I2C
ATTiny85 core:
https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/extras/Pinout_x5.jpg
Physical, port (PBx), Interupt (PCINTx),...
1, PB5, PCINT5, Reset GPIO5/A0
2, PB3, PCINT3, INPUT GPIO3/A3 --> Green
3, PB4, PCINT4, INPUT GPIO4/A2 --> Red
4, GND
5, PB0, PCINT0, OUTPUT GPIO0, I2C-SDA
6, PB1, PCINT1, OUTPUT GPIO1 --> Blue
7, PB2, PCINT2/INT0, INPUT GPIO2/A1, I2C-SCL
8, VCC
Power optimisation, see https://www.gammon.com.au/power
Reed switch for rain tip and wind rotation,
option1: use pull up 5k resistor, and 5k/1uF RC at input pin for debounce.
Option2: use vcc-10k-pin pullup, gnd-0.1uf-pin debounce, and reed between pin-gnd
Option3: use pinmode(pin, INPUT_PULLUP), connect pin to reed switch to gnd.
*/
#include "RAIN_AND_WIND.h"
volatile RAIN_AND_WIND_DATA data;
#ifndef AT_TINY_TICK
unsigned long previousMillis; //Note millisElaspe can overflow, after about 50days
#endif
void blinkLed(int gpio, int amount = 1, int time = 100) {
for (int i = 0; i < amount; i++) {
digitalWrite(gpio, HIGH);
delay(time);
digitalWrite(gpio, LOW);
delay(time);
}
}
//Connect led from gnd to 1k to pin
#define GREEN_LED_GPIO 3
#define BLUE_LED_GPIO 1
// #define RAIN_REED 4
// Enters the arduino into sleep mode.
void enterSleep(void) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //millis() not working
// set_sleep_mode(SLEEP_MODE_ADC); //millis() working
// set_sleep_mode(SLEEP_MODE_IDLE ); //millis() working
power_all_disable(); // power off ADC, Timer 0 and 1, serial interface
// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts();
// cli();
// setupWDT(); // get watchdog ready
sleep_enable();
// turn off brown-out enable in software //NOTE this seem to break the WDT time
// MCUCR = bit(BODS) | bit(BODSE); // turn on brown-out enable select
// MCUCR = bit(BODS); // this must be done within 4 clock cycles of above
// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts(); // one cycle
// sei();
sleep_cpu();
// sleep_mode(); // Start sleep mode: do sleep_enable(), sleep_cpu(), sleep_disable()
sleep_disable();
power_all_enable();
}
// Setup the Watch Dog Timer (WDT) note use WDTCR instead of WDTCSR on __AVR_ATtiny85__
void setupWDT() {
//https://forum.arduino.cc/t/using-wdt-in-combination-with-sleeping/568804/5 post#6
// wdt_reset();
// MCUSR &= ~(1 << WDRF); // Clear the WDRF (Reset Status Flag).
// MCUSR = 0; // clear various "reset" flags
// wdt_enable(WDTO_2S);
// Setting WDCE allows updates for 4 clock cycles end is needed to
// change WDE or the watchdog pre-scalers.
WDTCR = bit(WDCE) | bit(WDE) | bit(WDIF); // allow changes, disable reset, clear existing interrupt
// WDTCR |= (1 << WDCE) | (1 << WDE); //Interrupt Enable, Reset Enable
// WDTCR |= (1 << WDCE) | (0 << WDE); //Interrupt Enable, Reset Disable
/**
* Setting the watchdog pre-scaler value with VCC = 5.0V and 16mHZ
* WDP3 WDP2 WDP1 WDP0 | Number of WDT | Typical Time-out at Oscillator Cycles
* 0 0 0 0 | 2K cycles | 16 ms
* 0 0 0 1 | 4K cycles | 32 ms
* 0 0 1 0 | 8K cycles | 64 ms
* 0 0 1 1 | 16K cycles | 0.125 s
* 0 1 0 0 | 32K cycles | 0.25 s
* 0 1 0 1 | 64K cycles | 0.5 s
* 0 1 1 0 | 128K cycles | 1.0 s
* 0 1 1 1 | 256K cycles | 2.0 s
* 1 0 0 0 | 512K cycles | 4.0 s
* 1 0 0 1 | 1024K cycles | 8.0 s
*/
// WDTCR = (1 << WDP3) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0); //8 sec
// WDTCR = (1 << WDP3) | (0 << WDP2) | (0 << WDP1) | (0 << WDP0); //4 sec
// WDTCR |= _BV(WDIE); // Enable the WDT interrupt.
// WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 8 seconds delay
WDTCR = bit(WDIE) | bit(WDP3); // set WDIE (enable interrupt), and 4 seconds delay
wdt_reset(); // pat the dog ???
}
volatile bool requestHandlerCalled = false;
volatile bool receiveHandlerCalled = false;
volatile bool ISR_WDT_called = false;
// WDT Interrupt Need some code for coming out of sleep.
// but is does not need to do anything! (just exist).
ISR(WDT_vect) {
// digitalWrite(GREEN_LED_GPIO, HIGH);
// wdt_reset();
// wdt_disable(); // disable watchdog
ISR_WDT_called = true;
// digitalWrite(GREEN_LED_GPIO, LOW);
}
uint32_t lastPress = 0;
volatile bool ISR_PCINT0_called = false;
ISR(PCINT0_vect) {
// digitalWrite(GREEN_LED_GPIO, HIGH);
// uint32_t ms = millis();
// if ((ms - lastPress) > 100) { //100ms debounce
// ISR_PCINT0_called = true;
data.rain_tip += 1;
// }
// lastPress = ms;
// digitalWrite(GREEN_LED_GPIO, LOW);
}
void setup() {
setupWDT();
#ifdef GREEN_LED_GPIO
pinMode(GREEN_LED_GPIO, OUTPUT);
#endif
#ifdef BLUE_LED_GPIO
pinMode(BLUE_LED_GPIO, OUTPUT);
#endif
#ifdef RAIN_REED
pinMode(RAIN_REED, INPUT_PULLUP);
// Interrupts
PCMSK = bit(RAIN_REED); // enable interrupt handler (ISR)// want PCINT4 pin D4 / pin 3
GIFR |= bit(PCIF); // clear any outstanding interrupts
GIMSK |= bit(PCIE); // enable pin change interrupts
#endif
init_RAIN_AND_WIND_DATA(&data);
#ifdef GREEN_LED_GPIO
if (data.ticks != 0) {
blinkLed(BLUE_LED_GPIO, 5, 50);
}
#endif
#ifdef GREEN_LED_GPIO
blinkLed(GREEN_LED_GPIO, 3, 50);
#endif
#ifdef GREEN_LED_GPIO
blinkLed(BLUE_LED_GPIO, 3, 50);
#endif
Wire.begin(JL_CUST1_I2C_ADDR); // join i2c bus with address #JL_CUST1_I2C_ADDR
// Wire.setClock(100000); //set speed of 1KHz not working per https://github.com/SpenceKonde/ATTinyCore
Wire.onReceive(receiveHandler);
Wire.onRequest(requestHandler);
// ADCSRA &= ~(1 << ADEN); // Disable ADC, to reduce power consumtion by a factor of 1000, need to validate....
#ifndef AT_TINY_TICK
previousMillis = millis();
#endif
}
void loop() {
#ifdef GREEN_LED_GPIO
blinkLed(GREEN_LED_GPIO, 1, 200);
#endif
#ifdef GREEN_LED_GPIO
if (data.ticks > 20) {
blinkLed(BLUE_LED_GPIO, 5, 50);
}
#endif
// Wire.begin(JL_CUST1_I2C_ADDR); // join i2c bus with address #JL_CUST1_I2C_ADDR
// // Wire.onReceive(receiveHandler);
// // Wire.onRequest(requestHandler);
// WDTCR |= (1<<WDIE); // set WDIE=1 to wake up ATtiny (ie. stops the first watchdog trigger causing reboot)
// wdt_reset();
if (ISR_WDT_called) {
#ifdef GREEN_LED_GPIO
blinkLed(GREEN_LED_GPIO, 1, 50);
#endif
ISR_WDT_called = false;
}
if (ISR_PCINT0_called) {
#ifdef BLUE_LED_GPIO
blinkLed(BLUE_LED_GPIO, 1, 50);
#endif
ISR_PCINT0_called = false;
}
if (requestHandlerCalled) {
requestHandlerCalled = false;
#ifdef BLUE_LED_GPIO
blinkLed(BLUE_LED_GPIO, 1, 50);
#endif
}
if (receiveHandlerCalled) {
receiveHandlerCalled = false;
#ifdef GREEN_LED_GPIO
blinkLed(GREEN_LED_GPIO, 1, 50);
#endif
}
#ifndef RAIN_REED
data.rain_tip += 1; //Emulation
#endif
data.wind_turn += 2;
data.wind_gust_turn = 10;
#ifdef AT_TINY_TICK
data.ticks++;
#else
data.elapsed = millis() - previousMillis; //TODO handle/protect overflow of long millis()
#endif
enterSleep(); //8 Sec or awakened by interupt
// delay(4000);
// attachInterrupt();
}
void requestHandler() {
requestHandlerCalled = true;
Wire.write((uint8_t *)&data, sizeof(RAIN_AND_WIND_DATA));
}
void receiveHandler(int numBytes) {
receiveHandlerCalled = true;
while (Wire.available() > 0) {
uint8_t val = Wire.read();
// uint8_t val = Wire.receive();
if (val == 1) {
init_RAIN_AND_WIND_DATA(&data);
}
}
}