kuroの覚え書き

96の個人的覚え書き

ESP32+DS3231でRTClib.hを使ってdeep sleepから起きる

ここまでRTCとして主にDS1307を使っていたが、DS1307のRQWピンでは1Hzのインターバルしか作れないので、1時間とかのインターバルでスリープさせるとかはできない。そこでDS3231に切り替えようと思う。

そしてスリープ復帰のプログラムを書こうと思ったが、どうも世の中の先人たちはRTClibではなくRtcDS3231だとかDS3231RTCだとか別のライブラリを使っているらしい。
素直に先人の言うとおりにすればいいのだけれど、ライブラリを変えると他の部分もいろいろ書き換えなくてはならなくなるわけで、それはそれで面倒だな、ということでRTClibのままでスリープ復帰のプログラムを模索してみたところ、とりあえず想定通りの動作にたどり着いた。


ここまでのプログラム全部のせでESP32+DS3231で書いてみる。

/* TSL2591 Digital Light Sensor
   Dynamic Range: 600M:1
   Maximum Lux: 88K
   SHT4x Humidity & Temp Sensor
   I2C | ESP32
     SCL    22
     SDA    21
     Vin    3.3V
     GND    GND

   Connect the SD card to the following pins:
   SD Card | ESP32
     D2     -
     D3     SS   (5)
     CMD    MOSI (23)
     VSS    GND  (GND)
     VDD    3.3V (5V)
     CLK    SCK  (18)
     VSS    GND  (GND)
     D0     MISO (19)
     D1     -
*/
#include <Adafruit_SHT4x.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2591.h>
#include <Wire.h>
#include <RTClib.h>
#include <SPI.h>
#include <SD.h>
#include <EEPROM.h>

#define buttonPin 16         // calibration mode select
#define DEFAULT_DRY0 4095    // EEPROM default value
#define DEFAULT_WET0 0       // EEPROM default value
#define DATA_VERSION "v1.0"  // EEPROM

#define INTERRUPT_PIN GPIO_NUM_33  // the pin that is connected to SQW
//#define uS_TO_S_FACTOR 1000000  //sleep Timer
//#define TIME_TO_SLEEP  24       //sleep Timer
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex

Adafruit_SHT4x sht4 = Adafruit_SHT4x();
Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591);
RTC_DS3231 RTC;         //use DS3231
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

//
int sensorPin0 = A6;    // select the input pin for the potentiometer
int sensorValue0 = 0;   // variable to store the value coming from the sensor
//int sensvPin0 = 35;   // 
int serialStat = 0;     // To skip serial

const int chipSelect = 5; //SPI (SD)

int buttonStat = 1;     // calibration/measure mode status
int wetValue0 = 0;      // calibrated value 100% moisture
int dryValue0 = 4095;   // calibrated value complete dry
float humValue0 = 0;    // Humidity

RTC_DATA_ATTR int bootCount = 0;

/**************************************************************************/
// EEPROM
struct DATA_SET
{
  int dValue0;
  int wValue0;
  char check[10];
};
DATA_SET data;

void load_data() {
  EEPROM.get<DATA_SET>(0, data);
  if (strcmp(data.check, DATA_VERSION)) // Check for version
  {
    // If there is no data
    data.dValue0 = DEFAULT_DRY0;
    data.wValue0 = DEFAULT_WET0;
  }
}
void save_data() {
  strcpy(data.check, DATA_VERSION);
  EEPROM.put<DATA_SET>(0, data);
}

