Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
smi1100
Published © GPL3+

Battery operated thermometer with connection to BLYNK

self-sufficient with battery operation and multi-wifi support

IntermediateShowcase (no instructions)477
Battery operated thermometer with connection to BLYNK

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1
WEMOS dual shield
×1
Wemos battery shield
×1
Wemos DS18B20 shield
×1
Battery, 3.7 V
Battery, 3.7 V
×1
e-paper display 1.5
×1
Resistor 100k ohm
Resistor 100k ohm
×1

Software apps and online services

Arduino IDE
Arduino IDE
Blynk
Blynk

Hand tools and fabrication machines

Laser cutter (generic)
Laser cutter (generic)

Story

Read more

Custom parts and enclosures

Box

Inkscape file

Code

Sketch

Arduino
/*
thermometer
  by smi1100 - 08/08/2022
  
PINS:
  Display: 3.3(grey), GND(brown), DIN - D7(blue), CLK - D5(yellow), CS - D8(red), RST - D1(white), Busy - D6(purple) (because of DS18B20 Chip - D2)
  WEMOS: connect D0 with RST -> connect after uploading the program

WEMOS dual shield:
  1 - base shield 1 -> WEMOS
  2 - base shield 2 -> DS18B20 -> battery shield

battery:
  3.7 V 1.100 mAh - voltage between 3.2 and 4.2 V

soldering:
  100k ohm resistor between + and A0 on the battery shield

library:
  GxEPD2 from Zingg

fonts:
  https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts

convert image:
  http://javl.github.io/image2cpp/ - settings: 30x30, transparent, invert
*/

// voltage measurement

int volt_raw=0;
float volt=0.0;
float volt_percent=0;
int volt_percent_width=0;
float volt_min = 3.2;                 // adjust after replacing the battery          
float volt_max = 4.204;               // adjust after replacing the battery
float volt_critical = 3.76;           // below this, measurement data are incorrect
String Controller_Status;             // Status could be "battery loading, battery operation, low battery"

// EEPROM

#include <EEPROM.h>
float t_min = 30;
float t_min_address = 0;
float t_min_out;
float t_max = 10;
float t_max_address = 50;
float t_max_out;

// display

#include <GxEPD2_BW.h>                // library for b/w display
GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D1*/ 5, /*BUSY=von D2(4) auf D6(12)*/ 12));

#include <Fonts/FreeMono12pt7b.h>     // include fonts
#include <Fonts/FreeMonoBold12pt7b.h> 
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoBold24pt7b.h>

#include "imagedata.h"                // data for the picture


// Blynk

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "#########";       // WEMOS authentication BLYNK
char* ssid[] = {"#########","#########","#########"};  // MultiWIFI - tries to connect one after the other
char* pass[] = {"#########","#########","#########"};

String wlanname = "";              // connected WIFI -> sent to BLYNK


// DS18B20

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 4              //D2 PIN
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);

float temperature;


// Deep Sleep

int deep_sleep_time = 300;         // duration of deep sleep in sec.


// ++++++++++++++++++++++++++++++++++++ SETUP ++++++++++++++++++++++++++++++++++++

void setup()
{
  Serial.begin(115200);
      
  // voltage measurement

  pinMode(A0, INPUT);

  // EEPROM

  EEPROM.begin(512);
  //EEPROM.put(t_min_address, t_min); // activate on the first run
  //EEPROM.put(t_max_address, t_max); // activate on the first run
  EEPROM.commit();
  
  // display

  display.init();                     // initialize display
  display.setRotation(3);             // rotate display
  display.setTextColor(GxEPD_BLACK);  // choose black colour
  
  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);  // fill background with white colour
    
    display.setFont(&FreeMonoBold12pt7b);                       
    display.setCursor(0, 132); display.print("Batt.");
    
    display.setFont(&FreeMonoBold24pt7b);
    display.setCursor(160, 65); display.print("C");
    display.fillCircle(155, 35, 5, GxEPD_BLACK);
    
    display.drawRect(80, 115, 110, 25, GxEPD_BLACK);
  }
  while (display.nextPage());

    
  // Blynk

  MultyWiFiBlynkBegin();                  //instead Blynk.begin(auth, ssid, pass);
  
  Serial.println("Deep Sleep activated"); Serial.println("");
  
  // DS18B20
  
  sensor.begin();
  Serial.print("number of sensors: ");
  Serial.println(sensor.getDeviceCount()); Serial.println("");

  // Deep Sleep
  
  Serial.setTimeout(2000);          //as long as the serial interface is not ready, do... (nothing)
  while(!Serial) { }
}


