eyesblue
Published © MIT

Humidity Recorder control with Android App

A Humidity Recorder made with Arduino, control and display data with Android App, both Android and Arduino project are very stable.

IntermediateFull instructions provided117
Humidity Recorder control with Android App

Things used in this project

Hardware components

DS3231MPMB1 Peripheral Module
Maxim Integrated DS3231MPMB1 Peripheral Module
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1
7 Segment LED Display, InfoVue
7 Segment LED Display, InfoVue
4 digit 7 Segment LED displayer, drive with TM1637, Refer here: https://www.makerguides.com/tm1637-arduino-tutorial/
×1
Adafruit SHT31
×1
Arduino Nano R3
Arduino Nano R3
×1

Software apps and online services

Controller of Arduino humidity recorder

Story

Read more

Schematics

humidityrecoderatmega328pu_OVA8Z8Ol25.fzz

humidityrecoder_eWBFkJkf5I.fzz

Code

Humidity Recoder

Arduino
I write the code by Arduino IDE, you may choice your own editor and paste the code.
#include <Arduino.h>
#include <EEPROM.h>
#include <TM1637.h>
#include <Wire.h>
#include <Adafruit_SHT31.h>
#include <DS3231.h>
#include <ZEeprom.h>
#include <SoftwareSerial.h>
#include <Streaming.h>
#include <avr/sleep.h>
#include <LowPower.h>

//byte runtime[4] = {0, 0, 0, 0};
#define RT_BT 1;
#define RT_SYS 0;

// Runtime bluetooth
#define IsConnected 0
//#define IsVerified 1

// Settings address in ROM
#define ADDR_BRIGHTNESS 1023
#define ADDR_REC_INTERVAL 1022

#define RTC_ADDRESS 0x68
#define CLOCK_INT_PIN 2
#define YEAR 5
#define MON 4
#define DATE 3
#define HOUR 2
#define MIN 1
#define SEC 0
DS3231 rtc;
RTClib rtcLib; // For get now();
uint8_t sysClock[6] = {0, 0, 0, 0, 0, 0};
volatile bool clockIntr = false, bluetoothIntr = false;
uint32_t upTimer = 0;

#define CLOCK_WAKEUP 1
#define WDT_WAKEUP 2
byte wakeupPhase = 0;
unsigned long clockSecMillis = 0;

#define EEPROM_ADDRESS  0x57
ZEeprom * extRom;
unsigned int extRomSize = 4096;

uint8_t innerRomStartAddr = 0x00;
uint16_t innerRomEndAddr = 999;

#define CLK A1 //Pins for TM1637       
#define DIO A0
TM1637 tm1637(CLK, DIO);
int clockLedBright = BRIGHT_TYPICAL;

#define DHT31_ADDRESS 0x44
Adafruit_SHT31 sht31 = Adafruit_SHT31();
bool isRecHumdt = true;
bool isRecTemp = false;
uint8_t recInterval = 1; // minute.
struct RecordSet {
  uint32_t lastRecTime;
  uint16_t lastRecAddr;
} ThRec;

#define BLUETOOTH_STATE_PIN 3
bool isBluetoothConnected = false;
//SoftwareSerial BTserial(4, 5);
SoftwareSerial BTserial(17, 16);

#define DUMP 1
#define SET_TIME 2
#define GET_STAT 3
long dumpIndex = -1;
byte data[32];

#define READY 0
#define BUSY 1
#define ERR 2

#define NO_RTC 1
#define NO_EEPROM 2
#define NO_DHT31 3

class Led {
    byte ledPin = LED_BUILTIN;
    byte sysState = READY;
    unsigned long ledPrevMillis = 0;
    bool hasErr=false;  // hasErr is hightest priority flag, if it is true, skip sysState flag.

  public:
    Led() {}
    Led(int pin) {
      ledPin = pin;
    }

    void setup() {
      pinMode(ledPin, OUTPUT);
    }
    setState(int state) {
      sysState = state;
    }
    int getState() {
      return sysState;
    }

    void loop() {
      if(hasErr)  // If hasErr is true, skip sysState flag.
        ledErr();
      else if (sysState == READY)
        ledReady();
    }

    void busy(){
      digitalWrite(ledPin, HIGH );
      sysState = BUSY;
    }
    void unBusy(){
      digitalWrite(ledPin, LOW );
      sysState = READY;
    }

    void hasError(bool isErr){
      hasErr=isErr;
    }

