2023年10月11日 星期三

ESP32 Weather Clock

Purpose:

利用ESP32和LCD來做一個時間天氣顯示器. 主要是由ESP32連上網路之後抓取NTP的時間跟從openweathermap.org網站上獲取本地的天氣資料, 再將天氣、溫度、濕度、風速、氣壓呈現在LCD顯示器上.

Use ESP32 and LCD to make a time and weather display. After connecting to the Internet, ESP32 grabs the NTP time and obtains local weather data from the openweathermap.org website, and then displays the weather, temperature, humidity, wind speed, and air pressure on the LCD monitor.

20×2 Character LCD

20×2 Character LCD Modes of Operation

There are two ways to interface the LCD diver (controller) IC. You can use the full bus width (8-Bits) for data or alternatively you can use a 4-Bit interface for a reduced pin count needed to control the LCD. Specifically low pin count MCUs need to operate in the 4-Bit mode. And it’s the case for our ESP32 which has limited resources in terms of GPIO pin count.




之前的文章列表(List of previous articles):

Control WS2812B Addressable LEDs with ESP32

ESP32與WS2812的繽紛世界

ESP32溫濕度網路時鐘

ESP32 Temperature and Humidity I2C LCD Display

Circuit:


openweathermap.org
Register your account
login 
Get your API Key for ESP32

YouTubeDemo:

ESP32 Code:

#include <WiFi.h>
#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
//----------------------------------------------------------------------
// LCD I2C位址,默認為0x27或0x3F,依據背板的晶片不同而有差異,16、2為LCD顯示器大小。
//---LCD -----------
//LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 16, 2);
LiquidCrystal LCD(22,23,5,18,19,21);

#define NTP_SERVER     "pool.ntp.org"
#define UTC_OFFSET     0
#define UTC_OFFSET_DST 0
//-------------------------------------------
String openWeatherMapApiKey = "your api key";
// Replace with your country code and city
String city = "Hsinchu";
String countryCode = "TW";
String serverPath = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + countryCode + "&APPID=" + openWeatherMapApiKey;
double temp;
double oldtemp;
double humidity;
double pressure;
double wind;
String jsonBuffer;
String openWeatherData;

// ------ 以下修改成你自己的WiFi帳號密碼 ------
char* ssid = "your ssid";
char* password = "your password";
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t dht22 = 0;
  uint8_t sensor1_Flag = 0;
  uint8_t initial_Flag = 0;
  uint8_t FunctionFlag = 0;
} vFlag;
vFlag *flag_Ptr;
vFlag flag;
//--------- uart structure --------------------------------------
//----------uart--------------
#define LINE_BUFFER_LENGTH 64
typedef struct _vUart
{
  char c;
  int lineIndex = 0;
  int line1Index = 0;
  int BTlineIndex = 0;
  bool lineIsComment;
  bool lineSemiColon;
  //char *line;
  char line[128];
  //char line1[128];
  char BTline[20];
  //char R_line[20];
  //char L_line[20];
  String inputString;
  String BTinputString;
  String S1inputString;
  int V[16];
  char ctemp[30];
  char I2C_Data[80];
  int DC_Spped = 50;
  float Voltage[16];
  int Buffer[128];
  int StartCnt = 0;
  int ReadCnt = 0;
  int sensorValue = 0;
} vUart;
vUart *Uart_Ptr;
vUart Uart;

//---------------------------------------------------------------------------------
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
//----------------------------------------------------------------
TaskHandle_t hled;
TaskHandle_t huart;
//------------------------------------------------------------------------------
void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  xTaskCreatePinnedToCore(
    vUARTTask, "UARTTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &huart //handle
    ,
    0);

  //--------------- create task----------------------------------
  xTaskCreatePinnedToCore(
    vLEDTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------
  //----------------------------------------------------------------------
  //vTaskSuspend(hfunction); //暫停TASK運行
  //----------------------------------------------------------------------
}
void setup()
{
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();
  pinMode(LED_BUILTIN, OUTPUT);
  LCD.begin(16, 2);
  LCD.clear();
  LCD.setCursor(0, 0);
  LCD.print("Connecting to ");
  LCD.setCursor(0, 1);
  LCD.print("WiFi ");

  //WiFi.begin("Wokwi-GUEST", "", 6);
 
WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(250);
    spinner();
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  LCD.clear();
  LCD.setCursor(0, 0);
  LCD.println("Online");
  LCD.setCursor(0, 1);
  LCD.println("Updating time...");
  configTime(8*3600, 0, "pool.ntp.org","time.nist.gov"); // enable NTP for Taipei time
 
}

