Wiznet makers

nj_india

Published October 31, 2022 © MIT license (MIT)

0 UCC

0 VAR

2 Contests

0 Followers

0 Following

Enchanted WiFi Weather Clock

WIZFI360 Based weather clock that uses ntp server for time and realtime/predicted weather displayed on Enchanted clock using led and speaker

COMPONENTS Hardware components

WIZnet - WizFi360-EVB-Pico

x 1

Wizfi360 EVB PICO with onboard wizfi360 and rp2040 mcu

Software Apps and online services

Arduino - Arduino IDE

x 1

Arduino ide and rp2040 package


WIZnet - WIZnet WizFi360-EVB-Pico-C

x 1

arduino library for wizfi360 evb pico


PROJECT DESCRIPTION

Enchanted Wifi Weather Clock is made using WizFi360 EVB Pico

Led color mapping and push button function mapping

BLUE LED - HOUR

RED LED - MINUTE

GREEN LED - SECOND

BUTTON W - PLAYS WEATHER FORECAST ON SPEAKER

BUTTON T - PLAYS CURRENT TIME ON SPEAKER 

 

Features

Update Weather every 15 minutes using API of openweathermap.org

sync with network time every 1 hour using API of worldtimeapi.org

Timer interrupt used so time keeping and Neopixel update runs in background and no other task interrupt it so no need to use dedicated RTC Hardware for time keeping

button press can play current time / weather when required

Automatically play weather condition every 15 minutes 

Automatically play time on speaker every hour'

Play time in 24 hour time format on Speaker default press both button at same time makes it 12 hour format

As flexible NEOPIXEL LED Strip used to display enchanted time, It can be fixed on wall in any shape Circle, oval, square, heart shape etc

By using neopixel led with different LED distance, smaller to very bigger clock can be made

As this project made in limited time taken out of my free time will add more function in future like play date and day with time, automatic LED brightness adjustment based on ambient light by feedback from external LDR sensor, find more intuitive and simple way to update weather/weather forecast on same led ring etc

 

Hardware setup

 

Modules used in project

Wizfi360 EVB PICO

DF PLAYER MINI MP3 PLAYER

60 PIXEL 2 METER NEOPIXEL STRIPE

2W - 4W SPEAKER

 

Hardware Connection 

 

Arduino Code for Wizfi360-evb-pico

Serial Terminal log showing setting up wifi and network time and weather fetched from server using api 

one can just copy paste this code in arduino ide or download from attachment, DF Mini mp3 player functions are included in code so no library needed for DF Mini Mp3 player, All MP3 files are also in attachment.

Rest API from openweathermap.org with Json response used to get current weather and forecast - https://api.openweathermap.org/data/2.5/weather?lat=23.2167&lon=72.6833&appid=cd6370f2d6a5912dbb5b2bae7594c0.

find above line and change latitude longitude and API key before use, to get API key create account with openweathermap.org

Rest API from worldtimeapi.org with Json response used to get current time - http://worldtimeapi.org/api/timezone/Asia/Kolkata

find above line and change timezone before use.

one need to change atleast wifi credential and api key of openweathermap before uploading this code 

One need to install Raspberry Pi Pico Board package for Arduino from https://github.com/earlephilhower/arduino-pico

one also need to install following library to make this code work

  1. Wizfi360
  2. ADAFRUIT NEO PIXEL
  3. ARDUINO JSON
  4. RASPBERRY PI PICO TIMER INTERRUPT

Weather forecast and current weather derived from icon id and correspondingly MP3 file play weather report description. mapping of icon ID to description in next pic,

 

 

SD Card Setup

All MP3 Files used in project attached at last of this blog, Download it unzip it and use attached python file to copy mp3 files to SD card one by one automatically with small delay.

Note : normal copy paste  can not work as it messed up with DF player mini's file indexing and it will play wrong file randomly so use this python file and change source destination path to copy paste mp3 files to sd card root one by one automatically.

 

Arduino Code

// WizFi360 EVB PICO Based Enchanted WiFi Weather Clock
// Developer - N.J. Chhasatia
// Wiznet Contest project 2022

#include <ArduinoJson.h>
#include "WizFi360.h"
#include "RPi_Pico_TimerInterrupt.h"
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN   28 // pico pin
#define PIXEL_NUM   60  // NeoPixel clock size
#define MP3_TXPIN 12
#define MP3_RXPIN 13
#define PB_TIME   0 // Push button for play time
#define PB_WETH   1 // Push button for play weather

