chottoshitahanashi’s diary

基本的に間違ったことばかり書いてるブログ。検索ひっかかるな

0.96インチOLEDを加湿器コントロールに追加


いい感じ。表示した時の消費電流が4.6mAとやや大きかった(サーボの非動作時電流3.6mAくらいより大きい)のでon/offスイッチもつけた。
表示を消した時の消費電流は忘れたけど1mAちょい。ついでに今計ったら湿度センサDHT20は0.5mA、ATtiney85は7.3mAくらい。特にスリープとか使ってないんでこんなもんか。本当はもっと省電力にして電池駆動にしたいけど面倒なのでやらない。
ちょっと長いけど全コード。ATtiny85のメモリぎりぎり、printf()はめちゃめちゃメモリ食うことが判明した。

#include <Wire.h>
#include "DHT20.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

#define SERVO_PIN 3
#define CHECK_BUTTON_PIN 4

#define NO_BUTTON_PRESSED 0
#define TOGGLE_OLED_PRESSED 1
#define LEVEL_UP_PRESSED 2
#define LEVEL_DOWN_PRESSED 3

#define MIN_MODE_DURATION 180000 // 3 minutes
#define INTERVAL 2.5f

DHT20 DHT;
int humidTarget = 50;
long modeInTime = -MIN_MODE_DURATION;
bool isTurnedOn = false;
bool isOledOn = false;

#define I2C_ADDRESS 0x3C
#define RST_PIN -1
SSD1306AsciiWire oled;

// Uses CHECK_BUTTON_PIN=4
void adcSettings()
{
  //https://www.hackster.io/Ned_zib/getting-started-with-adc-and-serial-attiny85-0db9f6
  ADMUX =
            (0 << ADLAR) |     // do not left shift result (for 10-bit values)
            (0 << REFS2) |     // Sets ref. voltage to internal VCC, bit 2
            (0 << REFS1) |     // Sets ref. voltage to internal VCC, bit 1
            (0 << REFS0) |     // Sets ref. voltage to internal VCC, bit 0
            (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
            (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
            (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
            (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
  ADCSRA =
            (1 << ADEN)  |     // Enable ADC
            (1 << ADPS2) |     // set prescaler to 64, bit 2
            (1 << ADPS1) |     // set prescaler to 64, bit 1
            (1 << ADPS0);      // set prescaler to 64, bit 0
}

int readAdc()
{
  //https://www.hackster.io/Ned_zib/getting-started-with-adc-and-serial-attiny85-0db9f6
  uint8_t adc_lobyte;
  uint16_t raw_adc;
  int var=0;
  ADCSRA |= (1 << ADSC);           // start ADC measurement
  adc_lobyte = ADCL;               // get the sample value from ADCL
  raw_adc = ADCH<<8 | adc_lobyte;  // add lobyte and hibyte //raw_adc are our measure
  return raw_adc;
}

float getHumidity(float* temperature=NULL)
{
  DHT.readStatus();
  DHT.read();

  if (temperature)
  {
    *temperature = DHT.getTemperature();
  }
  return DHT.getHumidity();
}

float turnOnHumidityValue()
{
  return humidTarget;
}

float turnOffHumidityValue()
{
  return humidTarget + INTERVAL;
}

void moveToHomePosition(int numLoops=7, int delayPeriod=1000)
{
  for (int j = 0; j < numLoops; ++j)
  {
    digitalWrite(SERVO_PIN, HIGH);
    delayMicroseconds(500);
    digitalWrite(SERVO_PIN, LOW);
    delayMicroseconds(20000 - 500);
  }
  delay(delayPeriod);
}

void oledPrint(bool force=false)
{
  if ((!isOledOn) && (!force))
  {
    return;
  }

  oled.setCursor(0, 0);

  float temperature;
  int humidity = (int)getHumidity(&temperature);
  int temp10 = (int)(temperature * 10);

  oled.print(humidity);
  oled.print("% ");

  if (temperature < 10.0f)
  {
    oled.print(" ");
  }
  oled.print((int)temperature);
  oled.print(".");
  oled.print(temp10 % 10);
  oled.println("C");

  oled.println("");

  oled.print("SET ");
  oled.print(humidTarget);
  oled.println("%");

  oled.print(isTurnedOn? "ON " : "OFF");
}

void toggleOled()
{
  oled.clear();
  isOledOn = !isOledOn;
}

void pressSwitch()
{
  for (int j = 0; j < 32; ++j)
  {
    digitalWrite(SERVO_PIN, HIGH);
    delayMicroseconds(2400);
    digitalWrite(SERVO_PIN, LOW);
    delayMicroseconds(20000 - 2400);
  }
  moveToHomePosition();
}

int _checkButtonPress()
{
  int val = readAdc();

  if (val < 250)
  {
    return TOGGLE_OLED_PRESSED;
  }
  else if (val < 500)
  {
    return LEVEL_UP_PRESSED;
  }
  else if (val < 750)
  {
    return LEVEL_DOWN_PRESSED;
  }
  else
  {
    return NO_BUTTON_PRESSED;
  }
}

int checkButtonPress()
{
  int firstCheck = _checkButtonPress();
  delay(5);
  int secondCheck = _checkButtonPress();

  if (firstCheck == secondCheck)
  {
    return firstCheck;
  }
  else
  {
    return NO_BUTTON_PRESSED;
  }
}

void handleButtons()
{
  oledPrint();

  int button = checkButtonPress();

  if (button == NO_BUTTON_PRESSED)
  {
    return;
  }

  if (button == TOGGLE_OLED_PRESSED)
  {
    toggleOled();
    delay(500);
    return;
  }

  bool isFirstPress = true;
  while(button == LEVEL_UP_PRESSED || button == LEVEL_DOWN_PRESSED)
  {
    int newTarget = humidTarget + ((button == LEVEL_UP_PRESSED)? 1 : -1);
    humidTarget = min(max(newTarget, 10), 99);

    oledPrint(true);

    if (isFirstPress)
    {
      delay(500);
      isFirstPress = false;
    }

    button = checkButtonPress();
  }

  if (!isOledOn)
  {
    delay(1000);
    oled.clear();    
  }
}

void turnOnMode()
{
  isTurnedOn = true;
  moveToHomePosition(); // beep;
  pressSwitch(); // turn on;
  modeInTime = millis();

  while(getHumidity() < turnOffHumidityValue() ||
        millis() - modeInTime <= MIN_MODE_DURATION)
  {
    handleButtons();
  }
  moveToHomePosition(); // beep;
  pressSwitch(); // turn off
  isTurnedOn = false;
}

void setup()
{ 
  adcSettings();

  pinMode(SERVO_PIN, OUTPUT);
  pinMode(CHECK_BUTTON_PIN, INPUT_PULLUP);
  pinMode(SDA, INPUT); // LET DHT20 pull up the line
  pinMode(SCL, INPUT); // LET DHT20 pull up the line

  DHT.begin();
  Wire.setClock(400000);
  delay(1000);

  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(System5x7);
  oled.set2X();
  oled.clear();
  oledPrint(true);

  moveToHomePosition();

  // Test servo position
  pressSwitch();
  delay(1000);
  pressSwitch();
  oled.clear();
} 

void loop()
{ 
  float humidity = getHumidity();
  if (humidity < turnOnHumidityValue() &&
      millis() - modeInTime > MIN_MODE_DURATION)
  {
    turnOnMode();
    modeInTime = millis();
  }
  handleButtons();
}