  void loopErrSignal(int num){
    while(true){
      for(int i=0;i<num;i++){
        digitalWrite(ledPin, HIGH );
        delay(100);
        digitalWrite(ledPin, LOW );
        delay(300);
      }
      delay(1000);
    }
  }

  private:

    void ledReady() {
      if (sysClock[SEC] % 3 == 0) {
        if(wakeupPhase == WDT_WAKEUP)
          digitalWrite(ledPin, LOW );
        else 
          digitalWrite(ledPin, HIGH );
      }
    }

    void ledErr() {
      if(wakeupPhase == WDT_WAKEUP)
          digitalWrite(ledPin, LOW );
        else 
          digitalWrite(ledPin, HIGH );
    }
};

Led led;

void setup() {
  Serial.begin(57600);
  Wire.setClock(400000);
  Wire.begin();

  if (isDevExist(RTC_ADDRESS))
    Serial.println("FOUND RTC!");
  else {
    Serial.println("RTC NOT found!");
    led.loopErrSignal(NO_RTC);
  }
  
  // Check is EEPROM exist or not.
  if (isDevExist(EEPROM_ADDRESS))
    Serial.println("FOUND Eeprom!");
  else {
    Serial.println("Eeprom NOT found!");
    led.loopErrSignal(NO_EEPROM);
  }

  if (sht31.begin(DHT31_ADDRESS))// Set to 0x45 for alternate i2c addr
    Serial.println("FOUND SHT31!");
  else {   
    Serial.println("Couldn't find SHT31.");
    led.loopErrSignal(NO_DHT31);
  }
  
  extRom = new ZEeprom();
  extRom->begin(Wire, EEPROM_ADDRESS, AT24C32);

  rtc.setClockMode(false);
  syncRTC();
  Serial << "Get RTC time: " << sysClock[YEAR] << ", " << sysClock[MON] << ", " << sysClock[DATE] << ", " << sysClock[HOUR] << ", " << sysClock[MIN] << ", " << sysClock[SEC] << ", " << endl;
  rtc.enableOscillator(true, false, 0); // Turn on
  rtc.enable32kHz(true); 
  pinMode(CLOCK_INT_PIN, INPUT_PULLUP);
  attachInterrupt(0, clockIsr, FALLING); //assign int0

  BTserial.begin(38400);
  attachInterrupt(1, bluetoothIsr, CHANGE); //assign int0

  recInterval = EEPROM.read(ADDR_REC_INTERVAL);
  if (recInterval == 0xFF) recInterval = 1;
  ThRec.lastRecTime = 0;
  ThRec.lastRecAddr = 0;

  tm1637.init();
  int bright = EEPROM.read(ADDR_BRIGHTNESS);
  if (bright < 8) {
    Serial.print("Get bright from option ROM: ");
    Serial.println(bright);
    clockLedBright = bright;
    tm1637.set(bright);
  }
  else tm1637.set(clockLedBright);
  showClockLed();

}

void clockIsr() {
  clockIntr = true;
  //Serial.println("Clock interrupt!");
}

void bluetoothIsr() {
  bluetoothIntr = true;
  //Serial.println("Bluetooth interrupt!");
}

void loop() {
  //Serial.print("Program alive: ");
  if (clockIntr) maintainClock();
  if (bluetoothIntr) {
    byte pin = digitalRead(BLUETOOTH_STATE_PIN);
    if (pin == HIGH) {
      isBluetoothConnected = true;
      //Serial.println("Bluetooth Connected(state HIGH).");
    }
    else {
      isBluetoothConnected = false;
      //Serial.println("Bluetooth Disconnected(state LOW).");
    }
    bluetoothIntr = false;
  }

  led.loop();
  showClockLed();
  
  if (sysClock[SEC] == 0) {
    // Avoid record many times in one second.
    if (getUnixtimeFromSysClock() >= ThRec.lastRecTime + (recInterval * 10)) {
      recHumidity();
    }
  }

  checkBtCmd();

  if (!isBluetoothConnected) {
    if (wakeupPhase == CLOCK_WAKEUP) {
      //Serial.println("Sleep 500 ms.");
      LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF);
      wakeupPhase = WDT_WAKEUP;
    }
    else {
      //Serial.println("Sleep Forever");
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    }
  }

}