// MP3 number to name mapping
#define NWCH 102
#define NWOK 101
#define TMOK 113
#define HOUR 111
#define MINT 110
#define TIME 105
#define TEMP 108
#define HUMD 107
#define TMUN 104
#define HDUN 103
#define WLCM 112
#define WETH 106
#define FCST 109

#define BRIGHTNESS 90
uint8_t mp3_cmd[] = {0x7E,0xFF,0x06,0x03,0x00,0x00,0x03,0xFE,0xF5,0xEF};
uint8_t mp3_cmd_vol[] = {0x7E,0xFF,0x06,0x06,0x01,0x00,0x15,0xFE,0xDF,0xEF};

int status = WL_IDLE_STATUS;  // the Wifi radio's status

static const char* ssid     = "LAPTOP_NJC"; // Edit AP Credential
static const char* password = "password";
unsigned long epoche = 1666081881;
unsigned long premillis = 0;
static uint8_t sec;
static uint8_t mint;
static uint8_t hor;
static uint8_t hor_12;
static uint8_t flasher = 0;
float main_temp = 0;
int main_pressure = 0;
int main_humidity = 0;
uint8_t time_flag = 0; 
uint8_t cweather_flag = 0; 
 
StaticJsonDocument<16> filter_time;
StaticJsonDocument<250> filter_weather;

StaticJsonDocument<48> doc_time;
StaticJsonDocument<250> doc_weather;

WiFiClient client;
RPI_PICO_Timer ITimer0(0);
Adafruit_NeoPixel pixels(PIXEL_NUM, PIXEL_PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  Serial.begin(115200); // Initialize Serial port
  pinMode(PB_TIME,INPUT_PULLUP);
  pinMode(PB_WETH,INPUT_PULLUP);
  init_mp3_player();  // Initlize MP3 Player
  setup_neopixel(); // Initlize NeoPixel strip object (REQUIRED)
  play_mp3(WLCM); // Play welcome msg
  delay(1000);
  play_mp3(NWCH); // Play connecting to AP
  setup_wifi();   // Setup Wifi 
  delay(1000);
  play_mp3(NWOK); // Play connected to AP
  
  while(time_flag == 0){check_time_nw();neopixel_wave();} // Wait until Time available
  setup_timer();
  delay(20);
  while(cweather_flag == 0){check_current_weather_nw();delay(5000);}
  play_mp3(TMOK); // Play Time and weather available
  delay(2000);
  play_mp3_many(TIME,hor,HOUR,mint,MINT,0); // Play Time
  delay(2000);
  play_mp3_many(FCST,TEMP,(int)main_temp-273,TMUN,HUMD,(int)main_humidity); // Play forcast
  play_mp3_many(HDUN,WETH,0,0,0,0); // Play forcast
}

void loop() {
  if(mint%60 == 0 && sec == 0)
  {
    play_mp3_many(TIME,hor,HOUR,mint,MINT,0); // Play Time
    delay(100);
    check_time_nw();
    delay(100);
  }
  if(mint%20 == 0 && sec == 0)
  {
    delay(100);
    check_current_weather_nw();
    delay(100);
  }
  if(digitalRead(PB_TIME) == 0)
  {
    play_mp3_many(TIME,hor,HOUR,mint,MINT,0); // Play Time
  }
  if(digitalRead(PB_WETH) == 0)
  {
    play_mp3_many(FCST,TEMP,(int)main_temp-273,TMUN,HUMD,(int)main_humidity); // Play forcast
    play_mp3_many(HDUN,WETH,0,0,0,0); // Play forcast
  }
  //Debug Messages print time  
  //Serial.print(hor);Serial.print(":");
  //Serial.print(mint);Serial.print(":");
  //Serial.println(sec);
  delay(10);
}

// ================ convert unix to local time ===========
void epoche2dt(unsigned long ep)
{
  sec = ep%60;
  ep = ep/60;
  mint = ep%60;
  ep = ep/60;
  hor = ep%24;
  epoche = ep/24;
  Serial.print("Network_Time = ");
  Serial.print(hor);Serial.print(":");
  Serial.print(mint);Serial.print(":");
  Serial.println(sec);
}

// ================ Setup Wifi connection ===========
void setup_wifi(void)
{
  Serial2.setTX(4);
  Serial2.setRX(5);
  Serial2.begin(115200);
  // initialize WizFi360 module
  WiFi.init(&Serial2);
  WiFi.begin(ssid, password);
  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, password);
  }
  // you're connected now, so print out the data
  Serial.println("You're connected to the network");
}

