Kathleen Rogers is the president and founder of the Earth Day Network. For more than 20 years, she has been working as an environmental attorney and advocate. She also focuses on international and domestic environmental public policy and law.
As Kathleen said, one of the most important reasons for choosing April 22nd, 1970, was because it was a Wednesday, the perfect day to start a manifestation for the environment. That day, 20 million people in the US participated in the first Earth Day, which was so impressive! Since then, every April 22nd more than one billion people around 180 countries worldwide celebrate this day.
Did you know that exists a Climate Clock?
This year, we found out about the Climate Clock Network after researching initiatives seeking to call to action and move humans to be conscious of the Earth's protection. As an overview, their Climate Clocks have a countdown Deadline for the time left to act before global warming increases to 1.5°C more. However, not everything is lost yet, some Lifelines for the world's energy from renewables, land protected by indigenous people and the green climate fund to the date.
Please read this article to know how we are partnering with Climate Clock and the underlying ideas on why to build your Climate Clock powered by WisBlock, like the one you can see in the following image.
- RAK5005-O | WisBlock Base Board
- RAK11200 | ESP32 | Module for WiFi
- RAK1921 | SSD1306 | WisBlock OLED Display
- RAK12002 | RV-3028-C7 | RTC module
- 3D printer
- PLA Filament for 3D printing (red, blue, and green)
- Battery (optional)
- 4x Jumper wires (female to female)
The whole clock is composed of three different sections. The bottom section is where most of the hardware is located. The pyramid-shaped top part with holes to attach the screen, and the earth to put on top.
First, you need to download the three parts from RAKwireless - Thingiverse. The most straightforward model for printing is the bottom of the clock. We will use it as a reference to show the slicing process using Ultimaker Cura. Then, for the other models, we will specify the particular things to consider to achieve the desired result.
Bottom section
Open the WB_CClock_Bottom.stl file by clicking the icon folder on the top-left side. After that, when the model appears on the building plate, click on the Slice option at the bottom-right.
When the slicing process is finished, you get information about the time estimate and the material needed. Then, before saving it into your 3D printer storage, you can also preview it to check how the printing will be performed.
NOTE: We recommend using red filament for the bottom and top sections to highlight the emergency of the Climate Clock deadline. On the other hand, it would be nice to use blue and green filaments for the Earth. If you don’t have the colors we mentioned for printing the parts, you could consider using another filament color and paint it later.
Top section
The process is similar to the one we followed for the clock’s bottom. However, this part requires generating support for the side where the Earth section will be placed, no more, no less. So, load the model from the file WB_CClock_Top.stl, click on the section to select it, and add a support blocker after pressing the key (E). Afterwards, change the size of the block using the options on the left side to cover most of the model without including the peak, as you can see in the image below.
After placing the support blocker, click on the dropdown menu to show the Print settings. Check the Generate Support option within the Support section and set the Support Density to 10.0 %. Finally, click on Slice, open the previewand ensure the support is similar to the one we show in the following image.
When the printing process finishes, remove the support carefully before taking off the whole section from the building plate.
Earth section
You are already familiar with how to slice the models. This time you will use the file WB_CClock_Earth.stl. This one doesn’t need any support or additional considerations regarding the slicing process. Despite this, you will need to pay attention while printing to switch the filament color. You must switch it after finishing the “water layers“ and while starting the “continent layers.“ Look at the image below as a reference for the moment you must pause the printer to change the filament color.
The following pictures show the results we got while printing the models for this tutorial. In our case, the “water layers“ finished when the printing time was 1 hour and 15 minutes.
These are the WisBlock modules that we will use for this IoT hardware project. They consist of a base, a core, a display, an extra and a power module. Before anything else, let’s dig deeper into what each module does and how it works.
RAK5005-O, also known as the WisBlock Base Board
This baseboard provides power and data for all the WisBlock modules. It supports one core module, one module for the compatible I/O slot, up to four modules for sensors and other modules from the slot A to the slot D, one USB port for programming, a 3.7 volts rechargeable battery connector, a 5 volts solar panel connector, and some I2C, UART, GPIOs and analog inputs in the solder contacts.
RAK11200, also known as the WisBlock WiFi Module (ESP32)
This module is based on the famous Espressif ESP32-WROVER. It is a very powerful WiFi-BLE MCU module for many applications, equipped with two CPU cores that can be individually controlled. In this case, it will be responsible for the WiFi communication that allows consulting the external API in the Climate Clock domain for making the calculations for the most important number on Earth, our Deadline.
RAK12002, also known as the WisBlock RTCModule
This RTC (Real-Time Clock ) module is based on the RV-3028-C7 from Micro Crystal and uses an I2C communication interface. It gives you the time (hours, minutes, seconds) and date (years, months, days), vital information for the Climate Clock to be always synchronized.
RAK1921, also known as the WisBlock OLED Display
This OLED Display is perfect for showing information from your WisBlock solutions. It is a 0.96 inch OLED display with 128x64 pixels. For this project, it will show multiple clock frames such as:
- Analog clock
- Digital clock
- Critical time window for the zero emissions (Deadline)
Soldering step
Before connecting, you will need solder pin headers to the RAK1921 and the four holes tagged as J12 in the WisBlock base. Those pins will allow you to connect both using jumper wires.
We used some extra pin headers for the base, but you can also use a similar type that is also included with the RAK1921.
Connections diagram
First, connect the RAK11200 WisBlock Core to the Base Board and place the RTC in slot A. After that, make sure you fix the WisBlock modules using the compatible M1.2x3 screws. To better understand these instructions, refer to the diagram below. We will connect the display later.
Assembling on 3D printed case
First, fix the WisBlock Base Board with the four M2.5x4 screws using the holes on the bottom part.
Connect the four jumper wires to the screen and keep in mind the signals naming. Then, place the screen with the pin headers near the peak and fix it using its screws carefully without straining it.
Put together the screen to the base using the jumper wires, ensuring they match their signal naming. That is VCC↔︎VCC, GND↔︎GND, SCL↔︎SCL, and SDA↔︎SDA. Refer to the connections diagram we shared previously for connecting them properly.
Arduino BSP installation
For this step, you can refer to our Documentation as we need the BSP for the RAK11200 Core. After you have the BSP installed, there are some extra libraries required. Go to Tools > Manage libraries... to open the Library Manager. Then search them using the name and install those we highlight in the following images.
Search for the name “ESP32 OLED SSD1306“ made by ThingPulse.
Search for the name “ArduinoJson“ made by Benoit Blanchon.
Search for the name “Time“ made by Paul Stoffregen.
Search for the name “Melopero_RV3028“ made by Leonardo La Rocca.
Creating the sketch
In this step, you must copy and paste the following code into a new Arduino sketch by clicking on File>New.
NOTE: Every time you create a new sketch, a template with two empty functions calledvoid loop()
andvoid setup()
is created. We have already included them in the code we share with you in this step. Don't forget to remove them before copying and pasting!
// Install https://github.com/PaulStoffregen/Time
#include <TimeLib.h>
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// Include the UI lib
#include "OLEDDisplayUi.h"
// Include custom images
#include "images.h"
// Include the RTC library
#include "Melopero_RV3028.h" //http://librarymanager/All#Melopero_RV3028
// Include the WiFi library
#include "WiFi.h"
#include <ArduinoJson.h> // library
#include <HTTPClient.h>
// Debug output set to 0 to disable app debug output
#ifndef MY_DEBUG
#define MY_DEBUG 0
#endif
#if MY_DEBUG > 0
#define MYLOG(tag, ...) \
do \
{ \
if (tag) \
PRINTF("[%s] ", tag); \
PRINTF(__VA_ARGS__); \
PRINTF("\n"); \
} while (0)
#else
#define MYLOG(...)
#endif
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
OLEDDisplayUi ui ( &display );
// RTC class
Melopero_RV3028 rtc;
struct tm climateClockDeadline;
time_t timeClimateClockDeadline = 1879443963;
int screenW = 128;
int screenH = 64;
int clockCenterX = screenW / 2;
int clockCenterY = (screenH / 2); // top yellow part is 16 px height
int clockRadius = 23;
char wifiSSID[] = "YOUR_WIFI_NAME";
char wifiPassword[] = "YOUR_PASSWORD";
unsigned long previousMillis = 0;
const long interval = 60000; // interval at which to update the values (milliseconds)
// utility function for digital clock display: prints leading 0
String twoDigits(int digits) {
if (digits < 10) {
String i = '0' + String(digits);
return i;
}
else {
return String(digits);
}
}
int LeapYearsBetween(int startYear, int endYear)
{
int leapYears = 0;
int i;
for (i= startYear; i<= endYear; i++)
{
if((i%4==0) && ((i%400==0) || (i%100!=0)))
{
leapYears++;
//Serial.printf("Leap year : %d \n", i);
}
}
//Serial.printf("Leap years : %d \n", leapYears);
return leapYears;
}
void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
}
void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// ui.disableIndicator();
// Draw the clock face
// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
//
//hour ticks
for ( int z = 0; z < 360; z = z + 30 ) {
//Begin at 0° and stop at 360°
float angle = z ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
}
// display second hand
float angle = second() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display minute hand
angle = minute() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display hour hand
angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
}
void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
display->setFont(ArialMT_Plain_24);
display->setTextAlignment(TEXT_ALIGN_CENTER);
char string_to_show[12] = {0};
sprintf(string_to_show, "%02d:%02d:%02d", hourFormat12(),minute(),second());
display->drawString(64 + x, 4 + y, string_to_show);
display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_CENTER);
sprintf(string_to_show, "%02d/%02d/%d", day(),month(),year());
display->drawString(64 + x, 34 + y, string_to_show);
}
void climateClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
char time_to_show[12] = {0};
int current_diff, remaining, years, days, hours, minutes, seconds;
current_diff = timeClimateClockDeadline - now();
years = current_diff / 31536000;
remaining = current_diff % 31536000;
days = remaining / 86400 - LeapYearsBetween(year(), year(timeClimateClockDeadline));
remaining = remaining % 86400;
hours = remaining / 3600;
remaining = remaining % 3600;
minutes = remaining / 60;
seconds = remaining % 60;
//Serial.printf("Deadline : %d years %d days %d hours %d minutes %d seconds \n", years, days, hours, minutes, seconds);
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, 0 + y, "#ClimateClock");
display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_RIGHT);
sprintf(time_to_show, "%d", years);
display->drawString(30 + x, 12 + y, time_to_show);
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(31 + x, 17 + y, "YRS");
display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_RIGHT);
sprintf(time_to_show, "%03d", days);
display->drawString(84 + x, 12 + y, time_to_show);
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(85 + x, 17 + y, "DAYS");
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, 50 + y, "#ActInTime");
display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_CENTER);
//sprintf(rtc_time, "%02d:%02d:%02d", rtc.getHour(),rtc.getMinute(),rtc.getSecond());
sprintf(time_to_show, "%02d:%02d:%02d", hours, minutes, seconds);
display->drawString(64 + x, 32 + y, time_to_show);
}
// This array keeps function pointers to all frames
// frames are the single views that slide in
FrameCallback frames[] = { analogClockFrame, digitalClockFrame, climateClockFrame };
// how many frames are there?
int frameCount = 3;
// Overlays are statically drawn on top of a frame eg. a clock
OverlayCallback overlays[] = { clockOverlay };
int overlaysCount = 1;
bool init_rtc(void) {
MYLOG("RTC", "RAK12002 RTC Initialization");
Wire.begin();
rtc.initI2C(); // First initialize and create the rtc device
rtc.writeToRegister(0x35,0x00);
rtc.writeToRegister(0x37,0xB4); //Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP
rtc.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format
return true;
}
bool getClimateClockData(void) {
Serial.print("Connecting to Wifi");
//WiFi.forceSleepWake();
WiFi.begin(wifiSSID, wifiPassword);
int i = 0;
int wifi_connection_trials = 10;
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
i++;
delay (1250);
if (i >= wifi_connection_trials) {
Serial.printf("\nNot connected to WiFi aftter %d connection trials\n", wifi_connection_trials);
return false;
}
}
if ((WiFi.status() == WL_CONNECTED)) {
HTTPClient http;
Serial.println ("\nGet ClimateClock API\n");
/* The #ClimateClock is offered to the worldwide climate movement as
an internationally coordinated, participatory project. However, if you
are a private firm, big NGO, city government, or major University,
please contact them first, to be officially licensed & authorized.
Get more info at https://climateclock.world/join. */
http.begin ("https://api.climateclock.world/v1/clock");
int httpCode = http.GET();
if (httpCode > 0) {
}
Serial.println ("Statuscode: " + String(httpCode));
delay (100);
const size_t capacity =
3*JSON_ARRAY_SIZE(1) + 2*JSON_ARRAY_SIZE(2) + 7*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) +
2*JSON_OBJECT_SIZE(1) + 4*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 2*JSON_OBJECT_SIZE(4) +
2*JSON_OBJECT_SIZE(5) + 10*JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(7) + 3*JSON_OBJECT_SIZE(12) + 5540;;
DynamicJsonDocument doc(capacity);
String payload = http.getString();
const char* json = payload.c_str();
deserializeJson(doc, json);
JsonObject climateClockAPIData = doc["data"];
char climate_clock_deadline[25] = {0};
char climate_clock_time[25] = {0};
strcpy(climate_clock_deadline, climateClockAPIData["modules"]["carbon_deadline_1"]["timestamp"]);
strcpy(climate_clock_time, climateClockAPIData["retrieval_timestamp"]);
Serial.println(climate_clock_deadline);
Serial.println(climate_clock_time);
int api_year = atoi(climate_clock_time);
int api_month = atoi(climate_clock_time + 5);
int api_day = atoi(climate_clock_time + 8);
int api_hour = atoi(climate_clock_time + 11) - 5;
int api_min = atoi(climate_clock_time + 14);
int api_sec = atoi(climate_clock_time + 17);
if (api_hour < 0) {
api_hour = api_hour + 24;
api_day--;
}
Serial.printf("I got from API %d/%d/%d %02d:%02d:%02d\n",api_year,api_month,api_day,api_hour,api_min,api_sec);
rtc.setTime(api_year, api_month, 6, api_day, api_hour, api_min, api_sec);
setTime(rtc.getHour(), rtc.getMinute(), rtc.getSecond(), rtc.getDate(), rtc.getMonth(), rtc.getYear());
int api_deadline_year = atoi(climate_clock_deadline);
int api_deadline_month = atoi(climate_clock_deadline + 5);
int api_deadline_day = atoi(climate_clock_deadline + 8);
int api_deadline_hour = atoi(climate_clock_deadline + 11) - 5;
int api_deadline_min = atoi(climate_clock_deadline + 14);
int api_deadline_sec = atoi(climate_clock_deadline + 17);
climateClockDeadline.tm_year = api_deadline_year - 1900;
climateClockDeadline.tm_mon = api_deadline_month - 1;
climateClockDeadline.tm_mday = api_deadline_day;
climateClockDeadline.tm_hour = api_deadline_hour;
climateClockDeadline.tm_min = api_deadline_min;
climateClockDeadline.tm_sec = api_deadline_sec;
timeClimateClockDeadline = mktime(&climateClockDeadline);
Serial.printf("Deadline seconds : %d \n", timeClimateClockDeadline);
Serial.printf("Now : %d \n", now());
Serial.printf("Diferencia : %d \n", timeClimateClockDeadline - now());
Serial.printf("Saltos de año fuera de la función : %d \n", LeapYearsBetween(year(), year(timeClimateClockDeadline)));
Serial.printf("Desde funcion year : %d, desde climateClockDeadline.tm_year : %d \n", year(), climateClockDeadline.tm_year + 1900);
http.end();
}
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
return true;
}
void setup() {
Serial.begin(115200);
Serial.println();
// The ESP is capable of rendering 60fps in 80Mhz mode
// but that won't give you much time for anything else
// run it in 160Mhz mode or just set it to 30 fps
ui.setTargetFPS(60);
// Customize the active and inactive symbol
ui.setActiveSymbol(activeSymbol);
ui.setInactiveSymbol(inactiveSymbol);
// You can change this to
// TOP, LEFT, BOTTOM, RIGHT
ui.setIndicatorPosition(LEFT);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
// You can change the transition that is used
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
ui.setFrameAnimation(SLIDE_UP);
// Add frames
ui.setFrames(frames, frameCount);
// Add overlays
ui.setOverlays(overlays, overlaysCount);
ui.setTimePerFrame(10000);
ui.setTimePerTransition(500);
// Initialising the UI will init the display too.
ui.init();
display.flipScreenVertically();
init_rtc();
setTime(rtc.getHour(), rtc.getMinute(), rtc.getSecond(), rtc.getDate(), rtc.getMonth(), rtc.getYear());
if (getClimateClockData()) {
Serial.println("Updated corrrectly");
} else {
Serial.println("WiFi connection not available");
}
}
void loop() {
unsigned long currentMillis = millis();
int remainingTimeBudget = ui.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
delay(remainingTimeBudget);
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
getClimateClockData();
}
}
}
Adding your WiFi credentials
Similar to the way you connect your smartphone or laptop, the Climate Clock needs an Internet connection to get the data to show. So, you need to change these two values with the info of your WiFi network in the previous code before compiling it.
char wifiSSID[] = "YOUR_WIFI_NAME";
char wifiPassword[] = "YOUR_PASSWORD";
To take in mind before and after uploading
In order to upload the firmware to the core, we need to do an extra step. You need to short circuit BOOT0, and GND pins, just like in the next image. Then click on the reset button.
This is needed to upload the code to the core with the following configuration:
- Board: "Wiscore RAK11200 board"
- Upload Speed: "115200"
- Flash Frequency: "80MHZ"
- Flash Mode: "QIO"
- Partition Scheme:"Default 4MB with spiffs(1.2MB APP/1.5MB SPIFFS)"
- Core Debug Level: "No"
After a successful upload, you need to take out the short circuit between the BOOT0 pin and the GND pin and then click on the reset button in order to start the program.
Closing and putting the Earth on top
You’re almost finishing your Climate Clock! If you have a battery, don't forget to connect it by referring to the documentation about the Battery Connection. Then, you can use double-sided tape to place it on the space beside the base. Finally, put the top and bottom sections together.
Last but not least, place the Earth section on top!
After following all these steps, this project is finished with a nice journey of learning something new about Earth Day and the people working behind it. Also, successfully you will have built your own action Climate Clock powered by WisBlock. Climate change is there waiting to be solved, with this project you know what is our Deadline, and take all the necessary actions to mitigate this problem.
The Climate Clock team takes this feeling to action by promoting the building of many clocks around the world. We encourage you to support their campaigns and feel free to donate to them.
Also, follow us on our Hackster Hub and be part of our community to keep updated with more DIY IoT projects and news.
Please share with us, write on your doubts and interact with us in the comment section.
Comments