void maintainClock() {
  clockIntr = false;
  upTimer++;
  wakeupPhase = CLOCK_WAKEUP;
  clockSecMillis = millis();

  //Serial << "Clock Sec Millis = " << clockSecMillis << endl;

  sysClock[SEC]++;
  if (sysClock[SEC] < 60)
    return;

  sysClock[SEC] = 0;
  sysClock[MIN]++;

  if (sysClock[MIN] < 60) 
    return;
  
  sysClock[MIN] = 0;
  sysClock[HOUR]++;

  if (sysClock[HOUR] == 24)
    syncRTC();

  //Serial.print("Got a interrupt from clock at ");
  //Serial.println(millis());
}

void recHumidity() {
  float t = sht31.readTemperature();
  float h = sht31.readHumidity();

  if (! isnan(t)) {  // check if 'is not a number'
    //Serial.print("Temp *C = "); Serial.println(t);
    t += 0.5;
  } else {
    //Serial.println("Failed to read temperature");
    t = -1;
  }

  if (! isnan(h)) {  // check if 'is not a number'
    //Serial.print("Hum. % = "); Serial.println(h);
    h += 0.5;
  } else {
    //Serial.println("Failed to read humidity");
    h = -1;
  }
  //Serial.println();

  byte bt = (byte)t;
  byte bh = (byte)h;
  uint16_t innerRomSize = innerRomEndAddr - innerRomStartAddr + 1;
  byte dataLen = (isRecTemp) ? 2 : 1;

  //Serial << "Save data T: " << bt << ", H: " << bh << " to Addr ";
  if (ThRec.lastRecTime == 0 || ThRec.lastRecAddr + dataLen >= extRomSize + innerRomSize) { // if lastRecTime == 0 mean it is first time to record data.
    ThRec.lastRecAddr = 0;
    //Serial << ThRec.lastRecAddr << "(External ROM)" << endl;
    if (isRecHumdt)writeRom(ThRec.lastRecAddr, &bh, 0, 1);
    if (isRecTemp)writeRom(ThRec.lastRecAddr + 1, &bt, 0, 1);
  } else if (ThRec.lastRecAddr + dataLen >= extRomSize) {

    // Write in inner ROM
    ThRec.lastRecAddr += dataLen;
    int innerAddr = ThRec.lastRecAddr - extRomSize + innerRomStartAddr;
    //Serial << ThRec.lastRecAddr << "(Inner: " << innerAddr << ")" << endl;
    if (isRecHumdt)EEPROM.write(innerAddr, bh);
    if (isRecTemp)EEPROM.write(innerAddr + 1, bt);
  } else {
    // Write in external ROM
    ThRec.lastRecAddr += dataLen;
    //Serial << ThRec.lastRecAddr << "(External ROM)" << endl;
    if (isRecHumdt)writeRom(ThRec.lastRecAddr + 1, &bh, 0, 1);
    if (isRecTemp) writeRom(ThRec.lastRecAddr, &bt, 0, 1);
  }
  //DateTime dt = rtcLib.now();
  //ThRec.lastRecTime=dt.unixtime();
  ThRec.lastRecTime = getUnixtimeFromSysClock();
  //Serial << "Time: " << ThRec.lastRecTime <<endl;
}

uint32_t getUnixtimeFromSysClock() {
  uint32_t t;
  uint16_t days = date2days(sysClock[YEAR], sysClock[MON], sysClock[DATE]);
  t = time2long(days, sysClock[HOUR], sysClock[MIN], sysClock[SEC]);
  //t += 946684800; //SECONDS_FROM_1970_TO_2000

  return t;
}
static const uint8_t daysInMonth [] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
  if (y >= 2000)
    y -= 2000;
  uint16_t days = d;
  for (uint8_t i = 1; i < m; ++i)
    days += pgm_read_byte(daysInMonth + i - 1);
  if (m > 2 && y % 4 == 0)
    ++days;
  return days + 365 * y + (y + 3) / 4 - 1;
}

static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
  return ((days * 24L + h) * 60 + m) * 60 + s;
}

void syncRTC() {

  DateTime dt = rtcLib.now();

  sysClock[YEAR] = dt.year() - 2000;
  sysClock[MON] = dt.month();
  sysClock[DATE] = dt.day();
  sysClock[HOUR] = dt.hour();
  sysClock[MIN] = dt.minute();
  sysClock[SEC] = dt.second();
  //Serial << "RTC Unixtime: " << dt.unixtime() <<", sysClock Unixtime: " << getUnixtimeFromSysClock() << endl;
}

