ESP8266/DHT

From Noisebridge
Revision as of 19:27, 7 July 2016 by ESP8266 (talk | contribs) (#0ff)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Humidity/Temperature sensor with SD Card data logger.

Note: Requires modified Nokia library.

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <DHT.h>

#define DHTTYPE DHT11

#define PIN_DHT   1

#define PIN_CE    16  //Pin 2 on LCD
#define PIN_DC    15  //Pin 3 on LCD

#define PIN_PUSH  0  // Push Button Switch

#define PIN_SD_CS 10 // Make sure "DIO" is selected

#define PIN_SOUND 3 // Piezo, overlapped with Serial Rx

//You may find a different size screen, but this one is 84 by 48 pixels
#define LCD_X     84
#define LCD_Y     48

#define LINE_INTERVAL 200
#define PING_INTERVAL 1000

#define MODE_HUMIDITY 16
#define MODE_SOUND 1
#define MODE_PING 2
#define MODE_CARD 3
#define MODE_FILE 4
#define MODE_CONTRAST 5
#define MODE_FIRMWARE 6
#define MODE_INITIALIZE 255

#define SWITCH_DEBOUNCE_TIME 50 // Timeout for debouncing presses
#define SWITCH_MULTI_TIME 300   // Timeout for detecting multiple clicks
#define SWITCH_HOLD_TIME 2000   // Timeout for detecting press and hold

#define SWITCH_PRESS 1
#define SWITCH_RELEASE 2
#define SWITCH_MULTI 4
#define SWITCH_HOLD 8

#define DISPLAY_ENABLED true

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
bool sdEnabled = false;

unsigned long nextLineMillis = 0;
unsigned long nextPing = 0;
unsigned long pingMillis = 0;
unsigned long debounceMillis = 0;
unsigned long doubleMillis = 0;
unsigned long holdMillis = 0;

int lastPing = 0;

bool pushStateOn = false;

uint8_t activeMode = MODE_INITIALIZE;
uint8_t clickCount = 0;

WiFiClient client;

#if defined(DISPLAY_ENABLED)
Adafruit_PCD8544 display = Adafruit_PCD8544(PIN_DC, PIN_CE, -1);
#else
Adafruit_PCD8544 display = Adafruit_PCD8544(4, 5, 2, -1);
#endif

bool wifiEnabled = false;

DHT dht(PIN_DHT, DHTTYPE);

void setup() {
  pinMode(PIN_PUSH, INPUT_PULLUP);

  pinMode(PIN_SOUND, OUTPUT);
  
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);

  if (DISPLAY_ENABLED) {
    display.begin();
    display.setContrast(55);

    display.clearDisplay();

    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 0);
    display.println("#0FF DHT");
    display.setTextColor(WHITE, BLACK); // 'inverted' text
    display.setTextSize(2);
    display.println(" void ");
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.println("WiFi: " + String(ssid).substring(0, 7));
    display.display();
  }

  // WiFi.begin(ssid, password);
  WiFi.begin();
  unsigned long timeout = millis() + 10000;
  while (WiFi.status() != WL_CONNECTED){
    digitalWrite(5, HIGH);
    delay(50);
    digitalWrite(5, LOW);
    delay(450);
//    Serial.printf(".\n");
    if (millis() > timeout)
      break;
  }

  wifiEnabled = WiFi.status() == WL_CONNECTED;


  if (wifiEnabled) {
  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  // ArduinoOTA.setHostname("myesp8266");

  // No authentication by default
  // ArduinoOTA.setPassword((const char *)"123");

  ArduinoOTA.onStart([]() {
//    Serial.println("Start");
    if (DISPLAY_ENABLED) {
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(BLACK);
      display.setCursor(0, 0);
      display.println("*OTA Update*");
      display.println("");
      display.setTextSize(2);
      display.display();
    }
  });
  ArduinoOTA.onEnd([]() {
//    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    static uint8_t lastProgress = 0;
    static unsigned long nextTime = 0;
    uint8_t p = (progress / (total / 100));
//    Serial.printf("Progress: %u%%\r", p);
    if (p == 100 || (p != lastProgress && millis() > nextTime)) {
      nextTime = millis() + 100;
      if (p < 100) {
        if (DISPLAY_ENABLED) {
          display.clearDisplay();
          display.setTextSize(1);
          display.setCursor(0, 0);
          display.println("*OTA Update*");
          display.println("");
          display.setTextSize(2);
          display.setCursor(24, 24);
          display.println(String(p) + "%");
          display.display();
        }
        lastProgress = p;
      } else {
//        Serial.println("Start");
        if (DISPLAY_ENABLED) {
          display.clearDisplay();
          display.setTextSize(1);
          display.setTextColor(BLACK);
          display.setCursor(0, 0);
          display.println("*OTA Success*");
          display.println("");
          display.println("rebooting...");
          display.display();
        }
      }
    }
  });
  ArduinoOTA.onError([](ota_error_t error) {
//    Serial.printf("Error[%u]: ", error);
//    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
//    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
//    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
//    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
//    else if (error == OTA_END_ERROR) Serial.println("End Failed");
    if (DISPLAY_ENABLED) {
      display.println("ERROR: " + String(error));
      display.display();
      delay(1000);
    }
  });
  ArduinoOTA.begin();

//  Serial.println("Ready");
//  Serial.print("IP address: ");
//  Serial.println(WiFi.localIP());
  
  if (DISPLAY_ENABLED) {
    display.println(WiFi.localIP());
    display.display();
  }

  for (int i = 0; i < 10; i++) {
    ArduinoOTA.handle();
    delay(100);
    yield();
  }
}