void MultyWiFiBlynkBegin()
{
  int ssid_count=0;
  int ssid_mas_size = sizeof(ssid) / sizeof(ssid[0]);
  do
  {
    Serial.println("");
    Serial.println("Trying to connect to wi-fi " + String(ssid[ssid_count]));
    WiFi.begin(ssid[ssid_count], pass[ssid_count]);    
    int WiFi_timeout_count=0;
    while (WiFi.status() != WL_CONNECTED && WiFi_timeout_count<50) { //waiting 10 sec
      delay(200);
      Serial.print(".");
      ++WiFi_timeout_count;
    }
    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println("");
      Serial.println("Connected to wi-fi " + String(ssid[ssid_count]));
      Serial.println("");
      Serial.println("Check connection to the Blynk server");
      Blynk.config(auth);
      Blynk.connect(5000);          //waiting 5 sec
      wlanname = String(ssid[ssid_count]);
    }
    ++ssid_count; 
  }
  while (!Blynk.connected() && ssid_count<ssid_mas_size);
  if (!Blynk.connected() && ssid_count==ssid_mas_size)
  {
    Serial.println("");
    Serial.println("No connection to blynk, still try to connect to wi-fi " + String(ssid[ssid_count-1])); Serial.println("");
    wlanname = "offline";
  }
  updateWLAN(wlanname);
  Serial.println("");
  Blynk.virtualWrite(V3, wlanname);         // send name of connection to BLYNK
}

void updateWLAN(String& str)
{
  int16_t tbx, tby;                                   // Die Variablen tbx, tby, tbw und tbh werden mit den Werten gefllt, die das partielle Fenster einnehmen wird. Die Abkrzung "tb" steht fr "text boundary"
  uint16_t tbw, tbh;
  uint16_t x = 0;                                     // Die Variablen x und y sind der Eckpunkt unseres Ausgabefensters. Hier ist zu beachten, dass es nicht die linke obere Ecke ist, sondern die Grundlinie des Textes
  uint16_t y = 185;
  display.getTextBounds(wlanname, x, y, &tbx, &tby, &tbw, &tbh);  // berechnet die Gre des Fensters
  display.setFont(&FreeMonoBold12pt7b);
  display.setTextColor(GxEPD_BLACK);
  display.setPartialWindow(tbx, tby, tbw, tbh);
  display.firstPage();
  do {
    display.fillScreen(GxEPD_WHITE);
    display.setCursor(x, y);
    display.print(wlanname);
    }
  while (display.nextPage());
}

// ++++++++++++++++++++++++++++++++++++ LOOP ++++++++++++++++++++++++++++++++++++

void loop()
{
  Blynk.run();
  climateRoutine();                               
}