void checkBtCmd() {
  if ( ! BTserial.available() || led.getState() == BUSY)return;

  char c = BTserial.read();
  uint32_t startTime=millis();
  
  //Serial.print("Receive command from Bluetooth: ");
  //Serial << c << "(0x" ;
  //Serial.print(c , HEX);
  //Serial.println(')');

  if (c == 'S') {
    bool battery = rtc.oscillatorCheck();
    int temp = (int)(sht31.readTemperature() + 0.5);
    int hmd = (int)(sht31.readHumidity() + 0.5);

    //Serial << "Write uptimer: " << upTimer << endl;
    BTserial.write(upTimer >> 24);
    BTserial.write(upTimer >> 16);
    BTserial.write(upTimer >> 8);
    BTserial.write(upTimer);
    BTserial.write(battery);
    BTserial.write(recInterval);
    BTserial.write(ThRec.lastRecAddr >> 8);
    BTserial.write(ThRec.lastRecAddr);
    BTserial.write(temp);
    BTserial.write(hmd);
  }
  
  else if (c == 'D') { 
// Data format: dataLen(2byte), recInterval(byte), isIncludeTemp(byte), lastRecAddr(2byte), lastRecTime(4byte), data ...

    uint16_t dataLength = extRomSize + innerRomEndAddr - innerRomStartAddr + 1 + 8; // 8 is meta data length.
    byte isIncludeTemp = ((isRecTemp) ? (byte)1 : (byte)0);
    BTserial.write(dataLength >> 8);
    BTserial.write(dataLength);
    BTserial.write(recInterval);
    BTserial.write(isIncludeTemp);
    BTserial.write(ThRec.lastRecAddr >> 8);
    BTserial.write(ThRec.lastRecAddr);
    BTserial.write(ThRec.lastRecTime >> 24);
    BTserial.write(ThRec.lastRecTime >> 16);
    BTserial.write(ThRec.lastRecTime >> 8);
    BTserial.write(ThRec.lastRecTime);

    //Serial << "Record Interval: " << recInterval << endl;
    //Serial << "Last Record Index: " << ThRec.lastRecAddr << endl;
    //Serial << "Last record time: " << ThRec.lastRecTime << endl;
    led.busy();
    //Serial.println("Dump data from external ROM ...");
    dumpExtRom();
    dumpInnerRom();
    //Serial.println("Dump Finish.");
    led.unBusy();
  }
  else if (c == 'T') {
    // If Command is set time, then set clock time immediately no matter how system busy.

    int recv[6];
    for (int i = 0; i < 6; i++) {
      while(!BTserial.available()){
        if(millis()-startTime>=4000)return; // If it no data come over 4 second, skip command.
      }
      recv[i] = BTserial.read();
    }
    setClock(recv[0], recv[1], recv[2], recv[3], recv[4], recv[5]);
  }
  else if ( c == 'I') {
    // For setting humidity record interval.
    
    while(!BTserial.available()){
      if(millis()-startTime>=4000)return; // If it no data come over 4 second, skip command.
    }
    int interval = BTserial.read();
    //Serial.print("Get set record interval command: "); Serial.println(interval);

    recInterval = interval;
    EEPROM.write(ADDR_REC_INTERVAL, interval);
    ThRec.lastRecAddr = 0;
  }

  else if ( c == 'B') {
    // For setting brightness of 4 digital time LED.
    
    while(!BTserial.available()){
      if(millis()-startTime>=4000)return; // If it no data come over 4 second, skip command.
    }
    int b = BTserial.read();
    //Serial.print("Get brightness command: ");
    //Serial.println(b, DEC);
    if (b < 8) { // B指令後接收的資料必需<7,最後送出0xFF結束命令。
      //Serial.println("Set value to Clock LED.");
      clockLedBright = b;
      tm1637.set(b);
    }

    else if (b == 127) { // 當送出0x127後將數值存起來
      //Serial.println("Get FINAL brightness command, save value to ROM.");
      int bright = EEPROM.read(ADDR_BRIGHTNESS);
      if (bright == clockLedBright)Serial.println("The value same as value in ROM, skip save.");
      if (bright != clockLedBright) {
        //Serial.print("Write new bright value to ROM: ");
        //Serial.println(clockLedBright);
        EEPROM.write(ADDR_BRIGHTNESS, clockLedBright);
      }
    }

  }
}