/**************************************************************************/
void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  //  pinMode(sensvPin, OUTPUT);
  buttonStat = digitalRead(buttonPin);
  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  Serial.begin(9600);
  delay(1000);
  if (Serial) {
    serialStat = 1;
  } else {
    serialStat = 0;
  }

  // RTC
  Wire.begin();
  RTC.begin();
  delay(1000);
  if (serialStat == 1)
  {
    if (!RTC.begin())
    {
      Serial.println("RTC failed");
      Serial.flush();
      while (1) delay(10);
    }
    if(RTC.lostPower()) {
        // this will adjust to the date and time at compilation
      RTC.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
  }
  //we don't need the 32K Pin, so disable it
  RTC.disable32K();
  RTC.clearAlarm(1);
  RTC.clearAlarm(2);
  RTC.writeSqwPinMode(DS3231_OFF);
// turn off alarm 2 (in case it isn't off already) 
// again, this isn't done at reboot, so a previously set alarm could easily go overlooked
  RTC.disableAlarm(2);
  //Set RTC alerm
//  RTC.setAlarm1(RTC.now() + TimeSpan(0,0,4,58), DS3231_A1_Minute);   // In case of returning from sleep every 4'58" from the present time
  RTC.setAlarm1(DateTime(0, 0, 0, 0, 0, 0), DS3231_A1_Minute);  // Return from sleep every hour 0'0"
  //  RTC.adjust(DateTime(__DATE__, __TIME__));
  //  RTC.adjust(DateTime(2022, 10, 29, 0, 13, 30));
  /* Set RTC with PC initially */

  /* Configure the TSL2591 sensor */
  configureSensorTsl();
  /* Configure the SHT4x sensor */
  configureSensorSht();

  SD.begin();
  tsl.begin();
  sht4.begin();
  load_data();
  dryValue0 = data.dValue0;
  wetValue0 = data.wValue0;

  // for serial debug
  if (serialStat == 1)
  {
    // SD card
    //    Serial.print("Initializing SD card...");

    // see if the card is present and can be initialized:
    if (!SD.begin())
    {
      Serial.println("Card failed, or not present");
    }
    //    Serial.println("card initialized.");

    // TSL2591 & SHT40
    //    Serial.println("Adafruit SHT4x & TSL2591 sensors");
    // TSL2591
    if (! tsl.begin()) {
      Serial.println(F("Couldn't find TSL2591"));
      while (1) delay(1);
    }
    //    Serial.println(F("Found a TSL2591 sensor"));
    /* Display some basic information on this sensor */
    //    displaySensorDetailsTsl();

    // SHT40
    if (! sht4.begin()) {
      Serial.println("Couldn't find SHT4x");
      while (1) delay(1);
    }
    //    Serial.println("Found SHT4x sensor");
    //    Serial.print("Serial number 0x");
    //    Serial.println(sht4.readSerial(), HEX);

    if (buttonStat == LOW) {
      calibSens();
      data.dValue0 = dryValue0;
      data.wValue0 = wetValue0;
      save_data();
    }
  }
  //Increment boot number and print it every reboot
  ++bootCount;
  //sensing
  timeRead();
  delay(10);
  advancedReadTsl();
  delay(10);
  sensorReadSht();
  delay(10);
  sensorReadSw();
  
  esp_sleep_enable_ext0_wakeup(INTERRUPT_PIN,0); //1 = High, 0 = Low
  //Go to sleep now
  esp_deep_sleep_start();
}

/**************************************************************************/
void configureSensorTsl(void)
{
  /* You can change the gain on the fly, to adapt to brighter/dimmer light situations */
  //tsl.setGain(TSL2591_GAIN_LOW);    // 1x gain (bright light)
  tsl.setGain(TSL2591_GAIN_MED);      // 25x gain
  //tsl.setGain(TSL2591_GAIN_HIGH);   // 428x gain

  /* Changing the integration time gives you a longer time over which to sense light
     longer timelines are slower, but are good in very low light situtations! */
  //tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS);  // shortest integration time (bright light)
  // tsl.setTiming(TSL2591_INTEGRATIONTIME_200MS);
  tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS);
  // tsl.setTiming(TSL2591_INTEGRATIONTIME_400MS);
  // tsl.setTiming(TSL2591_INTEGRATIONTIME_500MS);
  // tsl.setTiming(TSL2591_INTEGRATIONTIME_600MS);  // longest integration time (dim light)
}

/**************************************************************************/
void configureSensorSht(void)
{
  /* You can have 3 different precisions, higher precision takes longer*/
  sht4.setPrecision(SHT4X_HIGH_PRECISION);
  //sht4.setPrecision(SHT4X_MED_PRECISION);
  //sht4.setPrecision(SHT4X_LOW_PRECISION);

  /* You can have 6 different heater settings
     higher heat and longer times uses more power
     and reads will take longer too!*/
  sht4.setHeater(SHT4X_NO_HEATER);
  //sht4.setHeater(SHT4X_HIGH_HEATER_1S);
  //sht4.setHeater(SHT4X_HIGH_HEATER_100MS);
  //sht4.setHeater(SHT4X_MED_HEATER_1S);
  //sht4.setHeater(SHT4X_MED_HEATER_100MS);
  //sht4.setHeater(SHT4X_LOW_HEATER_1S);
  //sht4.setHeater(SHT4X_LOW_HEATER_100MS);
}