//TODO - change delay to loop funtion

if(!SD.begin(PIN_SD_CS)) {
  //no SD found
  display.clearDisplay();
  display.println("No SD found");
  display.display();
  delay(1000);
  
  } else {
  //SD initialized
  sdEnabled = true;
  }
  dht.begin();

  setMode(MODE_HUMIDITY);
}

void loop() {
  if (wifiEnabled)
    ArduinoOTA.handle();

  unsigned long now = millis();
  bool pushed = !digitalRead(PIN_PUSH);
  uint8_t buttonAction = 0;

  if (client.connected()) {
    bool got = checkGet();
    if (got) {
      lastPing = millis() - pingMillis;
      nextPing = millis() + PING_INTERVAL;
    }
  } else if (millis() > nextPing) {
    sendGet();
  }
  
  // Handle switch state(s)
  if (now > debounceMillis &&  pushed != pushStateOn) {
    // Push switch state change
    debounceMillis = now + SWITCH_DEBOUNCE_TIME;
    if (pushed) {
      // Switch pushed
      pushStateOn = true;
      if (doubleMillis < now) {
        doubleMillis = now + SWITCH_MULTI_TIME;
        clickCount = 1;
        // string2Line("Switch: ON", 4);
        buttonAction = SWITCH_PRESS;
      } else {
        // Multi-click, goto next mode
        setMode(activeMode < 5 ? activeMode + 1 : 0);
        clickCount++;
        // string2Line("Mode: " + String(activeMode), 2);
        // string2Line("Click: " + String(clickCount), 4);
        buttonAction = SWITCH_MULTI;
      }
      holdMillis = now + SWITCH_HOLD_TIME;
    } else {
      // Switch released
      pushStateOn = false;
      // string2Line("Switch: OFF", 4);
      holdMillis = 0;
      buttonAction = SWITCH_RELEASE;
    }
  } else if (pushed && holdMillis > 0 && now > holdMillis) {
    // Long press
    // string2Line("Switch: HOLD", 4);
    holdMillis = 0;
    buttonAction = SWITCH_HOLD;
  }

  if (activeMode == MODE_HUMIDITY) {
    handleHumidity();
  } else if (activeMode == MODE_PING) {
    handlePingMode(buttonAction);
  } else if (activeMode == MODE_CARD) {
    handleCardMode(buttonAction);
  } else if (activeMode == MODE_FILE) {
    handleFileMode(buttonAction);
  } else if (activeMode == MODE_FIRMWARE) {
    handleFirmwareMode(buttonAction);
  } else if (activeMode == MODE_CONTRAST) {
    handleContrastMode();
  } else if (activeMode == MODE_SOUND) {
    handleSoundMode(buttonAction);
  }

  int a = analogRead(A0) / 4;
  if (a > 255)
    a = 255;
  analogWrite(4, a);
  // analogWrite(5, digitalRead(0) ? 0 : 127);

  delay(50);
}

void setMode(uint8_t newMode) {
  if (newMode != activeMode) {

    if (DISPLAY_ENABLED) {
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(BLACK);
      display.setCursor(28, 0);
      display.println("mode:");
      display.setCursor(32, 12);
      display.setTextSize(3);
      display.println(newMode);
      display.display();
      delay(300);
      yield();
    }

    if (newMode == MODE_PING) {
      handlePingMode(0);
    } else if (newMode == MODE_CARD) {
      handleCardMode(SWITCH_HOLD);
    } else if (newMode == MODE_FILE) {
      handleFileMode(SWITCH_PRESS);
    } else if (newMode == MODE_FIRMWARE) {
      handleFirmwareMode(0);
    } else if (newMode == MODE_CONTRAST) {
      handleContrastMode();
    } else if (newMode == MODE_SOUND) {
      handleSoundMode(SWITCH_RELEASE);
    }
    activeMode = newMode;
  } else {
    // Not a new mode selection
  }
}

