diff --git a/README.md b/README.md index b1f33e2..ca912ae 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Nixie clocks](https://i.imgur.com/FemMWax.jpg) -**A digital clock with perpetual calendar, alarm, countdown timer/appliance timer, and day counter.** Written for the Arduino Nano at the heart of [RLB Designs'](http://rlb-designs.com/) Universal Nixie Driver Board (UNDB), featuring a DS3231 real-time clock, and driving up to 6 digits multiplexed in pairs via two SN74141 driver chips. +**A digital clock with perpetual calendar, alarm, countdown timer/appliance timer, and day counter.** Written for the Arduino Nano at the heart of [RLB Designs'](http://rlb-designs.com/) Universal Nixie Driver Board (UNDB), featuring a DS3231 real-time clock, and driving up to 6 digits multiplexed in pairs via two SN74141 driver chips. Includes support for LED and relay control for UNDB v8+. [The latest release can be downloaded here.](https://github.com/clockspot/arduino-nixie/releases/latest) Skip to [Hardware Configuration](#hardware-configuration) for details on how to tweak the sketch. @@ -17,52 +17,71 @@ _Note: Many variations are possible, depending on your clock's hardware; but thi | Function | Looks like | Notes | | --- | --- | --- | -| **Time** | `12 34 56` | The time of day. You can choose 12h or 24h format in the options menu (1). When setting, it's in 24h format (so you can tell AM from PM) and the seconds will reset to :00 when you save. The clock keeps time during power outages and compensates for temperature effects.

If your clock is controlling an appliance ([relay in switched mode](#hardware-configuration)), use **Adjust** here to switch it on and off manually. | +| **Time** | `12 34 56` | The time of day. You can choose 12h or 24h format in the options menu (1). When setting, it's in 24h format (so you can tell AM from PM) and the seconds will reset to :00 when you save. The clock keeps time during power outages and compensates for temperature effects. | | **Date** | `_2 _4 _0`
(for Sun 2/4) | You can choose the date format in the options menu (2). Setting is done in three stages: first year, then month, then date.
Weekdays are: 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat | -| **Alarm** | `_7 00 1_` | Shows alarm time (always in 24hr format) and on/off status on 5th tube (1=on, 0=off) and by display brightness (bright=on, dim=off). Use **Adjust** to switch on/off. Hold **Select** to set time (same way as **Time**). When alarm sounds, press **Select** to snooze, or hold for 1sec (followed by a short beep) to silence the alarm for the day. Options menu lets you restrict the alarm to your workweek or weekend only. In a power outage, the alarm will remain set, but it will not sound if power is disconnected at alarm time. | -| **Timer** | `__ __ _0` | A countdown timer, in hours, minutes, and seconds; or `0` when stopped. Can be set to the minute, up to 18 hours. Begins running as soon as you set it, and will continue to run in the background if you change to a different function. To cancel while running, hold **Select**. When timer runs out, press **Select** to silence. If power is lost, the timer will reset to `0`. Can be configured to work as an interval timer in the options menu (10), or as an appliance timer instead ([relay in switched mode](#hardware-configuration)). | +| **Alarm** | `_7 00 1_` | Shows alarm time (always in 24hr format) and on/off status on 5th tube (1=on, 0=off) and by display brightness (bright=on, dim=off). Use **Adjust** to switch on/off. Hold **Select** to set time (same way as **Time**). When alarm sounds, press any button to snooze, or hold for 1sec (followed by a short beep) to silence the alarm for the day. Options menu lets you restrict the alarm to your workweek or weekend only. In a power outage, the alarm will remain set, but it will not sound if power is disconnected at alarm time. | +| **Timer** | `__ __ _0` | A countdown timer, in hours, minutes, and seconds; or `0` when stopped. Can be set to the minute, up to 18 hours. Begins running as soon as you set it, and will continue to run in the background if you change to a different function. To cancel while running, hold **Select**. When timer runs out, press **Select** to silence. If power is lost, the timer will reset to `0`. Can be configured to work as an interval timer in the options menu (10). | | **Day counter** | `_1 23 __` | Shows the number of days until/since a date you specify. Set the same way as **Date.** | -| **Temperature** | `__ 38 25` | (Disabled by default.) Shows the temperature of the onboard DS3231 chip (e.g. 38.25°C – I think). May not be very useful as it tends to read higher than ambient temperature and its tolerance is low. | +| **Temperature** | `__ 38 25` | Shows the temperature of the onboard DS3231 chip (e.g. 38.25°C – I think). May not be very useful as it tends to read higher than ambient temperature and its tolerance is low. Negative temperatures indicated with leading zeroes. | | **Tube tester** | `88 88 88` | (Disabled by default.) Cycles through all the digits on all the tubes. | +### LEDs and Relay + +Later UNDBs (v8 with mods, or v9+) are equipped with controllable LEDs, as well as a relay, which can be enabled in the [hardware configuration](#hardware-configuration) in **switch mode** (such as for a radio) or **pulse mode** (such as for a bell striker). If you have one of these UNDBs: + +* The LED behavior is configurable in option 7, and can be set to switch on and off with the relay if enabled (great for a radio!) +* The alarm, timer, and strike signals can be configured to use either the beeper or the relay if enabled (options 11, 21, and 31 – strike can only use the relay in pulse mode). +* With the relay in switch mode: + * **Alt** will switch it on and off at any time (except in options menu). (Otherwise, **Alt** does nothing.) + * If the alarm is set to use the relay, it will switch on the relay at alarm time, and switch off two hours later. If **Alt** is used to switch it off, the alarm will be silenced for the day (skipping snooze). + * If the timer is set to use the relay, it will switch on while the timer is running, and switch off when it runs out, like a clock radio's sleep function. If **Alt** is used to switch it off, the timer will be cancelled. (The interval timer option cannot be used in this case.) + ### Options Menu * To access this, hold **Select** for 3 seconds until you see a single `1` on the hour tubes. This indicates option number 1. * Use **Adjust** to go to the option number you want to set (see table below); press **Select** to open it for setting (display will flash); use **Adjust** to set; and **Select** to save. * When all done, hold **Select** to exit the options menu. -| Option | Settings | -| --- | --- | -| 1. Time format | 1 = 12-hour
2 = 24-hour
(time-of-day display only; setting times is always done in 24h) | -| 2. Date format | 1 = month/date/weekday
2 = date/month/weekday
3 = month/date/year
4 = date/month/year
5 = year/month/date
Note that four-tube clocks will display only the first two values in each of these options. | -| 3. Display date during time? | 0 = never
1 = date instead of seconds
2 = full date (as above) every minute at :30 seconds
3 = same as 2, but scrolls in and out | -| 4. Leading zero in hour, date, and month? | 0 = no
1 = yes | -| 5. Digit fade | 0–20 (in hundredths of a second) | -| 6. Auto DST | Add 1h for daylight saving time between these dates (at 2am):
0 = off
1 = second Sunday in March to first Sunday in November (US/CA)
2 = last Sunday in March to last Sunday in October (UK/EU)
3 = first Sunday in April to last Sunday in October (MX)
4 = last Sunday in September to first Sunday in April (NZ)
5 = first Sunday in October to first Sunday in April (AU)
6 = third Sunday in October to third Sunday in February (BZ) | -| 7. Alarm days | 0 = every day
1 = work week only (per settings below)
2 = weekend only | -| 8. Alarm snooze | 0–60 minutes. 0 disables snooze. | -| 9. Alarm signal | 0 = beeper
1 = relay (if in switch mode, will stay on for 2 hours)
(Clocks with both beeper and relay only) | -| 10. Alarm beeper pitch | [Note number on a piano keyboard](https://en.wikipedia.org/wiki/Piano_key_frequencies), from 49 (A4) to 88 (C8). Some are louder than others!
(Clocks with beeper only) | -| 11. Timer signal | 0 = beeper
1 = relay (if in switch mode, will stay on until timer runs down)
(Clocks with both beeper and relay only) | -| 12. Timer interval mode | 0 = count down and stop
1 = count down and restart (interval mode)
(Clocks with beeper and/or pulse relay only) | -| 13. Timer beeper pitch | Set the same way as the alarm pitch, above
(Clocks with beeper only) | -| 14. Strike | Make noise on the hour:
0 = off
1 = single beep
2 = pips
3 = strike the hour (1 to 12)
4 = ship's bell (hour and half hour)
Will not sound during day-off/night-off (except when off starts at top of hour)
(Clocks with beeper or pulse relay only) | -| 15. Strike signal | 0 = beeper
1 = relay
(Clocks with both beeper and pulse relay only) | -| 16. Strike beeper pitch | Set the same way as the alarm signal pitch, above. If using the pips, 63 (987 Hz) is closest to the real BBC pips frequency (1000 Hz).
(Clocks with beeper only) | -| 17. Night-off | To save tube life and/or preserve your sleep, dim or shut off tubes nightly when you're not around or sleeping.
0 = none (tubes fully on at night)
1 = dim tubes at night
2 = shut off tubes at night
When off, you can press **Select** to illuminate the tubes briefly. | -| 18. Night starts at | Time of day. | -| 19. Night ends at | Time of day. Set to 0:00 to use the alarm time. At this time (whether night-off/alarm is enabled or not), all tubes will briefly cycle through all digits at full brightness to help prevent [cathode poisoning](http://www.tube-tester.com/sites/nixie/different/cathode%20poisoning/cathode-poisoning.htm). | -| 20. Day-off | To further save tube life, shut off tubes during the day when you're not around.
0 = none (tubes fully on during the day)
1 = clock at work (shut off all day on weekends)
2 = clock at home (shut off during work hours)
When off, you can press **Select** to illuminuate the tubes briefly. | -| 21. First day of work week | 0–6 (Sunday–Saturday) | -| 22. Last day of work week | 0–6 (Sunday–Saturday) | -| 23. Work starts at | Time of day. | -| 24. Work ends at | Time of day. | +| | Option | Settings | +| --- | --- | --- | +| | **General** | | +| 1 | Time format | 1 = 12-hour
2 = 24-hour
(time-of-day display only; setting times is always done in 24h) | +| 2 | Date format | 1 = month/date/weekday
2 = date/month/weekday
3 = month/date/year
4 = date/month/year
5 = year/month/date
Note that four-tube clocks will display only the first two values in each of these options. | +| 3 | Display date during time? | 0 = never
1 = date instead of seconds
2 = full date (as above) every minute at :30 seconds
3 = same as 2, but scrolls in and out | +| 4 | Leading zero in hour, date, and month? | 0 = no
1 = yes | +| 5 | Digit fade | 0–20 (in hundredths of a second) | +| 6 | Auto DST | Add 1h for daylight saving time between these dates (at 2am):
0 = off
1 = second Sunday in March to first Sunday in November (US/CA)
2 = last Sunday in March to last Sunday in October (UK/EU)
3 = first Sunday in April to last Sunday in October (MX)
4 = last Sunday in September to first Sunday in April (NZ)
5 = first Sunday in October to first Sunday in April (AU)
6 = third Sunday in October to third Sunday in February (BZ) | +| 7 | LED behavior | 0 = always off
1 = always on
2 = on, but follow night/away modes if enabled
3 = off, but on when alarm/timer sounds
4 = off, but on with switched relay (if equipped)
(Clocks with LED control only, UNDB v8+) | +| 8 | Temperature format | 0 = Celsius
1 = Fahrenheit
(Clocks with temperature function enabled only) | +| 9 | Anti-cathode poisoning | Briefly cycles all digits to prevent [cathode poisoning](http://www.tube-tester.com/sites/nixie/different/cathode%20poisoning/cathode-poisoning.htm)
0 = once a day, at "Night ends at" time (or alarm time if applicable)
1 = at midnight
2 = at the top of every hour
3 = at the top of every minute | +| | **Alarm** | | +| 10 | Alarm days | 0 = every day
1 = work week only (per settings below)
2 = weekend only | +| 11 | Alarm signal | 0 = beeper
1 = relay (if in switch mode, will stay on for 2 hours)
(Clocks with both beeper and relay only) | +| 12 | Alarm beeper pitch | [Note number on a piano keyboard](https://en.wikipedia.org/wiki/Piano_key_frequencies), from 49 (A4) to 88 (C8). Some are louder than others!
(Clocks with beeper only) | +| 13 | Alarm snooze | 0–60 minutes. 0 disables snooze. | +| | **Timer** | | +| 20 | Timer interval mode | 0 = count down and stop
1 = count down and restart (interval mode)
(Clocks with beeper and/or pulse relay only) | +| 21 | Timer signal | 0 = beeper
1 = relay (if in switch mode, will stay on until timer runs down)
(Clocks with both beeper and relay only) | +| 22 | Timer beeper pitch | Set the same way as the alarm pitch, above
(Clocks with beeper only) | +| | **Strike** | | +| 30 | Strike | Make noise on the hour:
0 = off
1 = single beep
2 = pips
3 = strike the hour (1 to 12)
4 = ship's bell (hour and half hour)
Will not sound during night/away modes (except when off starts at top of hour)
(Clocks with beeper or pulse relay only) | +| 31 | Strike signal | 0 = beeper
1 = relay
(Clocks with both beeper and pulse relay only) | +| 32 | Strike beeper pitch | Set the same way as the alarm signal pitch, above. If using the pips, 63 (987 Hz) is closest to the real BBC pips frequency (1000 Hz).
(Clocks with beeper only) | +| | **Night mode and away mode** | | +| 40 | Night mode | To save tube life and/or preserve your sleep, dim or shut off tubes nightly when you're not around or sleeping.
0 = none (tubes fully on at night)
1 = dim tubes at night
2 = shut off tubes at night
When off, you can press **Select** to illuminate the tubes briefly. | +| 41 | Night starts at | Time of day. | +| 42 | Night ends at | Time of day. Set to 0:00 to use the alarm time. | +| 43 | Away mode | To further save tube life, shut off tubes during the day when you're not around.
0 = none (tubes fully on during the day)
1 = clock at work (shut off all day on weekends)
2 = clock at home (shut off during work hours)
When off, you can press **Select** to illuminuate the tubes briefly. | +| 44 | First day of work week | 0–6 (Sunday–Saturday) | +| 45 | Last day of work week | 0–6 (Sunday–Saturday) | +| 46 | Work starts at | Time of day. | +| 47 | Work ends at | Time of day. | To reset the options menu settings to "factory" defaults, hold **Select** while connecting the clock to power. ## Hardware Configuration -A number of hardware-related settings are specified in consts at the top of the sketch, which can be edited to suit the clock's hardware configuration. These include: +A number of hardware-related settings are specified in config files, one of which is included at the top of the sketch (so you can easily maintain multiple clocks with different hardware). These settings include: * **How many tubes** in the display module. Default is 6; small display adjustments are made for 4-tube clocks. * **Which functions** are enabled. Default is all but temperature and tube tester. @@ -77,7 +96,7 @@ A number of hardware-related settings are specified in consts at the top of the * **Soft power switch** enabled (switched relay only): default is yes; appliance can be switched manually with Adjust while viewing the time of day. Change to no if the appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. * **Various other durations** for things like scrolling speed, set mode timeouts, short and long button holds, "hold to set faster" thresholds, etc. -You can also set the **defaults for the options menu** to better suit the clock's intended use. +You can also set the **defaults for the options menu** (in main code, currently) to better suit the clock's intended use. **To compile the edited sketch:** You will need to add the [ooPinChangeInt](https://code.google.com/archive/p/oopinchangeint/downloads) and [NorthernWidget DS3231](https://github.com/NorthernWidget/DS3231) libraries to your Arduino IDE. diff --git a/sixtube_lm/configs/v5-4tube.h b/sixtube_lm/configs/v5-4tube.h new file mode 100644 index 0000000..9a57818 --- /dev/null +++ b/sixtube_lm/configs/v5-4tube.h @@ -0,0 +1,93 @@ +const byte displaySize = 4; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the UNDB v5 board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A2; //main select button - must be equipped +const byte mainAdjUp = A1; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A0; +const byte altSel = 0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 2; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = -1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; //don't change - not available until UNDB v8 +const byte relayMode = 0; //don't change - not available until UNDB v8 +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 0; //don't change - not available until UNDB v8 + +//LED circuit control +const char ledPin = -1; //don't change - not available until UNDB v8 + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v5-6tube-red.h b/sixtube_lm/configs/v5-6tube-red.h new file mode 100644 index 0000000..3d0f4f2 --- /dev/null +++ b/sixtube_lm/configs/v5-6tube-red.h @@ -0,0 +1,93 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the UNDB v5 board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A1; //main select button - must be equipped +const byte mainAdjUp = A6; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A2; +const byte altSel = 0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = -1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; //don't change - not available until UNDB v8 +const byte relayMode = 0; //don't change - not available until UNDB v8 +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 0; //don't change - not available until UNDB v8 + +//LED circuit control +const char ledPin = -1; //don't change - not available until UNDB v8 + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v5-6tube-rotary.h b/sixtube_lm/configs/v5-6tube-rotary.h new file mode 100644 index 0000000..a59cb2e --- /dev/null +++ b/sixtube_lm/configs/v5-6tube-rotary.h @@ -0,0 +1,93 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the UNDB v5 board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A2; //main select button - must be equipped +const byte mainAdjUp = A0; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A1; +const byte altSel = 0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 2; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = -1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; //don't change - not available until UNDB v8 +const byte relayMode = 0; //don't change - not available until UNDB v8 +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 0; //don't change - not available until UNDB v8 + +//LED circuit control +const char ledPin = -1; //don't change - not available until UNDB v8 + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v5-6tube.h b/sixtube_lm/configs/v5-6tube.h new file mode 100644 index 0000000..0b34ff6 --- /dev/null +++ b/sixtube_lm/configs/v5-6tube.h @@ -0,0 +1,93 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the UNDB v5 board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A2; //main select button - must be equipped +const byte mainAdjUp = A1; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A0; +const byte altSel = 0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = -1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; //don't change - not available until UNDB v8 +const byte relayMode = 0; //don't change - not available until UNDB v8 +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 0; //don't change - not available until UNDB v8 + +//LED circuit control +const char ledPin = -1; //don't change - not available until UNDB v8 + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8-4tube.h b/sixtube_lm/configs/v8-4tube.h new file mode 100644 index 0000000..af0fd3a --- /dev/null +++ b/sixtube_lm/configs/v8-4tube.h @@ -0,0 +1,99 @@ +const byte displaySize = 4; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A1; //main select button - must be equipped +const byte mainAdjUp = A2; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A3; +const byte altSel = A0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = fnIsAlarm; //go straight to alarm + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control +const char ledPin = -1; +// -1 to disable feature; A2 if equipped (UNDB v8) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8-6tube.h b/sixtube_lm/configs/v8-6tube.h new file mode 100644 index 0000000..fcca2a3 --- /dev/null +++ b/sixtube_lm/configs/v8-6tube.h @@ -0,0 +1,99 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A1; //main select button - must be equipped +const byte mainAdjUp = A2; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A3; +const byte altSel = A0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder. +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = fnIsAlarm; //go straight to alarm + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = -1; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control +const char ledPin = -1; +// -1 to disable feature; A2 if equipped (UNDB v8) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8a-6tube-relayswitch-pwm.h b/sixtube_lm/configs/v8a-6tube-relayswitch-pwm.h new file mode 100644 index 0000000..d62f503 --- /dev/null +++ b/sixtube_lm/configs/v8a-6tube-relayswitch-pwm.h @@ -0,0 +1,99 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A1; //main select button - must be equipped +const byte mainAdjUp = A6; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A7; +const byte altSel = A0; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder (not currently supported). +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = fnIsAlarm; //go straight to alarm + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = A3; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control with PWM +const char ledPin = 11; +// -1 to disable feature; 11 if equipped (UNDB v8 modded) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 16; //A2 +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8b-6tube-relayswitch-pwm-top.h b/sixtube_lm/configs/v8b-6tube-relayswitch-pwm-top.h new file mode 100644 index 0000000..d06270b --- /dev/null +++ b/sixtube_lm/configs/v8b-6tube-relayswitch-pwm-top.h @@ -0,0 +1,94 @@ +//v8 with B style modification, and flipped Sel and Alt buttons so Sel is in the front when buttons are mounted display-side with IN-8-A display + +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A7; +const byte mainAdjUp = A0; +const byte mainAdjDn = A1; +const byte altSel = A6; //if not equipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder (not currently supported). +const byte mainAdjType = 1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = A3; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control with PWM +const char ledPin = 11; +// -1 to disable feature; 11 if equipped (UNDB v8 modded) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 16; //A2 - was 11 before PWM fix +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8b-6tube-relayswitch-pwm.h b/sixtube_lm/configs/v8b-6tube-relayswitch-pwm.h new file mode 100644 index 0000000..4c8a54a --- /dev/null +++ b/sixtube_lm/configs/v8b-6tube-relayswitch-pwm.h @@ -0,0 +1,99 @@ +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A6; //main select button - must be equipped +const byte mainAdjUp = A0; //main up/down buttons or rotary encoder - must be equipped +const byte mainAdjDn = A1; +const byte altSel = A7; //alt select button - if unequipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder (not currently supported). +const byte mainAdjType = 1; + +// In normal running mode, what do the controls do? +// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function +// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. +const char mainSelFn = -2; +const char mainAdjFn = -1; +const byte altSelFn = fnIsAlarm; //go straight to alarm + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = A3; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control with PWM +const char ledPin = 11; +// -1 to disable feature; 11 if equipped (UNDB v8 modded) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 9; +//3 pins out to anode channel switches +const char anode1 = 16; //A2 +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8c-6tube-relayswitch-pwm-top.h b/sixtube_lm/configs/v8c-6tube-relayswitch-pwm-top.h new file mode 100644 index 0000000..aaf181d --- /dev/null +++ b/sixtube_lm/configs/v8c-6tube-relayswitch-pwm-top.h @@ -0,0 +1,94 @@ +//v8 with C style modification and flipped Sel and Alt buttons so Sel is in the front when buttons are mounted display-side with IN-8-A display + +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A7; +const byte mainAdjUp = A0; +const byte mainAdjDn = A1; +const byte altSel = A6; //if not equipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder (not currently supported). +const byte mainAdjType = 1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = A3; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control with PWM +const char ledPin = 9; +// -1 to disable feature; 11 if equipped (UNDB v8 modded) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 16; //A2 - was 9 before PWM fix pt2 +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/configs/v8c-6tube-relayswitch-pwm.h b/sixtube_lm/configs/v8c-6tube-relayswitch-pwm.h new file mode 100644 index 0000000..ff14df6 --- /dev/null +++ b/sixtube_lm/configs/v8c-6tube-relayswitch-pwm.h @@ -0,0 +1,94 @@ +//v8 with C style modification + +const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks + +// available clock functions, and unique IDs (between 0 and 200) +const byte fnIsTime = 0; +const byte fnIsDate = 1; +const byte fnIsAlarm = 2; +const byte fnIsTimer = 3; +const byte fnIsDayCount = 4; +const byte fnIsTemp = 5; +const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner +// functions enabled in this clock, in their display order. Only fnIsTime is required +const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester +// To control which of these display persistently vs. switch back to Time after a few seconds, search "Temporary-display mode timeout" + +// These are the RLB board connections to Arduino analog input pins. +// S1/PL13 = Reset +// S2/PL5 = A1 +// S3/PL6 = A0 +// S4/PL7 = A6 +// S5/PL8 = A3 +// S6/PL9 = A2 +// S7/PL14 = A7 +// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. + +// What input is associated with each control? +const byte mainSel = A6; +const byte mainAdjUp = A0; +const byte mainAdjDn = A1; +const byte altSel = A7; //if not equipped, set to 0 + +// What type of adj controls are equipped? +// 1 = momentary buttons. 2 = quadrature rotary encoder (not currently supported). +const byte mainAdjType = 1; + +//What are the signal pin(s) connected to? +const char piezoPin = 10; +const char relayPin = A3; +// -1 to disable feature (no relay item equipped); A3 if equipped (UNDB v8) +const byte relayMode = 0; //If relay is equipped, what does it do? +// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. +// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. +const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) +const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) +const word piezoPulse = 500; //ms - used with piezo via tone() +const word relayPulse = 200; //ms - used with pulsed relay + +//Soft power switches +const byte enableSoftAlarmSwitch = 1; +// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). +// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. +const byte enableSoftPowerSwitch = 1; //works with switched relay only +// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). +// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. + +//LED circuit control with PWM +const char ledPin = 9; +// -1 to disable feature; 11 if equipped (UNDB v8 modded) + +//When display is dim/off, a press will light the tubes for how long? +const byte unoffDur = 10; //sec + +// How long (in ms) are the button hold durations? +const word btnShortHold = 1000; //for setting the displayed feataure +const word btnLongHold = 3000; //for for entering options menu +const byte velThreshold = 150; //ms +// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). +// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. + +// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms +const word cleanSpeed = 200; //ms +const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 + +// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec +const unsigned long timeoutSet = 120; //sec +const unsigned long timeoutTempFn = 5; //sec + +//This clock is 2x3 multiplexed: two tubes powered at a time. +//The anode channel determines which two tubes are powered, +//and the two SN74141 cathode driver chips determine which digits are lit. +//4 pins out to each SN74141, representing a binary number with values [1,2,4,8] +const char outA1 = 2; +const char outA2 = 3; +const char outA3 = 4; +const char outA4 = 5; +const char outB1 = 6; +const char outB2 = 7; +const char outB3 = 8; +const char outB4 = 16; //A2 - was 9 before PWM fix pt2 +//3 pins out to anode channel switches +const char anode1 = 11; +const char anode2 = 12; +const char anode3 = 13; \ No newline at end of file diff --git a/sixtube_lm/sixtube_lm.ino b/sixtube_lm/sixtube_lm.ino index 678e3c6..e7ece6a 100644 --- a/sixtube_lm/sixtube_lm.ino +++ b/sixtube_lm/sixtube_lm.ino @@ -4,93 +4,21 @@ // based on original sketch by Robin Birtles (rlb-designs.com) and Chris Gerekos // based on http://arduinix.com/Main/Code/ANX-6Tube-Clock-Crossfade.txt -#include //Arduino - GNU LPGL -#include //Arduino - GNU LPGL -#include //NorthernWidget - The Unlicense - - -////////// Hardware configuration consts ////////// - -const byte displaySize = 6; //number of tubes in display module. Small display adjustments are made for 4-tube clocks - -// available clock functions, and unique IDs (between 0 and 200) -const byte fnIsTime = 0; -const byte fnIsDate = 1; -const byte fnIsAlarm = 2; -const byte fnIsTimer = 3; -const byte fnIsDayCount = 4; -const byte fnIsTemp = 5; -const byte fnIsTubeTester = 6; //cycles all digits on all tubes 1/second, similar to anti-cathode-poisoning cleaner -// functions enabled in this clock, in their display order. Only fnIsTime is required -const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsAlarm, fnIsTimer, fnIsDayCount}; //, fnIsTemp, fnIsTubeTester - -// These are the RLB board connections to Arduino analog input pins. -// S1/PL13 = Reset -// S2/PL5 = A1 -// S3/PL6 = A0 -// S4/PL7 = A6 -// S5/PL8 = A3 -// S6/PL9 = A2 -// S7/PL14 = A7 -// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts. - -// What input is associated with each control? -const byte mainSel = A2; //main select button - must be equipped -const byte mainAdjUp = A1; //main up/down buttons or rotary encoder - must be equipped -const byte mainAdjDn = A0; -const byte altSel = 0; //alt select button - if unequipped, set to 0 - -// What type of adj controls are equipped? -// 1 = momentary buttons. 2 = quadrature rotary encoder (not supported presently) -const byte mainAdjType = 1; - -// In normal running mode, what do the controls do? -// -1 = nothing/switch, -2 = cycle through functions, fn in fnsEnabled[] = go to that function -// If using soft alarm/power switch per below, the control(s) set to -1 will do the switching. -const char mainSelFn = -2; -const char mainAdjFn = -1; -const byte altSelFn = -1; - -//What are the signal pin(s) connected to? -const char piezoPin = 10; -const char relayPin = 127; -// If running a v5.0 board with only a piezo output, leave these set to 10 and -1 (disabled) respectively - unless removing the piezo to drive a relay instead, in which case, reverse them. -// If running a v5.x board with both piezo and relay, piezo is 10, relay is X. Extra menu options will appear to let end user decide which functions control which outputs. -// Relay toggles are written to the serial console for testing. To test without relay present, set relayPin to 127. -const byte relayMode = 0; //If relay is equipped, what does it do? -// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur. -// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse. -const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min) -const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr) -const word piezoPulse = 500; //ms - used with piezo via tone() -const word relayPulse = 200; //ms - used with pulsed relay - -const byte enableSoftAlarmSwitch = 1; -// 1 = yes. Alarm can be switched on and off when clock is displaying the alarm time (fnIsAlarm). -// 0 = no. Alarm will be permanently on. Use with switched relay if the appliance has its own switch on this relay circuit. -const byte enableSoftPowerSwitch = 1; //works with switched relay only -// 1 = yes. Relay can be switched on and off directly when clock is displaying time of day (fnIsTime). This is useful if connecting an appliance (e.g. radio) that doesn't have its own switch, or if replacing the clock unit in a clock radio where the clock does all the switching (e.g. Telechron). -// 0 = no. Use if the connected appliance has its own power switch (independent of this relay circuit) or does not need to be manually switched. - -const byte unoffDur = 10; //when display is dim/off, a press will light the tubes for this many seconds +//TODO: implement options for full date every 5 minutes +//TODO: see other TODOs throughout +//TODO: is it possible to trip the chime *after* determining if we're in night mode or not +//TODO: reenable rotary encoder with libraries with workable licenses -// How long (in ms) are the button hold durations? -const word btnShortHold = 1000; //for setting the displayed feataure -const word btnLongHold = 3000; //for for entering options menu -const byte velThreshold = 150; //ms -// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1). -// Recommend ~150 for rotaries. If you want to use this feature with buttons, extend to ~400. +////////// Hardware configuration ////////// +//Include the config file that matches your hardware setup. If needed, duplicate an existing one. -// What is the "frame rate" of the tube cleaning and display scrolling? up to 65535 ms -const word cleanSpeed = 200; //ms -const word scrollSpeed = 100; //ms - e.g. scroll-in-and-out date at :30 - to give the illusion of a slow scroll that doesn't pause, use (timeoutTempFn*1000)/(displaySize+1) - e.g. 714 for displaySize=6 and timeoutTempFn=5 +#include "configs/v8c-6tube-relayswitch-pwm-top.h" -// What are the timeouts for setting and temporarily-displayed functions? up to 65535 sec -const unsigned long timeoutSet = 120; //sec -const unsigned long timeoutTempFn = 5; //sec - -////////// Other global consts and vars ////////// +////////// Other includes, global consts, and vars ////////// +#include //Arduino - GNU LPGL +#include //Arduino - GNU LPGL +#include //NorthernWidget - The Unlicense /*EEPROM locations for non-volatile clock settings Don't change which location is associated with which setting; they should remain permanent to avoid the need for EEPROM initializations after code upgrades; and they are used directly in code. @@ -116,11 +44,11 @@ Some are skipped when they wouldn't apply to a given clock's hardware config, se 23 Alarm days 24 Alarm snooze 25 Timer interval mode - skipped when no piezo and relay is switch (start=0) -( 26 is available ) - 27 Night-off + 26 LED circuit behavior - skipped when no led pin + 27 Night mode 28-29 Night start, mins 30-31 Night end, mins - 32 Day-off + 32 Away mode 33 First day of workweek 34 Last day of workweek 35-36 Work starts at, mins @@ -131,16 +59,19 @@ Some are skipped when they wouldn't apply to a given clock's hardware config, se 42 Alarm signal, 0=beeper, 1=relay - skipped when no relay (start=0) or no piezo (start=0) 43 Timer signal - skipped when no relay (start=0) or no piezo (start=1) 44 Strike signal - skipped when no pulse relay (start=0) or no piezo (start=1) + 45 Temperature format - skipped when fnIsTemp is not in fnsEnabled + 46 Anti-cathode poisoning */ -//Options menu options' EEPROM locations and default/min/max values. -//Options' numbers may be changed by reordering these arrays (and changing readme accordingly). -//Although these arrays are 0-index, the option number displayed (and listed in readme) is 1-index. (search for "fn-fnOpts+1") -//1-index option number: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -const byte optsLoc[] = {16,17,18,19,20,22,23,24,42,39,43,25,40,21,44,41,27, 28, 30,32,33,34, 35, 37}; -const word optsDef[] = { 2, 1, 0, 0, 5, 0, 0, 9, 0,61, 0, 0,61, 0, 0,61, 0,1320, 360, 0, 1, 5, 480,1020}; -const word optsMin[] = { 1, 1, 0, 0, 0, 0, 0, 0, 0,49, 0, 0,49, 0, 0,49, 0, 0, 0, 0, 0, 0, 0, 0}; -const word optsMax[] = { 2, 5, 3, 1,20, 6, 2,60, 1,88, 1, 1,88, 4, 1,88, 2,1439,1439, 2, 6, 6,1439,1439}; +//Options menu numbers (displayed in UI and readme), EEPROM locations, and default/min/max values. +//Option numbers/order can be changed (though try to avoid for user convenience); +//but option locs should be maintained so EEPROM doesn't have to be reset after an upgrade. +// General Alarm Timer Strike Night and away mode +const byte optsNum[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13, 20,21,22, 30,31,32, 40, 41, 42,43,44,45, 46, 47}; +const byte optsLoc[] = {16,17,18,19,20,22,26,45,46, 23,42,39,24, 25,43,40, 21,44,41, 27, 28, 30,32,33,34, 35, 37}; +const word optsDef[] = { 2, 1, 0, 0, 5, 0, 1, 0, 0, 0, 0,61, 9, 0, 0,61, 0, 0,61, 0,1320, 360, 0, 1, 5, 480,1020}; +const word optsMin[] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,49, 0, 0, 0,49, 0, 0,49, 0, 0, 0, 0, 0, 0, 0, 0}; +const word optsMax[] = { 2, 5, 3, 1,20, 6, 4, 1, 3, 2, 1,88,60, 1, 1,88, 4, 1,88, 2,1439,1439, 2, 6, 6,1439,1439}; //RTC objects DS3231 ds3231; //an object to access the ds3231 specifically (temp, etc) @@ -153,6 +84,7 @@ byte btnCur = 0; //Momentary button currently in use - only one allowed at a tim byte btnCurHeld = 0; //Button hold thresholds: 0=none, 1=unused, 2=short, 3=long, 4=set by btnStop() unsigned long inputLast = 0; //When a button was last pressed unsigned long inputLast2 = 0; //Second-to-last of above +//TODO the math between these two may fail very rarely due to millis() rolling over while setting. Need to find a fix. I think it only applies to the rotary encoder though. const byte fnOpts = 201; //fn values from here to 255 correspond to options in the options menu byte fn = fnIsTime; //currently displayed fn (per fnsEnabled) @@ -169,8 +101,8 @@ word signalRemain = 0; //alarm/timer signal timeout counter, seconds word snoozeRemain = 0; //snooze timeout counter, seconds word timerInitial = 0; //timer original setting, seconds - up to 18 hours (64,800 seconds - fits just inside a word) word timerRemain = 0; //timer actual counter -unsigned long signalPulseStopTime = 0; //to stop beeps after a time -word unoffRemain = 0; //un-off (briefly turn on tubes during full night-off or day-off) timeout counter, seconds +unsigned long signalPulseStartTime = 0; //to keep track of individual pulses so we can stop them +word unoffRemain = 0; //un-off (briefly turn on tubes during full night or away modes) timeout counter, seconds byte displayDim = 2; //dim per display or function: 2=normal, 1=dim, 0=off byte cleanRemain = 11; //anti-cathode-poisoning clean timeout counter, increments at cleanSpeed ms (see loop()). Start at 11 to run at clock startup char scrollRemain = 0; //"frames" of scroll – 0=not scrolling, >0=coming in, <0=going out, -128=scroll out at next change @@ -181,7 +113,6 @@ char scrollRemain = 0; //"frames" of scroll – 0=not scrolling, >0=coming in, < void setup(){ Serial.begin(9600); Wire.begin(); - initOutputs(); initInputs(); delay(100); //prevents the below from firing in the event there's a capacitor stabilizing the input, which can read low falsely initEEPROM(readInput(mainSel)==LOW); //Do a hard init of EEPROM if button is held; else do a soft init to make sure vals in range @@ -200,21 +131,22 @@ void setup(){ writeEEPROM(25,0,false); //turn off timer interval mode } if(!enableSoftAlarmSwitch) writeEEPROM(2,1,false); //force alarm on if software switch is disabled + //if LED circuit is not switched (v5.0 board), the LED menu setting (eeprom 26) doesn't matter + initOutputs(); //depends on some EEPROM settings } -unsigned long pollLast = 0; //every 50ms unsigned long pollCleanLast = 0; //every cleanSpeed ms unsigned long pollScrollLast = 0; //every scrollSpeed ms void loop(){ unsigned long now = millis(); //If we're running a tube cleaning, advance it every cleanSpeed ms. - if(cleanRemain && pollCleanLast+cleanSpeed=cleanSpeed) { //account for rollover pollCleanLast=now; cleanRemain--; updateDisplay(); } //If we're scrolling an animation, advance it every scrollSpeed ms. - else if(scrollRemain!=0 && scrollRemain!=-128 && pollScrollLast+scrollSpeed=scrollSpeed) { pollScrollLast=now; if(scrollRemain<0) { scrollRemain++; updateDisplay(); @@ -224,11 +156,11 @@ void loop(){ } } //Every loop cycle, check the RTC and inputs (previously polled, but works fine without and less flicker) - pollLast=now; checkRTC(false); //if clock has ticked, decrement timer if running, and updateDisplay checkInputs(); //if inputs have changed, this will do things + updateDisplay as needed doSetHold(); //if inputs have been held, this will do more things + updateDisplay as needed cycleDisplay(); //keeps the display hardware multiplexing cycle going + cycleLEDs(); } @@ -236,13 +168,10 @@ void loop(){ void initInputs(){ //TODO are there no "loose" pins left floating after this? per https://electronics.stackexchange.com/q/37696/151805 - pinMode(A0, INPUT_PULLUP); - pinMode(A1, INPUT_PULLUP); - pinMode(A2, INPUT_PULLUP); - pinMode(A3, INPUT_PULLUP); - //4 and 5 used for I2C - pinMode(A6, INPUT); digitalWrite(A6, HIGH); - pinMode(A7, INPUT); digitalWrite(A7, HIGH); + pinMode(mainSel, INPUT_PULLUP); + pinMode(mainAdjUp, INPUT_PULLUP); + pinMode(mainAdjDn, INPUT_PULLUP); + pinMode(altSel, INPUT_PULLUP); //rotary encoder init //TODO encoder support } @@ -265,6 +194,7 @@ void checkBtn(byte btn){ //Changes in momentary buttons, LOW = pressed. //When a button event has occurred, will call ctrlEvt bool bnow = readInput(btn); + unsigned long now = millis(); //If the button has just been pressed, and no other buttons are in use... if(btnCur==0 && bnow==LOW) { btnCur = btn; btnCurHeld = 0; inputLast2 = inputLast; inputLast = millis(); @@ -272,11 +202,11 @@ void checkBtn(byte btn){ } //If the button is being held... if(btnCur==btn && bnow==LOW) { - if(millis() >= inputLast+btnLongHold && btnCurHeld < 3) { + if((unsigned long)(now-inputLast)>=btnLongHold && btnCurHeld < 3) { //account for rollover btnCurHeld = 3; ctrlEvt(btn,3); //hey, the button has been long-held } - else if(millis() >= inputLast+btnShortHold && btnCurHeld < 2) { + else if((unsigned long)(now-inputLast)>=btnShortHold && btnCurHeld < 2) { btnCurHeld = 2; ctrlEvt(btn,2); //hey, the button has been short-held } @@ -307,14 +237,21 @@ void ctrlEvt(byte ctrl, byte evt){ //evt: 1=press, 2=short hold, 3=long hold, 0=release. //We only handle press evts for adj ctrls, as that's the only evt encoders generate. //But we can handle short and long holds and releases for the sel ctrls (always buttons). - //TODO needs altSel //Before all else, is it a press to stop the signal? Silence it if(signalRemain>0 && evt==1){ stoppingSignal = true; signalStop(); - //If source is alarm, start snooze. 0 will have no effect. - if(signalSource==fnIsAlarm) snoozeRemain = readEEPROM(24,false)*60; //snoozeRemain is seconds, but snooze duration is minutes + if(signalSource==fnIsAlarm) { //If this was the alarm + //If the alarm is using the switched relay and this is the Alt button, don't set the snooze + if(relayMode==0 && readEEPROM(42,false)==1 && altSel!=0 && ctrl==altSel) { + //Short signal to indicate the alarm has been silenced until tomorrow - on beeper if relay is switched + if(getSignalOutput()==1 && relayMode==0) { if(piezoPin>=0) { tone(piezoPin, getSignalPitch(), 100); }} + else signalStart(fnIsAlarm,0,100); + } else { //start snooze + snoozeRemain = readEEPROM(24,false)*60; //snoozeRemain is seconds, but snooze duration is minutes + } + } return; } //After pressing to silence, short hold cancels a snooze; ignore other btn evts @@ -322,15 +259,17 @@ void ctrlEvt(byte ctrl, byte evt){ stoppingSignal = false; if(evt==2 && snoozeRemain>0) { snoozeRemain = 0; - signalStart(fnIsAlarm,0,100); //Short beep at alarm pitch + //Short signal to indicate the alarm has been silenced until tomorrow - on beeper if relay is switched + if(getSignalOutput()==1 && relayMode==0) { if(piezoPin>=0) { tone(piezoPin, getSignalPitch(), 100); }} + else signalStart(fnIsAlarm,0,100); } btnStop(); return; } //Is it a press for an un-off? + unoffRemain = unoffDur; //always do this so continued button presses during an unoff keep it alive if(displayDim==0 && evt==1) { - unoffRemain = unoffDur; updateDisplay(); btnStop(); return; @@ -365,28 +304,22 @@ void ctrlEvt(byte ctrl, byte evt){ } return; } - else if((ctrl==mainSel && evt==0) || ((ctrl==mainAdjUp || ctrl==mainAdjDn) && evt==1)) { //sel release or adj press - switch fn, depending on config - //-1 = nothing, -2 = cycle through functions, other = go to specific function (see fn) + else if((ctrl==mainSel && evt==0) || ((ctrl==mainAdjUp || ctrl==mainAdjDn) && evt==1)) { //sel release or adj press //we can't handle sel press here because, if attempting to enter setting mode, it would switch the fn first if(ctrl==mainSel){ - if(mainSelFn!=-1) { //do a function switch - if(mainSelFn==-2) fnScroll(1); //Go to next fn in the cycle - else fn = mainSelFn; - checkRTC(true); //updates display - } - else if(fn==fnIsAlarm) switchAlarm(0); //switch alarm - else if(fn==fnIsTime) switchPower(0); //switch power + fnScroll(1); //Go to next fn in the cycle + checkRTC(true); //updates display } else if(ctrl==mainAdjUp || ctrl==mainAdjDn) { - if(mainAdjFn!=-1) { //do a function switch - if(mainAdjFn==-2) fnScroll(ctrl==mainAdjUp?1:-1); //Go to next or previous fn in the cycle - else fn = mainAdjFn; - checkRTC(true); //updates display - } - else if(fn==fnIsAlarm) switchAlarm(ctrl==mainAdjUp?1:-1); //switch alarm - else if(fn==fnIsTime) switchPower(ctrl==mainAdjUp?1:-1); //switch power + if(fn==fnIsAlarm) switchAlarm(ctrl==mainAdjUp?1:-1); //switch alarm + //if(fn==fnIsTime) TODO volume in I2C radio } } + else if(altSel!=0 && ctrl==altSel) { //alt sel press + //TODO switch I2C radio + switchPower(0); + btnStop(); + } } //end fn running else { //fn setting @@ -430,8 +363,10 @@ void ctrlEvt(byte ctrl, byte evt){ timerInitial = fnSetVal*60; //timerRemain is seconds, but timer is set by minute timerRemain = timerInitial; //set actual timer going if(relayPin>=0 && relayMode==0 && readEEPROM(43,false)==1) { //if switched relay, and timer signal is set to it - if(relayPin!=127) digitalWrite(relayPin,HIGH); - Serial.print(millis(),DEC); Serial.println(F(" Relay on, timer set")); + digitalWrite(relayPin,LOW); //LOW = device on + //Serial.print(millis(),DEC); Serial.println(F(" Relay on, timer set")); + updateLEDs(); //LEDs following switch relay + //TODO will this cancel properly? especially if alarm interrupts? } clearSet(); break; case fnIsDayCount: //set like date, save in eeprom like finishOpt @@ -462,7 +397,7 @@ void ctrlEvt(byte ctrl, byte evt){ else { //options menu setting - to/from EEPROM - byte opt = fn-fnOpts; //current option number, 0-index (for opts[] arrays) + byte opt = fn-fnOpts; //current option index if(evt==2 && ctrl==mainSel) { //mainSel short hold: exit options menu btnStop(); @@ -515,7 +450,15 @@ void fnOptScroll(char dir){ || ((piezoPin<0 && relayMode==0) && (optLoc==21||optLoc==25)) //no piezo, and relay is switch: no strike or timer interval mode || ((relayPin<0 || piezoPin<0) && (optLoc==42||optLoc==43||optLoc==44)) //no relay or no piezo: no alarm/timer/strike signal || ((relayMode==0) && (optLoc==44)) //relay is switch: no strike signal - ) fnOptScroll(dir); + || ((ledPin<0) && (optLoc==26)) //no led pin: no led control + ) { + fnOptScroll(dir); + } + if(optLoc==45) { //temp not in fnsEnabled: skip temp format option (and calib if we get to it TODO) + bool found = 0; + for(byte fnct=0; fnct=250) { + doSetHoldLast = now; if(fnSetPg!=0 && (mainAdjType==1 && (btnCur==mainAdjUp || btnCur==mainAdjDn)) ){ //if we're setting, and this is an adj btn bool dir = (btnCur==mainAdjUp ? 1 : 0); //If short hold, or long hold but high velocity isn't supported, use low velocity (delta=1) @@ -545,6 +489,7 @@ void doSetHold(){ } void clearSet(){ //Exit set state startSet(0,0,0,0); + updateLEDs(); //in case LED setting was changed checkRTC(true); //force an update to tod and updateDisplay() } @@ -591,25 +536,20 @@ word readEEPROM(int loc, bool isWord){ } void writeEEPROM(int loc, int val, bool isWord){ if(isWord) { - Serial.print(F("EEPROM write word:")); - Serial.print(F(" loc ")); Serial.print(loc,DEC); - if(EEPROM.read(loc)!=highByte(val)) { - EEPROM.write(loc,highByte(val)); - Serial.print(F(" val ")); Serial.print(highByte(val),DEC); - } else Serial.print(F(" unchanged (no write).")); - Serial.print(F(" loc ")); Serial.print(loc+1,DEC); - if(EEPROM.read(loc+1)!=lowByte(val)) { - EEPROM.write(loc+1,lowByte(val)); - Serial.print(F(" val ")); Serial.print(lowByte(val),DEC); - } else Serial.print(F(" unchanged (no write).")); + // Serial.print(F("EEPROM write word:")); + // Serial.print(F(" loc ")); Serial.print(loc,DEC); Serial.print(F(" val ")); + // if(EEPROM.read(loc)!=highByte(val)) { Serial.print(highByte(val),DEC); } else { Serial.print(F("no change")); } + EEPROM.update(loc,highByte(val)); + // Serial.print(F(" loc ")); Serial.print(loc+1,DEC); Serial.print(F(" val ")); + // if(EEPROM.read(loc+1)!=lowByte(val)) { Serial.print(lowByte(val),DEC); } else { Serial.print(F("no change")); } + EEPROM.update(loc+1,lowByte(val)); } else { - Serial.print(F("EEPROM write byte:")); - Serial.print(F(" loc ")); Serial.print(loc,DEC); - if(EEPROM.read(loc)!=val) { EEPROM.write(loc,val); - Serial.print(F(" val ")); Serial.print(val,DEC); - } else Serial.print(F(" unchanged (no write).")); + // Serial.print(F("EEPROM write byte:")); + // Serial.print(F(" loc ")); Serial.print(loc,DEC); Serial.print(F(" val ")); + // if(EEPROM.read(loc)!=val) { Serial.print(val,DEC); } else { Serial.print(F("no change")); } + EEPROM.update(loc,val); } - Serial.println(); + //Serial.println(); } byte daysInMonth(word y, byte m){ @@ -647,20 +587,21 @@ void checkRTC(bool force){ //Checks display timeouts; //checks for new time-of-day second -> decrements timers and checks for timed events; //updates display for "running" functions. + unsigned long now = millis(); //Things to do every time this is called: timeouts to reset display. These may force a tick. - if(pollLast > inputLast){ //don't bother if the last input (which may have called checkRTC) was more recent than poll - //Option/setting timeout: if we're in the options menu, or we're setting a value - if(fnSetPg || fn>=fnOpts){ - if(pollLast>inputLast+(timeoutSet*1000)) { fnSetPg = 0; fn = fnIsTime; force=true; } //Time out after 2 mins - } - //Temporary-display mode timeout: if we're *not* in a permanent one (time, day counter, tester, or running/signaling timer) - else if(fn!=fnIsTime && fn!=fnIsTubeTester && fn!=fnIsDayCount && !(fn==fnIsTimer && (timerRemain>0 || signalRemain>0))){ - if(pollLast>inputLast+(timeoutTempFn*1000)) { fnSetPg = 0; fn = fnIsTime; force=true; } - } - //Stop a signal beep if it's time to - if(signalPulseStopTime && signalPulseStopTime=fnOpts){ + if((unsigned long)(now-inputLast)>=timeoutSet*1000) { fnSetPg = 0; fn = fnIsTime; force=true; } //Time out after 2 mins + } + //Temporary-display mode timeout: if we're *not* in a permanent one (time, day counter, temp, tester, or running/signaling timer) + else if(fn!=fnIsTime && fn!=fnIsTubeTester && fn!=fnIsDayCount && fn!=fnIsTemp && !(fn==fnIsTimer && (timerRemain>0 || signalRemain>0))){ + if((unsigned long)(now-inputLast)>=timeoutTempFn*1000) { fnSetPg = 0; fn = fnIsTime; force=true; } } + //Stop a signal pulse if it's time to + //This is only used for relay pulses, since beeper beep durations are done via tone() + //So we can safely assume the length of the pulse should be relayPulse + if(signalPulseStartTime && (unsigned long)(now-signalPulseStartTime)>=relayPulse) { signalPulseStop(); signalPulseStartTime = 0; } //Update things based on RTC tod = rtc.now(); @@ -680,17 +621,31 @@ void checkRTC(bool force){ fnSetPg = 0; fn = fnIsTime; signalStart(fnIsAlarm,1,0); } //end toddow check } //end alarm trigger - //check if we should trigger the cleaner (at night end time, or alarm time if night end is 0:00) - if(tod.hour()*60+tod.minute()==(readEEPROM(30,true)==0?readEEPROM(0,true):readEEPROM(30,true))) { + //If cleaner is set to option value 0 (at night end time, or alarm time if night end is 0:00), run it at that time + if(readEEPROM(46,false)==0 && tod.hour()*60+tod.minute()==(readEEPROM(30,true)==0?readEEPROM(0,true):readEEPROM(30,true))) { cleanRemain = 11; //loop() will pick this up - } //end cleaner check + } } if(tod.second()==30 && fn==fnIsTime && fnSetPg==0 && unoffRemain==0) { //At bottom of minute, maybe show date - not when unoffing - if(readEEPROM(18,false)>=2) { fn = fnIsDate; inputLast = pollLast; updateDisplay(); } + if(readEEPROM(18,false)>=2) { fn = fnIsDate; inputLast = now; updateDisplay(); } if(readEEPROM(18,false)==3) { startScroll(); } } + if(tod.second()==1) { //If cleaner is set to option value >0, run the cleaner at second :01 as applicable + switch(readEEPROM(46,false)) { + case 1: //at 00:00:01 + if(tod.hour()==0 && tod.minute()==0) cleanRemain = 11; + break; + case 2: //at :00:01 + if(tod.minute()==0) cleanRemain = 11; + break; + case 3: //at :01 + cleanRemain = 11; + break; + default: break; //case 0 is handled at top of minute + } + } - //Strikes - only if fn=clock, not setting, not night-off/day-off. Setting 21 will be off if signal type is no good + //Strikes - only if fn=clock, not setting, not night/away. Setting 21 will be off if signal type is no good //Short pips before the top of the hour if(tod.minute()==59 && tod.second()>=55 && readEEPROM(21,false)==2 && signalRemain==0 && snoozeRemain==0 && fn==fnIsTime && fnSetPg==0 && displayDim==2) { signalStart(fnIsTime,0,100); @@ -718,12 +673,12 @@ void checkRTC(bool force){ timerRemain--; if(timerRemain<=0) { //timer has elasped if(relayPin>=0 && relayMode==0 && readEEPROM(43,false)==1) { //if switched relay, and timer signal is set to it - signalStop(); + signalStop(); //interval timer setting is ignored (unlike below) } else { //not switched relay: turn on signal if(readEEPROM(25,false)) { //interval timer: a short signal and restart; don't change to timer fn signalStart(fnIsTimer,0,0); timerRemain = timerInitial; } else { - fnSetPg = 0; fn = fnIsTimer; inputLast = pollLast; signalStart(fnIsTimer,signalDur,0); + fnSetPg = 0; fn = fnIsTimer; inputLast = now; signalStart(fnIsTimer,signalDur,0); } } //end not switched relay } //end timer elapsed @@ -750,7 +705,7 @@ void checkRTC(bool force){ } //end natural second //Finally, update the display, whether natural tick or not, as long as we're not setting or on a scrolled display (unless forced eg. fn change) - //This also determines night-off/day-off, which is why strikes will happen if we go into off at top of hour TODO find a way to fix this + //This also determines night/away mode, which is why strikes will happen if we go into off at top of hour, and not when we come into on at the top of the hour TODO find a way to fix this if(fnSetPg==0 && (scrollRemain==0 || force)) updateDisplay(); rtcSecLast = tod.second(); @@ -810,15 +765,24 @@ void switchAlarm(char dir){ } void switchPower(char dir){ if(enableSoftPowerSwitch && relayPin>=0 && relayMode==0) { //if switched relay, and soft switch enabled - //signalStop(); could use this instead of the below to turn the radio off - if(relayPin!=127) { - digitalWrite(relayPin,(dir==1?HIGH:(dir==-1?LOW:!digitalRead(relayPin)))); + signalRemain = 0; snoozeRemain = 0; //in case alarm is going now - alternatively use signalStop()? + //If the timer is running and is using the switched relay, this instruction conflicts with it, so cancel it + if(timerRemain>0 && readEEPROM(43,false)==1) { + timerRemain=0; + updateDisplay(); } - Serial.print(millis(),DEC); - Serial.print(F(" Relay ")); - if(dir==0) { Serial.print(F("toggled")); if(relayPin!=127) { Serial.print(digitalRead(relayPin)==HIGH?F(" on"):F(" off")); } } - else Serial.print(dir==1?F("switched on"):F("switched off")); - Serial.println(F(", switchPower")); + //relayPin state is the reverse of the appliance state: LOW = device on, HIGH = device off + //Serial.print(millis(),DEC); + //Serial.print(F(" Relay requested to ")); + if(dir==0) { //toggle + dir = (digitalRead(relayPin)?1:-1); //LOW = device on, so this effectively does our dir reversion for us + //Serial.print(dir==1?F("toggle on"):F("toggle off")); + } else { + //Serial.print(dir==1?F("switch on"):F("switch off")); + } + digitalWrite(relayPin,(dir==1?0:1)); //LOW = device on + //Serial.println(F(", switchPower")); + updateLEDs(); //LEDs following switch relay } } @@ -903,25 +867,26 @@ void updateDisplay(){ } else if(fn >= fnOpts){ //options menu, but not setting a value displayDim = 2; - editDisplay(fn-fnOpts+1,0,1,false,false); //display option number (1-index) on hour tubes + editDisplay(optsNum[fn-fnOpts],0,1,false,false); //display option number on hour tubes blankDisplay(2,5,false); } else { //fn running - //Set displayDim per night-off and day-off settings - fnIsAlarm may override this + //Set displayDim per night/away settings - fnIsAlarm may override this //issue: moving from off alarm to next fn briefly shows alarm in full brightness. I think because of the display delays. TODO word todmins = tod.hour()*60+tod.minute(); //In order of precedence: //temporary unoff - if(unoffRemain > 0) displayDim = 2; - //day-off on weekends, all day + if(unoffRemain > 0) displayDim = 2; //TODO can we fade between dim states? + //clock at work: away on weekends, all day else if( readEEPROM(32,false)==1 && !isDayInRange(readEEPROM(33,false),readEEPROM(34,false),toddow) ) displayDim = 0; - //day-off on weekdays, during office hours only + //clock at home: away on weekdays, during office hours only else if( readEEPROM(32,false)==2 && isDayInRange(readEEPROM(33,false),readEEPROM(34,false),toddow) && isTimeInRange(readEEPROM(35,true), readEEPROM(37,true), todmins) ) displayDim = 0; - //night-off - if night end is 0:00, use alarm time instead + //night mode - if night end is 0:00, use alarm time instead else if( readEEPROM(27,false) && isTimeInRange(readEEPROM(28,true), (readEEPROM(30,true)==0?readEEPROM(0,true):readEEPROM(30,true)), todmins) ) displayDim = (readEEPROM(27,false)==1?1:0); //dim or off //normal else displayDim = 2; + updateLEDs(); switch(fn){ case fnIsTime: @@ -975,9 +940,13 @@ void updateDisplay(){ } else blankDisplay(mspos,mspos+1,true); //display second (in relative position). Leading zero if t>=1m. editDisplay(timerRemain%60,mspos+2,mspos+3,(timerRemain>=60?true:false),true); + //just in case 4-digit code is run on a 6-digit clock + if(displaySize<6) blankDisplay(4,5,false); break; case fnIsTemp: //thermometer int temp; temp = ds3231.getTemperature()*100; + if(readEEPROM(45,false)==1) temp = temp*1.8 + 3200; + //TODO another option to apply offset editDisplay(abs(temp)/100,1,3,(temp<0?true:false),true); //leading zeros if negative editDisplay(abs(temp)%100,4,5,true,true); break; @@ -990,7 +959,26 @@ void updateDisplay(){ editDisplay(tod.second(),5,5,true,false); default: break; }//end switch + } //end if fn running + + if(false) { //DEBUG MODE: when display's not working, just write it to the console, with time + if(tod.hour()<10) Serial.print(F("0")); + Serial.print(tod.hour(),DEC); + Serial.print(F(":")); + if(tod.minute()<10) Serial.print(F("0")); + Serial.print(tod.minute(),DEC); + Serial.print(F(":")); + if(tod.second()<10) Serial.print(F("0")); + Serial.print(tod.second(),DEC); + Serial.print(F(" ")); + for(byte i=0; i9) Serial.print(F("-")); //blanked tube + else Serial.print(displayNext[i],DEC); + } + Serial.println(); } + } //end updateDisplay() void editDisplay(word n, byte posStart, byte posEnd, bool leadingZeros, bool fade){ @@ -1026,10 +1014,10 @@ void startScroll() { //To scroll a value in, call this after calling editDisplay //The anode channel determines which two tubes are powered, //and the two SN74141 cathode driver chips determine which digits are lit. //4 pins out to each SN74141, representing a binary number with values [1,2,4,8] -byte binOutA[4] = {2,3,4,5}; -byte binOutB[4] = {6,7,8,9}; +byte binOutA[4] = {outA1,outA2,outA3,outA4}; +byte binOutB[4] = {outB1,outB2,outB3,outB4}; //3 pins out to anode channel switches -byte anodes[3] = {11,12,13}; +byte anodes[3] = {anode1,anode2,anode3}; const int fadeDur = 5; //ms - each multiplexed pair of digits appears for this amount of time per cycle const int dimDur = 4; //ms - portion of fadeDur that is left dark during dim times @@ -1042,7 +1030,9 @@ void initOutputs() { for(byte i=0; i<4; i++) { pinMode(binOutA[i],OUTPUT); pinMode(binOutB[i],OUTPUT); } for(byte i=0; i<3; i++) { pinMode(anodes[i],OUTPUT); } if(piezoPin>=0) pinMode(piezoPin, OUTPUT); - if(relayPin>=0 && relayPin!=127) pinMode(relayPin, OUTPUT); + if(relayPin>=0) pinMode(relayPin, OUTPUT); digitalWrite(relayPin, HIGH); //LOW = device on + if(ledPin>=0) pinMode(ledPin, OUTPUT); + updateLEDs(); //set to initial value } void cycleDisplay(){ @@ -1053,7 +1043,7 @@ void cycleDisplay(){ //But if we're setting, decide here to dim for every other 500ms since we started setting if(fnSetPg>0) { if(setStartLast==0) setStartLast = mils; - dim = 1-(((mils-setStartLast)/500)%2); + dim = 1-(((unsigned long)(mils-setStartLast)/500)%2); } else { if(setStartLast>0) setStartLast=0; } @@ -1074,8 +1064,9 @@ void cycleDisplay(){ // at 0ms, next = (( 0*(6-1))/20)+1 = 1; last = (6-nextDur) = 5; // at 10ms, next = ((10*(6-1))/20)+1 = 3; last = (6-nextDur) = 3; ... // at 20ms, next = ((20*(6-1))/20)+1 = 6; next = total, so fade is over! - //TODO facilitate longer fades by writing a tweening function that smooths the frames, i.e. 111121222 - fadeNextDur = (((mils-fadeStartLast)*(fadeDur-1))/(readEEPROM(20,false)*10))+1; + //TODO facilitate longer fades by writing a tweening function that smooths the frames, i.e. 111121222 - or use delayMicroseconds as below + //TODO does this have more problems with the mils rollover issue? + fadeNextDur = (((unsigned long)(mils-fadeStartLast)*(fadeDur-1))/(readEEPROM(20,false)*10))+1; if(fadeNextDur >= fadeLastDur) { //fade is over fadeStartLast = 0; fadeNextDur = 0; @@ -1088,6 +1079,7 @@ void cycleDisplay(){ } //end curently fading } //end fading enabled + //TODO consider using delayMicroseconds() which, with its tighter resolution, may give better control over fades and dim levels if(displayDim>0) { //if other display code says to shut off entirely, skip this part //Anode channel 0: tubes #2 (min x10) and #5 (sec x1) setCathodes(displayLast[2],displayLast[5]); //Via d2b decoder chip, set cathodes to old digits @@ -1144,26 +1136,28 @@ void signalStart(byte sigFn, byte sigDur, word pulseDur){ //make some noise! or //sigDur is the number of seconds to put on signalRemain, or 0 for a single immediate beep (skipped in radio mode). //Special case: if sigFn==fnIsAlarm, and sigDur>0, we'll use signalDur or switchDur as appropriate. //If doing a single beep, pulseDur is the number of ms it should last, or 0 for signal source's chosen output's pulse length (which will be used anyway if pulsed relay) + signalSource = sigFn; //Set this first so signalStop won't inadvertently turn off a switched relay started by something else signalStop(); - signalSource = sigFn; if(sigDur==0) signalPulseStart(pulseDur); //single immediate beep else { //long-duration signal (alarm, sleep, etc) if(sigFn==fnIsAlarm) signalRemain = (readEEPROM(42,false)==1 && relayPin>=0 && relayMode==0 ? switchDur : signalDur); else signalRemain = sigDur; //piezo or pulsed relay: checkRTC will handle it if(getSignalOutput()==1 && relayPin>=0 && relayMode==0) { //switched relay: turn it on now - if(relayPin!=127) digitalWrite(relayPin,HIGH); - Serial.print(millis(),DEC); Serial.println(F(" Relay on, signalStart")); + digitalWrite(relayPin,LOW); //LOW = device on + //Serial.print(millis(),DEC); Serial.println(F(" Relay on, signalStart")); } } + updateLEDs(); //LEDs following signal or relay } void signalStop(){ //stop current signal and clear out signal timer if applicable signalRemain = 0; snoozeRemain = 0; signalPulseStop(); //piezo or pulsed relay: stop now if(getSignalOutput()==1 && relayPin>=0 && relayMode==0) { //switched relay: turn it off now - if(relayPin!=127) digitalWrite(relayPin,LOW); - Serial.print(millis(),DEC); Serial.println(F(" Relay off, signalStop")); + digitalWrite(relayPin,HIGH); //LOW = device on + //Serial.print(millis(),DEC); Serial.println(F(" Relay off, signalStop")); } + updateLEDs(); //LEDs following relay } //beep start and stop should only be called by signalStart/signalStop and checkRTC void signalPulseStart(word pulseDur){ @@ -1172,9 +1166,9 @@ void signalPulseStart(word pulseDur){ tone(piezoPin, getSignalPitch(), (pulseDur==0?piezoPulse:pulseDur)); } else if(getSignalOutput()==1 && relayPin>=0 && relayMode==1) { //pulsed relay - if(relayPin!=127) digitalWrite(relayPin,HIGH); - signalPulseStopTime = millis()+relayPulse; //always use relayPulse in case timing is important for connected device - Serial.print(millis(),DEC); Serial.println(F(" Relay on, signalPulseStart")); + digitalWrite(relayPin,LOW); //LOW = device on + //Serial.print(millis(),DEC); Serial.println(F(" Relay on, signalPulseStart")); + signalPulseStartTime = millis(); } } void signalPulseStop(){ @@ -1182,9 +1176,9 @@ void signalPulseStop(){ noTone(piezoPin); } else if(getSignalOutput()==1 && relayPin>=0 && relayMode==1) { //pulsed relay - if(relayPin!=127) digitalWrite(relayPin,LOW); - signalPulseStopTime = 0; - Serial.print(millis(),DEC); Serial.println(F(" Relay off, signalPulseStart")); + digitalWrite(relayPin,HIGH); //LOW = device on + //Serial.print(millis(),DEC); Serial.println(F(" Relay off, signalPulseStop")); + signalPulseStartTime = 0; } } word getSignalPitch(){ //for current signal: time, timer, or (default) alarm @@ -1199,4 +1193,53 @@ word getHz(byte note){ } char getSignalOutput(){ //for current signal: time, timer, or (default) alarm: 0=piezo, 1=relay return readEEPROM((signalSource==fnIsTime?44:(signalSource==fnIsTimer?43:42)),false); +} + +const byte ledFadeStep = 10; //fade speed – with every loop() we'll increment/decrement the LED brightness (between 0-255) by this amount +byte ledStateNow = 0; +byte ledStateTarget = 0; +void updateLEDs(){ + //Run whenever something is changed that might affect the LED state: initial (initOutputs), signal start/stop, relay on/off, setting change + if(ledPin>=0) { + switch(readEEPROM(26,false)){ + case 0: //always off + ledStateTarget = 0; + //Serial.println(F("LEDs off always")); + break; + case 1: //always on + ledStateTarget = 255; + //Serial.println(F("LEDs on always")); + break; + case 2: //on, but follow night/away modes + ledStateTarget = (displayDim==2? 255: (displayDim==1? 127: 0)); + //Serial.print(displayDim==2? F("LEDs on"): (displayDim==1? F("LEDs dim"): F("LEDs off"))); Serial.println(F(" per dim state")); + break; + case 3: //off, but on when alarm/timer sounds + ledStateTarget = (signalRemain && (signalSource==fnIsAlarm || signalSource==fnIsTimer)? 255: 0); + //Serial.print(signalRemain && (signalSource==fnIsAlarm || signalSource==fnIsTimer)?F("LEDs on"):F("LEDs off")); Serial.println(F(" per alarm/timer")); + break; + case 4: //off, but on with switched relay + if(relayPin>=0 && relayMode==0) { + ledStateTarget = (!digitalRead(relayPin)? 255: 0); //LOW = device on + //Serial.print(!digitalRead(relayPin)? F("LEDs on"): F("LEDs off")); Serial.println(F(" per switched relay")); + } + break; + default: break; + } //end switch + } //if ledPin +} //end updateLEDs +void cycleLEDs() { + //Allows us to fade the LEDs to ledStateTarget by stepping via ledFadeStep + //TODO: it appears setting analogWrite(pin,0) does not completely turn the LEDs off. Anything else we could do? + if(ledStateNow != ledStateTarget) { + if(ledStateNow < ledStateTarget) { + ledStateNow = (ledStateTarget-ledStateNow <= ledFadeStep? ledStateTarget: ledStateNow+ledFadeStep); + } else { + ledStateNow = (ledStateNow-ledStateTarget <= ledFadeStep? ledStateTarget: ledStateNow-ledFadeStep); + } + // Serial.print(ledStateNow,DEC); + // Serial.print(F(" => ")); + // Serial.println(ledStateTarget,DEC); + analogWrite(ledPin,ledStateNow); + } } \ No newline at end of file diff --git a/v8a_mod_test/v8a_mod_test.ino b/v8a_mod_test/v8a_mod_test.ino new file mode 100644 index 0000000..ec73001 --- /dev/null +++ b/v8a_mod_test/v8a_mod_test.ino @@ -0,0 +1,129 @@ +//For testing mods to the v8.0 board, style A, with LED PWM + +const byte btnSel = A1; //was A1 +const byte btnAlt = A0; //was A0 +const byte btnUp = A6; //was A2 +const byte btnDn = A7; //was A3 +const byte pinLED = 11; //was A6 +const byte pinRelay = A3; //was A7 +byte binOutA[4] = {2,3,4,5}; +byte binOutB[4] = {6,7,8,9}; +byte anodes[3] = {16,12,13}; //first was 11 + +byte btnCur = 0; //Momentary button currently in use - only one allowed at a time + +byte ledStateNow = 0; +byte ledStateTarget = 0; + +void setup(){ + Serial.begin(9600); + //0 and 1: set as digital input + pinMode(A0, INPUT_PULLUP); + pinMode(A1, INPUT_PULLUP); + //2 and 3: set as digital output + pinMode(A2, OUTPUT); + pinMode(A3, OUTPUT); + //4 and 5: for I2C + //6 and 7: input, but analog pins with hardware pullup resistors, so nothing to do + analogWrite(pinLED,0); //0 = LEDs off + digitalWrite(pinRelay,1); //1 = connected device off + + //Set up just enough tube output to confirm the changed first anode is working + for(byte i=0; i<4; i++) { pinMode(binOutA[i],OUTPUT); pinMode(binOutB[i],OUTPUT); digitalWrite(binOutA[i],LOW); digitalWrite(binOutB[i],LOW); } + for(byte i=0; i<3; i++) { pinMode(anodes[i],OUTPUT); digitalWrite(anodes[i],LOW); } + digitalWrite(anodes[0],HIGH); //the one we want to test + + Serial.println(); + Serial.println(F("Ready for input.")); + Serial.println(F("Display should now read 0 on 3rd and 6th tubes.")); + Serial.println(F("SEL should change them to a 1; ALT should change them to 2.")); + Serial.println(F("UP should fade the LEDs in and out.")); + Serial.println(F("DOWN should toggle the relay.")); +} + +void loop(){ + checkInputs(); + if(ledStateNow != ledStateTarget) { + if(ledStateNow > ledStateTarget) { ledStateNow -= 5; } + else if(ledStateNow < ledStateTarget) { ledStateNow += 5; } + // Serial.print(ledStateNow,DEC); + // Serial.print(F(" => ")); + // Serial.println(ledStateTarget,DEC); + analogWrite(pinLED,ledStateNow); + } + delay(5); //in case of switch bounce? +} + +void checkInputs(){ + checkBtn(btnSel); + checkBtn(btnAlt); + checkBtn(btnUp); + checkBtn(btnDn); +} +bool readInput(byte pin){ + if(pin==A6 || pin==A7) return analogRead(pin)<100?0:1; //analog-only pins + else return digitalRead(pin); +} +void checkBtn(byte btn){ + //Changes in momentary buttons, LOW = pressed. + //When a button event has occurred, will call ctrlEvt + bool bnow = readInput(btn); + //If the button has just been pressed, and no other buttons are in use... + if(btnCur==0 && bnow==LOW) { + btnCur = btn; + Serial.println(); + bool newState = LOW; + switch(btn) { + case btnSel: + Serial.println(F("btnSel pressed")); + digitalWrite(binOutA[0],HIGH); + digitalWrite(binOutA[1],LOW); + digitalWrite(binOutB[0],HIGH); + digitalWrite(binOutB[1],LOW); + break; + case btnAlt: + Serial.println(F("btnAlt pressed")); + digitalWrite(binOutA[0],LOW); + digitalWrite(binOutA[1],HIGH); + digitalWrite(binOutB[0],LOW); + digitalWrite(binOutB[1],HIGH); + break; + case btnUp: + Serial.println(F("btnUp pressed")); + ledStateTarget = (ledStateTarget==0?255:0); + Serial.println(); + if(ledStateTarget==0) Serial.println(F("LED switched off. LED PWM pin should fade to open circuit.")); + else Serial.println(F("LED switched on. LED PWM pin should fade to closed circuit.")); + break; + case btnDn: + Serial.println(F("btnDn pressed")); + newState = !digitalRead(pinRelay); + digitalWrite(pinRelay, newState); + Serial.println(); + if(newState) Serial.println(F("Relay switched on. Relay pins should measure open circuit now (connected device off).")); + else Serial.println(F("Relay switched off. Relay pins should measure closed circuit now (connected device on).")); + break; + default: break; + } //end button printing switch + } //end if button presed + //If the button has just been released... + if(btnCur==btn && bnow==HIGH) { + Serial.println(); + switch(btn){ + case btnSel: + Serial.println(F("btnSel released")); + break; + case btnAlt: + Serial.println(F("btnAlt released")); + break; + case btnUp: + Serial.println(F("btnUp released")); + break; + case btnDn: + Serial.println(F("btnDn released")); + break; + default: break; + } + btnCur = 0; + } +} \ No newline at end of file diff --git a/v8b_mod_test/v8b_mod_test.ino b/v8b_mod_test/v8b_mod_test.ino new file mode 100644 index 0000000..2dbed76 --- /dev/null +++ b/v8b_mod_test/v8b_mod_test.ino @@ -0,0 +1,129 @@ +//For testing mods to the v8.0 board, style B, with LED PWM + +const byte btnSel = A6; //was A1 +const byte btnAlt = A7; //was A0 +const byte btnUp = A0; //was A2 +const byte btnDn = A1; //was A3 +const byte pinLED = 11; //was A6 +const byte pinRelay = A3; //was A7 +byte binOutA[4] = {2,3,4,5}; +byte binOutB[4] = {6,7,8,9}; +byte anodes[3] = {16,12,13}; //first was 11 + +byte btnCur = 0; //Momentary button currently in use - only one allowed at a time + +byte ledStateNow = 0; +byte ledStateTarget = 0; + +void setup(){ + Serial.begin(9600); + //0 and 1: set as digital input + pinMode(A0, INPUT_PULLUP); + pinMode(A1, INPUT_PULLUP); + //2 and 3: set as digital output + pinMode(A2, OUTPUT); + pinMode(A3, OUTPUT); + //4 and 5: for I2C + //6 and 7: input, but analog pins with hardware pullup resistors, so nothing to do + analogWrite(pinLED,0); //0 = LEDs off + digitalWrite(pinRelay,1); //1 = connected device off + + //Set up just enough tube output to confirm the changed first anode is working + for(byte i=0; i<4; i++) { pinMode(binOutA[i],OUTPUT); pinMode(binOutB[i],OUTPUT); digitalWrite(binOutA[i],LOW); digitalWrite(binOutB[i],LOW); } + for(byte i=0; i<3; i++) { pinMode(anodes[i],OUTPUT); digitalWrite(anodes[i],LOW); } + digitalWrite(anodes[0],HIGH); //the one we want to test + + Serial.println(); + Serial.println(F("Ready for input.")); + Serial.println(F("Display should now read 0 on 3rd and 6th tubes.")); + Serial.println(F("SEL should change them to a 1; ALT should change them to 2.")); + Serial.println(F("UP should fade the LEDs in and out.")); + Serial.println(F("DOWN should toggle the relay.")); +} + +void loop(){ + checkInputs(); + if(ledStateNow != ledStateTarget) { + if(ledStateNow > ledStateTarget) { ledStateNow -= 5; } + else if(ledStateNow < ledStateTarget) { ledStateNow += 5; } + // Serial.print(ledStateNow,DEC); + // Serial.print(F(" => ")); + // Serial.println(ledStateTarget,DEC); + analogWrite(pinLED,ledStateNow); + } + delay(5); //in case of switch bounce? +} + +void checkInputs(){ + checkBtn(btnSel); + checkBtn(btnAlt); + checkBtn(btnUp); + checkBtn(btnDn); +} +bool readInput(byte pin){ + if(pin==A6 || pin==A7) return analogRead(pin)<100?0:1; //analog-only pins + else return digitalRead(pin); +} +void checkBtn(byte btn){ + //Changes in momentary buttons, LOW = pressed. + //When a button event has occurred, will call ctrlEvt + bool bnow = readInput(btn); + //If the button has just been pressed, and no other buttons are in use... + if(btnCur==0 && bnow==LOW) { + btnCur = btn; + Serial.println(); + bool newState = LOW; + switch(btn) { + case btnSel: + Serial.println(F("btnSel pressed")); + digitalWrite(binOutA[0],HIGH); + digitalWrite(binOutA[1],LOW); + digitalWrite(binOutB[0],HIGH); + digitalWrite(binOutB[1],LOW); + break; + case btnAlt: + Serial.println(F("btnAlt pressed")); + digitalWrite(binOutA[0],LOW); + digitalWrite(binOutA[1],HIGH); + digitalWrite(binOutB[0],LOW); + digitalWrite(binOutB[1],HIGH); + break; + case btnUp: + Serial.println(F("btnUp pressed")); + ledStateTarget = (ledStateTarget==0?255:0); + Serial.println(); + if(ledStateTarget==0) Serial.println(F("LED switched off. LED PWM pin should fade to open circuit.")); + else Serial.println(F("LED switched on. LED PWM pin should fade to closed circuit.")); + break; + case btnDn: + Serial.println(F("btnDn pressed")); + newState = !digitalRead(pinRelay); + digitalWrite(pinRelay, newState); + Serial.println(); + if(newState) Serial.println(F("Relay switched on. Relay pins should measure open circuit now (connected device off).")); + else Serial.println(F("Relay switched off. Relay pins should measure closed circuit now (connected device on).")); + break; + default: break; + } //end button printing switch + } //end if button presed + //If the button has just been released... + if(btnCur==btn && bnow==HIGH) { + Serial.println(); + switch(btn){ + case btnSel: + Serial.println(F("btnSel released")); + break; + case btnAlt: + Serial.println(F("btnAlt released")); + break; + case btnUp: + Serial.println(F("btnUp released")); + break; + case btnDn: + Serial.println(F("btnDn released")); + break; + default: break; + } + btnCur = 0; + } +} \ No newline at end of file diff --git a/v8c_mod_test/v8c_mod_test.ino b/v8c_mod_test/v8c_mod_test.ino new file mode 100644 index 0000000..3cbd09a --- /dev/null +++ b/v8c_mod_test/v8c_mod_test.ino @@ -0,0 +1,129 @@ +//For testing mods to the v8.0 board, style B, with LED PWM + +const byte btnSel = A6; //was A1 +const byte btnAlt = A7; //was A0 +const byte btnUp = A0; //was A2 +const byte btnDn = A1; //was A3 +const byte pinLED = 9; //was A6 +const byte pinRelay = A3; //was A7 +byte binOutA[4] = {2,3,4,5}; +byte binOutB[4] = {6,7,8,16}; //last was 9 +byte anodes[3] = {11,12,13}; + +byte btnCur = 0; //Momentary button currently in use - only one allowed at a time + +byte ledStateNow = 0; +byte ledStateTarget = 0; + +void setup(){ + Serial.begin(9600); + //0 and 1: set as digital input + pinMode(A0, INPUT_PULLUP); + pinMode(A1, INPUT_PULLUP); + //2 and 3: set as digital output + pinMode(A2, OUTPUT); + pinMode(A3, OUTPUT); + //4 and 5: for I2C + //6 and 7: input, but analog pins with hardware pullup resistors, so nothing to do + analogWrite(pinLED,0); //0 = LEDs off + digitalWrite(pinRelay,1); //1 = connected device off + + //Set up just enough tube output to confirm the changed first anode is working + for(byte i=0; i<4; i++) { pinMode(binOutA[i],OUTPUT); pinMode(binOutB[i],OUTPUT); digitalWrite(binOutA[i],LOW); digitalWrite(binOutB[i],LOW); } + for(byte i=0; i<3; i++) { pinMode(anodes[i],OUTPUT); digitalWrite(anodes[i],LOW); } + digitalWrite(anodes[0],HIGH); //the one we want to test + + Serial.println(); + Serial.println(F("Ready for input.")); + Serial.println(F("Display should now read 0 on 3rd and 6th tubes.")); + Serial.println(F("SEL should change them to a 1; ALT should change them to 2.")); + Serial.println(F("UP should fade the LEDs in and out.")); + Serial.println(F("DOWN should toggle the relay.")); +} + +void loop(){ + checkInputs(); + if(ledStateNow != ledStateTarget) { + if(ledStateNow > ledStateTarget) { ledStateNow -= 5; } + else if(ledStateNow < ledStateTarget) { ledStateNow += 5; } + // Serial.print(ledStateNow,DEC); + // Serial.print(F(" => ")); + // Serial.println(ledStateTarget,DEC); + analogWrite(pinLED,ledStateNow); + } + delay(5); //in case of switch bounce? +} + +void checkInputs(){ + checkBtn(btnSel); + checkBtn(btnAlt); + checkBtn(btnUp); + checkBtn(btnDn); +} +bool readInput(byte pin){ + if(pin==A6 || pin==A7) return analogRead(pin)<100?0:1; //analog-only pins + else return digitalRead(pin); +} +void checkBtn(byte btn){ + //Changes in momentary buttons, LOW = pressed. + //When a button event has occurred, will call ctrlEvt + bool bnow = readInput(btn); + //If the button has just been pressed, and no other buttons are in use... + if(btnCur==0 && bnow==LOW) { + btnCur = btn; + Serial.println(); + bool newState = LOW; + switch(btn) { + case btnSel: + Serial.println(F("btnSel pressed")); + digitalWrite(binOutA[0],HIGH); + digitalWrite(binOutA[1],LOW); + digitalWrite(binOutB[0],HIGH); + digitalWrite(binOutB[1],LOW); + break; + case btnAlt: + Serial.println(F("btnAlt pressed")); + digitalWrite(binOutA[0],LOW); + digitalWrite(binOutA[1],HIGH); + digitalWrite(binOutB[0],LOW); + digitalWrite(binOutB[1],HIGH); + break; + case btnUp: + Serial.println(F("btnUp pressed")); + ledStateTarget = (ledStateTarget==0?255:0); + Serial.println(); + if(ledStateTarget==0) Serial.println(F("LED switched off. LED PWM pin should fade to open circuit.")); + else Serial.println(F("LED switched on. LED PWM pin should fade to closed circuit.")); + break; + case btnDn: + Serial.println(F("btnDn pressed")); + newState = !digitalRead(pinRelay); + digitalWrite(pinRelay, newState); + Serial.println(); + if(newState) Serial.println(F("Relay switched on. Relay pins should measure open circuit now (connected device off).")); + else Serial.println(F("Relay switched off. Relay pins should measure closed circuit now (connected device on).")); + break; + default: break; + } //end button printing switch + } //end if button presed + //If the button has just been released... + if(btnCur==btn && bnow==HIGH) { + Serial.println(); + switch(btn){ + case btnSel: + Serial.println(F("btnSel released")); + break; + case btnAlt: + Serial.println(F("btnAlt released")); + break; + case btnUp: + Serial.println(F("btnUp released")); + break; + case btnDn: + Serial.println(F("btnDn released")); + break; + default: break; + } + btnCur = 0; + } +} \ No newline at end of file