Final demo of the clock
/* SMART_CLOCK_MASTER BY THOMAS FEMINIER
* This sketch does the following actions :
* - Connects to the server where I installed the API
* - Calls for date, time and weather every now and then
* - Sends the information to the main controller using serial communication
* © Thomas Feminier - Licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
*/
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#include <SPI.h>
const char* ssid = "*******";
const char* password = "*******";
const char* host = "www.tomfem.fr";
byte current_temp;
byte current_icon;
byte date;
byte hour, last_hour;
byte minute, last_minute;
byte second, last_second;
void setup() {
Serial.begin(9600);
delay(100);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
// Serial.print(".");
}
}
int value = 0;
void loop() {
delay(5000);
++value;
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
//Serial.println("connection failed");
return;
}
/*///////////// Action to run every minute //////////////*/
if(last_minute != minute) { // Partie à faire toutes les minutes
///////////// Request for the hour //////////////
String url = "/php/weather.php?param=HOUR";
//Serial.print("Requesting URL: ");
//Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
//Serial.print(line);
hour = line.toInt();
}
// NOUVEAU CODE D'ENVOI DE VALEUR
Serial.write("HOUR");
Serial.write(hour); ////send param to slave
// FIN DU NOUVEAU CODE
last_minute = minute;
}
/*///////////// Action to run every hour //////////////*/
if(last_hour != hour) {
///////////// Request for the date //////////////
String url = "/php/weather.php?param=DATE";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
date = line.toInt();
}
Serial.write("DATE");
Serial.write(date);
///////////// Request for the current temperature //////////////
url = "/php/weather.php?param=CURRENT_TEMP";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
current_temp = line.toInt();
}
Serial.write("CURRENT_TEMP");
Serial.write(current_temp);
///////////// Request for the current icon //////////////
url = "/php/weather.php?param=CURRENT_ICON";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
current_icon = line.toInt(); //// print param to monitor
}
Serial.write("CURRENT_ICON");
Serial.write(current_icon);
last_hour = hour;
}
/*///////////// Action to run every 5 sec //////////////*/
if(last_second - second > 5000) { // Partie à faire toutes les 5 secondes
///////////// Request for the minutes //////////////
String url = "/php/weather.php?param=MINUTE";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
minute = line.toInt();
}
Serial.write("MINUTE");
Serial.write(minute);
///////////// Request for the seconds //////////////
url = "/php/weather.php?param=SECOND";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(2000);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
second = line.toInt();
Serial.write(second); ////send param to slave
}
last_second = second;
}
Serial.println();
Serial.println("closing connection");
}
The second one is uploaded on the Satshakit (or Arduino Uno). It sends the data from the ESP to the Uno which uses it to display the hours on the LED rings and the weather and date on the LED matrix :
/* SMART_CLOCK_SLAVE BY THOMAS FEMINIER
* This sketch does the following actions :
* - Reads the date send through serial by the EPS8266
* - Interprets this data
* - Prints them using the LED rings and the LED matrix
* © Thomas Feminier - Licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
*/
#include <Adafruit_GFX.h>
#include <RGBmatrixPanel.h>
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#define CLK 8 // MUST be on PORTB! (Use pin 11 on Mega)
#define LAT A3
#define OE 9
#define A A0
#define B A1
#define C A2
RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);
#define MIN 11
#define HOURS 10
Adafruit_NeoPixel stripmin = Adafruit_NeoPixel(60, MIN, NEO_GRB + NEO_KHZ800); //defines neopixel ring for min
Adafruit_NeoPixel striphours = Adafruit_NeoPixel(12, HOURS, NEO_GRB + NEO_KHZ800);
uint32_t ledscolor = stripmin.Color(0, 233,255); //defines the default color for the time
uint8_t r=0, g=5, b=7; //defines the default color for the matrix (r,g and b from 0 to 7)
const char current_icon;
const char date;
byte hour;
byte minute;
float current_temp;
// Similar to F(), but for PROGMEM string pointers rather than literals
#define F2(progmem_ptr) (const __FlashStringHelper *)progmem_ptr
//defining all the weather icons
const unsigned char PROGMEM clearday[] =
// CLEAR DAY
{
0x0, 0x8, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x49, 0x0, 0x0, 0x0, 0x1c, 0x0,
0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x7f, 0x40, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x1c,
0x0, 0x0, 0x0, 0x49, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM clearnight[] =
// CLEAR NIGHT
{
0x0, 0x30, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x0, 0xf0, 0x0,
0x0, 0x0, 0xf8, 0x0, 0x0, 0x0, 0xff, 0x80, 0x0, 0x0, 0xff, 0x80, 0x0, 0x0, 0x7f,
0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM rain[] =
// RAIN
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf8, 0x0, 0x0, 0x1, 0xfc,
0x0, 0x0, 0x3, 0xfe, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x1f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x22, 0x48, 0x0, 0x0, 0x66, 0xd8, 0x0, 0x0, 0x4c, 0x90, 0x0,
0x0, 0x8, 0x0, 0x0,
};
const unsigned char PROGMEM snow[] =
// SNOW
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf8, 0x0, 0x0, 0x1, 0xfc,
0x0, 0x0, 0x3, 0xfe, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x1f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x8, 0xa0, 0x0, 0x0, 0x14, 0x44, 0x0, 0x0, 0x8, 0xa, 0x0,
0x0, 0x0, 0x4, 0x0,
};
const unsigned char PROGMEM wind[] =
// WIND
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf8, 0x0, 0xf, 0xf1, 0xfc,
0x0, 0x0, 0x3, 0xfe, 0x0, 0x3f, 0xc7, 0xff, 0x0, 0x0, 0x7, 0xff, 0x0, 0xf, 0x9f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM fog[] =
// fog
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xe0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xfc, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM sleet[] =
// SLEET
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf8, 0x0, 0x0, 0x1, 0xfc,
0x0, 0x0, 0x3, 0xfe, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x1f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x30, 0x6c, 0x0, 0x0, 0x33, 0xc, 0x0,
0x0, 0x3, 0x0, 0x0,
};
const unsigned char PROGMEM cloudy[] =
// CLOUDY
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0xf8, 0x0, 0x0, 0x1, 0xfc,
0x0, 0x0, 0x3, 0xfe, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x7, 0xff, 0x0, 0x0, 0x1f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM partlycloudyday[] =
// SUN AND CLOUD DURING DAY
{
0x0, 0x8, 0x0, 0x0, 0x0, 0x80, 0x70, 0x0, 0x0, 0x48, 0xf8, 0x0, 0x0, 0x1d, 0xfc,
0x0, 0x0, 0x3f, 0xfe, 0x0, 0x1, 0x7f, 0xff, 0x0, 0x0, 0x3f, 0xff, 0x0, 0x0, 0x1f,
0xff, 0x80, 0x0, 0x7f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
const unsigned char PROGMEM partlycloudynight[] =
// SUN AND CLOUD DURING NIGHT
{
0x0, 0x30, 0x0, 0x0, 0x0, 0x70, 0x70, 0x0, 0x0, 0xf0, 0xf8, 0x0, 0x0, 0xf1, 0xfc,
0x0, 0x0, 0xfb, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x7f,
0xff, 0x80, 0x0, 0x3f, 0xff, 0x80, 0x0, 0xff, 0xff, 0x80, 0x1, 0xff, 0xff, 0x80, 0x1,
0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
};
void setup()
{
Serial.begin(9600);
stripmin.begin(); // This initializes the NeoPixel library for the two rings
striphours.begin();
matrix.begin(); // This initializes the led matrix
matrix.setTextWrap(false); // Allow text to run off right edge
matrix.setTextSize(1); // sets text size on the matrix to one (smaller)
}
//// read the value of the params through serial ////
void loop() {
matrix.fillScreen(0);
while(Serial.available() > 0){
//// reads the value through serial and converts to variables
if(Serial.readString() == "HOUR") {
delay(500);
while(Serial.available() > 0){
String line = Serial.readString();
hour = line.toInt();
}
}
else if(Serial.readString() == "MINUTE") {
delay(500);
while(Serial.available() > 0){
String line = Serial.readString();
minute = line.toInt();
}
}
else if(Serial.readString() == "CURRENT_TEMP") {
delay(500);
while(Serial.available() > 0){
String line = Serial.readString();
current_temp = line.toInt();
}
}
else if(Serial.readString() == "DATE") {
delay(500);
while(Serial.available() > 0){
String line = Serial.readString();
date = line.toInt();
}
}
else if(Serial.readString() == "CURRENT_ICON") {
delay(500);
while(Serial.available() > 0){
String line = Serial.readString();
current_icon = line.toInt();
}
}
}
///Define which icon to display according to the value of current_icon
switch (current_icon) {
case 'clear-day':
if (Serial.readString() == "clear-day") {
current_icon = clearday;
}
break;
case 'clear-night':
if (Serial.readString() == "clear-night") {
current_icon = clearnight;
}
break;
case 'rain':
if (Serial.readString() == "rain") {
current_icon = rain;
}
break;
case 'snow':
if (Serial.readString() == "snow") {
current_icon = snow;
}
break;
case 'sleet':
if (Serial.readString() == "sleet") {
current_icon = sleet;
}
break;
case 'fog':
if (Serial.readString() == "fog") {
current_icon = fog;
}
break;
case 'cloudy':
if (Serial.readString() == "cloudy") {
current_icon = cloudy;
}
break;
case 'partly-cloudy-day':
if (Serial.readString() == "partly-cloudy-day") {
current_icon = partlycloudyday;
}
break;
case 'partly-cloudy-night':
if (Serial.readString() == "partly-cloudy-night") {
current_icon = partlycloudynight;
}
break;
default:
current_icon = clearday;
break;
}
//display the hours on striphour
for(int i=0;i<=hour;i++){
striphours.setPixelColor(i,ledscolor);
striphours.show();
}
//display the minutes on stripmin
for(int j=0;j<=minutes;j++){
stripmin.setPixelColor(j,ledscolor);
stripmin.show();
}
//write the date
matrix.setCursor(3,4);
matrix.setTextColor(matrix.Color333(r,g,b));
matrix.print(date);
delay(3000);
matrix.fillScreen(0);
delay(0);
// draw the weather icon
matrix.drawBitmap(0, 0, current_icon, 32, 16, matrix.Color333(r,g,b));
delay (2000);
// clear matrix
matrix.fillScreen(0);
delay (0);
//write the temperature
matrix.setCursor(8, 4);
matrix.setTextColor(matrix.Color333(r,g,b));
matrix.print(current_temp);
matrix.drawCircle(21, 5, 1, matrix.Color333(r,g,b));
delay (2000);
matrix.fillScreen(0);
delay (0);
}
Finally, here is the .php code which talks to the API :
<html><body>
<?php
include('lib/forecast.io.php');
$param = $_GET[param];
$apiKeyParam = "541288a68a7158ad6065fe8c0102123e";
$latParam = "48.868438";
$lonParam = "2.348573";
$units = 'auto'; // Can be set to 'us', 'si', 'ca', 'uk' or 'auto' (see forecast.io API); default is auto
$lang = 'en'; // Can be set to 'en', 'de', 'pl', 'es', 'fr', 'it', 'tet' or 'x-pig-latin' (see forecast.io API); default is 'en'
//error_log(date(DATE_RFC822)." -- api=".$apiKeyParam.",lat=".$latParam.",lon=".$lonParam.",units=".$units.",lang=".$lang."\n", 3, '/home/weather.log');
if($param != "MINUTE" && $param != "HOUR" && $param != "DATE" ) {
$forecast = new ForecastIO($apiKeyParam, $units, $lang);
$condition = $forecast->getCurrentConditions($latParam, $lonParam);
$conditions_week = $forecast->getForecastWeek($latParam, $lonParam);
}
switch($param) {
case "DATE":
echo date('d')." ".date('M');
break;
case "HOUR":
echo date('h');
break;
case "MINUTE":
echo date('i');
break;
case "SECOND":
echo date('s');
break;
case "CURRENT_TEMP":
echo round($condition->getTemperature());
break;
case "CURRENT_ICON":
echo $condition->getIcon();
break;
case "MAX_TEMP_TOMORROW":
echo "MAX_TEMP_TOMORROW=" . round($conditions_week[1]->getMaxTemperature());
//echo round($conditions_week[1]->getMaxTemperature());
break;
case "ICON_TOMORROW":
echo $conditions_week[1]->getIcon();
break;
default:
echo date('d')." ".date('M').":".date('h').":".date('i').":".date('s').":".round($condition->getTemperature()).":".$condition->getIcon();
break;
}
?>
</body>
</html>
You can download those from the sources section.
Achieved | To be improved |
---|---|
Make a translucent wood pannel for the front of my clock Make the wood appear normal when the LEDs are turned off Create fab-made LED rings to fit my project Create a gage effect for the minutes : a continuous circle of light which fills itself up as the time passes Find a way to isolate each pixel of the LED matrix on the front of the clock Create fab-made LED rings to fit my project Make my own microcontroller board to pilot the clock Make the clock easy to read (time-wise) Make my own microcontroller board to pilot the clock Link the clock to Internet - Ongoing process |
Use the same technique for the hour ring as for the minutes one : mill segments of circles rather than connecting small PCBs with wires Add connector pins to the PCBs rather than using wires soldered on the : they keep falling appart Modify the internal structure of the clock to have a better nesting of all the components Improve the code to allow animations on the clock, more visual effects Change the colors according to the temperature Develop an interface to be able to change the settings of the clock : colors, icons, information displayed on it... Add a GPS device, to allow the clock to update the weather information according to the location Improve the powering cable hole in the casing to make it less visible |