void handleHumidity() {
  static unsigned long next = 5000;
  unsigned long now = millis();

  if (now > next) {
    next = now + 2000;
    int h, c, f;
    h = dht.readHumidity();
    c = dht.readTemperature();
    f = dht.readTemperature(true);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.setTextSize(2);
    display.println(String(c) + "/" + String(f));
    display.setTextSize(3);
    display.println(String(h) + "%");
    display.display();

    if (sdEnabled) {
      String data = String(millis()) + ",";
      data += String(h) + ",";
      data += String(c) + ",";
      data += String(f);
  
      File dataFile = SD.open("humidity.txt", FILE_WRITE);
  
      if (dataFile) {
        dataFile.println(data);
        dataFile.close();
      }
    }

    digitalWrite(5, HIGH);
    delay(200);
    digitalWrite(5, LOW);
  }
}

void handlePingMode(uint8_t action) {
  if (client.connected()) {
    bool got = checkGet();
    if (got) {
      lastPing = millis() - pingMillis;
      nextPing = millis() + PING_INTERVAL;
//      Serial.println("Ping: " + String(lastPing) + "ms");
    }
  } else if (action == SWITCH_PRESS && millis() > nextPing) {
    // Do new ping on switch press
    sendGet();
  }

  // Check/show signal strength
  if (millis() > nextLineMillis) {
    int signalStrength = WiFi.RSSI() >= -50 ? 100 : 2 * (WiFi.RSSI() + 100);
    String signalBars = " ";

    nextLineMillis = millis() + LINE_INTERVAL;

    if (DISPLAY_ENABLED) {
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(BLACK);
      display.setCursor(0, 0);

      for (int i = 0; i < signalStrength / 10; i++)
        signalBars += "*";
      display.println(signalBars);
      display.println(String(WiFi.RSSI()) + "db/" + String(signalStrength) + "%");
      display.println();
      display.println("Ping: " + String(lastPing) + "ms");
      display.println();
      display.println("#007 " + String(millis() / 1000));

      display.display();
    }
  }
}

void handleCardMode(uint8_t action) {
  if (action == SWITCH_HOLD)
    testSD();
}

void handleFileMode(uint8_t action) {
  if (action == SWITCH_PRESS)
    getNextFile();
}

void handleFirmwareMode(uint8_t action) {
  digitalWrite(PIN_SD_CS, digitalRead(2));
}

void handleContrastMode() {
  static uint8_t contrast = 50;
  uint8_t analog = analogRead(A0) / 8;

  if (!DISPLAY_ENABLED)
    return;

  if (analog != contrast) {
    contrast = analog;
    display.clearDisplay();
    display.setCursor(0, 0);
    display.setTextSize(1);
    display.println("  Contrast:");
    display.setTextSize(3);
    display.println(contrast);
    display.setContrast(contrast);
    display.display();
  }

  yield();
  delay(50);
}

void handleSoundMode(uint8_t action) {
  static bool soundOn = false;
  static int frequency = 1000;
  static int duty = 512;

  bool refresh = action != 0;
  
  if (action == SWITCH_HOLD) {
    // Toggle sound on
    soundOn = !soundOn;
    if (soundOn) {
      analogWrite(PIN_SOUND, duty);
    } else {
      analogWrite(PIN_SOUND, 0);
    }
  } else if (action == SWITCH_PRESS) {
    frequency = analogRead(A0) * 4;
    // Crash if set to 0, espressif PWM defined as 100Hz-1Khz
    if (frequency < 10) frequency = 10;
    analogWriteFreq(frequency);
  }

  if (!digitalRead(2)) {
    // Right switch pressed
    duty = analogRead(A0);
    if (soundOn)
      analogWrite(PIN_SOUND, duty);
    refresh = true;
  }

  if (refresh) {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.setTextSize(1);
    display.println(" Sound: " + String(soundOn ? "ON" : "OFF") + "\r\n");
    display.setTextSize(2);
    display.println((frequency < 100 ? " " : "") + String(frequency) + "Hz");
    display.println("  " + String(int(duty / 10.24)) + "%");
    display.display();
  }

  yield();
  delay(50);
}

