initial commit

This commit is contained in:
2022-05-03 07:21:56 +02:00
commit 228bfd031e
13 changed files with 1408 additions and 0 deletions

32
.gitignore vendored Normal file
View 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
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

BIN
esp32nixie/doc/hv5122.pdf Normal file

Binary file not shown.

39
esp32nixie/include/README Normal file
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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