void showClockLed() {

  if (!isBluetoothConnected && (wakeupPhase == CLOCK_WAKEUP) && (tm1637._PointFlag == POINT_OFF)
      || isBluetoothConnected && (millis() - clockSecMillis < 500) && (tm1637._PointFlag == POINT_OFF)
     ) {
    //Serial.println("Set number of clock LED.");
    //Serial.println("Colon ON");
    tm1637.point(true);
    tm1637.display(0, sysClock[HOUR] / 10);  // hour
    tm1637.display(1, sysClock[HOUR] % 10);
    tm1637.display(2, sysClock[MIN] / 10); // minutes
    tm1637.display(3, sysClock[MIN] % 10); //
  }


  if (!isBluetoothConnected && (wakeupPhase == WDT_WAKEUP) && (tm1637._PointFlag == POINT_ON)
      || isBluetoothConnected && (millis() - clockSecMillis > 500) && (tm1637._PointFlag == POINT_ON)
     ) {
    //Serial.println("Turn off colon of clock LED.");
    //Serial.println("Colon OFF");
    tm1637.point(false);
    tm1637.display(0, sysClock[HOUR] / 10);  // hour
    tm1637.display(1, sysClock[HOUR] % 10);
    tm1637.display(2, sysClock[MIN] / 10); // minutes
    tm1637.display(3, sysClock[MIN] % 10); //
  }

}

// Set time to RTC DS3231
void setClock(byte year, byte mon, byte dayOfMonth, byte hour, byte min, byte sec) {
  unsigned long startTime = millis();

  uint16_t y = (uint16_t)year;
  //Serial << "y = " << y << endl;
  rtc.setYear(y);        // use the time_t value to ensure correct weekday is set
  rtc.setMonth(mon);
  rtc.setDate(dayOfMonth);
  rtc.setHour(hour);
  rtc.setMinute(min);
  rtc.setSecond(sec);

  unsigned long spendTime = millis() - startTime;

  //Serial.print("RTC set to: ");
  //Serial << y <<":" <<mon <<":"<<dayOfMonth<<"_"<<hour<<":"<<min<<":"<<sec<<endl;
  //Serial << " spend " << spendTime << "ms." << endl;

  syncRTC();
  //rtc.adjust(DateTime(year, mon, dayOfMonth, hour, min, sec));
}

bool isDevExist(int addr) {
  Wire.beginTransmission(addr);
  int result = Wire.endTransmission();
  return (result == 0);
}

void accessEeprom() {
  const byte address = 31;
  //Serial.println("Read byte from EEPROM memory before write...");
  byte data = extRom->readByte(address);

  delay(10);
  extRom->writeByte(address, ((data == 0xFF) ? 0xAA : 0xFF));

  delay(10);
  data = extRom->readByte(address);
}

void dumpExtRom() {
  int c = 0;
  for (unsigned int addr = 0; addr < extRomSize; addr += 32) {
    //Serial.print("Read address ");
    //Serial.println(addr);

    //extRom->readBytes(addr, 32, data);
    readRom(addr, data, 0, 32);

    loop();

    // Write to BTserial.
    if (led.getState() == READY) return; // Abord command.
    BTserial.write(data, 32);
    loop();
    //}
  }
  BTserial.flush();
}

void dumpInnerRom() {
  int addr = 0;
  for (int i = 0; i < 40; i++) {
    for (int j = 0; j < 25; j++) {
      data[j] = EEPROM.read(addr++);
    }
    loop();
    BTserial.write(data, 25);
    loop();
  }
  BTserial.flush();
}

void writeRom(unsigned int address, byte * data, int offset, int n) {
  Wire.beginTransmission(EEPROM_ADDRESS);
  //if (Wire.endTransmission()==0) {
  Wire.beginTransmission(EEPROM_ADDRESS);
  Wire.write(address >> 8);
  Wire.write(address & 0xFF);
  byte *adr = data + offset;
  Wire.write(adr, n);
  Wire.endTransmission();
  delay(10);
}

// Read less then 32byte everytime.
void readRom(unsigned int address, byte * data, int offset, int n) {
  //unsigned int dataAddr=address & (uint32_t)0x00000fff;
  Wire.beginTransmission(EEPROM_ADDRESS);
  if (Wire.endTransmission() == 0) {
    Wire.beginTransmission(EEPROM_ADDRESS);
    Wire.write(address >> 8);
    Wire.write(address & 0xFF);
    if (Wire.endTransmission() == 0) {
      int r = 0;
      Wire.requestFrom(EEPROM_ADDRESS, n);
      while (Wire.available() > 0 && r < n) {
        data[offset + r] = (byte)Wire.read();
        r++;
      }
    }
  }
  else {
    //Serial.print("I2C bus not ready while readRom()!!!");
    led.loopErrSignal(NO_EEPROM);
  }
}

Credits

eyesblue

eyesblue

0 projects • 0 followers

Comments