// ================ Update latest time from network and convert unix to local time ===========
void check_time_nw(void)
{
  client.setTimeout(15000);
  //http://worldtimeapi.org/api/timezone/Asia/Kolkata
  if (!client.connect("worldtimeapi.org", 80)) {
    Serial.println("Connection failed");
    return;
  }
  Serial.println("Connected!");
  // Send HTTP request
  client.println("GET /api/timezone/Asia/Kolkata HTTP/1.0");
  client.println("Host: arduinojson.org");
  client.println("Connection: close");
  if (client.println() == 0) {
    Serial.println("Failed to send request");
    client.stop();
    return;
  }
  // Check HTTP status
  char status[32] = {0};
  client.readBytesUntil('\r', status, sizeof(status));
  // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
  if (strcmp(status + 9, "200 OK") != 0) {
    Serial.print("Unexpected response: ");
    Serial.println(status);
    client.stop();
    return;
  }
  // Skip HTTP headers
  char endOfHeaders[] = "\r\n\r\n";
  if (!client.find(endOfHeaders)) {
    Serial.println("Invalid response");
    client.stop();
    return;
  }
  // Allocate the JSON document
  // Use https://arduinojson.org/v6/assistant to compute the capacity.  

  filter_time["unixtime"] = true;
  DeserializationError error = deserializeJson(doc_time, client, DeserializationOption::Filter(filter_time));
  if (error) {
    Serial.print("deserializeJson() failed: ");
    return;
  }
  time_flag = 1;
  epoche = doc_time["unixtime"];
  client.stop();
  delay(100);
  //client.flush();
  epoche2dt(epoche + 19800);
}

// ================ Update current weather from network  ===========
void check_current_weather_nw(void)
{
  client.setTimeout(10000);
  //https://api.openweathermap.org/data/2.5/weather?lat=23.2167&lon=72.6833&appid=cd6370f2d6a5912dbb5b2bae7594c0
  if (!client.connect("api.openweathermap.org", 80)) {
    Serial.println("Connection failed");
    return;
  }
  Serial.println("Connected!");
  // Send HTTP request
  client.println("GET /data/2.5/weather?lat=23.2167&lon=72.6833&appid=cd6370f2d6a5912dbb5b2bae7594c0 HTTP/1.0");
  client.println("Host: arduinojson.org");
  client.println("Connection: close");
  if (client.println() == 0) {
    Serial.println("Failed to send request");
    client.stop();
    return;
  }
  // Check HTTP status
  char status[32] = {0};
  client.readBytesUntil('\r', status, sizeof(status));
  // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
  if (strcmp(status + 9, "200 OK") != 0) {
    Serial.print("Unexpected response: ");
    Serial.println(status);
    client.stop();
    return;
  }
  // Skip HTTP headers
  char endOfHeaders[] = "\r\n\r\n";
  if (!client.find(endOfHeaders)) {
    Serial.println("Invalid response");
    client.stop();
    return;
  }
  // Allocate the JSON document
  // Use https://arduinojson.org/v6/assistant to compute the capacity.  
  filter_weather["weather"][0]["icon"] = true;
  filter_weather["main"]["temp"] = true;
  filter_weather["main"]["pressure"] = true;
  filter_weather["main"]["humidity"] = true;
  

  DeserializationError error = deserializeJson(doc_weather, client, DeserializationOption::Filter(filter_weather));

  if (error) {
    Serial.print("deserializeJson() failed: ");
    return;
  }

  const char* weather_0_icon = doc_weather["weather"][0]["icon"]; // "01d"
  main_temp = doc_weather["main"]["temp"]; // 309.14
  main_pressure = doc_weather["main"]["pressure"]; // 1009
  main_humidity = doc_weather["main"]["humidity"]; // 34
  client.stop();
  cweather_flag = 1;
  delay(100);
  //client.flush();
  Serial.println("Current Weather : ");
  Serial.print("-->temp = ");Serial.println(main_temp);
  Serial.print("-->humd = ");Serial.println(main_humidity);
  Serial.print("-->pressure = ");Serial.println(main_pressure);
  Serial.print("-->icon = ");Serial.println(weather_0_icon);
}