void climateRoutine()
{     
    
// voltage measurement

volt_raw = analogRead(A0);
volt=volt_raw/1023.0*volt_max;
volt_percent=((volt-volt_min)/(volt_max-volt_min)*100);

Serial.print("sensor value: ");
Serial.print(volt_raw);
Serial.print(" | ");
Serial.print("battery voltage: ");
Serial.print(volt);
Serial.print(" | ");
Serial.print("battery voltage in %: ");
Serial.println(volt_percent);
Serial.println("");
Serial.println("calculation:"); Serial.println("");
Serial.print(volt); Serial.print(" - "); Serial.println(volt_min);
Serial.print("------------ * 100 = "); Serial.print(volt_percent); Serial.println(" %");
Serial.print(volt_max); Serial.print(" - "); Serial.println(volt_min); Serial.println("");

// DS18B20

sensor.requestTemperatures();
temperature = sensor.getTempCByIndex(0);
Serial.print("temperature = "); Serial.print(temperature); Serial.println(" C ");

// EEPROM

EEPROM.get(t_min_address, t_min_out);
Serial.print("   old temperature min = "); Serial.print(t_min_out); Serial.println(" C ");
if (temperature < t_min_out)
{
  t_min_out = temperature;
  EEPROM.put(t_min_address, t_min_out);
  Serial.print("      -> new temperature min = "); Serial.println(temperature); Serial.println("");
}
else Serial.println("     -> NO new temperature min"); Serial.println("");
// EEPROM.end();

EEPROM.get(t_max_address, t_max_out);
Serial.print("   old temperature max = "); Serial.print(t_max_out); Serial.println(" C ");
if (temperature > t_max_out)
{
  t_max_out = temperature;
  EEPROM.put(t_max_address, t_max_out);
  Serial.print("     -> new temperature max = "); Serial.println(temperature); Serial.println("");
}
else Serial.println("     -> NO new temperature max"); Serial.println("");
EEPROM.end();

updateTemp(temperature);
updateTemp(temperature);                        // twice, otherwise the display will not be correct at the beginning (cuts off the window)

if (volt_percent < 20)
{
  updateVoltage_alarm();
  updateVoltage_alarm();
}
else
{
  updateVoltage_percent(volt_percent);
  updateVoltage_percent(volt_percent);
}

// Blynk

if (volt < volt_critical)        // dont transmit temperature data if voltage is lower than 3.76 Volt -> incorrect data
{                                                   
  Controller_Status = "***** low battery *****";
    Blynk.virtualWrite(V0, temperature);
    Blynk.virtualWrite(V6, t_min_out);
    Blynk.virtualWrite(V7, t_max_out);
}

if (volt >= volt_critical)                             // && volt < volt_max)
{                                                   
  Blynk.virtualWrite(V0, temperature);
  Blynk.virtualWrite(V6, t_min_out);
  Blynk.virtualWrite(V7, t_max_out);
  Controller_Status = "operated by battery";
}

if (volt >= volt_max)               // dont transmit temperature data if voltage is higher than 4.2 Volt -> charging heats up the temperature sensor and leads to incorrect data
{                                                   
  Controller_Status = "operated with the grid - battery loading";
}
Serial.print("Controller Status = "); Serial.println(Controller_Status); Serial.println("");
Blynk.virtualWrite(V1, volt);
Blynk.virtualWrite(V2, volt_percent);
Blynk.virtualWrite(V4, deep_sleep_time/60);
Blynk.virtualWrite(V5, Controller_Status);
Serial.println("Daten an Blynk gesendet"); Serial.println("");


// display

display.hibernate();


// Deep Sleep
  
Serial.println("Initiate deep sleep"); Serial.println("");
ESP.deepSleep(deep_sleep_time*1000*1000);
delay(100);
}

void updateTemp(float temperature)
{
  char temp_string[] = {'0', '0', '\0'};              // Es wird ein character-Array erzeugt, das mit 0 befllt wird
  int16_t tbx, tby;                                   // Die Variablen tbx, tby, tbw und tbh werden mit den Werten gefllt, die das partielle Fenster einnehmen wird. Die Abkrzung "tb" steht fr "text boundary"
  uint16_t tbw, tbh;
 
  dtostrf(temperature, 4, 1, temp_string);            // Dann werden die bergebenen Sensordaten in das Array geschrieben. Dabei werden die Ziffern in char gewandelt.
                                                      // Die Funktion dtostrf() zum Konvertieren kann auch Nachkommastellen in das Array schreiben.
                                                      // temperature ist input (float), temp_string ist Output (char), 4 = Anzahl aller Stellen inkl. Komma, 0 Anzahl der Stellen nach dem Komma
  uint16_t x = 20;                                    // Die Variablen x und y sind der Eckpunkt unseres Ausgabefensters. Hier ist zu beachten, dass es nicht die linke obere Ecke ist, sondern die Grundlinie des Textes
  uint16_t y = 65;
  display.getTextBounds(temp_string, x, y, &tbx, &tby, &tbw, &tbh);  // berechnet die Gre des Fensters
  display.setFont(&FreeMonoBold24pt7b);
  display.setTextColor(GxEPD_BLACK);
  display.setPartialWindow(tbx, tby, tbw, tbh);
  display.firstPage();
  do {
    display.fillScreen(GxEPD_WHITE);
    display.setCursor(x, y);
    display.print(temp_string);   
    }
  while (display.nextPage());
}


void updateVoltage_percent(float volt_percent)
{
  display.setPartialWindow(85, 120, 100, 15);
  display.firstPage();
  do
  {
    display.fillRect(85, 120, volt_percent, 15, GxEPD_BLACK);
  }
  while (display.nextPage());
}


void updateVoltage_alarm()
{
  display.setPartialWindow(115, 112, 30, 30);
  display.firstPage();
  do
  {
    display.drawBitmap(117, 112, IMAGE_DATA, 30, 30, GxEPD_BLACK); 
  }
  while (display.nextPage());
}

Credits

smi1100
3 projects • 2 followers

Comments