You can buy a ready-made smartwatch, with many fancy functions but without the possibility to change anything. Or you can buy an steampunk like development Kit Watch and program the functions you are interested in by yourself. On Tindie you can find several ESP based wrist watches and among others the Deauther Watch from DSTIKE. This watch is actually not a watch, but rather designed as a device to scan for WiFi networks, block selected connections, create dozens of networks and confuse WiFi scanners. Spacehuhn developed the software, which contains many interesting functions, but no true time display. One reason for this might be the fact that the device does not contain a Real Time Clock (RTC). This means that a time setting is lost each time the unit is restarted. But the device has a WiFi chip, so with the right code, you can get the time from a time server.
NTPI am not interested in WiFi attacks but in how to implement time server functions. The Network Time Protocol (NTP) is a protocol for network based clock synchronization. The espressive ESP SDK provides functions to access NTP servers and convert times in an easy way. so that you do not need to worry about UDP connections and data exchange. Simply include the library Time.h, define the NTP server name, create a variable for the time from the server, configure the communication and fetch the time:
// library to handle times in seconds, minutes and so on...
#include <Time.h>
// network address of the Time Server
const char* NTP_SERVER = "ch.pool.ntp.org";
// epoch from the NTP Server
time_t NTPTime;
void setup() {
// configure the NTP Server
configTime(0, 0, NTP_SERVER);
// time() fetch the actual time from the NTP server
// and store it as epoch into the variable
time(&NTPTime);
}
In this simple way you get the current time in seconds from the time servers. This value (NTP time stamp) encode the seconds since 1 January 1900 00:00:00 and is also known as "epoch". For example, the value 1587775785 corresponds to the following date: Saturday, 25. April 2020 00:49:45
To convert the epoch value into manageable values, the function localtime_r() is available in the library. This function converts the epoch into a structure so that you can address the minutes, seconds, hours and date values directly:
// the tm structure contains the following data:
//
// int tm_sec; --> 0 .. 59
// int tm_min; --> 0 .. 59
// int tm_hour; --> 0 .. 23
// int tm_mday; --> 1 .. 31
// int tm_mon; --> 0 .. 11 (0 = January)
// int tm_year; --> years since 1900
// int tm_wday; --> 0 .. 6 (0 = Sunday)
// int tm_yday; --> 0 .. 365
// int tm_isdst; --> Daylight Saving Time flag
//
tm dateTime;
void loop() {
// time() fetch the actual time from the NTP server
// and store it as epoch into the variable
time(&NTPTime);
// localtime_r() converts the epoch into the tm-structure
localtime_r(&NTPTime, &dateTime);
Serial.print(dateTime.tm_hour);
Serial.print(':');
Serial.print(dateTime.tm_min);
Serial.print(':');
Serial.println(dateTime.tm_sec);
delay(1000);
}
Be careful with the values of month (tm_mon) and day of the week (tm_wday): They are starting with 0 not with 1! To get the right year value, you simply need to add 1900 the the value tm_year. A very good video about all this has been made by G6EJD and Andreas Spiess.
The WatchTo keep the code simple, clean and readable, I wrote a class for the watch that encapsulates the LEDs, the buttons and the display. The Watch-object need to be initialized at the beginning. Then you can easily access the LEDs, the display or the buttons without any further initialization.
The library requires the following libraries:
/****** Neopixel ******/
// library to control the WS2812B Neopixel LED
#include <Adafruit_NeoPixel.h>
// install:
// pio lib install "Adafruit NeoPixel"
/****** OLED display ******/
// "ESP8266 and ESP32 OLED driver for SSD1306 displays"
// Display type: SH1106 1.3" OLED display I2C
// Resolution: 128 x 64 Pixel
#include "SH1106Wire.h"
// install:
// pio lib install 2978
Ensure that they are installed. The easiest way is to enter the lib dependencies in the Platformio.ini file:
[env:esp07]
platform = espressif8266
board = nodemcuv2
framework = arduino
; Adafruit NeoPixel
lib_deps = 28
; ESP8266 and ESP32 OLED driver for SSD1306 displays
lib_deps = 2978
; Custom Serial Monitor speed (baud rate)
monitor_speed = 115200
One can also see that I chose the standard espressif8266 platform and nodemcuv2 as board definition, which made the upload work fine without any further adaptations.
Time driftSince the watch does not have a Real Time Clock (RTC), the processor clock must be used to calculate the time. The sysTime_now() function returns the seconds that have passed since the system has been started. This value is also an epoch and can therefore be converted with the function localtime_r(). But that's not all: You can also simply add an epoch to it and get a new time value. This is exactly what I do in the code to synchronize the system time with the NTP time. At startup the system time is zero, i.e. 01.01.1970. At the push of the navigation button downwards, the current time information is retrieved from the time server. This information is then added to the system time. Thus the system time has the current time and with each call of the function sysTime_now() the actual time is counted up. Pressing the button upwards again retrieves the current time value from the time server. But now the current system time value is only compared with the time server value. The time itself is not changed. If the processor clock is very accurate, then there will be no measurable deviation even over a longer period of time. However, this is not to be expected, so that depending on the temperature a deviation is displayed after some time.
Time zoneThe return value of the time server is always in UTC. But there is a pleasantly simple method to automatically convert the time to a desired time zone: You simply specify the time zone by setting the TZ
environment variable:
const char* TZ_INFO = "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00";
void setup() {
// configure the NTP Server
configTime(0, 0, NTP_SERVER);
// define the timezone (POSIX)
setenv("TZ", TZ_INFO, 1);
...
}
Now, whenever you call the localtime_r() function, the values are automatically converted regarding the configured time zone. The Timezone String looks strange, but there is a list of Timezone Strings here to simply copy the one you need. And the best thing is that daylight saving time is defined in the string too, so that the switchover also happens automatically. The only thing to take care of is that the battery is always charged. Depending on the usage it might lasts about 7 hours or so....
Other versionsThe firmware works "out of the box" also with the older version of the watch and with the "DSTIKE WiFi Deauther OLED V6" board:
With the latest version of the display driver, the screen no longer works. It remains black. This will surely be fixed soon, but until then, version 4.1.0 should be used.
For this, the lib-deps in the platformio.ini file must be edited:
lib_deps =
28 ; Adafruit NeoPixel
2978@4.1.0 ; ESP8266 and ESP32 OLED driver for SSD1306 displays
Then with these commands
pio lib uninstall 2978
pio lib install 2978@4.1.0
the library can be uninstalled and reinstalled with the older version. Now the display should work again.
FeedbackI hope this code can prove to be useful to the wider community. Feel free to message me here if you have questions or comments.
Enjoy!
Regards,
Hans-Günther
Comments