void loop()
{
  String staticMessage = "LCD Weather clock";
  String scrollingMessage = "Welcome to WinsonDiy!";

  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  LCD.clear();
  while(1)
  {
    printLocalTime();
    delay(200);
    GetWeatherData();
    scrollMessage(1, openWeatherData, 250, 16);
  }
}
//-------------------------------------------
void vUARTTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.print(F("UARTTask at core:"));
  Serial.println(xPortGetCoreID());
  vTaskDelay(100);
  for (;;)
  {
    while (Serial.available() > 0)
    {
      Uart.c = Serial.read();
 
      if ((Uart.c == '\n') || (Uart.c == '\r'))
      { // End of line reached
        if (Uart.lineIndex > 0)
        { // Line is complete. Then execute!
          Uart.line[Uart.lineIndex] = '\0'; // Terminate string
          //Serial.println( F("Debug") );
          //Serial.println( Uart.inputString );
          processCommand(Uart.line); // do something with the command
          Uart.lineIndex = 0;
          Uart.inputString = "";
        }
        else
        {
          // Empty or comment line. Skip block.
        }
        Uart.lineIsComment = false;
        Uart.lineSemiColon = false;
        Serial.println(F("ok>"));
      }
      else
      {
        //Serial.println( c );
        if ((Uart.lineIsComment) || (Uart.lineSemiColon))
        {
          if (Uart.c == ')')
            Uart.lineIsComment = false; // End of comment. Resume line.
        }
        else
        {
          if (Uart.c == '/')
          { // Block delete not supported. Ignore character.
          }
          else if (Uart.c == '~')
          { // Enable comments flag and ignore all characters until ')' or EOL.
            Uart.lineIsComment = true;
          }
          else if (Uart.c == ';')
          {
            Uart.lineSemiColon = true;
          }
          else if (Uart.lineIndex >= LINE_BUFFER_LENGTH - 1)
          {
            Serial.println("ERROR - lineBuffer overflow");
            Uart.lineIsComment = false;
            Uart.lineSemiColon = false;
          }
          else if (Uart.c >= 'a' && Uart.c <= 'z')
          { // Upcase lowercase
            Uart.line[Uart.lineIndex] = Uart.c - 'a' + 'A';
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += (char)(Uart.c - 'a' + 'A');
          }
          else
          {
            Uart.line[Uart.lineIndex] = Uart.c;
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += Uart.c;
          }
        }
      }
    } //while (Serial.available() > 0)
    vTaskDelay(5);
  }
}
//-------------------------------------------------------------------------
static void vLEDTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.println(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
    vTaskDelay(200);
    digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
    vTaskDelay(200);
  }
}
//----------------------------------------
void processCommand(char *data)
{
  int len, xlen, ylen, zlen, alen;
  int tempDIO;
  String stemp;

  len = Uart.inputString.length();
  //---------------------------------------
  if (strstr(data, "VER") != NULL)
  {
    Serial.println(F("ESP32_20231010"));
  }
}
//-----------------------------------------
void printLocalTime()
{
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    LCD.setCursor(0, 1);
    LCD.println("Connection Err");
    return;
  }

  char timeStringBuff[50]; //50 chars should be enough
  strftime(timeStringBuff, sizeof(timeStringBuff), "%d/%m/%Y %H:%M:%S", &timeinfo);
  String asString(timeStringBuff);

  LCD.setCursor(0, 0);
  LCD.print(timeStringBuff);
  //LCD.println(&timeinfo, "%d/%m/%Y");  

}
void GetWeatherData()
{
  jsonBuffer = httpGETRequest(serverPath.c_str());
  //Serial.println(jsonBuffer);
  JSONVar myObject = JSON.parse(jsonBuffer);
  //Serial.print("JSON object = ");
  //Serial.println(myObject);
   
  if (JSON.typeof(myObject) == "undefined")
  {
    Serial.println("Parsing input failed!");
    return;
  }
   
  Serial.print("Temperature: ");
   
  temp= myObject["main"]["temp"];
  if(String(temp) == "nan")
  {
    oldtemp=oldtemp;
  }
  else
  {
    oldtemp=temp-273.15;
  }
  Serial.println(oldtemp);
  openWeatherData="Temperature:"+String(oldtemp);
  pressure= myObject["main"]["pressure"];
  Serial.print("Pressure: ");
  Serial.println(pressure);
  openWeatherData=openWeatherData+" Pressure:"+String(pressure);
  //Serial.println(myObject["main"]["pressure"]);
  humidity= myObject["main"]["humidity"];
  Serial.print("Humidity: ");
  Serial.println(humidity);
  openWeatherData=openWeatherData+" Humidity:"+String(humidity);
  //Serial.println(myObject["main"]["humidity"]);
  Serial.print("Wind Speed: ");
  wind= myObject["wind"]["speed"];
  Serial.println(wind);
  openWeatherData=openWeatherData+" Wind Speed:"+String(wind);
}
//----------------------------------------------
void spinner()
{
  static int8_t counter = 0;
  const char* glyphs = "\xa1\xa5\xdb";
  LCD.setCursor(15, 1);
  LCD.print(glyphs[counter++]);
  if (counter == strlen(glyphs)) {
    counter = 0;
  }
}
//----------------------------------------------
void scrollMessage(int row, String message, int delayTime, int totalColumns)
{
  for (int i=0; i < totalColumns; i++)
  {
    message = " " + message;  
  }
  message = message + " ";
  for (int position = 0; position < message.length(); position++)
  {
printLocalTime();   
LCD.setCursor(0, row);
    LCD.print(message.substring(position, position + totalColumns));
    delay(delayTime);
  }
}
//----------------------------------------------
String httpGETRequest(const char* serverName)
{
  HTTPClient http;
 
  http.begin(serverName);
 
  // Send HTTP POST request
  int httpResponseCode = http.GET();
 
  String payload = "{}";
 
  if (httpResponseCode > 0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}
//-----------------------------------------

沒有留言:

張貼留言