The full sketch that I compiled in the IDE:
#define HV5522
#define SP352
#include "dummy.h"
// All updated to drive two HV5622 chips with 64 outputs to provide 6 digis on SP=101 Panaplex displays with colons, dots and most commas etc.
// 28/06/2020 - still in lockdown
// Original code written in response to 2 Tube Clock required challenge by Roddy Scott - I knew I'd get to use those 74141's one day!
//
// Use 3 GPIO's to drive Shift Register that in turn will drive 2 x 4 bit BCD converters (74141) to drive 2 x Nixie Tubes directly. UPDATED to single HV5622 in May 2020
// Use 1 x GPIO's to PWM drive Opto Couplers for individual tube brightness - Removed Opto Couplers and using blanking signal on HV5622 in May 2020
// Use 2 x GPIO's to I2C coupled to a BMP280 temp/pressure sensor
// Use 1 x GPIO to drive a single (or multiple) Neopixels WS2811 - NOT ON PANAPLEX VERSION
// Use 1 x GPIO to connect a PIR module
//
// 02/03/2019 - converted to traditional time library sync method - using PJRC example Code
// 04/03/2019 - added timezone library to deal with timezones!
// 05/03/2019 - tidied up some things including seconds display (no longer send redundant first loop second) and created a variable clockTuner that can be used to help match seconds display with another clock
// 06/03/2019 - set up function for nightMode instead of multiple checks
// 16/03/2019 - Added code to deal with PIR connected to D5, clock must work with NO PIR connected AND must turn off after suitable delay if PIR is connected
// 16/03/2019 - Removed WiFi resetting code - not really needed - redeployed pin for PIR connection
// 20/03/2019 - Transitioned from using WiFi Manager to using ESPAsyncWiFiManager and associated librarys - had to add ESPAsyncWebServer.h, ESPAsynWiFiManager.h and ESPAsyncTCP.h libraries to the IDE
// 31/03/2019 - Checked PIR to find that when clock goes off, Also extended PIR on time to 5 minutes.
// 25/04/2019 - Changed so that PIR can activate 24x7 and the delaytime varies depending on whether it is daytime or nighttime (nightmode)
// 28/06/2020 - Code re-deployed for 6 x Beckman SP-101 displays (or similar)- need to consult the SP-151 Code to work out how I did it and do the same in here.
// 06/07/2020 - V07 - Mostly there - last thing I want to look at in this version is the main timeing loop and remove dependance on NonBlock counters and move over to updating time on second change only
// Need to anaylse main loop and be sure about where setLocal is being used - use only once per loop itteration - check in the various voids displaytime and displaydate.
// then at some point in the main loop - only call displaytime if the second has changed.
// 29/11/2020 Changed to read BME sensor instead - because that is what I seem to have now!
//
// 01/12/2020 Added humidity now that there is a BME sensor
// 06/12/2020 Moved to Sperry SP-353 code base for 6 digit SP-353 based clocks
// 15/12/2020 Findally got some meaningful data displayed - now on to tidying up the code
// Moved to V03 - remove all redundant code (neopixel stuff)
// Added DEF for BME or BMP - set only 1 and it will control what library is loaded and how it is used and whether or not humidity is displayed.
// 31/12/2020 Moved to V04 - added def for SP-353 or SP-352 as there is an error in the footprint for the SP-352 which I need to fix in software - B/F and E/C swapped on left hand digit.
// 03/01/2021 Moved to V03 - taking out some of the old LED requests and start looking to see if I can get status info displayed on power up.
// 12/01/2021 Moved to V06 - now I've worked out how to get things displayed on startup!
// 13/01/2021 Moved to V7, changing colon bits in setAllBits to reflect the six decimal points available
// SetTransitionBits still uses CTR, CTL etc
// 16/02/2021 Saved as Beckman352x8v07 to move to 8 digits on two drivers for SP-352 and SP-356 displays.
// Because some of the 8 digits were spread across two drivers I changed the code to use a single 64 bit variable 'L' which has its bits set and is shifted out all together.
// 18/02/2021 Moved to V08 as I start to sort out the display - having got the basic character shapes working!
// 21/02/2021 Moved to V09 as I want to add compile switch to set time display to HH-MM-SS instead of <blank> HHMMSS <blank>
// 25/02/2021 Moved to V10 - added compile switch for HV5622 drivers
// 15/04/2021 WIth help from Nick Stock - finally (it seems) got it working with HV5622 drivers - nothing obvious (to me) about the trnsition from 5522 to 5622 !!!!
// 17/04/2021 Moved to V11 - adding startup segment test routine
// 09-22-2023 Changed from using 'l' to BitArray, also changed pointers array for 4 x SP-332 displays, also added name scroll stuff
// 09-29-2023 Fixed the scolling name stuff having finally plugged a sensor in!
//
// 03-05-2024 This code is the combination of the following two files:
// Nixology_SP_352_356_8_digit_Panaplex_03_12_24
// Nixology_SP_332_8_digit_Panaplex_03_12_24
// No use a #define to choose between the two when compiling
// New Code is Panaplex_8_Digit - committed to New Repo
//
// 05-08-2024 Moved into PlatformIO
// USER CHANGES - JEFF
// 07-07-2023 Begin to change configuration to support USA parameters
// 07-09-2023 Added additional display character definitions; changed all instances of HH-MM-SS to HH_MM_SS to fix compiler warnings
// 07-17-2023 Move all includes to top of file
// 07-19-2023 Added sensor detection and logic to display humidity only with the BME sensor type
// 07-19-2023 Added serial monitor test display of sensor type and status - noSenSor; E-SenSor; P-SenSor
// 07-23-2023 Converted the clock to display degrees F and inches of mercury
// 07-23-2023 Correction to displaytest to exercise segments and decimals
// 07-23-2023 Added Nixieshift controls for individual decimal points
// 07-23-2023 Added logic for 12/24 hour Display; added variable to adjust scroll speed
// 07-23-2023 Added variable to set what second if clock would trigger date or environmentals
// 07-23-2023 Added sweeping decimals during AP Connection on start-up
// 07-24-2023 Spaced the Hg for the pressure display
// 08-09-2023 Added tail to 9 - add in definition and transition sequence.
// 08-09-2023 Added variables to set imperial or metric in one place in the environmental area.
// 08-09-2023 Added metric or US Units to initial display after sensor type
// 10-14-2023 Moved date right one place; Scroll name - JEFF -; set startup rotation of segments to five times
// 10-23-2023 Commented the zerobits references - function not needed and causes compiler warnings
// 10-23-2023 Added horizontal and vertical segments to startup test routine
// 10-26-2023 Added option to skip the name message display
// 10-29-2023 Fixed metric temp to show tenths of a degree
// 11-03-2023 Date now selected by the environmental settings for metric or imperial
// Metric displays DDdd-MMMM-YYYY; imperial (US Units) displays MMMM-DDdd-YYYY
// 11-18-2023 IP Address displays as a scroll to the left instead of two blocks
// 12-24-2023 Added new scrolling display - JEFFS SP-332 -
// 02-07-2024 Added brightness control to the user settings (0-1023 range)
// 03-12-2024 Cleaned up the Time Zone and DST Section
// ALL LIBRARY INCLUDES AT TOP OF CODE---------------------------------------------------
// includes for time
#include <TimeLib.h>
// includes for timezone and DST calculations - change your rules here - see the timezone library documentation for details
#include <Timezone.h>
// includes for WiFi
// #include <ESP8266WiFi.h>
// #include <WiFiUdp.h>
#include <DNSServer.h>
// includes for web gui
#include "EEPROMConfig.h"
#include "web.h"
#include "configs.h"
// OTA Include:
#include <ArduinoOTA.h>
// Includes for AsyncWifi Manager
// #include <ESPAsyncWebServer.h>
#include <ESPAsyncWiFiManager.h>
AsyncWebServer server(80);
DNSServer dns;
// includes etc for BMP280/BME280
// #include <Wire.h>
// #include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Adafruit_BMP280.h>
// for the external eeprom read
//#include <wire.h> //for eeprom
int wordtext[4]; // used to store returned word
int wordpointer=0; // points to element of returned word to store it
#define eepromaddr 0x50 //defines the base address of the external EEPROM
// NeopixelBus Stuff
#include <NeoPixelBus.h>
const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 128
NeoPixelBus<NeoGrbFeature, NeoWs2812xMethod> strip(PixelCount, PixelPin);
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);
HslColor hslRed(red);
HslColor hslGreen(green);
HslColor hslBlue(blue);
HslColor hslWhite(white);
HslColor hslBlack(black);
// end of neopixelbus stuff
//defines the readEEPROM function
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) {
byte rdata = 0xFF;
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); //writes the MSB
Wire.write((int)(eeaddress & 0xFF)); //writes the LSB
Wire.endTransmission();
delay(15);
Wire.requestFrom(deviceaddress,1);
if (Wire.available()) {
Serial.println("Wire Available");
rdata = Wire.read();
};
return rdata;
}
//-------------------------------------------------------------------------------------------------------------
// CUSTOMIZATION AREA FOR TWEAKING CLOCK OPERATION BETWEEN THE HASHED LINES
//*****ENVIRONMENTAL VARIABLES*****
// These variables can be used to fine tune the temp and pressure readings to known source
// HUMIDITY OFFSET - PERCENTAGE RH
int humidityoffset = 11; // Calibrate the sensor in percentage
//*****Metric Section*****
// #define metricunits //uncomment for metric units
// PRESSURE OFFSET - PASCALS **METRIC**
// int PascalOffset = 0; //Pressure offset in Pascals if using that system (default = 0) Adjust for altitude
// TEMPERATURE OFFSET - CENTIGRADE
// float CelciusOffset = 0; //Temp offset in degrees C if displaying Centigrade - to calibrate sensor (default = 0)
//*****Imperial Section*****
// #define imperialunits //uncomment for imperial units
// PRESSURE OFFSET - INCHES OF MERCURY **IMPERIAL** Set for Altitude
// int inHgoffset = 98; //Pressure offset inches of Mercury/100 if displaying inches of Hg - to calibrate sensor (default = 0 at sea level)
// Example: 100 will change pressure from 29.50 to 30.50. Altitude offset is 100 for every 1000 feet elevation. Flagstaff add about 648.
// Barometric pressute is normalized to sea level.
// TEMPERATURE OFFSET - FAHRENHEIT
// float FahrenheitOffset = -11.9; //Temp offset in degrees F if displaying Fahrenheit - to calibrate sensor (default = 0) - Clock will add heat
//*****DISPLAY AND APPEARANCE VARIABLES*****
// int Maxbright = 400; //Set clock maximum Display brightness (max=1023 default 400)
// What second do we want to display date and time? Note: Setting to less than 9 causes the date and environmentals to display each time
// int datedisplaysecs = 8; //set to the second that you want the date and environmentals displayed - Set to 9 or greater!
// 12 OR 24 HOUR OPERATION - Used to change between 12 and 24 hour time system - "0" is for 24 hour time and "12" is for 12 hour time
// int timesystem = 12;
// TIME TWEAK - Set ClockTuner to a number of seconds that you want to add or subtract from the displayed time in order to make it match another clock
// int clockTuner = 1;
// SCROLL SPEED - milliseconds per step. Default is 90. Lower number like 50 is faster, higher number like 120 is slower
// Used to control the speed that the display shifts left for the year and barometric pressure
int scrollspeed = 90;
// HOURS OF OPERATION AND PIR
// Operational Hours setup - change values here according to when you would like the clock to be active
// If you want the clock to be always on then set firsthour to 0 and lasthour to 23, night mode will never activate!
// int firsthour = 0; //clock comes on at 07:00
// int lasthour = 23; //clock goes off at 00:00, ie - last hour on is 23.00
// PIR ACTIVATION - Set the time (in minutes) the clock will remain active following PIR activation
// Now have different delays for day and Night time
// int DayclockONtime = 10;
// int NightclockONtime = 4;
// DISPLAY DATE OR ENVIRONMENTALS - on alternate minute cycles. Set "1" to alternate or "0" for date only
int dispDate = 1;
// SHOW SCROLLING NAME MESSAGE -
// If set to show message - Display order: Date --> Message --> Environmentals (3 minute cycle)
// if set to not show message - Display order: Date --> Skip Message --> Environmentals (3 minute cycle)
int showname = 1; // To show the name message, set showname to "1"; To skip, set to "0"
// All New New string processing vars
char buf[70]; // made 5 longer than string max length - definable string is 40 - 12 digit clock adds 12+12
String MessageString1;
// HTML items will be showin in this order
BaseConfigItem *clockSet[] = {
// Clock
&firsthour,
&lasthour,
&DayclockONtime,
&NightclockONtime,
×ystem,
&leadingzero,
&ordinals,
&timezone,
&metricUnits,
&PressOffset,
&TempOffset,
&clockTuner,
&Maxbright,
&datedisplaysecs,
&MessageString,
0};
// A composite config item can contain other config items (including other CompositeConfigItems)
// It is just an easy way to group config items together
CompositeConfigItem clockConfig("clock", 0, clockSet);
// This controls saving config items to EEPROM and retrieving them from there.
EEPROMConfig config(clockConfig);
//*************PRE-DEFINE VOIDS******
void pirTubes();
void nonBlock(unsigned long delay_time);
void bitArrayWrite(const unsigned int index, const boolean value);
void setLocalTime();
// void fillMessageTime(int sgap2);
void bitbashTX();
void setAllBits(uint8_t aa, uint8_t bb, uint8_t cc, uint8_t dd, uint8_t DP1, uint8_t DP2, uint8_t DP3, uint8_t DP4);
void disptest();
void nixieshift(uint8_t d, uint8_t c, uint8_t b, uint8_t a, uint8_t dpstatus);
void pirChange();
void displaytime();
void displaytemp();
void displayhumidity();
void displaypressure();
void displaydate();
void displayword();
void zerobits();
void startup();
void displayname();
void dimtube();
void pixelsOff();
//void colorWipe(uint32_t c, uint8_t wait);
//*****CHOOSE TIMEZONE SERVER - TIMEZONE - DST START/STOP SETTINGS*****
// NTP SERVER: - Select appropriate NTP Time Server for Connection
static const char ntpServerName[] = "us.pool.ntp.org";
// static const char ntpServerName[] = "europe.pool.ntp.org";
// Australia Eastern Time Zone (Sydney, Melbourne)
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours
Timezone ausET(aEDT, aEST);
// Moscow Standard Time (MSK, does not observe DST)
TimeChangeRule msk = {"MSK", Last, Sun, Mar, 1, 180};
Timezone tzMSK(msk);
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone CE(CEST, CET);
// United Kingdom (London, Belfast)
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time
Timezone UK(BST, GMT);
// UTC
TimeChangeRule utcRule = {"UTC", Last, Sun, Mar, 1, 0}; // UTC
Timezone UTC(utcRule);
// US Eastern Time Zone (New York, Detroit)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; // Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; // Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);
// US Central Time Zone (Chicago, Houston)
TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300};
TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360};
Timezone usCT(usCDT, usCST);
// US Mountain Time Zone (Denver, Salt Lake City)
TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360};
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
Timezone usMT(usMDT, usMST);
// Arizona is US Mountain Time Zone but does not use DST
Timezone usAZ(usMST);
// 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 usPT(usPDT, usPST);
std::map<String, Timezone &> timeZones = {
{"Aus Eastern", ausET},
{"EU Central", CE},
{"Moscow", tzMSK},
{"UK", UK},
{"US Eastern", usET},
{"US Central", usCT},
{"US Mountain", usMT},
{"US Arizona", usAZ},
{"US Pacific", usPT},
{"UTC", UTC}};
//-----------------------------------------------------------------------------------------
// How to set Timezone and DST calculations
// Define a TimeChangeRule for Standard(STD) or Daylight Saving Time(DST) as follows:
// TimeChangeRule myRule = {abbrev, week, dow, month, hour, offset};
// Where:
// abbrev is a character string abbreviation for the time zone; it must be no longer than five characters.
// week is the week of the month that the rule starts.
// dow is the "day of week" that the rule starts.
// hour is the hour in local time that the rule starts (0-23).
// offset is the UTC offset in minutes for the time zone being defined.
// For convenience, the following symbolic names can be used:
// week: First, Second, Third, Fourth, Last
// dow: Sun, Mon, Tue, Wed, Thu, Fri, Sat
// month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
//-----------------------------------------------------------------------------------------
// Timezone ukTZ(myDST, mySTD);
// TimeChangeRule *tcr; // pointer to the time change rule, use to get TZ abbrev
//-------------------------------------------------------------------------------------------------------------
// Declare Variables
/*const int namelength = 24; // (8 blanks) + (8 characters) + (8 blanks)
//Variables for scrolling name // "- JEFF -" (36, 35, 17, 21, 20, 20, 35, 36) = eight characters
int nametext[namelength] = { 35, 35, 35, 35, 35, 35, 35, 35, 36, 35, 17, 21, 20, 20, 35, 36, 35, 35, 35, 35, 35, 35, 35, 35 };
int namescroll = 16; // the number of scroll steps to get the name in and out again (# of characters in the message + 8 initial blanks)
*/
const int namelength = 32; // (8 blanks) + (16 characters) + (8 blanks)
// Variables for scrolling name // "- JEFFS SP-332 -" (36, 35, 17, 21, 20, 20, 35, 36) = eight characters
int nametext[namelength] = {35, 35, 35, 35, 35, 35, 35, 35, 36, 35, 17, 21, 20, 20, 15, 35, 15, 25, 46, 3, 3, 2, 35, 36, 35, 35, 35, 35, 35, 35, 35, 35};
int namescroll = 24; // the number of scroll steps to get the name in and out again (# of characters in the message + 8 initial blanks)
int temphour = 0; // Used to set the hours in display routine based on 12 or 24 hour time
int hundredchar = 0; // variables used reading temp sensor
int hundredsreadtemp = 0;
int tensreadtemp = 0;
int unitsreadtemp = 0;
float readtemp = 0;
int tenthsreadtemp = 0;
int readpres = 0; // variables for reading pressure from the sensor
int unitsreadpres = 0;
int tensreadpres = 0;
int hundredsreadpres = 0;
int thousreadpres = 0;
// declare variables for timezone
time_t utc;
time_t local;
// Variables that define the decimal points in the displays. There are 8. These are reset after each nixieshift - "0"=off, "1"=on
// Need to define the bit set in void setallbits
int dpc1 = 0; // control for dp1 (Decimal Point Control) 1 (left decimal)
int dpc2 = 0; // control for dp2 (Decimal Point Control) 2 (second from left)
int dpc3 = 0; // control for dp3 (Decimal Point Control) 3 (third from left)
int dpc4 = 0; // control for dp4 (Decimal Point Control) 4 (fourth from left)
int dpc5 = 0; // control for dp5 (Decimal Point Control) 5 (fourth from right)
int dpc6 = 0; // control for dp6 (Decimal Point Control) 6 (third from right)
int dpc7 = 0; // control for dp6 (Decimal Point Control) 7 (second from right)
int dpc8 = 0; // control for dp6 (Decimal Point Control) 8 (right decimal)
/*PIN Mapping Guide - Just a note to remind me of the physical to numerical mapping of GPIO/s on a NodeMCU
const uint8_t D0 = 16; Spare - really? - now Allocated to PIR detection - initialise as input with Pullup - so it is permanently triggered when nothing is connected.
const uint8_t D1 = 5; SCL
const uint8_t D2 = 4; SDA
const uint8_t D3 = 0; neopixels
const uint8_t D4 = 2; pwmpin
const uint8_t D5 = 14; PIR - connect if desired
const uint8_t D6 = 12; datapin to Shift Register
const uint8_t D7 = 13; clockpin to Shift Register
const uint8_t D8 = 15; latchpin to Shift Register
const uint8_t D9 = 3; Not Available on Wemos D1 (or is it?)
const uint8_t D10 = 1; Not Available on Wemos D1 (or is it?)
*/
// define debug - various testing purposes - generally writes more to the display
// #define debug
// define for display mode of time on 8 digits
// #define HH-MM-SS (changed all instances of HH-MM-SS to use HH_MM_SS. Fixes compiler warning and autoformat issues)
#define HH_MM_SS
// define which drivers are being used - only use one of the following two #defines
// #define HV5622rev
// #define HV5522
// define which clock type - used for startup display and OTA setup SP352 or SP332
// #define SP332 - moved to platformio.ini
// #define SP352
// use this to control colon status when displaying time - set to 1 to display colons
bool colonstatus = false;
// PWM Output pin
int pwmPin = D5; // D5
// PIR Stuff
const int PIRpin = D4; // set to use D4
int clockStatus = HIGH; // should the clock be on?
int PIRstatus = HIGH; // used to record PIR Status - set to 1 if active - deals with situation where no PIR is connected
int prevPIRstatus = LOW; // This should force a change detected on first pass due to pullup if PIR not connected
int currentPIRstatus; // used to see what the current status of the PIR pin is - if it never goes low (no PIR) then clock will never switch off.
int tubeStatus; // used to determine whether tubes should be illuminated accorging to clockStatus and PIRstatus
unsigned long triggerTime;
unsigned long daydelay;
unsigned long nightdelay;
unsigned long delaytime; // used to store either daydelay or night delay - depending on the time of day.
// Vars used to compute IP Address for display on startup
int dpos1;
int dpos2;
int dpos3;
String ippart1;
String ippart2;
String ippart3;
String ippart4;
// var to pad seconds display in the last 10 seconds of the minute
int extraSecs;
// var to store the current second - used to see if second has changed and then display the updated time.
int oldSecond = 0;
// declare function to return nightMode
// Add code here to make it not night mode if the PIR has been tripped - ie PIRstatus goes to LOW
// With NO PIR connected, ClockStatus, PIRStatus and tubeStatus always = HIGH
// With PIR connected and PIR triggered then ClockStatus and tubeStatus are HIGH, PIRstatus is LOW
// With PIR connected and PIR not triggered then PIRstatus goes HIGH
// We want to say that, if it is nightmode the say so, unless PIR has been trigered.
//
// Also consider that, if night time hours are valid then PIR Timer should be set to a shorter time
boolean nightMode()
{
if ((hour(local) < firsthour || hour(local) > lasthour) && PIRstatus == HIGH)
return true;
else
return false;
}
// Another night mode function that is used to determine whether the PIR delay is (for example) 1 minute during night and 5 minutes during day.
boolean nightModeDelay()
{
if (hour(local) < firsthour || hour(local) > lasthour)
return true;
else
return false;
}
WiFiUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
time_t getNtpTime();
void sendNTPpacket(IPAddress &address);
Adafruit_BME280 bme; // I2C Initialize both BMP and BME to detect the installed sensor
Adafruit_BMP280 bmp; // I2C Initialize both BMP and BME to detect the installed sensor
int FoundSensor = 0; // Variables for sensor detection
int FoundSensorP = 0; // Variables for sensor detection
int SensorType = 0; // Variables for sensor detection
int foundBMP = 1; // set initially to 1 - gets set to 0 later if BMP is NOT found
uint8_t currentValue = 0;
// const byte DIpin = 13; // MOSI, D7
// const byte CLpin = 14; // SCK, D5
const byte LEpin = 16;
int latchPin = D0; //16; //D0
int clockPin = D6; //12; //D6
int dataPin = D7; //13; //D7
int blankpin = D5; //14; //D5
unsigned char BitArray[64 / 8]; // space for 64 bits
unsigned char OldBitArray1[64 / 8]; // space for 64 bits - to be remembered for fade out purposes
unsigned char OldBitArray2[64 / 8]; // space for 64 bits - to be remembered for fade out purposes
// These variables are used to store the previous displayed number when performing a transition so that, if the number isn't changing then it keeps displaying the same number; otherwise it displays transitional number patterns
uint8_t olda = 0;
uint8_t oldb = 0;
uint8_t oldc = 0;
uint8_t oldd = 0;
uint8_t olde = 0;
uint8_t oldf = 0;
uint8_t oldg = 0;
uint8_t oldh = 0;
//
//
// variables used in stages of transtion
uint8_t stagea = 0;
uint8_t stageb = 0;
uint8_t stagec = 0;
uint8_t staged = 0;
uint8_t stagee = 0;
uint8_t stagef = 0;
uint8_t stageg = 0;
uint8_t stageh = 0;
//
// variable used to store current second - used in main loop to determine whether or not the displaytime should be called.
uint8_t storedsecond = 0;
// When calling nixieshift from void displaytime, remember to store the new digits as old ones at the end of performing the transitionv
// Modified below for Beckman SP-352 x 4 displays
// Going to have to re-think this because (I believe that) some digits are spread across the two shift registers - well that's bad planning!
// No Drama, re-organise into a single [64] element array and then have one set of pointers to all the 'bits' - then we shall just have to send all 64 bits out in one go!
#ifdef HV5522
#ifdef SP352 // actually now for 4 x B7971
int pointers[64] = { 38, 41, 40, 37, 46, 34, 45, 48, 42, 39, 43, 35, 44, 36, 47,
53, 56, 55, 52, 61, 49, 60, 63, 57, 54, 58, 50, 59, 51, 62,
12, 0, 14, 11, 5, 8, 4, 7, 1, 13, 2, 9, 3, 10, 6,
27, 15, 29, 26, 20, 23, 19, 22, 16, 28, 17, 24, 18, 25, 21,
30, 31, 32, 33};
//int fader[15] = {1, 8, 9, 2, 10, 3, 11, 12, 4, 13, 5, 14, 6, 7, 8};
int fader[15] = {0, 7, 8, 1, 9, 2, 10, 3, 14, 11, 12, 4, 13, 5, 6};
#endif
// digits are 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 39 30 31
// 4 4 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#ifdef SP332
int pointers[64] = {54, 53, 43, 41, 40, 55, 52, 42, 50, 49, 47, 45, 44, 51, 48, 46, 62, 61, 35, 33, 32, 63, 60, 34, 58, 57, 39, 37, 36, 59, 56, 38,
6, 5, 27, 25, 24, 7, 4, 26, 2, 1, 31, 29, 28, 3, 0, 30, 14, 13, 19, 17, 16, 15, 12, 18, 10, 9, 23, 21, 20, 11, 8, 22};
#endif
#endif
// digits are 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 39 30 31
// 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
// define an array of segments to build and then clear for cirlce thing
// For the 'outer ring' there are 8 top and bottom, 2 left and right so 20 in total - and yes, I know for 20 - we only need 19 (includes element '0') but - hey ho
// start top - right of centre - segment 'a' on 4th digit from right No.4 in above definition
int circle[20] = {32, 40, 48, 56, 57, 58, 59, 51, 43, 35, 27, 19, 11, 3, 4, 5, 0, 8, 16, 24};
// The data below has been tested by Nick Stock - he used HV5622's in palce HV5522's - took ages to work out what was going on but with many videos back and forth between UK and US - we think it is sorted!
// Phew!
#ifdef HV5622rev
int pointers[64] = {10, 11, 22, 21, 23, 8, 9, 20, 14, 15, 17, 18, 19, 12, 13, 16, 2, 0, 31, 30, 4, 3, 1, 29, 7, 57, 26, 28, 5, 6, 56, 27,
59, 58, 25, 24, 38, 61, 60, 39, 63, 33, 34, 36, 37, 62, 48, 35, 50, 52, 46, 47, 32, 49, 51, 45, 54, 55, 41, 43, 42, 53, 40, 44};
#endif
// digits are (rev) 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 39 30 31
// 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
// digits are 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 39 30 31
// 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8
// A B C D E F G P A B C D E F G P A B C D E F G P A B C D E F G P
// 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
// int pointers[64] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 ,53 ,54 ,55, 56, 57, 58, 59, 60, 61, 62, 63 };
// offsets in pointers are NOT 0,10,20
// Character shapes for digits 0-9 using 'standard' seven segment notation, Initially set to 10 parts for the digits 0-9, expanded to include 'Cdhnrst' for st, nd, rd, th and degrees C
// May increase this yet further if I decide to show month as Jan, Feb, March, April, May, June, July, August Sept Oct Nov Dec
// set blankdigit to number of available digits+1 - this is used to make no segments light
uint8_t blankdigit = 136;
// Segments
// GFEDCBA
int sevenseg[136] = {
// numbers 0-9 in positions 0-9 for hisoric reasons
0b111111001000100, // 0
0b011000001000000, // 1
0b100110001000010, // 2
0b101100001100000, // 3
0b011001000100010, // 4
0b100101000010010, // 5
0b101111000100010, // 6
0b100000001000100, // 7
0b111111000100010, // 8
0b111101000100010, // 9
0b000000000000000, //blank 10
0b000000000000000, //blank 11
0b000000000000000, //blank 12
0b000000000000000, //blank 13
0b000000000000000, //blank 14
0b000000000000000, //blank 15
0b000000000000000, //blank 10
0b000000000000000, //blank 11
0b000000000000000, //blank 12
0b000000000000000, //blank 13
0b000000000000000, //blank 14
0b000000000000000, //blank 15
0b000000000000000, //blank 16
0b000000000000000, //blank 17
0b000000000000000, //blank 18
0b000000000000000, //blank 19
0b000000000000000, //blank 1A
0b000000000000000, //blank 10b
0b000000000000000, //blank 1C
0b000000000000000, //blank 1D
0b000000000000000, //blank 1E
0b000000000000000, //blank 1F
0b000000000000000, //blank 20 - SPACE
0b011000000000000, // 21 !
0b011000000000000, // 22 " - not yet defined
0b000011010001010, // 23 #
0b011000000000000, // 24 $ - not yet defined
0b001001101110110, // 25 % - not yet defined
0b000011010001010, // 26 & - not yet defined
0b000000100000000, // 27 '
0b000000001010000, // 28 (
0b000000100000100, // 29 )
0b000000111111110, // 2A *
0b000000010101010, // 2B +
0b000000000000100, // 2C ,
0b000000000100010, // 2D -
0b000000000000001, // 2E . not yet defined properly - this is underscore
0b000000001000100, // 2F /
0b111111001000100, // 30 0
0b011000001000000, // 31 1
0b100110001000010, // 32 2
0b101100001100000, // 33 3
0b011001000100010, // 34 4
0b100101000010010, // 35 5
0b101111000100010, // 36 6
0b100000001000100, // 37 7
0b111111000100010, // 38 8
0b111101000100010, // 39 9
0b000000001010000, // 3A : - not yet defined
0b000000001010000, // 3B ; - not yet defined
0b000000001010000, // 3C <
0b000100000100010, // 3D =
0b000000100000100, // 3E >
0b110001000101000, // 3F ?
0b111110000001010, // 40 @
0b111011000100010, // 41 A
0b111100010101000, // 42 B
0b100111000000000, // 43 C
0b111100010001000, // 44 D
0b100111000000010, // 45 E
0b100011000000010, // 46 F
0b101111000100000, // 47 G
0b011011000100010, // 48 H
0b100100010001000, // 49 I
0b011110000000000, // 4A J
0b000000011011000, // 4B K
0b000111000000000, // 4C L
0b011011101000000, // 4D M
0b011011100010000, // 4E N
0b111111000000000, // 4F O
0b110011000100010, // 50 P
0b111111000010000, // 51 Q
0b100011001010010, // 52 R
0b101101000100010, // 53 S
0b100000010001000, // 54 T
0b011111000000000, // 55 U
0b000011001000100, // 56 V
0b011011000010100, // 57 W
0b000000101010100, // 58 X
0b000000101001000, // 59 Y
0b100100001000100, // 5A Z
0b100111000000000, // 5B [
0b000000100010000, // 5C "\"
0b111100000000000, // 5D ]
0b010000001000000, // 5E ^
0b000100000000000, // 5F _
0b000000010000001, // 60 ' - need to check
0b111110000100010, // 61 a
0b000111000010010, // 62 b
0b000110000100010, // 63 c
0b011100000100100, // 64 d
0b100111000000010, // 65 e
0b100011000000010, // 66 f
0b111100100100000, // 67 g
0b001011000100010, // 68 h
0b000000000001000, // 69 i
0b011100000000000, // 6A j
0b000000011011000, // 6B k
0b000000010001000, // 6C l
0b001010000101010, // 6D m
0b000010000010010, // 6E n
0b001110000100010, // 6F o
0b100011001000010, // 70 p
0b110001000110010, // 71 q
0b000000000101000, // 72 r
0b101100100100000, // 73 s
0b000111000000010, // 74 t
0b001110000000000, // 75 u
0b000010000000100, // 76 v
0b001010000010100, // 77 w
0b000000101010100, // 78 x
0b011100100100000, // 79 y
0b100100001000100, // 7A z
0b000000001010010, // 7B {
0b001000100000000, // 7C ! - to be tested
0b000000100100100, // 7D }
};
// Standard definitions of 7 segment display characters 0-9 plus Cdhnrst - as at 29/06/2020
// Added additional letters to make months 01/07/2020
// use value of maxdigits to set blank digits for now
//
// Months as follows
// JAN 17,18,19
// FEb 20,21,22
// nnArch 13,13,18,14,24,12
// APri1 18,25,14,26,30
// nnAY 23,23,18,27
// JUNE 17,28,19,21
// JuLY 17,29,30,27
// AuGuSt 18,29,31,29,5,16
// 5EPT 5,21,25,16
// OCt 0,10,16
// Nou 19,0,29
// DEC 11,21,10
//
// these are used to calculate the st, nd, rd and th to add to the day number
uint8_t dayletter1 = 0;
uint8_t dayletter2 = 0;
// variables for diplaying certain items at certain times
int donedatetemp = 0; // have we displayed date or temp/pressure yet?
void setup()
{
EEPROM.begin(2048);
// config.setDebugPrint(&Serial);
config.init();
// clockConfig.debug(&Serial);
clockConfig.get(); // Read all of the config values from EEPROM
// clockConfig.debug(&Serial);
// set up SPI
//SPI.begin();
//SPI.setFrequency(100000);
//SPI.setBitOrder(MSBFIRST);
//SPI.setDataMode(SPI_MODE1);
// initialise serial comms
Serial.begin(9600);
Serial.println("");
Serial.println("FLW (4) digit Clock");
Serial.println("");
Serial.println("Reads status of BMP/BME Connection and runs accordingly");
Serial.println("Responds to PIR if connected");
Serial.println("Supports OTA programming");
Serial.println("");
// Set up neopixels
// this resets all the neopixels to an off state
strip.Begin();
strip.Show();
// display some colour for testing
strip.SetPixelColor(0, red);
strip.SetPixelColor(1, green);
strip.SetPixelColor(2, blue);
strip.SetPixelColor(3, hslWhite);
strip.Show();
nonBlock(1000);
strip.SetPixelColor(0, red);
strip.SetPixelColor(1, red);
strip.SetPixelColor(2, red);
strip.SetPixelColor(3, red);
strip.Show();
// eeprom raed testing
for(int readword=0; readword<10; readword++)
{
wordpointer = 0; // reset word pointer
//Now Read 4 bytes
for(int address = (readword * 4); address< (readword*4+4); address++) {
wordtext[wordpointer] = readEEPROM(eepromaddr, address);
wordpointer++;
}
// now print out contents of wordtext
Serial.print(wordtext[0]);
Serial.print(wordtext[1]);
Serial.print(wordtext[2]);
Serial.print(wordtext[3]);
Serial.println("");
}
Serial.println("Words Tested");
// set outputs for SPI
pinMode(LEpin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(blankpin, OUTPUT);
// For PIR detection
pinMode(PIRpin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PIRpin), pirChange, RISING); // If something changes then some motion was detected - start the timer in the main loop
//************************************************
// Call Display Test if desired!
startup();
//disptest();
//************************************************
#ifdef SP332
// set tubes to display startup message SP-332 - now centered OK
nixieshift(32, 83, 80, 45, 3, 3, 2, 32, 0);
analogWrite(pwmPin, Maxbright);
nonBlock(3000);
#endif
#ifdef SP352
// set tubes to display startup message 7971
// now display 7971
zerobits();
nixieshift(7, 9, 7, 1, 0);
analogWrite(pwmPin, Maxbright);
nonBlock(3000);
#endif
// Add call to startup display routine here:
// displaytest();
//
// now say 'AP' on the display and wait for connection - centred OK
// nixieshift(35, 35, 35, 18, 25, 35, 35, 35, 0); test - remove comment if fail
// Routine to add moving decimal points to the word 'AP-CON' when establishing wi-fi connection
// 'AP-COn__' with sweeping decimal points acts as progress bar
// loop the decimal sweep in pairs - 9 times before switching to 'CONECT'
// dp is temporary loop counter
// set tubes to display Connected message ' AP '
nixieshift(31, 65, 80, 32, 0);
//nonBlock(2000); // wait a moment
// WiFiManager
// Local intialization. Once its business is done, there is no need to keep it around
// WiFiManager wifiManager;
AsyncWiFiManager wifiManager(&server, &dns); // changed from line above
// reset saved settings
// wifiManager.resetSettings();
// Connect to WiFi
// fetches ssid and pass from eeprom and tries to connect
// if it does not connect it starts an access point with the specified name
// here
// and goes into a blocking loop awaiting configuration
#ifdef SP332
wifiManager.autoConnect("SP-332_x8_digit-ClockAP");
#endif
#ifdef SP352
wifiManager.autoConnect("FLW_4_digit-ClockAP");
#endif
// added delay to ensure that initial NTP request succeeds
nonBlock(2000);
createWebPages(server, &clockConfig, name2html);
// set tubes to display Connected message 'CNCT'
nixieshift(67, 78, 67, 84, 0);
nonBlock(2000); // wait a moment
Serial.println("Connected");
Serial.print("IP number assigned by DHCP is ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(Udp.localPort());
// set tubes to display IP address
// use the .toString thing
String ipstring = WiFi.localIP().toString();
Serial.print("Wifi String = ");
Serial.println(ipstring);
Serial.println("");
// next job, split the string into indiviadual octets so that the clock can display aaaa.bbbb then cccc.dddd so that the IP address is displayed for the user
// ultimately this will be helpful in accessing the web config page - one day !
// depends on whether the returned string is always nnn.nnn.nnn.nnn - or could it be nn.nnn.n.nn?
// if the former then just pluck the groups of three digits out.
// if the latter then get the positions of the three dots and the length of the string then divi it up based on that - see the code in Teensy for parsing the GPS string.
// get positions of the three DP's in the string to break it up correctly
// pad each octet to 'nnn' if not long enough
// use nixieshift to send out the parts
// need to add code to set the DP's on in the right place. Need DP on 3rd digit only
dpos1 = ipstring.indexOf(".");
dpos2 = ipstring.indexOf(".", dpos1 + 1);
dpos3 = ipstring.indexOf(".", dpos2 + 1);
/*
Serial.println(dpos1);
Serial.println(dpos2);
Serial.println(dpos3);
*/
ippart1 = ipstring.substring(0, dpos1);
ippart2 = ipstring.substring(dpos1 + 1, dpos2);
ippart3 = ipstring.substring(dpos2 + 1, dpos3);
ippart4 = ipstring.substring(dpos3 + 1, ipstring.length());
/*
Serial.println(ippart1);
Serial.println(ippart2);
Serial.println(ippart3);
Serial.println(ippart4);
*/
// Pad the strings if necessary, ie if octet was <100 or <10 it would need to be 099 or 009
if (ippart1.length() < 3)
ippart1 = "0" + ippart1;
if (ippart1.length() < 3)
ippart1 = "0" + ippart1;
if (ippart2.length() < 3)
ippart2 = "0" + ippart2;
if (ippart2.length() < 3)
ippart2 = "0" + ippart2;
if (ippart3.length() < 3)
ippart3 = "0" + ippart3;
if (ippart3.length() < 3)
ippart3 = "0" + ippart3;
if (ippart4.length() < 3)
ippart4 = "0" + ippart4;
if (ippart4.length() < 3)
ippart4 = "0" + ippart4;
// use colon type 3 for this
// need to modify setallbits to cope with the additional colon parts
// put out 'IPAD' first
nixieshift(73, 80, 65, 68, 0);
nonBlock(1000);
// Display IP address in 4 separate blocks!
nixieshift(ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(),32,0);
nonBlock(1000);
nixieshift(ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(),32,0);
nonBlock(1000);
nixieshift(ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(),32,0);
nonBlock(1000);
nixieshift(ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(),32,0);
nonBlock(1000);
/*
// Display IP address in two separate blocks
nixieshift(35, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 35, 3);
nonBlock(750);
nixieshift(35, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 35, 3);
nonBlock(1000);
*/
// Display the IP Address as a scrolling display
// Scroll IPAddr to left
/*
nixieshift(32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, 32, 32, 32, 32, ippart1.substring(0, 1).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, 32, 32, 32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, 32, 32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, 32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart1.substring(0, 1).toInt(), ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart1.substring(1, 2).toInt(), ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart1.substring(2, 3).toInt(), 45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(45, ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart2.substring(0, 1).toInt(), ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart2.substring(1, 2).toInt(), ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart2.substring(2, 3).toInt(), 45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(45, ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart3.substring(0, 1).toInt(), ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart3.substring(1, 2).toInt(), ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart3.substring(2, 3).toInt(), 45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(45, ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart4.substring(0, 1).toInt(), ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart4.substring(1, 2).toInt(), ippart4.substring(2, 3).toInt(), 32, 32, 32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(ippart4.substring(2, 3).toInt(), 32, 32, 32, 32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
nixieshift(32, 32, 32, 32, 32, 32, 32, 32, 0);
nonBlock(scrollspeed * 2.5);
*/
// set the TimeSync thing
setSyncProvider(getNtpTime);
setSyncInterval(300);
// Time to pause - to see if we can ensure that first getNTP returns good data
Serial.begin(9600);
while (!Serial)
; // time to get serial running
Serial.println("");
Serial.println("");
Serial.print(("BMP280/BME280 test: "));
unsigned status;
unsigned status1;
status = bme.begin(0x76); // status = bme.begin();
status1 = bmp.begin(0x76); // status = bmp.begin();
// Note: BME returns 96 and BMP returns 88
FoundSensorP = (bmp.sensorID());
FoundSensor = (bme.sensorID());
Serial.println(FoundSensor);
if (FoundSensor == 88)
Serial.print("Found BMP: ");
if (FoundSensor == 96)
Serial.print("Found BME: ");
if (FoundSensor == 0)
Serial.println("No Sensor");
SensorType = 0; // for No sensor (default)
if (FoundSensor == 88)
SensorType = 1; // For BMP
if (FoundSensor == 96)
SensorType = 2; // For BME
// Display Sensor Found Status here:
//
if (SensorType == 0)
{
// Display 'NoSE'
nixieshift(78, 111, 83, 69, 0);
nonBlock(750);
}
if (SensorType == 1)
{
// Display 'P-SE'
nixieshift(80, 45, 83, 69, 0);
nonBlock(750);
}
if (SensorType == 2)
{
// Display 'E-SE'
nixieshift(69, 45, 83, 69, 0);
nonBlock(750);
}
// Now we have a value in SensorType that shows which is fitted - or none fitted!
Serial.print("SensorType is ");
Serial.println(SensorType);
Serial.println("");
delay(1200);
Serial.println("Read temperature: ");
if (SensorType == 1)
{
// Display 'P-SenS'
Serial.print(bmp.readTemperature());
Serial.print(" Degrees C");
Serial.println("");
Serial.print((1.8 * (bmp.readTemperature()) + 32));
Serial.print(" Degrees F");
delay(1200);
Serial.println("");
Serial.println("");
Serial.println("Read pressure: ");
Serial.print(bmp.readPressure() / 100);
Serial.println(" Millibars Pressure");
Serial.print(((bmp.readPressure()) / 33.864) / 100);
Serial.println(" inches Hg");
Serial.println("");
delay(1200);
}
if (SensorType == 2)
{
// Display 'E-Sens'
Serial.print(bme.readTemperature());
Serial.print(" Degrees C");
Serial.println("");
Serial.print((1.8 * (bme.readTemperature()) + 32));
Serial.print(" Degrees F");
delay(1200);
Serial.println("");
Serial.println("");
Serial.println("Read humidity: ");
Serial.print(bme.readHumidity());
Serial.print("% Relative Humidity");
Serial.println("");
delay(1200);
Serial.println("");
Serial.println("Read pressure: ");
Serial.print(bme.readPressure() / 100);
Serial.println(" Millibars Pressure");
Serial.print(((bme.readPressure()) / 33.864) / 100);
Serial.println(" inches Hg");
Serial.println("");
delay(1200);
}
if (metricUnits == true)
{ // #ifdef metricunits
nixieshift(77, 101, 116, 114, 0); // Spells Metr
nonBlock(1000);
} // #endif
else
{ // #ifdef imperialunits
nixieshift(85, 83, 117, 110, 0); // Spells USun
nonBlock(1000);
} // #endif
nixieshift(85, 110, 105, 116, 0); // Unit
nonBlock(1000);
/* Default settings from datasheet. */
// bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
// Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
// Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
// Adafruit_BMP280::FILTER_X16, /* Filtering. */
// Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
// OTA Added Startup Code
#ifdef SP332
ArduinoOTA.setHostname("SP-332-8_digit");
#endif
#ifdef SP352
ArduinoOTA.setHostname("SP-352-8_digit");
#endif
// Set authentication for OTA
ArduinoOTA.setPassword("nixology");
ArduinoOTA.onStart([]()
{
Serial.println("Start");
// display '-UPLOAd-'
nixieshift(85, 80, 76, 68, 0); });
ArduinoOTA.onEnd([]()
{ Serial.println("\nEnd"); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{ Serial.printf("Progress: %u%%\r", (progress / (total / 100))); });
ArduinoOTA.onError([](ota_error_t error)
{
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed"); });
ArduinoOTA.begin();
Serial.println("OTA Ready");
Serial.print("OTA IP Address: ");
Serial.println(WiFi.localIP());
// End of OTA Section
}
unsigned long lastCommit = 0;
void loop()
{
// update the LED's
strip.SetPixelColor(0, red);
strip.SetPixelColor(1, red);
strip.SetPixelColor(2, red);
strip.SetPixelColor(3, red);
strip.Show();
// Re-create web page in filing system if any config values were saved
checkRewriteWebPage(&clockConfig, name2html);
unsigned long now = millis();
// The web code does a commit when the user saves values, so probably don't need this
if (now - lastCommit > 60000)
{
lastCommit = now;
// Any ConfigItems that had put() called will be written to EEPROM
config.commit();
}
// Added OTA Functionality
ArduinoOTA.handle();
// Loop showing time (HH:MM:SS)
// on (datedisplaysecs) seconds go to either date OR temp/pressure
// work out the local DST corrected time - do this once per main loop cycle - check that displaytime and displaydate are not also doing it!
setLocalTime();
int tensmin = int(minute(local) / 10);
int unitsmin = minute(local) - (tensmin * 10);
int storesecs = second(local) % 60;
int tenssec = int(storesecs / 10);
int unitssec = storesecs - (tenssec * 10);
// reset check vars at the start of the cycle, this is done because we want to display the data when the seconds are (datedisplaysecs) - this could be any time after (datedisplaysecs) seconds as the display routine takes time and will probably never hit dead on (datedisplaysecs) seconds
// so if second(local)<8 then we know for sure that we can reset the variable
if (second(local) < 8)
{
donedatetemp = 0;
#ifdef debug
Serial.println("Reset donedatetemp");
#endif
}
if (second(local) != oldSecond)
{
oldSecond = second(local); // store the updated second
displaytime(); // display the time - which will incur a small delay itself due to the digit transformations
}
// work out the local DST corrected time - this has to be done each time before using second(local) as it only updates the (local) versions when called.
// setLocalTime(); // commented out because this has already been done at the top of the main loop
if ((second(local) > (datedisplaysecs)) && (donedatetemp == 0))
{ // if second(local)> (datedisplaysecs) and we have not displayed it yet then lets do it
// Must decide which one to do - based on value if dispDate
//#ifdef debug
Serial.print("dispDate=");
Serial.println(dispDate);
Serial.print("SensorType");
Serial.println(SensorType);
//#endif
if (dispDate == 0 && SensorType > 0)
{ // only display the temp and pressure if a sensor is attached - ie foundBMP is true - otherwise - display date EVERY minute instead,
// call routine to display temp
displaytemp();
nonBlock(1000);
// call routine to display Humidity - if we have BME280 sensor
if (SensorType == 2)
{ // For BME
displayhumidity();
nonBlock(1000);
}
// call routine to display pressure
displaypressure();
nonBlock(500); // This is a delay at the end of the pressure display
}
if (dispDate == 1)
displaydate(); // call routine to display date
if (dispDate == 2)
displayname(); // call routine to display scrolling name
// increment dispdate and reset to 0 if >2
dispDate = dispDate + 1;
if (dispDate > 2)
dispDate = 0;
// set var to say we've done it.
donedatetemp = 1;
// at this point it would be good if clock could sync up with the next second so that when the year or pressure has been displayed, the time that scrolls in is displayed for a whole second as right
// now, once the date has been been displayed, the time that scrolls in canges immediately - it would be nicer if it hung around!
// Perhps trick it into thinking that second(local) does equal oldSecond so that when it gets back to the top of the loop, the time is not displayed until the next second, Hmm, interesting....
setLocalTime();
oldSecond = second(local);
}
// Check to see it doing word display #1
if ((second(local) > 15) && second(local) < 20)
{
displayword();
setLocalTime();
oldSecond = second(local);
}
// Check to see it doing word display #2
if ((second(local) > 45) && second(local) < 50)
{
displayword();
setLocalTime();
oldSecond = second(local);
}
if (PIRstatus != prevPIRstatus)
{ // Change detected, start the timer
triggerTime = millis(); // note the time
prevPIRstatus = PIRstatus; // remember the previous status so that the timer will not be restarted next time around
clockStatus = HIGH; // make the clock ON!
Serial.println("Trigger"); // A trigger was received
}
// Now check to see if on timer has expired AND the PIR input pin is low (if PIR is connected then it will be driven low.
currentPIRstatus = digitalRead(PIRpin);
// Set the delaytime to the normal day time delay and then change it if it is night time
daydelay = DayclockONtime * 60 * 1000;
nightdelay = NightclockONtime * 60 * 1000;
delaytime = daydelay;
if (nightModeDelay())
delaytime = nightdelay;
if ((millis() - triggerTime >= delaytime) && currentPIRstatus == LOW)
{ // ie - delay time has been reached AND the PIR has gone low again then we can turn the clock off
clockStatus = LOW; // Set the clock off
PIRstatus = HIGH; // reset PIR status and prevPIR status so that the timer is not retriggered
prevPIRstatus = HIGH;
Serial.println("Clock Turned Off");
dimtube();
}
// now wait for ???one??? second before checking again, this is so the colons change status once a second or thereabouts
// nonBlock(579); // commented out as I want to switch over to ONLY calling displaytime if the second(local) has changed.
// end of display loop
}
// Subroutines - it's an age thing!
void dimtube()
{
// dim the light
// now need to check that hour is within range of start hour to end hour
pirTubes(); // work out whether to display anything according to PIR readings
// work out the local DST corrected time
// setLocalTime();
if (!nightMode() && tubeStatus == HIGH)
{
for (int pwmval = 0; pwmval <= Maxbright; pwmval = pwmval + 2)
{
yield();
analogWrite(pwmPin, Maxbright - pwmval);
delay(2);
}
// wait a mo
nonBlock(200);
}
else
analogWrite(pwmPin, 0); // set to max for good measure - also - just in case clock is switched on during 'off' times - the value may never have been set - It is now!
}
void brighttube()
{
// raise the light value
// now need to check that its daytime (ie. !nightMode)
// work out the local DST corrected time
// Add a reference to PIRstatus, if PIRstatus goes low - PIR has been activated and clock should show digits / if PIR status is high then do not show digits
pirTubes(); // work out whether to display anything according to PIR readings
// setLocalTime();
if (!nightMode() && tubeStatus == HIGH)
{
for (int pwmval = 0; pwmval <= Maxbright; pwmval = pwmval + 2)
{
yield();
analogWrite(pwmPin, pwmval);
delay(2);
}
}
}
// turn on instantly - used for seconds display start - to avoid delay of brighttube loop
void tubeon()
{
pirTubes(); // work out whether to display anything according to PIR readings
// work out the local DST corrected time
// setLocalTime();
if (!nightMode() && tubeStatus == HIGH)
analogWrite(pwmPin, Maxbright); // Turn on instantly during the day time
}
// This is taken from previous clock code and now needs to set 8 digits as well as any DP status etc/
void nixieshift(uint8_t d, uint8_t c, uint8_t b, uint8_t a, uint8_t dpstatus)
{
// Set all bits - and set colon status as required on, off, degrees
setAllBits(a, b, c, d, 0, 0, 0, 0);
/*
if (dpstatus == 0)
setAllBits(a, b, c, d, 0, 0, 0, 0);
if (dpstatus == 1)
setAllBits(a, b, c, d, 1, 1, 1, 1);
if (dpstatus == 2)
setAllBits(a, b, c, d, 1, 0, 0, 0);
// this one for IP address display, need third digit DP on
// Need to change setAllBits to deal with those digits
if (dpstatus == 3)
setAllBits(a, b, c, d, 0, 0, 0, 0);
// this one for the display test, light all DP's
if (dpstatus == 9)
setAllBits(a, b, c, d, 1, 1, 1, 1);
// Individual decimal points within the Nixieshift routine
if (dpstatus == 11)
setAllBits(a, b, c, d, 0, 0, 0, 1); // far right
if (dpstatus == 12)
setAllBits(a, b, c, d, 0, 0, 1, 0); // 2nd from right
if (dpstatus == 13)
setAllBits(a, b, c, d, 0, 1, 0, 0); // 3rd from right
if (dpstatus == 14)
setAllBits(a, b, c, d, 1, 0, 0, 0); // 4th from right
*/
// SPI transfer code
bitbashTX();
zerobits();
}
void displaypressure()
{
if (metricUnits == true)
{ // #ifdef metricunits
// now calculate the pressure, we need 4 digits.
// pressure will always be between 900 and 1500
if (SensorType == 1)
{
readpres = (bmp.readPressure() / 100) + PressOffset; // useful to add offset to match Accuweather or known source. Remember elevation!
}
if (SensorType == 2)
{
readpres = (bme.readPressure() / 100) + PressOffset;// useful to add offset to match Accuweather or known source. Remember elevation!
}
// now convert to inches of Hg x 100 - that way we can get the 4 digits and then display as XX.XX
#ifdef debug
Serial.print("Just read pressure ");
Serial.println(readpres);
#endif
// Get the thousands digit
int thousreadpres = int(readpres / 1000);
// Get the hundreds digit - take the total, subtract the number of thousands then divide the result by a 100 and take the int
int hundredsreadpres = int((readpres - (thousreadpres * 1000)) / 100);
// Get the tens digit
int tensreadpres = int((readpres - (thousreadpres * 1000) - (hundredsreadpres * 100)) / 10);
// Get the units digit
int unitsreadpres = readpres - (thousreadpres * 1000) - (hundredsreadpres * 100) - (tensreadpres * 10);
if (thousreadpres < 1)
thousreadpres = blankdigit; // blank leading "0"
// display the Pressure
dimtube();
// 110,98 is for nb if reading pascal; 72,103 is for Hg inches of mercury;
nixieshift(thousreadpres, hundredsreadpres, tensreadpres, unitsreadpres, 0); // Sending blankdigit causes the display to be blank
brighttube();
nonBlock(1000);
// display ' mb '
nixieshift(32, 109,98,32,0);
nonBlock(1000);
// consider scrolling the pressure out to the left!
// move pressure one to the left and wait 200ms, repeat 5 times
// PPPPPP
// PPPPP.
// PPP...
// PP....
// P.....
// ......
/*
nixieshift(thousreadpres, hundredsreadpres, tensreadpres, unitsreadpres, blankdigit, 110, 98, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(hundredsreadpres, tensreadpres, unitsreadpres, blankdigit, 110, 98, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(tensreadpres, unitsreadpres, blankdigit, 110, 98, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(unitsreadpres, blankdigit, 110, 98, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(blankdigit, 110, 98, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(110, 98, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(98, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
*/
// consider setting the 'remembered' time variables so that the next nixieshift routine will transition in from blank displays!
olda = blankdigit;
oldb = blankdigit;
oldc = blankdigit;
oldd = blankdigit;
olde = blankdigit;
oldf = blankdigit;
oldg = blankdigit;
oldh = blankdigit;
} // endif
else
{ // #ifdef imperialunits
// now calculate the pressure, we need 4 digits.
if (SensorType == 1)
{
readpres = (bmp.readPressure()) / 100;
readpres = ((readpres / 33.864 * 100) + PressOffset*100); // useful to add offset to match Accuweather or known source. Remember elevation!
}
if (SensorType == 2)
{
readpres = (bme.readPressure()) / 100;
readpres = ((readpres / 33.864 * 100) + PressOffset*100); // useful to add offset to match Accuweather or known source. Remember Elevation!
}
// now convert to inches of Hg x 100 - that way we can get the 4 digits and then display as XX.XX
// based on mb / 33.864 gives HG, do that then multiply by 100.
// the inHgoffset allows calibrating the result directly in inches of Hg instead of Pascal mb
#ifdef debug
Serial.print("Just read pressure ");
Serial.println(readpres);
#endif
// Get the thousands digit
int thousreadpres = int(readpres / 1000);
// Get the hundreds digit - take the total, subtract the number of thousands then divide the result by a 100 and take the int
int hundredsreadpres = int((readpres - (thousreadpres * 1000)) / 100);
// Get the tens digit
int tensreadpres = int((readpres - (thousreadpres * 1000) - (hundredsreadpres * 100)) / 10);
// Get the units digit
int unitsreadpres = readpres - (thousreadpres * 1000) - (hundredsreadpres * 100) - (tensreadpres * 10);
// display the Pressure
dimtube();
// 110, 98 is for nb if reading pascal; 72,103, is for Hg inches of mercury;
nixieshift( thousreadpres, hundredsreadpres, tensreadpres, unitsreadpres, 16); // Sending blankdigit causes the display to be blank - 16 is 3rd from left dp
brighttube();
nonBlock(1000);
// display 'INhg'
nixieshift(73, 110,72,103,0);
nonBlock(1000);
// consider scrolling the pressure out to the left!
// move pressure one to the left and wait 200ms, repeat 5 times
// PPPPPP
// PPPPP.
// PPP...
// PP....
// P.....
// ......
/*
nixieshift(thousreadpres, hundredsreadpres, tensreadpres, unitsreadpres, blankdigit, 72, 103, blankdigit, 17);
nonBlock(scrollspeed);
nixieshift(hundredsreadpres, tensreadpres, unitsreadpres, blankdigit, 72, 103, blankdigit, blankdigit, 18);
nonBlock(scrollspeed);
nixieshift(tensreadpres, unitsreadpres, blankdigit, 72, 103, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(unitsreadpres, blankdigit, 72, 103, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(blankdigit, 72, 103, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(72, 103, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(scrollspeed);
nixieshift(103, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
*/
// consider setting the 'remembered' time variables so that the next nixieshift routine will transition in from blank displays!
olda = blankdigit;
oldb = blankdigit;
oldc = blankdigit;
oldd = blankdigit;
olde = blankdigit;
oldf = blankdigit;
oldg = blankdigit;
oldh = blankdigit;
}
} // #endif
void displaytemp()
{
// read temp and get int of temp and decimal of temp and display
if (metricUnits == true)
{
// #ifdef metricunits //for Metric Display
if (SensorType == 1)
{
readtemp = (bmp.readTemperature() + TempOffset); // Celcius with CelciusOffset
hundredsreadtemp = int(readtemp / 100);
tensreadtemp = int(readtemp / 10);
unitsreadtemp = int(readtemp - (tensreadtemp * 10));
tenthsreadtemp = int((readtemp - (tensreadtemp * 10) - (unitsreadtemp)) * 10);
// hundredsreadtemp will generally be 0, sometimes 1 if it is really hot and the air conditioning is broken
// set the variable for the hundredchar to be either a '1' or 'blank'
if (hundredsreadtemp < 1)
hundredchar = blankdigit;
if (hundredsreadtemp > 0)
hundredchar = 1;
}
if (SensorType == 2)
{
readtemp = (bme.readTemperature() + TempOffset); // Celcius with CelciusOffset
hundredsreadtemp = int(readtemp / 100);
tensreadtemp = int(readtemp / 10);
unitsreadtemp = int(readtemp - (tensreadtemp * 10));
tenthsreadtemp = int((readtemp - (tensreadtemp * 10) - (unitsreadtemp)) * 10);
// hundredsreadtemp will generally be 0, sometimes 1 if it is really hot and the air conditioning is broken
// set the variable for the hundredchar to be either a '1' or 'blank'
if (hundredsreadtemp < 1)
hundredchar = blankdigit;
if (hundredsreadtemp > 0)
hundredchar = 1;
}
dimtube();
// sending a '67' should display a capital "C" for degrees
// sending a '70' should display a capital "F" for degrees
// select decimal position with last digit in nixieshift. 15 is the 4th from left
nixieshift(tensreadtemp, unitsreadtemp, blankdigit, 67, 0);
brighttube();
} // #endif
else
{ // #ifdef imperialunits //for USA Display
// int readtemp = (bmp.readTemperature() + CelciusOffset); //Celcius with CelciusOffset
if (SensorType == 1)
{
readtemp = 1.8 * (bmp.readTemperature()) + 32 + TempOffset; // converts C to F and uses offset for Fahrenheit
hundredsreadtemp = int(readtemp / 100);
tensreadtemp = int(readtemp / 10);
unitsreadtemp = int(readtemp - (tensreadtemp * 10));
tenthsreadtemp = int((readtemp - (tensreadtemp * 10) - (unitsreadtemp)) * 10);
// hundredsreadtemp will generally be 0, sometimes 1 if it is really hot and the air conditioning is broken
// set the variable for the hundredchar to be either a '1' or 'blank'
if (hundredsreadtemp < 1)
hundredchar = blankdigit;
if (hundredsreadtemp > 0)
hundredchar = 1;
}
if (SensorType == 2)
{
readtemp = 1.8 * (bme.readTemperature()) + 32 + TempOffset; // converts C to F and uses offset for Fahrenheit
hundredsreadtemp = int(readtemp / 100);
tensreadtemp = int(readtemp / 10);
unitsreadtemp = int(readtemp - (tensreadtemp * 10));
tenthsreadtemp = int((readtemp - (tensreadtemp * 10) - (unitsreadtemp)) * 10);
// hundredsreadtemp will generally be 0, sometimes 1 if it is really hot and the air conditioning is broken
// set the variable for the hundredchar to be either a '1' or 'blank'
if (hundredsreadtemp < 1)
hundredchar = blankdigit;
if (hundredsreadtemp > 0)
hundredchar = 1;
}
dimtube();
// sending a '10' should display a capital "C" for degrees
// sending a '20' should display a capital "F" for degrees
// select decimal position with last digit in nixieshift. 15 is the 4th from left
nixieshift( tensreadtemp, unitsreadtemp, blankdigit, 70, 0);
brighttube();
} // #endif
}
// Do not run the void displayhumidity for BMP280. Only works with BME280
void displayhumidity()
{
if (SensorType == 0)
{
nonBlock(0);
}
if (SensorType == 1)
{
nonBlock(0);
}
if (SensorType == 2)
{
// read humidity and display
int readhumidity = bme.readHumidity();
readhumidity = (readhumidity + humidityoffset); // use humidity offset to calibrate sensor
// routine to make sure that the offset does not cause an invalid result
if (readhumidity > 99)
readhumidity = 99;
if (readhumidity < 0)
readhumidity = 0;
int tenshumidity = int(readhumidity / 10);
int unitshumidity = readhumidity - (tenshumidity * 10);
dimtube();
nixieshift(tenshumidity, unitshumidity, blankdigit, 37, 0); // 114=r, 104=h, 0=no colons
brighttube();
}
}
void displaytime()
{
// work out the local DST corrected time - do this again in case minutes changed whilst hours was being displayed.
// setLocalTime(); already done just before we got here
// Display hours
// dimtube();
// check to see if 12/24 hours and set hours accordingly - set temp variable for interim steps
temphour = hour(local);
// Check the initial setting to make sure that the timesystem is either "0" or "12" and not an wrong value
if (timesystem > 0)
timesystem = 12;
// if the clock thinks it is 13 hundred hours or later and clock is set for 12 hour time, set the temphour variable to 12 hour time
if (timesystem > 0 && temphour > 12)
temphour = (temphour - timesystem);
// if the clock thinks it is 00 hours and clock is set for 12 hour time, set the temphour variable to 12
if (timesystem > 0 && temphour == 0)
temphour = 12;
int tenshour = int(temphour / 10);
int unitshour = temphour - (tenshour * 10);
int tensmin = int(minute(local) / 10);
int unitsmin = minute(local) - (tensmin * 10);
int storesecs = second(local) % 60;
int tenssec = int(storesecs / 10);
int unitssec = storesecs - (tenssec * 10);
// blank the leading zero if desired
if (!leadingzero && tenshour == 0)
tenshour = blankdigit;
tubeon();
// nixieshift(tenshour, unitshour, tensmin, unitsmin, tenssec, unitssec, colonstatus);
#ifdef HHMMSS
nixieshift(tenshour, unitshour, tensmin, unitsmin, colonstatus);
#endif
// Here I Am
#ifdef HH_MM_SS
nixieshift(tensmin, unitsmin, tenssec, unitssec, colonstatus);
#endif
// toggle colon status
colonstatus = !colonstatus;
}
// Display the date
void displaydate()
{
// work out the local DST corrected time
// setLocalTime(); // no need - it's done in the main llop
// Display Day and Month
#ifdef debug
Serial.print("Day=");
Serial.print(day(local));
Serial.print(" ");
Serial.print("Month=");
Serial.print(month(local));
Serial.print(" ");
#endif
dimtube();
int tensday = int(day(local) / 10);
int unitsday = day(local) - (tensday * 10);
int tensmonth = int(month(local) / 10);
int unitsmonth = month(local) - (tensmonth * 10);
// Display Day
// use numbers to display the two letters following the numeric date as follows:
//
// B0111001, C 10
// B1011110, d 100
// B1110100, h 104
// B1010100, n 110
// B1010000, r 114
// B1101101, s 83
// B1110000 t 116
// Cdhnrst
if (ordinals == true)
{
// Preset the two digits to be 'th'
dayletter1 = 116;
dayletter2 = 104;
// now modify for the special cases
// deal with 'st' for the 1st,21st and 31st - but not the 11th!
if (day(local) == 1 || day(local) == 21 || day(local) == 31)
{
dayletter1 = 83;
dayletter2 = 116;
}
// deal with 'nd' for the 2nd and 22nd - but not the 12th!
if (day(local) == 2 || day(local) == 22)
{
dayletter1 = 110;
dayletter2 = 100;
}
// deal with 'rd' for the 3rd and 23rd - but not the 13th!
if (day(local) == 3 || day(local) == 23)
{
dayletter1 = 114;
dayletter2 = 100;
}
}
else
{ // ie - do not display ordinals
dayletter1 = 125;
dayletter2 = 125;
}
// do not display tensday if it is '0' - ie blank leading 0 on day number!
if (tensday == 0)
tensday = blankdigit;
if (metricUnits == true)
{ // #ifdef metricunits // ****************Use DDdd-MMMM-YYYY format on separate lines******************************
nixieshift(tensday, unitsday, dayletter1, dayletter2, 0);
brighttube();
nonBlock(1000);
// Display Month
#ifdef debug
Serial.print("Month=");
Serial.print(month(local));
Serial.print(" ");
#endif
dimtube();
// JAN 74, 65, 78
// FEb 70, 69, 98
// nnArch 110, 110, 65, 114, 99, 104
// APri1 65, 80, 114, 105, 108
// nnAY 110, 110, 65, 89
// JUNE 74, 85, 78, 69
// JuLY 74, 117, 76, 89
// AuGuSt 65, 117, 71, 117, 83, 116
// 5EPT 83, 69, 80, 116
// OCt 79, 67, 116
// Nou 78, 79, 118
// DEC 68, 69, 67
//
// nixieshift(blankdigit, tensmonth, unitsmonth, blankdigit,blankdigit,blankdigit,0);
switch (month(local))
{
case 1:
nixieshift( 74, 65, 78, blankdigit, 0);
break;
case 2:
nixieshift( 70, 69, 98, blankdigit, 0);
break;
case 3:
nixieshift(110, 110, 65, 114, 0);
break;
case 4:
nixieshift( 65, 80, 114, 105, 0);
break;
case 5:
nixieshift( 110, 110, 65, 89, 0);
break;
case 6:
nixieshift( 74, 85, 78, 69, 0);
break;
case 7:
nixieshift(74, 117, 76, 89, 0);
break;
case 8:
nixieshift(65, 117, 71, 117, 0);
break;
case 9:
nixieshift( 83, 69, 80, 116, 0);
break;
case 10:
nixieshift(79, 67, 116, blankdigit, 0);
break;
case 11:
nixieshift(78, 79, 118, blankdigit, 0);
break;
case 12:
nixieshift( 68, 69, 67, blankdigit, 0);
break;
}
brighttube();
nonBlock(1000);
// Display Year
int thisyear = year(local);
#ifdef debug
Serial.print("Year = ");
Serial.print(thisyear);
Serial.print(" ");
#endif
// Get the thousands digit
int thousyear = int(thisyear / 1000);
// Get the hundreds digit - take the total, subtract the number of thousands then divide the result by a 100 and take the int
int hundredsyear = int((thisyear - (thousyear * 1000)) / 100);
// Get the tens digit
int tensyear = int((thisyear - (thousyear * 1000) - (hundredsyear * 100)) / 10);
// Get the units digit
int unitsyear = thisyear - (thousyear * 1000) - (hundredsyear * 100) - (tensyear * 10);
// display the year (four digits)
dimtube();
nixieshift(thousyear, hundredsyear, tensyear, unitsyear, 0);
brighttube();
nonBlock(1000);
// consider scrolling the year out to the left!
// move year one to the left and wait 200ms, repeat 6 times
// ..YYYY..
// .YYYY...
// .YYY....
// .YY.....
// .Y......
// ........
/*
nixieshift(blankdigit, thousyear, hundredsyear, tensyear, unitsyear, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(hundredsyear, tensyear, unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(tensyear, unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
*/
} // #endif
else
{ // #ifdef imperialunits // ****************Use MMMM-DDdd-YYYY format on separate lines******************************
// Display Month First - MMMM-DDdd-YYYY
#ifdef debug
Serial.print("Month=");
Serial.print(month(local));
Serial.print(" ");
#endif
// dimtube();
// JAN 17,18,19 74, 65, 78
// FEb 20,21,22 70, 69, 98
// nnArch 13,13,18,14,24,12 110, 110, 65, 114, 99, 104
// APri1 18,25,14,26,30 65, 80, 114, 105, 108
// nnAY 23,23,18,27 110, 110, 65, 89
// JUNE 17,28,19,21 74, 85, 78, 69
// JuLY 17,29,30,27 74, 117, 76, 89
// AuGuSt 18,29,31,29,5,16 65, 117, 71, 117, 83, 116
// 5EPT 5,21,25,16 83, 69, 80, 116
// OCt 0,10,16 79, 67, 116
// Nou 19,0,29 78, 79, 118
// DEC 11,21,10 68, 69, 67
//
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, 0);
switch (month(local))
{
case 1:
nixieshift(74, 65, 78, blankdigit, 0);
break;
case 2:
nixieshift(70, 69, 98, blankdigit, 0);
break;
case 3:
nixieshift( 110, 110, 65, 114, 0);
break;
case 4:
nixieshift( 65, 80, 114, 105, 0);
break;
case 5:
nixieshift(110, 110, 65, 89, 0);
break;
case 6:
nixieshift(74, 85, 78, 69, 0);
break;
case 7:
nixieshift(74, 117, 76, 89, 0);
break;
case 8:
nixieshift( 65, 117, 71, 117, 0);
break;
case 9:
nixieshift( 83, 69, 80, 116, 0);
break;
case 10:
nixieshift( 79, 67, 116, blankdigit, 0);
break;
case 11:
nixieshift( 78, 79, 118, blankdigit, 0);
break;
case 12:
nixieshift(68, 69, 67, blankdigit, 0);
break;
}
brighttube();
nonBlock(1000);
// Display the Day
dimtube();
nixieshift(tensday, unitsday,dayletter1, dayletter2, 0);
brighttube();
nonBlock(1000);
// Display Year
int thisyear = year(local);
#ifdef debug
Serial.print("Year = ");
Serial.print(thisyear);
Serial.print(" ");
#endif
// Get the thousands digit
int thousyear = int(thisyear / 1000);
// Get the hundreds digit - take the total, subtract the number of thousands then divide the result by a 100 and take the int
int hundredsyear = int((thisyear - (thousyear * 1000)) / 100);
// Get the tens digit
int tensyear = int((thisyear - (thousyear * 1000) - (hundredsyear * 100)) / 10);
// Get the units digit
int unitsyear = thisyear - (thousyear * 1000) - (hundredsyear * 100) - (tensyear * 10);
// display the year (four digits)
// consider scrolling the year out to the left!
// move year one to the left and wait 200ms, repeat 6 times
// ..YYYY..
// .YYYY...
// .YYY....
// .YY.....
// .Y......
// ........
dimtube();
/*
nixieshift(blankdigit, blankdigit, thousyear, hundredsyear, tensyear, unitsyear, blankdigit, blankdigit, 0);
brighttube();
nonBlock(1000);
nixieshift(blankdigit, thousyear, hundredsyear, tensyear, unitsyear, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(hundredsyear, tensyear, unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(tensyear, unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(unitsyear, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
nonBlock(100);
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0);
*/
// brighttube();
tubeon();
} // #endif
// consider setting the 'remembered' time variables so that the next nixieshift routine will transition in from blank displays!
olda = blankdigit;
oldb = blankdigit;
oldc = blankdigit;
oldd = blankdigit;
olde = blankdigit;
oldf = blankdigit;
oldg = blankdigit;
oldh = blankdigit;
}
// This is a non-blocking 'delay' - use instead of 'delay' to avoid ESP8266 WDT resets
void nonBlock(unsigned long delay_time)
{
unsigned long time_now = millis();
while (millis() - time_now < delay_time)
{
yield();
ESP.wdtFeed();
// waiting!
}
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0)
; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500)
{
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE)
{
Serial.println("Receive NTP Response");
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];
return secsSince1900 - 2208988800UL + clockTuner;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); // NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
// work out the local DST corrected time
void setLocalTime()
{
utc = now();
TimeChangeRule *tcr; // pointer to the time change rule, use to get the TZ abbrev
local = timeZones.at(timezone).toLocal(utc, &tcr);
}
ICACHE_RAM_ATTR void pirChange()
{
PIRstatus = LOW;
}
// work out whether tubes should be on or off depending on the values of PIRstatus and clockStatus
/*
This is derived from the following truth table which will deal with NO PIR connected or PIR connected and clock at rest or if it has been triggered both immediately and then after the current cycle has been completed
clockStatus PIRstatus Show Tubes
NO PIR 1 1 Y
PIR and off 0 1 N Clock is at rest and should not show anything
PIR and on 1 0 Y Clock was recently triggered and is counting down the on time
PIR and Trigg 0 0 Y Clock is mid way through showing something but has just been triggered (not yest set clockStatus)
*/
void pirTubes()
{
if (clockStatus == LOW && PIRstatus == HIGH)
tubeStatus = LOW;
else
tubeStatus = HIGH;
}
// Taken originally from the SP-151 code, now change to handle six sigits, set digit to 11 in order to leave blank - now using variable blankdigit
// Start with six digits P8, P7, P6, P5, P4, P3, P2 and P1
// Add DP's later
//
// Set all bits for the six numbers (a,b,c,d,e,f, g, h left to right), the decimal points (DP) for each digit on SP-352
// There are offsets at 0,8,16,24,32,40,48 and 56 as each digit has 7 segments plus a DP - 8 bits in total
void setAllBits(uint8_t aa, uint8_t bb, uint8_t cc, uint8_t dd, uint8_t DP1, uint8_t DP2, uint8_t DP3, uint8_t DP4)
{
// Old Comment - left here just in case: Load L bits for numbers dd,ee and ff ON THE LEFT - shifted out first
// Load the bits using bitArrayWrite(pointers[thebittoset],1);
/*
Serial.print(aa);
Serial.print(" ");
Serial.print(bb);
Serial.print(" ");
Serial.print(cc);
Serial.print(" ");
Serial.println(dd);
*/
// Set bits for dd Tens of hours
if (dd < blankdigit)
{
for (uint8_t bitpointer = 0; bitpointer < 15; bitpointer++)
{
// if(bitRead(sevenseg[dd], (14-bitpointer)) ==1) Serial.print("1");
// else Serial.print("0");
if (bitRead(sevenseg[dd], (14-bitpointer)) == 1)
bitArrayWrite(pointers[0 + bitpointer], 1);
}
}
//Serial.println("");
// Set bits for cc hours
if (cc < blankdigit)
{
for (uint8_t bitpointer = 0; bitpointer < 15; bitpointer++)
{
// if(bitRead(sevenseg[dd], (14-bitpointer)) ==1) Serial.print("1");
// else Serial.print("0");
if (bitRead(sevenseg[cc], (14-bitpointer)) == 1)
bitArrayWrite(pointers[15 + bitpointer], 1);
}
}
// Set bits for bb tens of minutes
if (bb < blankdigit)
{
for (uint8_t bitpointer = 0; bitpointer < 15; bitpointer++)
{
if (bitRead(sevenseg[bb], (14-bitpointer)) == 1)
bitArrayWrite(pointers[30 + bitpointer], 1);
}
}
// Set bits for aa minutes
if (aa < blankdigit)
{
for (uint8_t bitpointer = 0; bitpointer < 15; bitpointer++)
{
if (bitRead(sevenseg[aa], (14-bitpointer)) == 1)
bitArrayWrite(pointers[45 + bitpointer], 1);
}
}
// Set bits for CTL, CBL, CTR, CBR as required
// Now modified so that we actually control the 8 DP's as follows:
/*
if (DP1 == 1)
bitArrayWrite(pointers[7], 1);
if (DP2 == 1)
bitArrayWrite(pointers[15], 1);
if (DP3 == 1)
bitArrayWrite(pointers[23], 1);
if (DP4 == 1)
bitArrayWrite(pointers[31], 1);
if (DP5 == 1)
bitArrayWrite(pointers[39], 1);
if (DP6 == 1)
bitArrayWrite(pointers[47], 1);
if (DP7 == 1)
bitArrayWrite(pointers[55], 1);
if (DP8 == 1)
bitArrayWrite(pointers[63], 1);
*/
// remember what we set - perhaps for use when doing transition to WORD display
olda = aa;
oldb = bb;
oldc = cc;
oldd = dd;
}
void bitbashTX()
{
// send data out
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[7]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[6]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[5]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[4]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[3]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[2]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[1]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[0]);
digitalWrite(LEpin, HIGH);
digitalWrite(LEpin, LOW);
//remember the contents of the old bitarry - for fade out before word display
OldBitArray1[0] = BitArray[0];
OldBitArray1[1] = BitArray[1];
OldBitArray1[2] = BitArray[2];
OldBitArray1[3] = BitArray[3];
OldBitArray1[4] = BitArray[4];
OldBitArray1[5] = BitArray[5];
OldBitArray1[6] = BitArray[6];
OldBitArray1[7] = BitArray[7];
// this is now performed after the call to bitbashTX so that it can be used for fade out
//zerobits(); // clear the data store
}
void disptest()
{
analogWrite(pwmPin, Maxbright);
for (int testcount = 0; testcount < 10; testcount++)
{
Serial.println(testcount);
for (int bittest = 0; bittest < 64; bittest++)
{
// zerobits();
// bitSet(j, bittest);
bitArrayWrite(bittest, 1);
Serial.print("j=");
Serial.println(bittest);
bitbashTX();
zerobits();
nonBlock(40);
}
}
}
void zerobits() {
for (int zerobits = 0; zerobits < 9; zerobits++) BitArray[zerobits] = 0x00;
}
void bitArrayWrite(const unsigned int index, const boolean value)
{
if (index > 64)
return;
bitWrite(BitArray[index / 8], index % 8, value); // write the right bit of the right char
}
void startup()
{
// clear the bit array
zerobits();
// odd behaviour noted here - I wanted to add bits to the array and write them out each time as it grew - it seemed that the array was cleared after the bitbashTX - so for now - I complete the whole thing before updating the display each time.
// set inter step speed
int startdel = 100;
// turn display up
//analogWrite(pwmPin, Maxbright);
digitalWrite(blankpin,HIGH);
Serial.println("Doing offset 0");
// do this thing 5 times. Circletimes is the number of rotations the segments make aroung the clock at startup
for (int circletimes = 0; circletimes < 5; circletimes++)
{
//Serial.println("Pass");
for (int testbit = 0; testbit < 15; testbit++)
{
//Serial.print(testbit);
//Serial.print("-");
//Serial.println(pointers[testbit]);
zerobits(); // clear all bits
// set the bit
// bitArrayWrite(testbit, 1);
bitArrayWrite(pointers[0+testbit], 1);
bitArrayWrite(pointers[15+testbit], 1);
bitArrayWrite(pointers[30+testbit], 1);
bitArrayWrite(pointers[45+testbit], 1);
/*
// show contents of bit array
for(int barseg=0; barseg<8; barseg++){
for(int bar=0; bar<8; bar++) {
if((BitArray[barseg] & (1 << bar)) == 0 ) Serial.print("0");
else Serial.print("1");
}
Serial.println("");
}
*/
// update the display
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[7]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[6]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[5]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[4]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[3]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[2]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[1]);
shiftOut(dataPin, clockPin, MSBFIRST, BitArray[0]);
digitalWrite(latchPin, HIGH);
digitalWrite(latchPin, LOW);
nonBlock(startdel);
}
}
}
/*
//Display the name
void displayname() {
//Scroll the name as defined across the 8 characters of the display
// loop namescroll times
// nixieshift only 8 parts out that is starting at 0 (with the subsequent 7 parts) and then cycling namescroll times.
if (showname == 1) {
for (int namepos = 0; namepos <= namescroll; namepos++) {
nixieshift(nametext[namepos], nametext[namepos + 1], nametext[namepos + 2], nametext[namepos + 3], nametext[namepos + 4], nametext[namepos + 5], nametext[namepos + 6], nametext[namepos + 7], 0);
nonBlock(200);
}
nonBlock(100);
}
nonBlock(0);
}
*/
/*
//Display the scrolling name message - NOW COMMENTED OUT AS IT HAS BEEN REPLACED BY DEFINED STRING VERSION AT END OF FILE
void displayname() {
//scrollgap ads spaces before the date comes in
//Scroll the name as defined across the 12 characters of the display
// loop namescroll times
// nixieshift only 12 parts out that is starting at 0 (with the subsequent 11 parts) and then cycling namescroll times.
dimtube();
nixieshift(blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, blankdigit, 0); //Sending blankdigit causes the display to be blank
brighttube();
for (int namepos = 0; namepos <= namescroll; namepos++) {
nixieshift(nametext[namepos], nametext[namepos + 1], nametext[namepos + 2], nametext[namepos + 3], nametext[namepos + 4], nametext[namepos + 5], nametext[namepos + 6], nametext[namepos + 7], nametext[namepos + 8], nametext[namepos + 9], nametext[namepos + 10], nametext[namepos + 11], 0);
nonBlock(175);
}
nonBlock(200);
}
*/
/*
void fillMessageTime(int sgap2)
{
// sgap2 is used to pass the previously defined scrollgap on to this routine.
// get the time components and populate the message string that is being scrolled out of the date display.
// It will fill the middle 8 of the last 12 character positions.
setLocalTime(); // set the local time
// check to see if 12/24 hours and set hours accordingly - set temp variable for interim steps
temphour = hour(local);
// Check the initial setting to make sure that the timesystem is either "0" or "12" and not an wrong value
if (timesystem > 0)
timesystem = 12;
// if the clock thinks it is 13 hundred hours or later and clock is set for 12 hour time, set the temphour variable to 12 hour time
if (timesystem > 0 && temphour > 12)
temphour = (temphour - timesystem);
// if the clock thinks it is 00 hours and clock is set for 12 hour time, set the temphour variable to 12
if (timesystem > 0 && temphour == 0)
temphour = 12;
int tenshour = int(temphour / 10);
int unitshour = temphour - (tenshour * 10);
int tensmin = int(minute(local) / 10);
int unitsmin = minute(local) - (tensmin * 10);
int storesecs = second(local) % 60;
int tenssec = int(storesecs / 10);
int unitssec = storesecs - (tenssec * 10);
if (!leadingzero && tenshour == 0) //set leading time zero from web interface
{
tenshour = blankdigit;
}
// so now we have everything we need
// time to stuff the numbers into the string
// regardless of the imperial/metric - the new time will always start in the last 12 characters of the array - so - set the new start pos,
// later - pass this as a paramater.
if (metricUnits == true)
{
newTimePos = datePTR + 6;
}
else
{
newTimePos = datePTR + 11;
}
// for (int dateScroll = 1; dateScroll < (datePTR + 3 + 2); dateScroll++) {
dateText[newTimePos + 0 + sgap2] = tenshour;
dateText[newTimePos + 1 + sgap2] = unitshour;
dateText[newTimePos + 2 + sgap2] = 36;
dateText[newTimePos + 3 + sgap2] = tensmin;
dateText[newTimePos + 4 + sgap2] = unitsmin;
dateText[newTimePos + 5 + sgap2] = 36;
dateText[newTimePos + 6 + sgap2] = tenssec;
dateText[newTimePos + 7 + sgap2] = unitssec;
// toggle colon status
// colonstatus = !colonstatus;
}
*/
// Display the name - scrolling
void displayname()
{
int nametextPTR = 0; // used when filling out nametext[]
// Scroll the name as defined across the 8 characters of the display
// loop namescroll times
// nixieshift only 6 parts out that is starting at 0 (with the subsequent 5 parts) and then cycling namescroll times.
// set the old digits to blank so that the next display scrolls in.
// Take defined MessageString and store in MessageString1 (which is a regular string, MessageString is NOT!)
MessageString1 = MessageString;
// Add the required number of spaces at the beginning and end of the string for blank at each end of the display. For 8digit - there will be 8 at each end!
MessageString1 = " " + MessageString1 + " "; // add the padding for scrolling the message in then out again - remember to add 8+8 to all definitions involving the string length - allow 40+6+6+1 for good measure.
// initialise buf to all null - useful when shorter string is defined after longer one
for (int bufptr = 0; bufptr < 64; bufptr++) buf[bufptr] = 0;
MessageString1.toCharArray(buf, 64); // place the contents of MessageString into 40 chars of buf HMM that is all wrong, there will be at least 40 chars of message+24 of blank spaces = 64
for (int bufptr = 0; bufptr < 64; bufptr++)
{
if (buf[bufptr] > 0)
{ // here check to see if there is an ascii position defined, it should be 0 if there is not - ie NULL
// Serial.println(buf[bufptr], DEC); // used for debugging only
// now set an element in nametext[]
nametext[bufptr] = buf[bufptr];
nametextPTR++; // incrememnt nametextPTR = this will become namescroll
}
}
// now scroll the message
// in below changed from -8 to - 4
for (int namepos = 0; namepos <= nametextPTR - 4; namepos++)
{
nixieshift(nametext[namepos], nametext[namepos + 1], nametext[namepos + 2], nametext[namepos + 3], 0);
nonBlock(250);
}
olda = blankdigit;
oldb = blankdigit;
oldc = blankdigit;
oldd = blankdigit;
olde = blankdigit;
oldf = blankdigit;
oldg = blankdigit;
oldh = blankdigit;
}
void displayword()
{
/// now transition existing display to blank using a
// preserve the contents of the bit array as OldBitArray1 is overwritten in bitbashTX
OldBitArray2[0] = OldBitArray1[0];
OldBitArray2[1] = OldBitArray1[1];
OldBitArray2[2] = OldBitArray1[2];
OldBitArray2[3] = OldBitArray1[3];
OldBitArray2[4] = OldBitArray1[4];
OldBitArray2[5] = OldBitArray1[5];
OldBitArray2[6] = OldBitArray1[6];
OldBitArray2[7] = OldBitArray1[7];
// at this point - OldBitArray2 will contain the last things that were displayed
// now a loop ot 15 passes
// each pass put all of OldBitArray2 into BitArray
// then progressively blank out segment 1-15 of each digit
// call bitbashTX
for (int fadeloop=0; fadeloop <15; fadeloop++)
{
// write all of OldBitArray2 into BitArray
for (int rewrite = 0; rewrite<8; rewrite++) BitArray[rewrite] = OldBitArray2[rewrite];
/*
//put on the segment that we are about to blank and display it
bitArrayWrite(pointers[ 0+fadeloop],1);
bitArrayWrite(pointers[15+fadeloop],1);
bitArrayWrite(pointers[30+fadeloop],1);
bitArrayWrite(pointers[45+fadeloop],1);
// update the display
bitbashTX();
//small delay
nonBlock(100);
*/
// now blank out segments 0 to fadeloop
for (int fadeloopblank = 0; fadeloopblank < fadeloop; fadeloopblank++) {
bitArrayWrite(pointers[ 0+fader[fadeloopblank]],0);
bitArrayWrite(pointers[15+fader[fadeloopblank]],0);
bitArrayWrite(pointers[30+fader[fadeloopblank]],0);
bitArrayWrite(pointers[45+fader[fadeloopblank]],0);
}
// update the display
bitbashTX();
//small delay
nonBlock(80);
}
// now clear the array before writing the real word
zerobits();
dimtube();
nixieshift(87, 111, 114, 100, 0);
brighttube();
nonBlock(2000);
dimtube();
nixieshift(32,32,32,32,0);
brighttube();
}