// ================ ISR Handler for Timer triggered at every 1 second ================
bool TimerHandler0(struct repeating_timer *t)
{ 
  sec=sec+1;
  if(sec>59){sec = 0; mint = mint+1;}
  if(mint>59){mint = 0; hor = hor+1;}
  if(hor>23){hor = 0;}
  if(hor>11)hor_12 = hor-12;
  else hor_12 = hor;
  pixels.clear();
  pixels.setPixelColor(sec, pixels.Color(0, BRIGHTNESS, 0));         //  Set pixel's color (in RAM)
  if((hor_12*5)+(mint/12) == mint)
  {
    if(flasher%2 == 1)pixels.setPixelColor(mint, pixels.Color(BRIGHTNESS, 0, 0)); 
    else pixels.setPixelColor(mint, pixels.Color(0, 0, BRIGHTNESS)); 
    flasher++;   
  }
  else
  {
    pixels.setPixelColor(mint, pixels.Color(BRIGHTNESS, 0, 0));         //  Set pixel's color (in RAM)
    pixels.setPixelColor(((hor_12*5)+(mint/12)), pixels.Color(0, 0, BRIGHTNESS));         //  Set pixel's color (in RAM)
  }
  
  pixels.show();
  return true;
}

// ================ initlize timer 0 pico ================== 
void setup_timer(void)
{
  delay(100);
  Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz"));
  // Interval in microsecs
  if (ITimer0.attachInterruptInterval(1000000, TimerHandler0))
    Serial.print(F("Starting ITimer0 OK"));
  else
    Serial.println(F("Can't set ITimer0. Select another freq. or timer"));
}

// ================ Neo pixel clock ring setup ==================
void setup_neopixel(void)
{
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.clear(); // Set all pixel colors to 'off'
  for(int i=0; i<pixels.numPixels(); i++) { // For each pixel in strip...
    pixels.setPixelColor(i, pixels.Color(0, BRIGHTNESS/2, 0));         //  Set pixel's color (in RAM)
    pixels.show();                          //  Update strip to match
    delay(20);                           //  Pause for a moment
  }
  for(int i=pixels.numPixels()-1; i>=0; i--) { // For each pixel in strip...
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));         //  Set pixel's color (in RAM)
    pixels.show();                          //  Update strip to match
    delay(20);                           //  Pause for a moment
  }
}

// ================ Neo pixel wave red ==================
void neopixel_wave(void)
{
  pixels.clear(); // Set all pixel colors to 'off'
  for(int i=0; i<pixels.numPixels(); i++) { // For each pixel in strip...
    pixels.setPixelColor(i, pixels.Color(BRIGHTNESS/2, 0, 0));         //  Set pixel's color (in RAM)
    pixels.show();                          //  Update strip to match
    delay(20);                           //  Pause for a moment
  }
  for(int i=pixels.numPixels()-1; i>=0; i--) { // For each pixel in strip...
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));         //  Set pixel's color (in RAM)
    pixels.show();                          //  Update strip to match
    delay(20);                           //  Pause for a moment
  }
}

// =========== init MP3 Player ========
void init_mp3_player(void)
{
  Serial1.setTX(MP3_TXPIN);  // TX PIN for Serial1 MP3
  Serial1.setRX(MP3_RXPIN);  // RX PIN for Serial1 MP3
  Serial1.begin(9600);       // MP3
  delay(1000);
  Serial1.write(mp3_cmd_vol,10);
  delay(100);
}

//========== Play Track ==========
void play_mp3(uint8_t track)
{
  uint8_t btx;
  mp3_cmd[6] = track;
  mp3_cmd[8] = 0xF8 - track;
  for(btx=0;btx<=9;btx++)
  {
    Serial1.write(mp3_cmd[btx]);
  }
}

//========== Play multiple Track upto 6 ==========
void play_mp3_many(uint8_t track1,uint8_t track2,uint8_t track3,uint8_t track4,uint8_t track5,uint8_t track6)
{
  char dummy;
  Serial.println("Playing tracks..");
  while(Serial1.available()>0) dummy = Serial1.read();
  if(track1 != 0){play_mp3(track1);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println();
  delay(20);
  if(track2 != 0){play_mp3(track2);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println();
  delay(20);
  if(track3 != 0){play_mp3(track3);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println();
  delay(20);
  if(track4 != 0){play_mp3(track4);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println();
  delay(20);
  if(track5 != 0){play_mp3(track5);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println();
  delay(20);
  if(track6 != 0){play_mp3(track6);
  while(Serial1.available()==0);
  delay(20);
  while(Serial1.available()>0)dummy = Serial1.read();}
  Serial.println("Playing tracks done.");
}

 

 

Documents
  • mp3 files used in project

    All mp3 files used in project

  • python code to copy mp3 files to sd card

    normal copy paste can messed up with DF player mini's file indexing so use this python file and change source destination path to copy paste mp3 files to sd card one by one automatically

  • Wiznet Weather clock code

    Arduino ino code of wiznet weather clock

  • Modules Connections

    Modules connections

Comments Write