void sendGet() {
  pingMillis = millis();

  if (!client.connect("void-local.tablesugar.info", 80)) {
    // Error
    return;
  }

  client.print("GET /none.html HTTP/1.1\r\nHost: void-local.tablesugar.info\r\nConnection: close\r\n\r\n");
}

bool checkGet() {
  if (client.available()) {
    // Got something
    client.stop();
    return true;
  } else if (millis() - pingMillis > 2000) {
    // Timeout
    client.stop();
    return true;
  }
  return false;
}

void testSD() {
//  Serial.print("\nInitializing SD card...");

  if (DISPLAY_ENABLED) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 0);
    display.println("*Testing SD*");
    display.display();
  }

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  // if (!card.init(SPI_HALF_SPEED, PIN_SD_CS)) {
  if (!card.init(400000, PIN_SD_CS)) {
//    Serial.println("initialization failed. Things to check:");
//    Serial.println("* is a card inserted?");
//    Serial.println("* is your wiring correct?");
//    Serial.println("* did you change the PIN_SD_CS pin to match your shield or module?");
    if (DISPLAY_ENABLED) {
      display.println("NO CARD!!!");
      display.display();
    }
    return;
  } else {
//    Serial.println("Wiring is correct and a card is present.");
    // string2Line("Card found", 1);
    nextLineMillis = nextPing = 5000;
  }

  // print the type of card
//  Serial.print("\nCard type: ");
  String cardType;
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      cardType = "SD1";
      break;
    case SD_CARD_TYPE_SD2:
      cardType = "SD2";
      break;
    case SD_CARD_TYPE_SDHC:
      cardType = "SDHC";
      break;
//    default:
//      Serial.println("????");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
//    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
//    Serial.println(cardType);
    if (DISPLAY_ENABLED) {
      display.println("No partition\r\nFAT16/FAT32\r\nfmt needed.");
      display.display();
    }
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
//  Serial.print("\nVolume type is FAT");
//  Serial.println(volume.fatType(), DEC);
//  Serial.println();

//  Serial.println(cardType);

  if (DISPLAY_ENABLED)
    display.println(cardType + "/FAT" + String(volume.fatType()));

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
//  Serial.print("Volume size (bytes): ");
//  Serial.println(volumesize);
//  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
//  Serial.println(volumesize);
//  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
//  Serial.println(volumesize);

  if (volumesize < 1000) {
    if (DISPLAY_ENABLED)
      display.println("Size: " + String(volumesize) + "MB");
  } else {
    if (DISPLAY_ENABLED)
      display.println("Size: " + String(round(volumesize / 100) / 10) + "GB");
  }

  if (DISPLAY_ENABLED)
    display.display();


//  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
}

void getNextFile() {
  static bool inited = false;
  static int fileCount = 0;

//  Serial.println("Get files...");

  if (DISPLAY_ENABLED) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 0);
    display.print("SD: " + String(fileCount) + " ");
    display.display();
  }

  if (!inited && !SD.begin(PIN_SD_CS)) {
//    Serial.println("Initialization failed!");
    if (DISPLAY_ENABLED) {
      display.println("Initializat-\r\nion failed!");
      display.display();
    }
    return;
  }

  inited = true;

  File files = SD.open("/");
  File file;
  unsigned long fileSize;

  int index = 0;
  while (true) {
    file = files.openNextFile();
    if (!file) {
      // end
//      Serial.println("end of files");
      if (DISPLAY_ENABLED)
        display.println("end of files");
        display.display();
      fileCount = 0;
      return;
    }
    if (!file.isDirectory()) {
      if (index == fileCount) {
        // name of next file
//        Serial.print(String(file.name()));
        fileSize = file.size();
        if (fileSize < 1024) {
          if (DISPLAY_ENABLED)
            display.print(String(fileSize) + "Bytes");
        } else if (fileSize < 100000) {
          if (DISPLAY_ENABLED)
            display.print(String(round(file.size() / 1024)) + "KB");
        } else {
          if (DISPLAY_ENABLED)
            display.print(String(round(file.size() / 1024 / 10.24) / 100.0) + "MB");
        }
        if (DISPLAY_ENABLED)
          display.println("\r\n" + String(file.name()));
        fileCount++;
        break;
      }
      index++;
    }
  }

  String fileContent = "";
  while (file.available() && fileContent.length() < 36) {
    fileContent += char(file.read());
  }

  while (fileContent.length() < 64)
    fileContent += " ";

  if (DISPLAY_ENABLED)
    display.println(fileContent);

  display.display();
}