diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | include/README | 39 | ||||
| -rw-r--r-- | include/header.h | 11 | ||||
| -rw-r--r-- | include/user_settings.h | 18 | ||||
| -rw-r--r-- | makefile | 2 | ||||
| -rw-r--r-- | platformio.ini | 26 | ||||
| -rw-r--r-- | src/main.cpp | 235 |
7 files changed, 333 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..602d044 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pio +/include/wifi_ssid_pass.h diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/include/header.h b/include/header.h new file mode 100644 index 0000000..18d92a4 --- /dev/null +++ b/include/header.h @@ -0,0 +1,11 @@ +#define LED_PIN 2 // Pin the LED strip is connected to +#define PHOTORESISTOR_PIN 3 // Used for dimming the LEDs based on light level +#define SEGMENT_COUNT 7 // 7 segment display +#define DIGIT_COUNT 4 // Number of digits 4 for a clock +#define START_OFFSET 1 // First segment flickers, voltage level shifter? +#define LED_COUNT (DIGIT_COUNT * SEGMENT_COUNT) + START_OFFSET +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB +#define SATURATION 255 +#define WIFI_CONN_ANI_TIMEOUT 1000 * 60 // Stop WiFi connection animation after one minute + diff --git a/include/user_settings.h b/include/user_settings.h new file mode 100644 index 0000000..1538f7d --- /dev/null +++ b/include/user_settings.h @@ -0,0 +1,18 @@ +// #define WIFI_SSID "" +// #define WIFI_PASSWORD "" + +// #define NTP_SERVER "pool.ntp.org" // Don't do a D-Link +#define NTP_UPDATE_INTERVAL 60000 + +#ifndef WIFI_SSID +#error You must specify a WiFi SSID above, remember to uncomment the line +#endif + +#ifndef WIFI_PASSWORD +#error You must specify a WiFi password above, remember to uncomment the line +#endif + +#ifndef NTP_SERVER +#error You must specify an NTP server above, remember to uncomment the line +#endif + diff --git a/makefile b/makefile new file mode 100644 index 0000000..7e49d4b --- /dev/null +++ b/makefile @@ -0,0 +1,2 @@ +upload: + pio run --target upload -e esp01 diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..34de014 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,26 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[common] +platform = espressif8266 +framework = arduino +lib_deps = + FastLED@3.4.0 + ESP8266WiFi@1.0 + ezTime@0.8.3 + +[env:nodemcuv2] +extends = common +board = nodemcuv2 + +[env:esp01] +extends = common +board = esp01 +board_build.flash_mode = dout diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a0bbc6f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,235 @@ +// #define DEBUG 1 + +#include <user_settings.h> +#include <header.h> + +#include <FastLED.h> +#include <ESP8266WiFi.h> +#include <ezTime.h> + +// Define the array of leds +CRGB leds[LED_COUNT]; + +Timezone CopenhagenTime; + +int hue = 0; +int max_brightness = 255; +bool on_state[SEGMENT_COUNT * DIGIT_COUNT] = {false}; + +typedef enum : uint8_t { + seg_a = 1<<0, // 00000001 + seg_b = 1<<1, // 00000010 + seg_c = 1<<2, // 00000100 + seg_d = 1<<3, // ... + seg_e = 1<<4, + seg_f = 1<<5, + seg_g = 1<<6 +} segments; + +uint8_t symbols[] = { + /* 0 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f, + /* 1 */ seg_b | seg_c, + /* 2 */ seg_a | seg_b | seg_g | seg_e | seg_d, + /* 3 */ seg_a | seg_b | seg_g | seg_c | seg_d, + /* 4 */ seg_f | seg_g | seg_c | seg_b, + /* 5 */ seg_a | seg_f | seg_g | seg_c | seg_d, + /* 6 */ seg_a | seg_f | seg_g | seg_c | seg_d | seg_e, + /* 7 */ seg_a | seg_b | seg_c, + /* 8 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g, + /* 9 */ seg_g | seg_f | seg_a | seg_b | seg_c | seg_d, + /* ° */ seg_a | seg_b | seg_g | seg_f, + /* C */ seg_a | seg_f | seg_e | seg_d +}; + +uint8_t spin_animation[] = { + seg_a, + seg_b, + seg_c, + seg_d, + seg_e, + seg_f, +}; + + +uint8_t dual_spin_animation[] = { + seg_a | seg_d, + seg_b | seg_e, + seg_c | seg_f, + seg_d | seg_a, + seg_e | seg_b, + seg_f | seg_c, +}; + +void display_symbols(uint8_t *symbols_mask) { + // Fades all at once + for (int brightness = 0; brightness <= max_brightness; brightness ++) { + for (uint8_t digit_index = 0; digit_index < DIGIT_COUNT; digit_index++) { + // Counts up by bitshifting + uint8_t seg_counter = 1; + + for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) { + int digit_offset = digit_index * SEGMENT_COUNT; + int led_raw_index = digit_offset + segment_index; + int led_index = START_OFFSET + led_raw_index; + bool previously_on = on_state[led_raw_index]; + + // Compare the current segment bit with the mask for the symbol + bool turn_on = symbols_mask[digit_index] & seg_counter; + + if (turn_on) { + // Turn on LED + if(previously_on){ + // Previously on, allow hue change + leds[led_index] = CHSV(hue, SATURATION, max_brightness); + } else { + leds[led_index] = CHSV(hue, SATURATION, brightness); + } + } else { + // Turn off LED + if(previously_on) { + leds[led_index] = CHSV(hue, SATURATION, max_brightness - brightness); + } + } + + if (brightness == max_brightness) { + on_state[led_raw_index] = turn_on; + } + + seg_counter = seg_counter<<1; + } + } + FastLED.show(); + delay(2); + } +} + +void display_number(int number) { + uint8_t symbols_mask[DIGIT_COUNT]; + // Start from the last digit + for (int digit_index = DIGIT_COUNT - 1; digit_index >= 0; digit_index--) { + symbols_mask[digit_index] = symbols[number % 10]; + number /= 10; + } + display_symbols(symbols_mask); +} + +void display_symbol(int digit_index, uint8_t symbol) { + // Counts up by bitshifting + uint8_t seg_counter = 1; + for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) { + int offset = digit_index * SEGMENT_COUNT; + int led_index = START_OFFSET + offset + segment_index; + + // Compare the current segment bit with the mask for the symbol + if (symbol & seg_counter) { + // Turn on LED + leds[led_index] = CHSV(hue, SATURATION, max_brightness); + FastLED.show(); + } + + seg_counter = seg_counter<<1; + } +} + +void play_animation(uint8_t *animation, int animation_size, int segment_delay) { + for (int i = 0; i < animation_size; i++){ + for (int digit_index = 0; digit_index < 4; digit_index++) { + display_symbol(digit_index, animation[i]); + } + + hue += 16; + FastLED.show(); + delay(segment_delay); + } +} + +void update_clock() { + // Format the time as a 4-digit integer + int timeInt = CopenhagenTime.dateTime("Hi").toInt(); // Sadly string format + display_number(timeInt); + hue++; +} + +void setup() { + FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, LED_COUNT).setCorrection( TypicalLEDStrip ); + FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, LED_COUNT); // GRB ordering is assumed + + // Turn off all LEDs + FastLED.clear(); + + #ifdef DEBUG + Serial.begin(9600); + #endif + + setServer(NTP_SERVER); + + WiFi.setHostname("7-segment-esp-ws2811"); //define hostname + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + Serial.print("Connecting"); + while (WiFi.status() != WL_CONNECTED) + { + if (millis() < WIFI_CONN_ANI_TIMEOUT) { + play_animation(spin_animation, sizeof(spin_animation), 100); + } else { + // Turn off all LEDs + FastLED.clear(); + } + } + + delay(100); + + hue = 0; + // Turn off all LEDs + FastLED.clear(); + + Serial.println(); + Serial.print("Connected, IP address: "); + Serial.println(WiFi.localIP()); + + waitForSync(); + Serial.println("UTC: " + UTC.dateTime()); + CopenhagenTime.setLocation("Europe/Copenhagen"); + Serial.println("Copenhagen time: " + CopenhagenTime.dateTime()); + + update_clock(); +} + +/* +// Test each segment +int i = 0; + +void loop() { + leds[i] = CRGB::Red; + FastLED.show(); + delay(500); + + leds[i] = CRGB::Black; + FastLED.show(); + delay(500); + i++; + if (i > LED_COUNT - 1) { + i = 0; + } +} +*/ + +void loop() { + // Only update on each hole minute to avoid flickering + int seconds = CopenhagenTime.dateTime("s").toInt(); + int minutes = CopenhagenTime.dateTime("i").toInt(); + + if(minutes == 59 && seconds >= 55) { //if(minutes == 59 && seconds >= 55) { + play_animation(dual_spin_animation, sizeof(spin_animation), 50); + FastLED.clear(); + } + + if(seconds != 0) { + delay(10); + return; + } + + update_clock(); + + delay(1000); +} + |