/**************************************************************************/
void calibSens(void)
{
  //  digitalWrite(sensvPin, HIGH);
  dryValue0 =  analogRead(sensorPin0);
  while (dryValue0 < 4000)
  {
    if (serialStat == 1)
    {
      Serial.println("Calibrate Dry");
    }
    delay(1000);
    dryValue0 =  analogRead(sensorPin0);
  }
  //  digitalWrite(sensvPin0, HIGH);
  sensorValue0 =  analogRead(sensorPin0);
  while ((dryValue0 - sensorValue0) < 100)
  {
    if (serialStat == 1)
    {
      Serial.print("DryValue0 = "); Serial.println(dryValue0);
      Serial.println("Calibrate Wet0");
      delay(1000);
    }
    delay(1000);
    sensorValue0 =  analogRead(sensorPin0);
  }
  sensorValue0 =  analogRead(sensorPin0);
  delay(5000);
  wetValue0 =  analogRead(sensorPin0);
  while (!(wetValue0 == sensorValue0))
  {
    if (serialStat == 1)
    {
      Serial.print("Value is not stable; "); Serial.println(wetValue0);
    }
    sensorValue0 =  analogRead(sensorPin0);
    delay(5000);
    wetValue0 =  analogRead(sensorPin0);
  }
  if (serialStat == 1)
  {
    Serial.print("WetValue0: "); Serial.println(wetValue0);
  }
  delay(5000);
  //  digitalWrite(sensvPin, LOW);
}

/**************************************************************************/
void timeRead(void)
{
  DateTime now = RTC.now();
  String timeStamp = String(now.year()) + '/' + String(now.month()) + '/' + String(now.day()) + " (" + daysOfTheWeek[now.dayOfTheWeek()] + ") " + String(now.hour()) + ':' + String(now.minute()) + ':' + String(now.second());
  File dataFile = SD.open("/datalog.txt", FILE_APPEND);
  dataFile.print(timeStamp);
  dataFile.close();

  if (serialStat == 1)
  {
    Serial.println(timeStamp);
  }
}
/**************************************************************************/
void advancedReadTsl(void)
{
  // More advanced data read example. Read 32 bits with top 16 bits IR, bottom 16 bits full spectrum
  // That way you can do whatever math and comparisons you want!
  uint32_t lum = tsl.getFullLuminosity();
  uint16_t ir, full;
  ir = lum >> 16;
  full = lum & 0xFFFF;

  File dataFile = SD.open("/datalog.txt", FILE_APPEND);
  dataFile.print(", ");
  dataFile.print(ir);
  dataFile.print(", ");
  dataFile.print(full);
  dataFile.print(", ");
  dataFile.print(full - ir);
  dataFile.print(", ");
  dataFile.print(tsl.calculateLux(full, ir), 6);
  dataFile.close();

  if (serialStat == 1)
  {
    //Serial.print(F("[ ")); Serial.print(millis()); Serial.println(F(" ms ] "));
    Serial.print(F("IR: ")); Serial.print(ir);  Serial.print(F("  "));
    Serial.print(F("Full: ")); Serial.print(full); Serial.print(F("  "));
    Serial.print(F("Visible: ")); Serial.print(full - ir); Serial.print(F("  "));
    Serial.print(F("Lux: ")); Serial.println(tsl.calculateLux(full, ir), 6);
  }
}
/**************************************************************************/
void sensorReadSht(void)
{
  sensors_event_t humidity, temp;

  //  uint32_t timestamp = millis();
  sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
  //  timestamp = millis() - timestamp;

  File dataFile = SD.open("/datalog.txt", FILE_APPEND);
  dataFile.print(", ");
  dataFile.print(temp.temperature);
  dataFile.print(", ");
  dataFile.print(humidity.relative_humidity);
  dataFile.close();

  if (serialStat == 1)
  {
    Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
    Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");
    //Serial.print("Read duration (ms): ");
    //Serial.println(timestamp);
  }
}
/**************************************************************************/
void sensorReadSw(void)
{
  //  digitalWrite(sensvPin, HIGH);
  sensorValue0 = analogRead(sensorPin0);
  //  digitalWrite(sensvPin, LOW);
  humValue0 = ((float)(dryValue0 - sensorValue0)) / ((float)(dryValue0 - wetValue0)) * 100;
  File dataFile = SD.open("/datalog.txt", FILE_APPEND);
  dataFile.print(", ");
  dataFile.print(sensorValue0);
  dataFile.print(", ");
  dataFile.print(humValue0);
  dataFile.println("");
  dataFile.close();

  if (serialStat == 1)
  {
    Serial.print("Soil sensor value0: "); Serial.println(sensorValue0);
    Serial.print("Soil Humidity0: "); Serial.print(humValue0); Serial.println("%");
  }
}

/**************************************************************************/
void loop() {
}

とりあえずこの状態で毎時00分にスリープから起きてデータを計測できる様になった。
00分、15分、30分、45分とかで起きるようなセッティングはTimeSpanを使うしかないんだろうかね。
センサーの電源をIOピンをoutputにしてとればもっと省エネになりそうだけど、そこまでしなくてもいいかな。


https://amzn.to/3kHzmPy

マイコンをモバイルバッテリーで駆動するのは案外難しい。流れる電流が小さいと、充電完了、ということで止まってしまうようだ。
このバッテリーなら微弱なマイコンの電流でも停止せずにバッテリーが切れるまで運転できる。