initial commit
This commit is contained in:
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.DS_Store
|
||||||
|
.pio
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# kicad Temporary files
|
||||||
|
*.000
|
||||||
|
*.bak
|
||||||
|
*.bck
|
||||||
|
*.kicad_pcb-bak
|
||||||
|
*.kicad_sch-bak
|
||||||
|
*.kicad_prl
|
||||||
|
*.sch-bak
|
||||||
|
*~
|
||||||
|
_autosave-*
|
||||||
|
*.tmp
|
||||||
|
*-save.pro
|
||||||
|
*-save.kicad_pcb
|
||||||
|
fp-info-cache
|
||||||
|
|
||||||
|
# Netlist files (exported from Eeschema)
|
||||||
|
*.net
|
||||||
|
|
||||||
|
# Autorouter files (exported from Pcbnew)
|
||||||
|
*.dsn
|
||||||
|
*.ses
|
||||||
|
|
||||||
|
# Exported BOM files
|
||||||
|
*.xml
|
||||||
|
*.csv
|
||||||
|
|
||||||
|
# other files
|
||||||
|
CAD/Leo_muziekdoos_ESP32/~$ESP32 Pins.xlsx
|
||||||
5
esp32nixie/.gitignore
vendored
Normal file
5
esp32nixie/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
BIN
esp32nixie/doc/Scheme_Nixie_Clock_MCU_NCM109_v1.1-min-1.jpg
Normal file
BIN
esp32nixie/doc/Scheme_Nixie_Clock_MCU_NCM109_v1.1-min-1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 510 KiB |
BIN
esp32nixie/doc/hv5122.pdf
Normal file
BIN
esp32nixie/doc/hv5122.pdf
Normal file
Binary file not shown.
39
esp32nixie/include/README
Normal file
39
esp32nixie/include/README
Normal file
@@ -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
|
||||||
46
esp32nixie/lib/README
Normal file
46
esp32nixie/lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
21
esp32nixie/platformio.ini
Normal file
21
esp32nixie/platformio.ini
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
; 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
|
||||||
|
|
||||||
|
[env:esp-wrover-kit]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp-wrover-kit
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
paulstoffregen/Time@^1.6.1
|
||||||
|
jchristensen/Timezone@^1.2.4
|
||||||
|
khoih-prog/ESP_WifiManager@^1.10.2
|
||||||
|
raronoff/clickButton
|
||||||
|
lib_ldf_mode = deep+
|
||||||
|
monitor_speed = 115200
|
||||||
137
esp32nixie/src/LEDControl.h
Normal file
137
esp32nixie/src/LEDControl.h
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
ESP32 NTP Nixie Tube Clock Program
|
||||||
|
|
||||||
|
LEDControl.h - LED Control Code
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LED_CONTROL_H
|
||||||
|
#define LED_CONTROL_H
|
||||||
|
|
||||||
|
// A 24 bit color type
|
||||||
|
typedef struct {
|
||||||
|
byte red;
|
||||||
|
byte green;
|
||||||
|
byte blue;
|
||||||
|
}
|
||||||
|
RGB24;
|
||||||
|
|
||||||
|
// Misc color definitions
|
||||||
|
RGB24 black = { 0, 0, 0};
|
||||||
|
RGB24 blue = { 0, 0, 127};
|
||||||
|
RGB24 green = { 0, 127, 0};
|
||||||
|
RGB24 cyan = { 0, 127, 127};
|
||||||
|
RGB24 red = {127, 0, 0};
|
||||||
|
RGB24 magenta = {127, 0, 127};
|
||||||
|
RGB24 yellow = {127, 127, 0};
|
||||||
|
RGB24 white = {127, 127, 127};
|
||||||
|
|
||||||
|
#define LED_RED_CHANNEL 1
|
||||||
|
#define LED_GREEN_CHANNEL 2
|
||||||
|
#define LED_BLUE_CHANNEL 3
|
||||||
|
|
||||||
|
// LEDControl Class Definition
|
||||||
|
class LEDControl {
|
||||||
|
public:
|
||||||
|
// Class constructor
|
||||||
|
LEDControl(int _redPin, int _greenPin, int _bluePin) {
|
||||||
|
|
||||||
|
// Save incoming pin assignments
|
||||||
|
redPin = _redPin;
|
||||||
|
greenPin = _greenPin;
|
||||||
|
bluePin = _bluePin;
|
||||||
|
|
||||||
|
// Set up the output pins for the RGB LED
|
||||||
|
pinMode(redPin, OUTPUT);
|
||||||
|
pinMode(greenPin, OUTPUT);
|
||||||
|
pinMode(bluePin, OUTPUT);
|
||||||
|
|
||||||
|
// Each channel is set up for 12kHz and 10-bit resolution
|
||||||
|
ledcSetup(LED_RED_CHANNEL, 12000, 10);
|
||||||
|
ledcSetup(LED_GREEN_CHANNEL, 12000, 10);
|
||||||
|
ledcSetup(LED_BLUE_CHANNEL, 12000, 10);
|
||||||
|
|
||||||
|
ledcAttachPin(redPin, LED_RED_CHANNEL);
|
||||||
|
ledcAttachPin(greenPin, LED_GREEN_CHANNEL);
|
||||||
|
ledcAttachPin(bluePin, LED_BLUE_CHANNEL);
|
||||||
|
|
||||||
|
// Turn off RGB LEDs
|
||||||
|
ledcWrite(LED_RED_CHANNEL, 0);
|
||||||
|
ledcWrite(LED_GREEN_CHANNEL, 0);
|
||||||
|
ledcWrite(LED_BLUE_CHANNEL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input a value 0 to 255 to get a color value.
|
||||||
|
// The colors are a transition r - g - b - back to r.
|
||||||
|
RGB24 colorWheel(int wheelPos) {
|
||||||
|
RGB24 color;
|
||||||
|
|
||||||
|
wheelPos %= 256;
|
||||||
|
|
||||||
|
if (wheelPos < 85) {
|
||||||
|
color.red = 255 - wheelPos * 3;
|
||||||
|
color.green = wheelPos * 3;
|
||||||
|
color.blue = 0;
|
||||||
|
}
|
||||||
|
else if (wheelPos < 170) {
|
||||||
|
wheelPos -= 85;
|
||||||
|
color.red = 0;
|
||||||
|
color.green = 255 - wheelPos * 3;
|
||||||
|
color.blue = wheelPos * 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wheelPos -= 170;
|
||||||
|
color.red = wheelPos * 3;
|
||||||
|
color.green = 0;
|
||||||
|
color.blue = 255 - wheelPos * 3;
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the RGB LEDs color
|
||||||
|
void setLEDColor(byte red, byte green, byte blue) {
|
||||||
|
red = gammaArray[red];
|
||||||
|
green = gammaArray[green];
|
||||||
|
blue = gammaArray[blue];
|
||||||
|
|
||||||
|
// Change 8 bits to 10 bits
|
||||||
|
int rred = map(red, 0, 255, 0, 1023);
|
||||||
|
int rgrn = map(green, 0, 255, 0, 1023);
|
||||||
|
int rblu = map(blue, 0, 255, 0, 1023);
|
||||||
|
|
||||||
|
// Use PWM to control LED brightness
|
||||||
|
ledcWrite(LED_RED_CHANNEL, rred);
|
||||||
|
ledcWrite(LED_GREEN_CHANNEL, rgrn);
|
||||||
|
ledcWrite(LED_BLUE_CHANNEL, rblu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the RGB LEDs color
|
||||||
|
void setLEDColor(RGB24 color) {
|
||||||
|
setLEDColor(color.red, color.green, color.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private data
|
||||||
|
int redPin, greenPin, bluePin;
|
||||||
|
|
||||||
|
// Gamma correction array
|
||||||
|
const byte gammaArray [256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||||
|
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||||
|
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||||
|
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||||
|
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||||
|
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||||
|
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||||
|
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||||
|
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
|
||||||
|
115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
|
||||||
|
144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
|
||||||
|
177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
|
||||||
|
215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
168
esp32nixie/src/NTP.h
Normal file
168
esp32nixie/src/NTP.h
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
ESP32 NTP Nixie Tube Clock Program
|
||||||
|
|
||||||
|
NTP.h - Network Time Protocol Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NTP_H
|
||||||
|
#define NTP_H
|
||||||
|
|
||||||
|
#include <TimeLib.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
|
||||||
|
// Define the time between sync events
|
||||||
|
#define SYNC_INTERVAL_HOURS 1
|
||||||
|
#define SYNC_INTERVAL_MINUTES (SYNC_INTERVAL_HOURS * 60L)
|
||||||
|
#define SYNC_INTERVAL_SECONDS (SYNC_INTERVAL_MINUTES * 60L)
|
||||||
|
|
||||||
|
#define NTP_SERVER_NAME "time.nist.gov" // NTP request server
|
||||||
|
#define NTP_SERVER_PORT 123 // NTP requests are to port 123
|
||||||
|
#define LOCALPORT 2390 // Local port to listen for UDP packets
|
||||||
|
#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message
|
||||||
|
#define RETRIES 20 // Times to try getting NTP time before failing
|
||||||
|
|
||||||
|
class NTP {
|
||||||
|
public:
|
||||||
|
NTP(NixieTubeShield& shield) : _shield(shield) {
|
||||||
|
// Login succeeded so set UDP local port
|
||||||
|
udp.begin(LOCALPORT);
|
||||||
|
};
|
||||||
|
|
||||||
|
static NTP& getInstance() {
|
||||||
|
return *_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createSingleton(NixieTubeShield& shield) {
|
||||||
|
NTP* ntp = new NTP(shield);
|
||||||
|
_instance = ntp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get NTP time with retries on access failure
|
||||||
|
time_t getTime() {
|
||||||
|
unsigned long result;
|
||||||
|
|
||||||
|
for (int i = 0; i < RETRIES; i++) {
|
||||||
|
result = _getTime();
|
||||||
|
if (result != 0) {
|
||||||
|
// Update RTC
|
||||||
|
tmElements_t tm;
|
||||||
|
breakTime(result, tm);
|
||||||
|
_shield.setRTCDateTime(tm);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Serial.println("Problem getting NTP time. Retrying...");
|
||||||
|
delay(300);
|
||||||
|
}
|
||||||
|
Serial.println("NTP Problem - Could not obtain time. Falling back to RTC");
|
||||||
|
|
||||||
|
return _getRTCTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Static NTP instance
|
||||||
|
static NTP* _instance;
|
||||||
|
|
||||||
|
// NTP Time Provider Code
|
||||||
|
time_t _getTime() {
|
||||||
|
// Set all bytes in the buffer to 0
|
||||||
|
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||||
|
|
||||||
|
// Initialize values needed to form NTP request
|
||||||
|
packetBuffer[0] = 0xE3; // LI, Version, Mode
|
||||||
|
packetBuffer[2] = 0x06; // Polling Interval
|
||||||
|
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||||
|
packetBuffer[12] = 0x31;
|
||||||
|
packetBuffer[13] = 0x4E;
|
||||||
|
packetBuffer[14] = 0x31;
|
||||||
|
packetBuffer[15] = 0x34;
|
||||||
|
|
||||||
|
// All NTP fields initialized, now send a packet requesting a timestamp
|
||||||
|
udp.beginPacket(NTP_SERVER_NAME, NTP_SERVER_PORT);
|
||||||
|
udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||||
|
udp.endPacket();
|
||||||
|
|
||||||
|
// Wait a second for the response
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Listen for the response
|
||||||
|
if (udp.parsePacket() == NTP_PACKET_SIZE) {
|
||||||
|
udp.read(packetBuffer, NTP_PACKET_SIZE); // Read packet into the buffer
|
||||||
|
unsigned long secsSince1900;
|
||||||
|
|
||||||
|
// Convert four bytes starting at location 40 to a long integer
|
||||||
|
secsSince1900 = (unsigned long) packetBuffer[40] << 24;
|
||||||
|
secsSince1900 |= (unsigned long) packetBuffer[41] << 16;
|
||||||
|
secsSince1900 |= (unsigned long) packetBuffer[42] << 8;
|
||||||
|
secsSince1900 |= (unsigned long) packetBuffer[43];
|
||||||
|
|
||||||
|
Serial.println("Got NTP time");
|
||||||
|
|
||||||
|
return secsSince1900 - 2208988800UL;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get system time from real-time clock
|
||||||
|
time_t _getRTCTime() {
|
||||||
|
bool isRTCAvailable = true;
|
||||||
|
tmElements_t m;
|
||||||
|
_shield.getRTCTime(m);
|
||||||
|
|
||||||
|
byte prevSeconds = m.Second;
|
||||||
|
unsigned long RTC_ReadingStartTime = millis();
|
||||||
|
|
||||||
|
Serial.print("Real-time clock: ");
|
||||||
|
Serial.print(m.Hour);
|
||||||
|
Serial.print(":");
|
||||||
|
Serial.println(m.Minute);
|
||||||
|
Serial.print(":");
|
||||||
|
Serial.println(m.Second);
|
||||||
|
|
||||||
|
while (prevSeconds == m.Second) {
|
||||||
|
_shield.getRTCTime(m);
|
||||||
|
if ((millis() - RTC_ReadingStartTime) > 3000) {
|
||||||
|
Serial.println("Warning! RTC did not respond!");
|
||||||
|
isRTCAvailable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set system time if RTC is available
|
||||||
|
if (isRTCAvailable) {
|
||||||
|
Serial.println("Got time from RTC");
|
||||||
|
return makeTime(m);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance of shield
|
||||||
|
NixieTubeShield& _shield;
|
||||||
|
|
||||||
|
// A UDP instance to let us send and receive packets over UDP
|
||||||
|
WiFiUDP udp;
|
||||||
|
|
||||||
|
// Buffer to hold outgoing and incoming packets
|
||||||
|
byte packetBuffer[NTP_PACKET_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
NTP* NTP::_instance = 0;
|
||||||
|
|
||||||
|
time_t getNTPTime() {
|
||||||
|
return NTP::getInstance().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the NTP code
|
||||||
|
void initNTP(NixieTubeShield& shield) {
|
||||||
|
// Create instance of NTP class
|
||||||
|
NTP::createSingleton(shield);
|
||||||
|
|
||||||
|
// Set the time provider to NTP
|
||||||
|
setSyncProvider(getNTPTime);
|
||||||
|
|
||||||
|
// Set the interval between NTP calls
|
||||||
|
setSyncInterval(SYNC_INTERVAL_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
304
esp32nixie/src/NixieTubeShield.h
Normal file
304
esp32nixie/src/NixieTubeShield.h
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
ESP32 NTP Nixie Tube Clock Program
|
||||||
|
|
||||||
|
NixieTubeShield.h - Interface to Nixie Tube Shield
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NIXIE_TUBE_SHIELD_H
|
||||||
|
#define NIXIE_TUBE_SHIELD_H
|
||||||
|
|
||||||
|
#include "ClickButton.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "LEDControl.h"
|
||||||
|
#include "Tone.h"
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// ESP32 Pin Configuration
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#define LED_GREEN 14
|
||||||
|
#define LED_RED 13
|
||||||
|
#define LED_BLUE 15
|
||||||
|
#define HV_ENABLE 17 //TODO
|
||||||
|
#define LATCH_ENABLE 5
|
||||||
|
#define NEON_DOTS 25
|
||||||
|
#define MODE_BUTTON 12
|
||||||
|
#define UP_BUTTON 16
|
||||||
|
#define DOWN_BUTTON 4
|
||||||
|
#define BUZZER_PIN 32
|
||||||
|
|
||||||
|
#define DS1307_ADDRESS 0x68
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// Digit Data Definitions
|
||||||
|
// Each digit, except blank, has a single active low bit in a
|
||||||
|
// field of 10 bits
|
||||||
|
// ***************************************************************
|
||||||
|
#define DIGIT_0 0
|
||||||
|
#define DIGIT_1 1
|
||||||
|
#define DIGIT_2 2
|
||||||
|
#define DIGIT_3 3
|
||||||
|
#define DIGIT_4 4
|
||||||
|
#define DIGIT_5 5
|
||||||
|
#define DIGIT_6 6
|
||||||
|
#define DIGIT_7 7
|
||||||
|
#define DIGIT_8 8
|
||||||
|
#define DIGIT_9 9
|
||||||
|
#define DIGIT_BLANK 0xFF
|
||||||
|
|
||||||
|
#define BLANK_DIGIT 10
|
||||||
|
|
||||||
|
#define UpperDotsMask 0x80000000
|
||||||
|
#define LowerDotsMask 0x40000000
|
||||||
|
|
||||||
|
ClickButton setButton(MODE_BUTTON, LOW, CLICKBTN_PULLUP);
|
||||||
|
ClickButton upButton(UP_BUTTON, LOW, CLICKBTN_PULLUP);
|
||||||
|
ClickButton downButton(DOWN_BUTTON, LOW, CLICKBTN_PULLUP);
|
||||||
|
|
||||||
|
// Class Definition
|
||||||
|
class NixieTubeShield : public LEDControl {
|
||||||
|
unsigned int SymbolArray[10]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Class Constructor
|
||||||
|
NixieTubeShield() : LEDControl(LED_RED, LED_GREEN, LED_BLUE) {
|
||||||
|
|
||||||
|
// Setup output pins
|
||||||
|
pinMode(HV_ENABLE, OUTPUT);
|
||||||
|
pinMode(LATCH_ENABLE, OUTPUT);
|
||||||
|
pinMode(NEON_DOTS, OUTPUT);
|
||||||
|
pinMode(MODE_BUTTON, INPUT_PULLUP);
|
||||||
|
pinMode(UP_BUTTON, INPUT_PULLUP);
|
||||||
|
pinMode(DOWN_BUTTON, INPUT_PULLUP);
|
||||||
|
//pinMode(NEON_DOTS_LOWER, OUTPUT);
|
||||||
|
|
||||||
|
// Set default outputs
|
||||||
|
digitalWrite(HV_ENABLE, LOW);
|
||||||
|
digitalWrite(LATCH_ENABLE, LOW);
|
||||||
|
digitalWrite(NEON_DOTS, LOW);
|
||||||
|
|
||||||
|
setButton.debounceTime = 20; // Debounce timer in ms
|
||||||
|
setButton.multiclickTime = 30; // Time limit for multi clicks
|
||||||
|
setButton.longClickTime = 2000; // time until "held-down clicks" register
|
||||||
|
|
||||||
|
upButton.debounceTime = 20; // Debounce timer in ms
|
||||||
|
upButton.multiclickTime = 30; // Time limit for multi clicks
|
||||||
|
upButton.longClickTime = 2000; // time until "held-down clicks" register
|
||||||
|
|
||||||
|
downButton.debounceTime = 20; // Debounce timer in ms
|
||||||
|
downButton.multiclickTime = 30; // Time limit for multi clicks
|
||||||
|
downButton.longClickTime = 2000; // time until "held-down clicks" register
|
||||||
|
|
||||||
|
// Initialize digit storage to all ones so all digits are off
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
digits[i] = DIGIT_BLANK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// High voltage control
|
||||||
|
void hvEnable(boolean state) {
|
||||||
|
digitalWrite(HV_ENABLE, state ? HIGH : LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neon lamp control
|
||||||
|
void dotsEnable(boolean state) {
|
||||||
|
digitalWrite(NEON_DOTS, state ? HIGH : LOW);
|
||||||
|
dotsEnabled = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX1 (most significant) digit
|
||||||
|
void setNX1Digit(int d) {
|
||||||
|
digits[5] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX2 digit
|
||||||
|
void setNX2Digit(int d) {
|
||||||
|
digits[4] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX3 digit
|
||||||
|
void setNX3Digit(int d) {
|
||||||
|
digits[3] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX4 digit
|
||||||
|
void setNX4Digit(int d) {
|
||||||
|
digits[2] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX5 digit
|
||||||
|
void setNX5Digit(int d) {
|
||||||
|
digits[1] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the NX6 (least significant) digit
|
||||||
|
void setNX6Digit(int d) {
|
||||||
|
digits[0] = NUMERIC_DIGITS[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
void show() {
|
||||||
|
digitalWrite(LATCH_ENABLE, LOW); // allow data input (Transparent mode)
|
||||||
|
unsigned long Var32=0;
|
||||||
|
|
||||||
|
//-------- REG 1 -----------------------------------------------
|
||||||
|
Var32=0;
|
||||||
|
|
||||||
|
Var32|=(unsigned long)(SymbolArray[digits[0]])<<20; // s2
|
||||||
|
Var32|=(unsigned long)(SymbolArray[digits[1]])<<10; //s1
|
||||||
|
Var32|=(unsigned long) (SymbolArray[digits[2]]); //m2
|
||||||
|
|
||||||
|
if (dotsEnabled) Var32|=LowerDotsMask;
|
||||||
|
else Var32&=~LowerDotsMask;
|
||||||
|
|
||||||
|
if (dotsEnabled) Var32|=UpperDotsMask;
|
||||||
|
else Var32&=~UpperDotsMask;
|
||||||
|
|
||||||
|
SPI.transfer(Var32>>24);
|
||||||
|
SPI.transfer(Var32>>16);
|
||||||
|
SPI.transfer(Var32>>8);
|
||||||
|
SPI.transfer(Var32);
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-------- REG 0 -----------------------------------------------
|
||||||
|
Var32=0;
|
||||||
|
|
||||||
|
Var32|=(unsigned long)(SymbolArray[digits[3]])<<20; // m1
|
||||||
|
Var32|=(unsigned long)(SymbolArray[digits[4]])<<10; //h2
|
||||||
|
Var32|=(unsigned long)SymbolArray[digits[5]]; //h1
|
||||||
|
|
||||||
|
if (dotsEnabled) Var32|=LowerDotsMask;
|
||||||
|
else Var32&=~LowerDotsMask;
|
||||||
|
|
||||||
|
if (dotsEnabled) Var32|=UpperDotsMask;
|
||||||
|
else Var32&=~UpperDotsMask;
|
||||||
|
|
||||||
|
SPI.transfer(Var32>>24);
|
||||||
|
SPI.transfer(Var32>>16);
|
||||||
|
SPI.transfer(Var32>>8);
|
||||||
|
SPI.transfer(Var32);
|
||||||
|
|
||||||
|
digitalWrite(LATCH_ENABLE, HIGH); // latching data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do anti-poisoning routine and then turn off all tubes
|
||||||
|
void doAntiPoisoning(void) {
|
||||||
|
bool dots = false;
|
||||||
|
dotsEnable(false);
|
||||||
|
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
setNX1Digit(i);
|
||||||
|
setNX2Digit(i);
|
||||||
|
setNX3Digit(i);
|
||||||
|
setNX4Digit(i);
|
||||||
|
setNX5Digit(i);
|
||||||
|
setNX6Digit(i);
|
||||||
|
|
||||||
|
// Make the changes visible
|
||||||
|
show();
|
||||||
|
|
||||||
|
// Small delay
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
setLEDColor(i%3==0?255:0, i%3==1?255:0, i%3==2?255:0);
|
||||||
|
dots = !dots;
|
||||||
|
dotsEnable(dots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processButtons() {
|
||||||
|
setButton.Update();
|
||||||
|
upButton.Update();
|
||||||
|
downButton.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSetButtonClicked() {
|
||||||
|
return (setButton.clicks > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSetButtonLongClicked() {
|
||||||
|
return (setButton.clicks < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUpButtonClicked() {
|
||||||
|
return (upButton.clicks > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUpButtonLongClicked() {
|
||||||
|
return (upButton.clicks < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDownButtonClicked() {
|
||||||
|
return (downButton.clicks > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDownButtonLongClicked() {
|
||||||
|
return (downButton.clicks < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getRTCTime(tmElements_t &m) {
|
||||||
|
Wire.beginTransmission(DS1307_ADDRESS);
|
||||||
|
Wire.write(zero);
|
||||||
|
Wire.endTransmission();
|
||||||
|
|
||||||
|
Wire.requestFrom(DS1307_ADDRESS, 7);
|
||||||
|
|
||||||
|
m.Second = bcdToDec(Wire.read());
|
||||||
|
m.Minute = bcdToDec(Wire.read());
|
||||||
|
m.Hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
|
||||||
|
m.Wday = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
|
||||||
|
m.Day = bcdToDec(Wire.read());
|
||||||
|
m.Month = bcdToDec(Wire.read());
|
||||||
|
m.Year = bcdToDec(Wire.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRTCDateTime(const tmElements_t &m) {
|
||||||
|
Wire.beginTransmission(DS1307_ADDRESS);
|
||||||
|
Wire.write(zero); //stop Oscillator
|
||||||
|
|
||||||
|
Wire.write(decToBcd(m.Second));
|
||||||
|
Wire.write(decToBcd(m.Minute));
|
||||||
|
Wire.write(decToBcd(m.Hour));
|
||||||
|
Wire.write(decToBcd(m.Wday));
|
||||||
|
Wire.write(decToBcd(m.Day));
|
||||||
|
Wire.write(decToBcd(m.Month));
|
||||||
|
Wire.write(decToBcd(m.Year));
|
||||||
|
|
||||||
|
Wire.write(zero); //start
|
||||||
|
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
byte decToBcd(byte val) {
|
||||||
|
// Convert normal decimal numbers to binary coded decimal
|
||||||
|
return ( (val / 10 * 16) + (val % 10) );
|
||||||
|
}
|
||||||
|
|
||||||
|
byte bcdToDec(byte val) {
|
||||||
|
// Convert binary coded decimal to normal decimal numbers
|
||||||
|
return ( (val / 16 * 10) + (val % 16) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private data
|
||||||
|
// Array of codes for each nixie digit
|
||||||
|
int NUMERIC_DIGITS[11] = {
|
||||||
|
DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4,
|
||||||
|
DIGIT_5, DIGIT_6, DIGIT_7, DIGIT_8, DIGIT_9,
|
||||||
|
DIGIT_BLANK
|
||||||
|
};
|
||||||
|
|
||||||
|
byte zero = 0x00; //workaround for issue #527
|
||||||
|
int RTC_hours, RTC_minutes, RTC_seconds, RTC_day, RTC_month, RTC_year, RTC_day_of_week;
|
||||||
|
|
||||||
|
// Storage for codes for each nixie digit
|
||||||
|
// Digit order is: NX6, NX5, NX4, NX3, NX2, NX1
|
||||||
|
uint16_t digits[6];
|
||||||
|
|
||||||
|
bool dotsEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
124
esp32nixie/src/Tone.h
Normal file
124
esp32nixie/src/Tone.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#ifndef TONE_H
|
||||||
|
#define TONE_H
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Public Constants
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
#define NOTE_B0 31
|
||||||
|
#define NOTE_C1 33
|
||||||
|
#define NOTE_CS1 35
|
||||||
|
#define NOTE_D1 37
|
||||||
|
#define NOTE_DS1 39
|
||||||
|
#define NOTE_E1 41
|
||||||
|
#define NOTE_F1 44
|
||||||
|
#define NOTE_FS1 46
|
||||||
|
#define NOTE_G1 49
|
||||||
|
#define NOTE_GS1 52
|
||||||
|
#define NOTE_A1 55
|
||||||
|
#define NOTE_AS1 58
|
||||||
|
#define NOTE_B1 62
|
||||||
|
#define NOTE_C2 65
|
||||||
|
#define NOTE_CS2 69
|
||||||
|
#define NOTE_D2 73
|
||||||
|
#define NOTE_DS2 78
|
||||||
|
#define NOTE_E2 82
|
||||||
|
#define NOTE_F2 87
|
||||||
|
#define NOTE_FS2 93
|
||||||
|
#define NOTE_G2 98
|
||||||
|
#define NOTE_GS2 104
|
||||||
|
#define NOTE_A2 110
|
||||||
|
#define NOTE_AS2 117
|
||||||
|
#define NOTE_B2 123
|
||||||
|
#define NOTE_C3 131
|
||||||
|
#define NOTE_CS3 139
|
||||||
|
#define NOTE_D3 147
|
||||||
|
#define NOTE_DS3 156
|
||||||
|
#define NOTE_E3 165
|
||||||
|
#define NOTE_F3 175
|
||||||
|
#define NOTE_FS3 185
|
||||||
|
#define NOTE_G3 196
|
||||||
|
#define NOTE_GS3 208
|
||||||
|
#define NOTE_A3 220
|
||||||
|
#define NOTE_AS3 233
|
||||||
|
#define NOTE_B3 247
|
||||||
|
#define NOTE_C4 262
|
||||||
|
#define NOTE_CS4 277
|
||||||
|
#define NOTE_D4 294
|
||||||
|
#define NOTE_DS4 311
|
||||||
|
#define NOTE_E4 330
|
||||||
|
#define NOTE_F4 349
|
||||||
|
#define NOTE_FS4 370
|
||||||
|
#define NOTE_G4 392
|
||||||
|
#define NOTE_GS4 415
|
||||||
|
#define NOTE_A4 440
|
||||||
|
#define NOTE_AS4 466
|
||||||
|
#define NOTE_B4 494
|
||||||
|
#define NOTE_C5 523
|
||||||
|
#define NOTE_CS5 554
|
||||||
|
#define NOTE_D5 587
|
||||||
|
#define NOTE_DS5 622
|
||||||
|
#define NOTE_E5 659
|
||||||
|
#define NOTE_F5 698
|
||||||
|
#define NOTE_FS5 740
|
||||||
|
#define NOTE_G5 784
|
||||||
|
#define NOTE_GS5 831
|
||||||
|
#define NOTE_A5 880
|
||||||
|
#define NOTE_AS5 932
|
||||||
|
#define NOTE_B5 988
|
||||||
|
#define NOTE_C6 1047
|
||||||
|
#define NOTE_CS6 1109
|
||||||
|
#define NOTE_D6 1175
|
||||||
|
#define NOTE_DS6 1245
|
||||||
|
#define NOTE_E6 1319
|
||||||
|
#define NOTE_F6 1397
|
||||||
|
#define NOTE_FS6 1480
|
||||||
|
#define NOTE_G6 1568
|
||||||
|
#define NOTE_GS6 1661
|
||||||
|
#define NOTE_A6 1760
|
||||||
|
#define NOTE_AS6 1865
|
||||||
|
#define NOTE_B6 1976
|
||||||
|
#define NOTE_C7 2093
|
||||||
|
#define NOTE_CS7 2217
|
||||||
|
#define NOTE_D7 2349
|
||||||
|
#define NOTE_DS7 2489
|
||||||
|
#define NOTE_E7 2637
|
||||||
|
#define NOTE_F7 2794
|
||||||
|
#define NOTE_FS7 2960
|
||||||
|
#define NOTE_G7 3136
|
||||||
|
#define NOTE_GS7 3322
|
||||||
|
#define NOTE_A7 3520
|
||||||
|
#define NOTE_AS7 3729
|
||||||
|
#define NOTE_B7 3951
|
||||||
|
#define NOTE_C8 4186
|
||||||
|
#define NOTE_CS8 4435
|
||||||
|
#define NOTE_D8 4699
|
||||||
|
#define NOTE_DS8 4978
|
||||||
|
|
||||||
|
#define TONE_CHANNEL 5
|
||||||
|
|
||||||
|
class Tone {
|
||||||
|
public:
|
||||||
|
Tone() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(uint8_t pin) {
|
||||||
|
ledcSetup(TONE_CHANNEL, 1E5, 12);
|
||||||
|
ledcAttachPin(pin,TONE_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void play(unsigned int freq, unsigned long duration) {
|
||||||
|
ledcWriteTone(TONE_CHANNEL, freq);
|
||||||
|
// TODO
|
||||||
|
//tone(_pin, freq, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
ledcWrite(TONE_CHANNEL,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _pin;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
521
esp32nixie/src/main.cpp
Normal file
521
esp32nixie/src/main.cpp
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
/*
|
||||||
|
ESP32 NTP Nixie Tube Clock Program
|
||||||
|
|
||||||
|
This hardware/software combination implements a Nixie tube digital clock that
|
||||||
|
never needs setting as it gets the current time and date by polling
|
||||||
|
Network Time Protocol (NTP) servers on the Internet. The clock's time
|
||||||
|
is synchronized to NTP time periodically. Use of the TimeZone
|
||||||
|
library means that Daylight Savings Time (DST) is automatically
|
||||||
|
taken into consideration so no time change buttons are necessary.
|
||||||
|
Clock can run in 24 hour or 12 hour mode.
|
||||||
|
|
||||||
|
This program uses the WiFiManager library to allow WiFi credentials to be set
|
||||||
|
via a web interface.
|
||||||
|
|
||||||
|
How it works:
|
||||||
|
|
||||||
|
When the program is first started it creates a wireless access point called NixieClock
|
||||||
|
that the user needs to connect to and then if the user points his/her browser to
|
||||||
|
192.168.4.1, a page is presented that allows the credentials for the actual WiFi network
|
||||||
|
to be entered. This only needs to be done once since the credentials will be stored in
|
||||||
|
EEPROM and will be used from that point forward.
|
||||||
|
|
||||||
|
Once WiFi connection has been established, it uses NTP to initialize the real-time clock (RTC).
|
||||||
|
If ESP32 cannot connect to the WiFi network using the stored crecentials, it will fall back
|
||||||
|
to the real-time clock (RTC).
|
||||||
|
|
||||||
|
Press the mode button to enter WiFi AP configuration mode.
|
||||||
|
Long-press the mode button to reset the ESP32.
|
||||||
|
|
||||||
|
The hardware consists of the following parts:
|
||||||
|
ESP32
|
||||||
|
Nixie Tubes Clock Arduino Shield NCS314 for xUSSR IN-14 Nixie Tubes from eBay
|
||||||
|
|
||||||
|
The connections between the ESP32 board and the Nixie Clock Shield are as follows:
|
||||||
|
|
||||||
|
ESP32 Pin Shield Pin Function
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
GIOP18 SCK / avr19 SPI Clock
|
||||||
|
GIOP23 MOSI / avr17 Master Out Slave In (data from ESP32->clock)
|
||||||
|
5 (SS) LE / avr16 Latch Enable
|
||||||
|
25 DOT1 -- Neon dot 1
|
||||||
|
25 DOT2 -- Neon dot 2
|
||||||
|
14 PWM1 / avr12 Green LED drive
|
||||||
|
13 PWM2 / avr15 Red LED drive
|
||||||
|
15 PWM3 / avr5 Blue LED drive
|
||||||
|
17 SHTDN High voltage enable
|
||||||
|
12 SET / avr23 Set (mode) button
|
||||||
|
16 UP / avr24 Up button
|
||||||
|
4 DOWN / avr25 Down button
|
||||||
|
32 BUZZER /avr4 Buzzer pin
|
||||||
|
|
||||||
|
The ESP32 is powered (via Vin) with 5VDC from a 5 volt regulator driven from
|
||||||
|
the 12 VDC supply. The shield is powered directly from the 12 VDC supply.
|
||||||
|
|
||||||
|
Based on ESP8266_NTPNixieClock by Craig A. Lindley and NixeTubesShieldNCS314 by GRA & AFCH.
|
||||||
|
|
||||||
|
Copyright (c) 2019 Tomer Verona
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <driver/dac.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <TimeLib.h>
|
||||||
|
#include <Timezone.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
|
#include <WebServer.h>
|
||||||
|
//#include <WiFiManager.h>
|
||||||
|
|
||||||
|
#include "LEDControl.h"
|
||||||
|
#include "NixieTubeShield.h"
|
||||||
|
#include "NTP.h"
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// Start of user configuration items
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
// Name of AP when configuring WiFi credentials
|
||||||
|
#define AP_NAME "NixieClock"
|
||||||
|
|
||||||
|
// Checks WiFi connection. Reset after this time, if WiFi is not connected
|
||||||
|
#define WIFI_CHK_TIME_SEC 3600
|
||||||
|
#define WIFI_CHK_TIME_MS (WIFI_CHK_TIME_SEC * 1000)
|
||||||
|
|
||||||
|
// Set to false for 24 hour time mode
|
||||||
|
#define HOUR_FORMAT_12 true
|
||||||
|
|
||||||
|
// Nixie tubes are turned off at night to increase their lifetime
|
||||||
|
// Clock off and on times are in 24 hour format
|
||||||
|
#define CLOCK_OFF_HOUR 23
|
||||||
|
#define CLOCK_ON_HOUR 07
|
||||||
|
|
||||||
|
// Suppress leading zeros
|
||||||
|
// Set to false to having leading zeros displayed
|
||||||
|
#define SUPPRESS_LEADING_ZEROS true
|
||||||
|
|
||||||
|
// Define the timezone in which the clock will operate
|
||||||
|
// See the Timezone library for details
|
||||||
|
// US Pacific Time Zone (Las Vegas, Los Angeles)
|
||||||
|
TimeChangeRule usPDT = {"PDT", Second, Sun, Mar, 2, -420};
|
||||||
|
TimeChangeRule usPST = {"PST", First, Sun, Nov, 2, -480};
|
||||||
|
Timezone TZ(usPDT, usPST);
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// End of user configuration items
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
// Instantiate the Nixie Tube Shield object
|
||||||
|
NixieTubeShield SHIELD;
|
||||||
|
|
||||||
|
// Instantiate the WifiManager object
|
||||||
|
//WiFiManager wifiManager;
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// Utility Functions
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
// Misc variables
|
||||||
|
int previousMinute = 0;
|
||||||
|
int minutes = 0;
|
||||||
|
boolean dotToggle = false;
|
||||||
|
boolean clockOn = true;
|
||||||
|
|
||||||
|
// This function is called once a second
|
||||||
|
void updateDisplay(void) {
|
||||||
|
|
||||||
|
// Get the current time and date
|
||||||
|
// Get the time for specified timezone
|
||||||
|
time_t localTime = TZ.toLocal(now());
|
||||||
|
|
||||||
|
// Determine if clock should be on or off
|
||||||
|
int hr = hour(localTime);
|
||||||
|
|
||||||
|
// Clock is on between these hours
|
||||||
|
if ((hr >= CLOCK_ON_HOUR) && (hr < CLOCK_OFF_HOUR)) {
|
||||||
|
if (!clockOn) {
|
||||||
|
clockOn = true;
|
||||||
|
Serial.println("Clock is On");
|
||||||
|
|
||||||
|
// Clock should be on so turn high voltage on
|
||||||
|
SHIELD.hvEnable(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (clockOn) {
|
||||||
|
clockOn = false;
|
||||||
|
|
||||||
|
Serial.println("Clock is Off");
|
||||||
|
|
||||||
|
// Clock is going off so shut things down in an orderly fashion
|
||||||
|
// First turn off the high voltage so the nixie's go off
|
||||||
|
SHIELD.hvEnable(false);
|
||||||
|
|
||||||
|
// Next turn off the dots
|
||||||
|
SHIELD.dotsEnable(false);
|
||||||
|
SHIELD.setNX1Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX2Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX3Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX4Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX5Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX6Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.show();
|
||||||
|
|
||||||
|
// Finally turn the LEDs off as well
|
||||||
|
SHIELD.setLEDColor(black);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to continue as the clock is effectively off
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current minute
|
||||||
|
minutes = minute(localTime);
|
||||||
|
|
||||||
|
// Dispatch events in priority order
|
||||||
|
|
||||||
|
// 60 minute event is anti-poisoning routine
|
||||||
|
if ((minutes != previousMinute) && (minutes == 0)) {
|
||||||
|
previousMinute = minutes;
|
||||||
|
|
||||||
|
// Set all LEDs to red to indicate anti-poisoning
|
||||||
|
SHIELD.setLEDColor(red);
|
||||||
|
|
||||||
|
// Do anti-poisoning function
|
||||||
|
SHIELD.doAntiPoisoning();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15 minute event is rainbow display
|
||||||
|
else if ((minutes != previousMinute) && (minutes != 0) && ((minutes % 15) == 0)) {
|
||||||
|
previousMinute = minutes;
|
||||||
|
|
||||||
|
// Turn off dots
|
||||||
|
SHIELD.dotsEnable(false);
|
||||||
|
|
||||||
|
// Blank all nixie tube digits
|
||||||
|
SHIELD.setNX1Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX2Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX3Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX4Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX5Digit(BLANK_DIGIT);
|
||||||
|
SHIELD.setNX6Digit(BLANK_DIGIT);
|
||||||
|
|
||||||
|
// Blank the nixie tubes
|
||||||
|
SHIELD.show();
|
||||||
|
|
||||||
|
// Cycle the LEDs through rainbows of color
|
||||||
|
for (int cycles = 0; cycles < 4; cycles++) {
|
||||||
|
for (int cycle = 0; cycle < 256; cycle += 16) {
|
||||||
|
// Set the LED's color
|
||||||
|
SHIELD.setLEDColor(SHIELD.colorWheel(cycle));
|
||||||
|
delay(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10 minute event is the date display
|
||||||
|
else if ((minutes != previousMinute) && (minutes != 0) && ((minutes % 10) == 0)) {
|
||||||
|
previousMinute = minutes;
|
||||||
|
|
||||||
|
// Turn off dots
|
||||||
|
SHIELD.dotsEnable(false);
|
||||||
|
|
||||||
|
// Set all LEDs to blue to indicate date display
|
||||||
|
SHIELD.setLEDColor(blue);
|
||||||
|
|
||||||
|
SHIELD.dotsEnable(true);
|
||||||
|
|
||||||
|
// Get the current month 1..12
|
||||||
|
int now_mon = month(localTime);
|
||||||
|
|
||||||
|
// Display the NX1 digit
|
||||||
|
if (now_mon >= 10) {
|
||||||
|
SHIELD.setNX1Digit(now_mon / 10);
|
||||||
|
} else {
|
||||||
|
if (SUPPRESS_LEADING_ZEROS) {
|
||||||
|
SHIELD.setNX1Digit(BLANK_DIGIT);
|
||||||
|
} else {
|
||||||
|
SHIELD.setNX1Digit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Display the NX2 digit
|
||||||
|
SHIELD.setNX2Digit(now_mon % 10);
|
||||||
|
|
||||||
|
// Get the current day 1..31
|
||||||
|
int now_day = day(localTime);
|
||||||
|
|
||||||
|
// Display the NX3 digit
|
||||||
|
if (now_day >= 10) {
|
||||||
|
SHIELD.setNX3Digit(now_day / 10);
|
||||||
|
} else {
|
||||||
|
if (SUPPRESS_LEADING_ZEROS) {
|
||||||
|
SHIELD.setNX3Digit(BLANK_DIGIT);
|
||||||
|
} else {
|
||||||
|
SHIELD.setNX3Digit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Display the NX4 digit
|
||||||
|
SHIELD.setNX4Digit(now_day % 10);
|
||||||
|
|
||||||
|
// Get the current year
|
||||||
|
int now_year = year(localTime) - 2000;
|
||||||
|
|
||||||
|
// Display the NX5 digit
|
||||||
|
if (now_year >= 10) {
|
||||||
|
SHIELD.setNX5Digit(now_year / 10);
|
||||||
|
} else {
|
||||||
|
if (SUPPRESS_LEADING_ZEROS) {
|
||||||
|
SHIELD.setNX5Digit(BLANK_DIGIT);
|
||||||
|
} else {
|
||||||
|
SHIELD.setNX5Digit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Display the NX6 digit
|
||||||
|
SHIELD.setNX6Digit(now_year % 10);
|
||||||
|
|
||||||
|
// Display date on clock
|
||||||
|
SHIELD.show();
|
||||||
|
|
||||||
|
// Delay for date display
|
||||||
|
delay(10000);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Display the time
|
||||||
|
|
||||||
|
int now_hour;
|
||||||
|
float colorInc;
|
||||||
|
|
||||||
|
// Make the dots blink off and on
|
||||||
|
if (dotToggle) {
|
||||||
|
dotToggle = false;
|
||||||
|
SHIELD.dotsEnable(true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dotToggle = true;
|
||||||
|
SHIELD.dotsEnable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the LED's color depending upon the hour
|
||||||
|
// Get the current hour
|
||||||
|
if (HOUR_FORMAT_12) {
|
||||||
|
// Using 12 hour format
|
||||||
|
now_hour = hourFormat12(localTime);
|
||||||
|
// Calculate the color of the LEDs based on the hour in 12 hour format
|
||||||
|
colorInc = 256 / 12.0;
|
||||||
|
} else {
|
||||||
|
// Using 24 hour format
|
||||||
|
now_hour = hour(localTime);
|
||||||
|
// Calculate the color of the LEDs based on the hour in 24 hour format
|
||||||
|
colorInc = 256 / 24.0;
|
||||||
|
}
|
||||||
|
// Set the shields's LED color
|
||||||
|
SHIELD.setLEDColor(SHIELD.colorWheel(colorInc * now_hour));
|
||||||
|
|
||||||
|
// Display the NX1 digit
|
||||||
|
if (now_hour >= 10) {
|
||||||
|
SHIELD.setNX1Digit(now_hour / 10);
|
||||||
|
} else {
|
||||||
|
if (SUPPRESS_LEADING_ZEROS) {
|
||||||
|
SHIELD.setNX1Digit(BLANK_DIGIT);
|
||||||
|
} else {
|
||||||
|
SHIELD.setNX1Digit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Display the NX2 digit
|
||||||
|
SHIELD.setNX2Digit(now_hour % 10);
|
||||||
|
|
||||||
|
// Get the current minute
|
||||||
|
int now_min = minute(localTime);
|
||||||
|
|
||||||
|
// Display the NX3 digit
|
||||||
|
SHIELD.setNX3Digit(now_min / 10);
|
||||||
|
|
||||||
|
// Display the NX4 digit
|
||||||
|
SHIELD.setNX4Digit(now_min % 10);
|
||||||
|
|
||||||
|
// Get the current second
|
||||||
|
int now_sec = second(localTime);
|
||||||
|
|
||||||
|
// Display the NX5 digit
|
||||||
|
SHIELD.setNX5Digit(now_sec / 10);
|
||||||
|
|
||||||
|
// Display the NX6 digit
|
||||||
|
SHIELD.setNX6Digit(now_sec % 10);
|
||||||
|
|
||||||
|
// Display time on clock
|
||||||
|
SHIELD.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get WiFi SSID
|
||||||
|
String WIFI_GetSSID() {
|
||||||
|
// wifi_config_t conf;
|
||||||
|
// esp_wifi_get_config(WIFI_IF_STA, &conf);
|
||||||
|
// return String(reinterpret_cast<const char*>(conf.sta.ssid));
|
||||||
|
return String("iot");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get WiFi Password
|
||||||
|
String WIFI_GetPassword() {
|
||||||
|
// wifi_config_t conf;
|
||||||
|
// esp_wifi_get_config(WIFI_IF_STA, &conf);
|
||||||
|
// return String(reinterpret_cast<const char*>(conf.sta.password));
|
||||||
|
return String("Rijnstraat214");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to connect to WiFi
|
||||||
|
void WIFI_Connect() {
|
||||||
|
WiFi.disconnect();
|
||||||
|
Serial.println("Connecting to WiFi...");
|
||||||
|
WiFi.begin(WIFI_GetSSID().c_str(), WIFI_GetPassword().c_str());
|
||||||
|
|
||||||
|
for (int i = 0; i < 40; i++) {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay (250);
|
||||||
|
SHIELD.setLEDColor(black);
|
||||||
|
Serial.print(".");
|
||||||
|
delay (250);
|
||||||
|
SHIELD.setLEDColor(green);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.println("");
|
||||||
|
Serial.println("WiFi Connected");
|
||||||
|
Serial.println("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
} else {
|
||||||
|
Serial.println("Wifi NOT connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WIFI_StartAccessPoint() {
|
||||||
|
// Starts an access point with the specified name
|
||||||
|
// and goes into a blocking loop awaiting configuration
|
||||||
|
Serial.println("Starting Wifi AP");
|
||||||
|
//WiFiManager wifiManager;
|
||||||
|
//wifiManager.startConfigPortal(AP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// Program Setup
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Turn off the high voltage for the clock
|
||||||
|
SHIELD.hvEnable(false);
|
||||||
|
|
||||||
|
// Turn off DAC channels
|
||||||
|
dac_output_disable(DAC_CHANNEL_1);
|
||||||
|
dac_output_disable(DAC_CHANNEL_2);
|
||||||
|
dac_i2s_disable();
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
// Configure serial interface
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(1000);
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
pinMode(LATCH_ENABLE, OUTPUT);
|
||||||
|
|
||||||
|
// Setup SPI interface to defaults for shield
|
||||||
|
SPI.begin();
|
||||||
|
SPI.setDataMode (SPI_MODE2); // Mode 2 SPI
|
||||||
|
SPI.setClockDivider(2000000); // SCK = 2MHz
|
||||||
|
|
||||||
|
// Reset saved settings for testing purposes
|
||||||
|
// Should be commented out for normal operation
|
||||||
|
// wifiManager.resetSettings();
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_AP_STA);
|
||||||
|
Serial.println(WIFI_GetSSID());
|
||||||
|
Serial.println(WIFI_GetPassword());
|
||||||
|
|
||||||
|
// If WiFi setup is not configured, start access point
|
||||||
|
// Otherwise, connect to WiFi
|
||||||
|
if (0 == WIFI_GetSSID().length()) {
|
||||||
|
WIFI_StartAccessPoint();
|
||||||
|
} else {
|
||||||
|
WIFI_Connect();
|
||||||
|
}
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
initNTP(SHIELD);
|
||||||
|
|
||||||
|
// Set all LEDs to black or off
|
||||||
|
SHIELD.setLEDColor(black);
|
||||||
|
|
||||||
|
// Turn off the dots
|
||||||
|
SHIELD.dotsEnable(false);
|
||||||
|
|
||||||
|
// Turn on the high voltage for the clock
|
||||||
|
SHIELD.hvEnable(true);
|
||||||
|
|
||||||
|
// Do the anti poisoning routine
|
||||||
|
SHIELD.doAntiPoisoning();
|
||||||
|
|
||||||
|
Serial.println("\nReady!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************
|
||||||
|
// Program Loop
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
unsigned long nextConnectionCheckTime = 0;
|
||||||
|
int previousSecond = 0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Check WiFi connectivity
|
||||||
|
if (millis() > nextConnectionCheckTime) {
|
||||||
|
Serial.print("\n\nChecking WiFi... ");
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.println("WiFi connection lost. Reconnecting...");
|
||||||
|
WIFI_Connect();
|
||||||
|
} else {
|
||||||
|
Serial.println("Wifi connected");
|
||||||
|
}
|
||||||
|
nextConnectionCheckTime = millis() + WIFI_CHK_TIME_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process button status
|
||||||
|
SHIELD.processButtons();
|
||||||
|
|
||||||
|
// If set button is pressed, enter Wifi AP mode
|
||||||
|
if (SHIELD.isSetButtonClicked()) {
|
||||||
|
WIFI_StartAccessPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If set button is long-pressed, restart ESP
|
||||||
|
if (SHIELD.isSetButtonLongClicked()) {
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the display only if time has changed
|
||||||
|
if (timeStatus() != timeNotSet) {
|
||||||
|
if (second() != previousSecond) {
|
||||||
|
previousSecond = second();
|
||||||
|
|
||||||
|
// Display updated once a second
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
11
esp32nixie/test/README
Normal file
11
esp32nixie/test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||||
Reference in New Issue
Block a user