Skip to content

Commit

Permalink
Merge pull request #54 from techniccontroller/main
Browse files Browse the repository at this point in the history
Update with changes from main
  • Loading branch information
techniccontroller authored Feb 23, 2025
2 parents 25500e0 + 9e1364b commit 22ba189
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 73 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/compile_esp8266.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

name: Compile ESP8266 Sketch

on:
push:
pull_request:

jobs:
compile:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Install Arduino CLI
run: |
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
echo "$(pwd)/bin" >> $GITHUB_PATH
- name: Configure Arduino CLI
run: |
cat <<EOF > .cli-config.yml
board_manager:
additional_urls:
- http://arduino.esp8266.com/stable/package_esp8266com_index.json
EOF
arduino-cli config init
arduino-cli core update-index
arduino-cli core install esp8266:esp8266 --config-file .cli-config.yml
- name: Install Required Libraries
run: |
arduino-cli lib install "Adafruit GFX Library"
arduino-cli lib install "Adafruit NeoMatrix"
arduino-cli lib install "Adafruit NeoPixel"
arduino-cli lib install "WiFiManager"
arduino-cli lib install "Adafruit BusIO"
- name: Compile Sketch
run: |
mv secrets_example.h secrets.h
arduino-cli compile -v --fqbn esp8266:esp8266:nodemcuv2 wordclock_esp8266.ino
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
secrets.h
/.vscode
/build
log.txt
log*.txt
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Wordclock 2.0
![compile esp8266 workflow](https://github.com/techniccontroller/wordclock_esp8266/actions/workflows/compile_esp8266.yml/badge.svg?branch=main)

Wordclock 2.0 with ESP8266 and NTP time

Expand All @@ -13,11 +14,22 @@ The code compiles only with one file named *wordclockfunctions.ino*. So please r

Thank you to everyone who provided feedback on adding new languages and testing their accuracy — your efforts have been invaluable in making this project truly inclusive and reliable!

**Special Branches**

We've got some interesting branches in this repo inspired by user feedback. These branches explore unique features and experimental ideas. Some will stay updated with the main branch's features.

- [**hour_animation**](https://github.com/techniccontroller/wordclock_esp8266/tree/hour_animation): This branch replaces the spiral animation with some custom pattern animation defined as x/y coordinate pattern including custom color for each letter. Also, this animation is show ones per hour.
- [**mode_seconds**](https://github.com/techniccontroller/wordclock_esp8266/tree/mode_seconds): This branch adds one additional mode to show the seconds as numbers on the clock. Thanks to [@Bram](https://github.com/BramWerbrouck)
- [**rgbw_leds**](https://github.com/techniccontroller/wordclock_esp8266/tree/rgbw_leds): This branch uses RGBW LEDs instead of RGB LEDs.
- [**static_background_pattern**](https://github.com/techniccontroller/wordclock_esp8266/tree/static_background_pattern): This branch allows to light up specific letters always during clock mode. E.G., to display some special words in another color.



## Features
- 6 modes (Clock, Digital Clock, SPIRAL animation, TETRIS, SNAKE, PONG)
- time update via NTP server
- automatic summer/wintertime change
- automatic timezone selection
- easy WIFI setup with WifiManager
- configurable color
- configurable night mode (start and end time)
Expand Down Expand Up @@ -62,7 +74,9 @@ Please download all these libraries as ZIP from GitHub, and extract them in the
- https://github.com/tzapu/WiFiManager
- https://github.com/adafruit/Adafruit_BusIO

folder structure should look like this:
You can als install these libraries via the library manager in the Arduino IDE.

The folder structure should look like this:

```
MySketchbookLocation
Expand Down
70 changes: 35 additions & 35 deletions multicastUDP_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
multicast_group = '230.120.10.2'
server_address = ('', 8123)

def start(filter = None):

def start(filters=None):
if filters is None:
filters = []

# Create the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

Expand All @@ -19,55 +23,51 @@ def start(filter = None):

print("Start")

# Tell the operating system to add the socket to the multicast group
# on all interfaces.
# Tell the operating system to add the socket to the multicast group on the specified interface
group = socket.inet_aton(multicast_group)
mreq = struct.pack('4s4s', group, socket.inet_aton(MCAST_IF_IP))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

print("Ready")
saveCounter = 0

buffer = queue.Queue(20)
# Initialize buffers and save counters for each filter
buffers = {filter_val: queue.Queue(20) for filter_val in filters}
save_counters = {filter_val: 0 for filter_val in filters}

# Receive/respond loop
while True:
data, address = sock.recvfrom(1024)
data_str = data.decode("utf-8").strip()
if filter is not None and filter not in data_str:
continue
data_str = "[" + str(address[0]) + " - " + datetime.now().strftime('%b-%d-%Y_%H:%M:%S') + "] " + data_str
print(data_str)
buffer.put(data_str)
if buffer.full():
buffer.get()


if "NTP-Update not successful" in data_str or "Start program" in data_str:
f = open("log.txt",'a')
while not buffer.empty():
f.write(buffer.get())
f.write("\n")
f.close()
saveCounter = 20

if saveCounter > 0:
f = open("log.txt",'a')
f.write(data_str)
f.write("\n")
if saveCounter == 1:
f.write("\n")
f.close()
saveCounter -= 1
timestamped_data = f"[{address[0]} - {datetime.now().strftime('%b-%d-%Y_%H:%M:%S')}] {data_str}"

# Check each filter and process data accordingly
for filter_val in filters:
if filter_val in data_str:
print(timestamped_data)
buffers[filter_val].put(timestamped_data)
if buffers[filter_val].full():
buffers[filter_val].get()

# Main
if __name__ == '__main__':
# Save data if specific keywords are found or if save counter is active
if "NTP-Update not successful" in data_str or "Start program" in data_str:
with open(f"log_{filter_val}.txt", 'a') as f:
while not buffers[filter_val].empty():
f.write(buffers[filter_val].get() + "\n")
save_counters[filter_val] = 20 # Start the save counter

# Check if a filter is given
# use filter as argument to filter messages: python3 multicastUDP_receiver.py "filter"
if save_counters[filter_val] > 0:
with open(f"log_{filter_val}.txt", 'a') as f:
f.write(timestamped_data + "\n")
if save_counters[filter_val] == 1:
f.write("\n")
save_counters[filter_val] -= 1


# Main
if __name__ == '__main__':
# Check if filters are given
# Use filters as arguments: python3 multicastUDP_receiver.py "filter1" "filter2" ...
if len(sys.argv) > 1:
start(sys.argv[1])
start(sys.argv[1:])
else:
start()
32 changes: 10 additions & 22 deletions ntp_client_plus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ NTPClientPlus::NTPClientPlus(UDP &udp, const char *poolServerName, int utcx, boo
{
this->_udp = &udp;
this->_utcx = utcx;
this->_timeOffset = this->secondperhour * this->_utcx;
this->_poolServerName = poolServerName;
this->_swChange = _swChange;
}
Expand Down Expand Up @@ -104,18 +103,13 @@ void NTPClientPlus::end()
}

/**
* @brief Setter TimeOffset
* @brief Setter UTC offset
*
* @param timeOffset offset from UTC in seconds
* @param utcOffset offset from UTC in minutes
*/
void NTPClientPlus::setTimeOffset(int timeOffset)
void NTPClientPlus::setUTCOffset(int utcOffset)
{
this->_timeOffset = timeOffset;
}

long NTPClientPlus::getTimeOffset()
{
return this->_timeOffset;
this->_utcx = utcOffset;
}

/**
Expand All @@ -135,9 +129,10 @@ void NTPClientPlus::setPoolServerName(const char *poolServerName)
*/
unsigned long NTPClientPlus::getSecsSince1900() const
{
return this->_timeOffset + // User offset
this->_secsSince1900 + // seconds returned by the NTP server
((millis() - this->_lastUpdate) / 1000); // Time since last update
return this->_utcx * this->secondperminute + // UTC offset
this->_summertime * this->secondperhour + // Summer time offset
this->_secsSince1900 + // seconds returned by the NTP server
((millis() - this->_lastUpdate) / 1000); // Time since last update
}

/**
Expand Down Expand Up @@ -556,18 +551,11 @@ void NTPClientPlus::sendNTPPacket()
*/
void NTPClientPlus::setSummertime(bool summertime)
{
if (summertime)
{
this->_timeOffset = this->secondperhour * (this->_utcx + 1);
}
else
{
this->_timeOffset = this->secondperhour * (this->_utcx);
}
this->_summertime = summertime;
}

/**
* @brief (private) Update Summer/Winter time change
* @brief Update Summer/Winter time change
*
* @returns bool summertime active
*/
Expand Down
7 changes: 3 additions & 4 deletions ntp_client_plus.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class NTPClientPlus{
void setupNTPClient();
int updateNTP();
void end();
void setTimeOffset(int timeOffset);
void setUTCOffset(int utcOffset);
void setPoolServerName(const char* poolServerName);
unsigned long getSecsSince1900() const;
unsigned long getEpochTime() const;
Expand All @@ -36,7 +36,6 @@ class NTPClientPlus{
unsigned int getYear();
bool isLeapYear(unsigned int year);
int getMonth(int dayOfYear);
long getTimeOffset();
bool updateSWChange();


Expand All @@ -47,9 +46,9 @@ class NTPClientPlus{
const char* _poolServerName = "pool.ntp.org"; // Default time server
IPAddress _poolServerIP;
unsigned int _port = NTP_DEFAULT_LOCAL_PORT;
long _timeOffset = 0;
int _utcx = 0;
bool _swChange = 1;
bool _swChange = 1;
bool _summertime = false;

unsigned long _updateInterval = 60000; // In ms

Expand Down
99 changes: 99 additions & 0 deletions timezonefunctions.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <ESP8266HTTPClient.h>
#include "ntp_client_plus.h"
#include "udplogger.h"

int api_offset = 0;
String api_timezone = "";
float api_lat = 0.0;
float api_lon = 0.0;

/**
* @brief Request the timezone and other data from the IP-API
*
* @param logger UDPLogger object to log messages
* @return bool true if the api request was successful
*/
bool requestAPIData(UDPLogger &logger) {
WiFiClient client;
HTTPClient http;
bool res = false;
logger.logString("[HTTP] Requesting timezone from IP-API");
// see API documentation on https://ip-api.com/docs/api:json to see which fields are available
if (http.begin(client, "http://ip-api.com/json/?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,offset,query")) {
int httpCode = http.GET();

if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = http.getString();

api_timezone = getJsonParameterValue(payload, "timezone", true);
logger.logString("[HTTP] Received timezone: " + api_timezone);

String offsetString = getJsonParameterValue(payload, "offset", false);
api_offset = offsetString.toInt() / 60;
logger.logString("[HTTP] Received offset (min): " + String(api_offset));

String latString = getJsonParameterValue(payload, "lat", false);
api_lat = latString.toFloat();
logger.logString("[HTTP] Received latitude: " + String(api_lat));

String lonString = getJsonParameterValue(payload, "lon", false);
api_lon = lonString.toFloat();
logger.logString("[HTTP] Received longitude: " + String(api_lon));

}
}
else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
res = false;
}
http.end(); // Close connection
}
else {
logger.logString("[HTTP] Unable to connect");
res = false;
}
return res;
}

/**
* @brief Get the Json Parameter Value object
*
* @param json
* @param parameter
* @return String
*/
String getJsonParameterValue(String json, String parameter, bool isString) {
String value = "";
if(isString) {
int index = json.indexOf("\"" + parameter + "\":\"");
if (index != -1) {
int start = index + parameter.length() + 4;
int end = json.indexOf("\"", start);
value = json.substring(start, end);
}
}
else {
int index = json.indexOf("\"" + parameter + "\":");
if (index != -1) {
int start = index + parameter.length() + 3;
int end = json.indexOf(",", start);
value = json.substring(start, end);
}
}
return value;
}

/**
* @brief Update the UTC offset from the timezone string obtained from the IP-API
*
* @param logger UDPLogger object to log messages
* @param ntp NTPClientPlus object to set the UTC offset
* @return int
*/
void updateUTCOffsetFromTimezoneAPI(UDPLogger &logger, NTPClientPlus &ntp) {
bool res = requestAPIData(logger);
if (res) {
ntp.setUTCOffset(api_offset);
}
}
Loading

0 comments on commit 22ba189

Please sign in to comment.