Thank you for the suggestion - it gives the correct result. Perhaps you could explain why my statement didn't work as there are numerous others of the same format that do (I struggle with C++). For example these 4 statements
if((curVal < 50) && (curVal >= 40)) ltemp = ltemp * .75;
if((curVal < 40) && (curVal >= 30)) ltemp = ltemp * .5;
if((curVal < 30) && (curVal >= 20)) ltemp = ltemp * .25;
if(curVal < 20) ltemp = 0;
And since others have chastised me for not including the whole sketch, here it is. The subject line is line number 712.
Regards, Dale Gloer
// Steam Throttle Version 2E_8.1 2-19-2023 EZSBC ESP32 Dev Board
// Based on many enhancements for the Steam Throttle Dave Merrill 12-9-2022,2023
// Based on Simple Wifi Throttle Version 5.3X Geoff Bunza 2020,2023
//
// Second throttle built by Dale Gloer 07/04/2023 using an EzSBC ESP32 board
// Throttle name is EzSBC_Throttle 1
//
// Stripped all history updates - see the original for history.
// DG 07072023 Mod to stop sound looping when mute switch is on - tag is DEGMod01
// DG Corrected Sound file list and timing - Tag is DEGMod02
// DG added line of code to prevent continuous restarts of sound when buttons are held ON eg. stoker or water fill - tag is DEGMod03
// DG added code to select which sounds stop playing when speed goes to zero - tag is DEGMod04
//
// Disabled DEGMod01 and DEGMod03 since they are unecessary
//
/* **********************************************************************************
* CAUTION
* make sure that the pin assignments in this sketch match the
* functions that are assigned to the as built throttle.
*
* **********************************************************************************
*/
//
// Special CONDITIONS:
// Turning Power On and holding the PB (Whistle) switch ON requests a Loco Address change
// 1. Let go of PB, display will change
// 2. Headlight on changes 1000 digit, 1st Toggle changes 100 digit, 2nd Toggle
// changes 10 digit, 3rd Toggle changes 1st digit
// 3. Press Water Injector switch to increase, press Coal Stoker switch to decrease
// 4. Holding PB for 1 Sec gets out of address set mode
//
// Setting 3 rightmost function switches on before powering up will turn DCC Power On
//
/* ********************************************************************************
********************************************************************************
* IMPORTANT Due to conflicts with another library you must ensure that the copy of
* library esp-oled-ssd1306 is the one stored in the
* .../Users/Owner/Documents/Arduino/Alternate_Libraries/SteamThrottle directory
* in order to compile this without errors.
********************************************************************************
********************************************************************************
*/
// For a connection via I2C using Wire include
#include <SSD1306Wire.h>
#include <WiFi.h>
#include <EEPROM.h>
#include <HardwareSerial.h> // Used with DFPlayer
#include <DFRobotDFPlayerMini.h>
#include <TFT_ST7735.h>
#include "_images/Steam_FaceG_96.c"
SSD1306Wire display(0x3c, 21, 22); // I2C addr, SDA, SCK Initialize the OLED display using Wire library
DFRobotDFPlayerMini myDFPlayer; // Set up the Audio interface
#define __CS 5 // Chip Select, AKA Slave Select VPSI pin 5
#define __DC 16 // Data Command, AKA AO, AKA RS-Register Select
#define __RST 19 // Reset
TFT_ST7735 tft = TFT_ST7735(__CS, __DC, __RST); // Set up the large display for Water Glass and Steam Gauge
// ******** REMOVE THE "//" IN THE FOOLOWING LINE TO SEND DEBUGGING
// ******** INFO TO THE SERIAL MONITOR
//#define DEBUG
//#define LEVER // Use with Serial Monitor to see pot reading and resultant for all 3 levers
#define SOUND // Use with serial monitor to debug sound problems
// WiFi Server Definitions
const char* ssid = "TELUS0360-2.4G";
const char* password = "selkirk5934";
const char* host = "192.168.1.94";
/*
const char* ssid = "Railway";
const char* password = "";
const char* host = "192.168.1.91";
*/
const int tpPort = 12090; // 12090
String tname="EzSBC_Throttle1"; // This is the defined name of this throttle for the WiThrottle Server display
#define Throttle_audio_volume 30 // 1=30 This is the default audio volume for cab sounds in the throttle
#define background_sounds_volume 30 // 1-30 This is the default audio volume for random background cab sounds
#define background_sound_speed 30 // speed must be > this for background cab sounds to play
//
int speedmin =178;
int speedmax =291;
int jbarmin = 172;
int jbarmax = 287;
int brakemin =178;
int brakemax =319;
// Switch assignment table for all pins:
#define PB 26 // Pushbutton active LOW usually assigned to F2 (Whistle)
#define TA 2 // SPDT toggle active LOW Headlight
#define TB 4 // SPDT toggle active LOW Bell
#define TC 12 // SPDT toggle active LOW Mute
#define TD 14 // SPDT toggle active LOW Spare
#define TE 13 // SPDT toggle active LOW Dim
#define TF 27 // SPDT push active LOW Cylinder Cock - Left side upper
#define mute_sound TC
#define cylcockblow TF
#define ADC_Ch 39 // Center Wiper of 10K Speed Potentiometer Ends=GND & +3.3V
#define jBar_Ch 32 // Center wiper of 10K Johnson Bar Potentiometer Ends=GND & +3.3v
#define brake_Ch 36 // Center wiper of 10K Brake Potentiometer Ends=GND & 3.3v
#define waterInj 33 // Water Injector PB
#define coalStok 15 // Coal Stoke PB
#define tenderFill 25 // Tender Fill PB
#define autoFireman 34 // Slide Switch to automatically refill water and steam gauges
#define spareSlide 35 // Spare slide switch
//Function Assignments - with standard function numbers used with Steam Throttle
#define F0_pin TA // Function 0 pin assignment F0 Headlight
#define F1_pin TB // Function 1 pin assignment F1 Bell
#define F2_pin PB // Function 2 pin assignment F2 Whistle
#define F3_pin TC // Function 3 pin assignment F8 Mute
#define F4_pin TD // Function 4 pin assignment F6 Spare
#define F5_pin TE // Function 5 pin assignment F7 Dim
#define F6_pin TF // Function 6 pin assignment F5 Cylinder Cock
int num_functions = 7; // Number of functions supported by this throttle
int function_pin [ ] ={ F0_pin,F1_pin,F2_pin,F3_pin,F4_pin,F5_pin,F6_pin }; // Ordered list of function pins
int function_map [ ] ={ 0, 1, 2, 8, 6, 7 , 5}; // this will map the pin list to actual DCC function numbers
#define brake_sets_DCC_function 35 // This function will be set if the Brake Lever is >50% on
#define short_whistle_time 400 // Set to 400ms to enable short whistle function, or 1 to disable
byte function_value [ ]={HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH }; // retained value of each function
byte foffset[ ] = {0, 12, 24, 39, 54, 66, 78}; // These are display offsets in info display for SWitch Abbreves
String fcs[ ] = {"L","B","W","M","S","D","C"}; // These are SWitch Abbreves in info display
boolean fvalue[ ]={false, false, false, false, false, false, false }; // These are the retained values of functions
// the following lines are wrong - silence is track 14 and should be listed last
// therefore the time line should have 200 as the last entry
// silence, CoalStoke, Water, TenderFill, 4hiss, 5rattle, 6hiss, 7clink, 8rattle, 9hiss, 11,12rattle,13blowdown
// unsigned long audio_track_time [ ]={200,22900,28000,42000,2200,86700,3600,1800,29200,3250,12500,118760,118760,62300};
//
// corrected lines follow DEGMod02
// CoalStoke, Water, TenderFill, 4hiss, 5rattle, 6hiss, 7clink, 8rattle, 9hiss, 10hiss, 11,12rattle,13blowdown, 14silence
unsigned long audio_track_time [ ]={22900,28000,42000,2200,86700,3600,1800,29200,3250,12500,118760,118760,62300,200};
//
boolean sound_on = false;
boolean Bypass = false;
byte Test = 0;
byte sound_playing, old_sound = 0;
unsigned long audio_limit = 0;
#define silence_s 14
#define coalshovel_s 1
#define waterinject_s 2
#define tenderfill_s 3
#define blowdown_s 13
byte set_volume_level = 1;
byte setF = 1;
byte setR = 1;
boolean testv = false;
int DCC_address;
int temp_address;
#define bounce_del 20
#define speed_repeat_bump 5
int speed_repeat_ctr = speed_repeat_bump;
boolean bvalue = false;
int lspeed = 0; // Speed steps, 0-127
int ltemp = 0; // Mapped speed pot value
int ltemp2 = 0; // Temporary value used in calculating speed steps
int ltemp_prev = 5; // Temporary value used in calculating speed steps
int prevSetSpeed = 1; // Temporary value used in uploading speed
int jbar_prev = 5; // Temporary value used in calculating jBar value
int brake_prev = 5; // Temporary value used in calculation brake steps
int tender_prev = 0; // Temporary value used in calculation tender water level
int tenderVal = 0; // Temporary value used in calculation tender water level
int tenderLevel = 400; // Start level for full tender water
int speedPot = 0; // Pot reading used to map speed
int dirPot = 0; // Pot reading used to map Johnson Bar setting
int brakePot = 0; // Pot reading used to map brake
boolean ftn_temp = false;
boolean f0temp = false;
int i,j,k,l;
String DCCAdr_string, xmits;
#define fdelay 1
#define ldelay 50
String jmri_res; //buffer to hold incoming response
int lastread = 0;
WiFiClient client;
boolean setaddr;
#define EEPROM_SIZE 64
const int audiocmddelay = 34; // Short wait to let the DFPlayer receive & process the command
const uint8_t bar_Width = 10;
#define dshift 10
byte g;
byte newVal;
byte curVal;
byte oldVal;
byte jbarVal;
byte brakeVal;
byte vgauge;
byte jFactor;
byte autoSwitch;
byte st2;
unsigned int changeSteam; // Variable for steam consumption
unsigned int changeWater; // Variable for water consumption
unsigned long changeSpeed; // Variable for speed limiting
unsigned long currentTimeSteam;
unsigned long currentTimeWater;
unsigned long currentTimeSpeed;
unsigned long currentWaterDraw;
unsigned long currentSteamDraw;
unsigned long loopTimeSteam;
unsigned long loopTimeWater;
unsigned long loopTimeSpeed;
unsigned long loopWaterDraw;
unsigned long loopSteamDraw;
#define loop_sample_time 250
bool autoFill = HIGH;
bool autoStok = HIGH;
bool firstFill = HIGH;
/////////////////// Setup ////////////////////////////////////////////
void setup() {
#ifdef DEBUG
Serial.begin(115200);
#else
#ifdef LEVER
Serial.begin(115200);
#else
#ifdef SOUND
Serial.begin(115200);
#endif
#endif
#endif
analogReadResolution(9); // Sets analog resolution to 9-bit, range 0-511
tft.begin(); // Intro message until WiFi connects - serves as a reminder
tft.setRotation(3); // Reorientation of display based on the Steam Throttle mounting
tft.drawImage(47, 17, &Steam_FaceG_96, NONE, 0); // Display the Pressure Gauge Background
tft.drawFastVLine( dshift+2, 17, 96, WHITE); // Draw a Scale
tft.drawFastHLine( dshift, 17, 4, WHITE);
for ( g = 10; g < 95; g += 10 ) {
tft.drawFastHLine( dshift, (g + 10), 4, WHITE);
}
curVal = 84;
drawNeedle(curVal, 94, 64, 33, BLACK, WHITE); // Draw Steam Level at 250
vgauge = 85; // Draw a pretty level
tft.drawRect(dshift+4, 17, bar_Width, 97, WHITE); // Draw the water sight glass
st2 = map(vgauge, 0, 100, (96 - 2), 0); //
tft.fillRect(dshift+5, 1, (bar_Width - 2), st2, BLACK); // Empty
tft.fillRect(dshift+5, (st2 + 18), (bar_Width - 2), (96 - 2 - st2), BLUE); // Fill Up
tft.setCursor(76, 50); // Now display the notice for WiFi Set Up
tft.setTextColor(BLACK);
tft.println("WAITING");
tft.setCursor(73,72);
tft.println("FOR WI-FI");
tft.setTextColor(WHITE);
if (!EEPROM.begin(EEPROM_SIZE)) // Start the EEPROM interface
{
#ifdef DEBUG
Serial.println("failed to initialise EEPROM"); // Send a message if we are in Debug mode
#endif
delay(1000000);
}
setaddr = false;
for (i=0; i<num_functions; i++) { // now set up the pins as inputs
pinMode ( function_pin[i], INPUT_PULLUP); // Sets function pins to HIGH defaults
}
pinMode (waterInj, INPUT_PULLUP); // Water Injector button
pinMode (coalStok, INPUT_PULLUP); // Coal/Stoker button
pinMode (tenderFill, INPUT_PULLUP); // Tendder Fill button - left side lower
pinMode (autoFireman, INPUT_PULLUP); // Auto Fireman switch - rear center
pinMode (spareSlide, INPUT_PULLUP); // Spare slide switch
#ifdef DEBUG
Serial.println("Starting Setup");
#endif
display.init(); // Initialize the lower Data Display
display.flipScreenVertically(); // Set the orientation
display.setFont(ArialMT_Plain_16); // Set the font to Arial
display.clear(); // Start with a clear display
display.setTextAlignment(TEXT_ALIGN_LEFT); // Align the text to the left side
while (digitalRead(PB)==LOW) { // Starts Set Address if Whistle is activated during power-up
display.setColor(DWHITE);
display.drawString(0, 12, "SET ADDR?");
display.display();
delay(500);
display.setColor(DBLACK);
display.drawString(0, 12, "SET ADDR?");
display.display();
delay(350); // Message flashes until PB released
setaddr = true; // Flag the need to enter a new address later
}
// We get here only when PB was released --> PB == HIGH
DCC_address = EEPROM.read(1); // Read previously saved address from EEPROM
DCC_address = (DCC_address<<8)+EEPROM.read(2); // 2 bytes long - we can handle long DCC addresses
if ((DCC_address <= 0 )||(DCC_address > 9999)) { // If stored address is out of bounds use address 3
#ifdef DEBUG
Serial.print("Resetting DCC Addr ");
Serial.println(DCC_address);
#endif
DCC_address= 3; // We found a bad DCC stored address
EEPROM.write(1, 0); // So We are defaulting to address 3
EEPROM.write(2, 3); // and saving it in EEPROM for next time
EEPROM.commit();
}
while (setaddr) { // Check if we need to enter a new address
#ifdef DEBUG
Serial.println("Starting Address");
delay(500);
#endif
temp_address = DCC_address;
display.setColor(DWHITE);
display.drawString(0, 12, "SET ADDR?"); // Throttle indication to enter new DCC address
display.drawString(0, 30, String(DCC_address));
display.display();
while (setaddr==true) { // Check each DCC address digit for increment/decrement
if ((digitalRead(coalStok)==LOW)&&(digitalRead(F4_pin)==LOW)) temp_address++;
if ((digitalRead(coalStok)==LOW)&&(digitalRead(F3_pin)==LOW)) temp_address = temp_address+10;
if ((digitalRead(coalStok)==LOW)&&(digitalRead(F1_pin)==LOW)) temp_address = temp_address+100;
if ((digitalRead(coalStok)==LOW)&&(digitalRead(F0_pin)==LOW)) temp_address = temp_address+1000;
if ((digitalRead(waterInj)==LOW)&&(digitalRead(F4_pin)==LOW)) temp_address--;
if ((digitalRead(waterInj)==LOW)&&(digitalRead(F3_pin)==LOW)) temp_address = temp_address-10;
if ((digitalRead(waterInj)==LOW)&&(digitalRead(F1_pin)==LOW)) temp_address = temp_address-100;
if ((digitalRead(waterInj)==LOW)&&(digitalRead(F0_pin)==LOW)) temp_address = temp_address-1000;
if (temp_address <= 1) temp_address = 1; // Trim address between 1 to 9999
if (temp_address >= 9999) temp_address = 9999;
if ((digitalRead(waterInj)==HIGH)&&(digitalRead(coalStok)==HIGH)) { // Set Neutral
delay(1000);
if (digitalRead(PB)==LOW) setaddr = false; // Exits Set Address by pulling PB Whistle
}
display.setColor(DBLACK);
display.drawString(0, 30, String(DCC_address)); // Erases previous address from display
DCC_address = temp_address;
display.setColor(DWHITE);
display.drawString(0, 30, String(DCC_address)); // Dispalys current address on display
display.display();
delay(1000); // Loops 1 second until waterInj and coalStok are released and whistle pressed
}
}
if ((DCC_address <= 1 )||(DCC_address > 9999)) DCC_address = 1; // Make sure we have a legitimate address
EEPROM.write(1, (DCC_address>>8)&0xff); // Save the working DCC address for next start up
EEPROM.write(2, DCC_address&0xff); // both pieces for a long address
EEPROM.commit(); // Writes current address to EEPROM
delay(1000);
display.clear();
display.drawString(5, 12, tname); // throttle name is now Steam Throttle but can be reset by tname
display.display();
// Connect to WiFi network
#ifdef DEBUG
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
#endif
display.drawString(0, 30, "WiFi: " );
display.drawString(50,30, ssid );
display.display();
WiFi.begin(ssid, password); // Start the WiFi connection using entered ssid and password
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
#ifdef DEBUG
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("connecting to ");
Serial.println(host);
if (!client.connect(host, tpPort)) Serial.println("connection failed");
#endif
if (!client.connect(host, tpPort)) return; // Now connect to the WiThrottle server using the host IP Address and port
#ifdef DEBUG
Serial.print("Throttle Name ");
Serial.println(tname);
#endif
client.println("N"+tname); // Tell the WiThrottle server the Name of Throttle
delay(ldelay);
display.setColor(DWHITE);
display.clear();
display.drawString(0, 12, "Loco: ");
display.drawString(42, 12, String(DCC_address) );
display.drawString(0, 48, "0 1 2 8 ");
display.display();
delay(3000);
display.setColor(DBLACK);
display.drawString(0, 48, "0 1 2 8 ");
display.display();
display.setColor(DWHITE);
client.flush();
display.drawString(0, 30, "Spd: " );
display.drawString(36,30, String(lspeed) );
display.display();
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
char c = client.read();
#ifdef DEBUG
Serial.write(c);
#endif
}
delay(fdelay);
#ifdef DEBUG
Serial.print("set address ");
Serial.println(DCC_address);
#endif
DCCAdr_string="S3"; // Unlikely default
if ((DCC_address>0 ) && (DCC_address<128)) DCCAdr_string="S"+ String(DCC_address);
else if ((DCC_address>127) && (DCC_address<9999)) DCCAdr_string="L"+ String(DCC_address);
xmits = "MT+" + DCCAdr_string + "<;>" + DCCAdr_string + "\n\0";
client.print(xmits); // set DCC address
delay(ldelay);
while (client.available()) {
String jmri_res = client.readStringUntil('\n');
if (jmri_res.startsWith("RPF")) {
#ifdef DEBUG
Serial.println(jmri_res);
#endif
jmri_res = jmri_res.substring(15);
for (i=0; i<num_functions; i++) {
if (jmri_res.startsWith("true")) {
fvalue[i]= true;
jmri_res = jmri_res.substring(12);
}
else {
fvalue[i]= false;
jmri_res = jmri_res.substring(13);
}
}
}
}
delay(fdelay);
//client.flush();
////////////// Turn on track power ////////////
if ((digitalRead(F1_pin)==LOW) && (digitalRead(F3_pin)==LOW) && (digitalRead(F4_pin)==LOW)) {
#ifdef DEBUG
Serial.println("Power ON");
#endif
set_power_on (); // set POWER ON
delay(fdelay);
client.flush();
}
speed_repeat_ctr = 0;
///////////////// Setup for Audio DFPlayer //////////////////
Serial2.begin (9600, SERIAL_8N1, 34, 17); // DFPlayer pins are actually 3.3 V compliant
myDFPlayer.begin (Serial2); // set up serial channel to DFPlayer for commands
delay(100); // Wait for DFPlayer to initialize
myDFPlayer.volume (Throttle_audio_volume); // Set the volume level (1-30)
delay(audiocmddelay); // Wait for DFPlayer to process commands
///////////////// Setup for SteamWater Gauge ///////////////////
tft.begin(); // Start the large tft display for gauges
tft.setRotation(3); // Set the orientation
tft.drawImage(47, 17, &Steam_FaceG_96, NONE, 0); // Draw the background image for the steam gauge
tft.drawFastVLine( dshift+2, 17, 96, WHITE); // Draw the Scale
tft.drawFastHLine( dshift, 17, 4, WHITE);
for ( g = 10; g < 95; g += 10 ) {
tft.drawFastHLine( dshift, (g + 10), 4, WHITE);
}
vgauge = 5;
tft.drawRect(dshift+4, 17, bar_Width, 97, WHITE); // Draw the water sight glass
st2 = map(vgauge, 0, 100, (96 - 2), 0); //
tft.fillRect(dshift+5, 1, (bar_Width - 2), st2, BLACK); // Empty
tft.fillRect(dshift+5, (st2 + 18), (bar_Width - 2), (96 - 2 - st2), BLUE); // Fill Up
currentTimeSteam = millis(); // Set initial values
currentTimeWater = currentTimeSteam;
currentTimeSpeed = currentTimeSteam;
currentWaterDraw = currentTimeSteam;
currentSteamDraw = currentTimeSteam;
loopTimeSteam = currentTimeSteam;
loopTimeWater = currentTimeSteam;
loopTimeSpeed = currentTimeSteam;
loopWaterDraw = currentTimeSteam;
loopSteamDraw = currentTimeSteam;
curVal=84;
vgauge = 90;
} // End of Setup ///////////////////////////////////////
void loop() {
///////// The Need for SPEED ///////////
speedPot = analogRead( ADC_Ch ); // Get Throttle pot reading
speedPot = constrain (speedPot, speedmin, speedmax) ; // constrain is here for possible future bounding
ltemp = map(speedPot, speedmin, speedmax, 0, 127); // First two numbers adjust for
// lever travel, next two numbers are speed steps
#ifdef DEBUG
if(ltemp != ltemp_prev){ // If ltemp has changed
Serial.print("Pot Speed ");
Serial.print(speedPot);
Serial.print(" Speed step ");
Serial.println(ltemp);
ltemp_prev = ltemp;
}
#else
#ifdef LEVER
if(ltemp != ltemp_prev){ // If ltemp has changed
Serial.print("Pot Speed ");
Serial.print(speedPot);
Serial.print(" Speed step ");
Serial.println(ltemp);
ltemp_prev = ltemp;
}
#endif
#endif
//////////////// Read and Calculate Johnson Bar //////////////////////
dirPot = analogRead( jBar_Ch ); // Get Johnson Bar pot reading
dirPot = constrain (dirPot, jbarmin, jbarmax); // constrain is here for possible future bounding
jbarVal = map(dirPot, jbarmin, jbarmax, 0, 60); // First two numbers adjust for
// lever travel, next two numbers are Johnson bar working values
#ifdef DEBUG
if(jbarVal != jbar_prev){ // If jBar has changed
Serial.print("jBar Pot ");
Serial.print(dirPot);
Serial.print(" jBar Setting ");
Serial.println(jbarVal);
jbar_prev = jbarVal;
}
#else
#ifdef LEVER
if(jbarVal != jbar_prev){ // If jBar has changed
Serial.print("jBar Pot ");
Serial.print(dirPot);
Serial.print(" jBar Setting ");
Serial.println(jbarVal);
jbar_prev = jbarVal;
}
#endif
#endif
// jFactor needs to be defined for neutral JBar!
if(jbarVal >= 35) jFactor = (jbarVal - 30)/10; // Define jBar value in Fwd or Rev jbarval == 1,2,3
if(jbarVal <= 25) jFactor = (30 - jbarVal)/10; // jFactor == 1,2,3
if((jbarVal > 25) && (jbarVal < 35)) ltemp = 0; // jBar has no effect in neutral
changeSteam = 10000 - ((ltemp * 9) * jFactor); // 0 to 1143 -> 6571 to 10000
changeWater = 14000 - ((ltemp * 33) * jFactor); // 0 to 4191 -> 1427 to 14000 //change 12000 to 14000
//////// Limit acceleration based on jBar setting ///////////
changeSpeed = 1000/pow(jFactor, 3); // ==1000/ < 1, 8, 27 > == 1000, 125, 37
#ifdef DEBUG
Serial.print("changeSpeed= ");
Serial.print(changeSpeed);
Serial.print(" lspeed= ");
Serial.print(lspeed);
Serial.print(" ltemp= ");
Serial.println(ltemp);
#endif
currentTimeSpeed = millis();
if (ltemp > lspeed) {
if (currentTimeSpeed < (loopTimeSpeed + changeSpeed)) ltemp=lspeed+1;
else loopTimeSpeed = currentTimeSpeed;
}
if (ltemp < lspeed) {
if (currentTimeSpeed < (loopTimeSpeed + changeSpeed)) ltemp=lspeed-1;
else loopTimeSpeed = currentTimeSpeed;
}
#ifdef DEBUG
Serial.print("Speed after JBar limit ltemp= ");
Serial.println(ltemp);
#endif
///////////// Read Brake Pot ///////////////
brakePot = analogRead(brake_Ch);
brakePot = constrain (brakePot, brakemin, brakemax); // constrain is here for possible future bounding
brakeVal = map(brakePot, brakemin, brakemax, 0, 10); // change the pat range to 0-10
#ifdef DEBUG
if(brakeVal != brake_prev){ // If Brake has changed
Serial.print("Brake Pot ");
Serial.print(brakePot);
Serial.print(" Brake Setting ");
Serial.println(brakeVal);
brake_prev = brakeVal;
}
#else
#ifdef LEVER
if(brakeVal != brake_prev){ // If Brake has changed
Serial.print("Brake Pot ");
Serial.print(brakePot);
Serial.print(" Brake Setting ");
Serial.println(brakeVal);
brake_prev = brakeVal;
}
#endif
#endif
//////////////////// Set Direction ////////////////////////////////
if (jbarVal >=35) {
if (setF==1) {
set_forward();
setF=0;
delay (bounce_del);
}
} else setF=1;
if (jbarVal <=25) {
if (setR==1) {
set_reverse();
setR=0;
delay (bounce_del);
}
} else setR=1;
if ((setF==1)&&(setR==1)) {
display.setColor(DBLACK);
display.fillRect(70, 30, 30, 18);
display.setColor(DWHITE);
display.drawString(70,30, "---" );
display.display();
}
//////////////////// Steam Loop /////////////
currentTimeSteam = millis();
if((currentTimeSteam >= (loopTimeSteam + changeSteam)) && (digitalRead(coalStok) == HIGH) && (autoStok == HIGH)){
// If time and coal not pushed and coal not auto-refilling
curVal--; // Depletes steam
if(curVal <=1) curVal = 1; // Stops at zero pressure
loopTimeSteam = currentTimeSteam;
}
if((currentTimeSteam >= (loopTimeSteam + loop_sample_time)) && (digitalRead(coalStok) == LOW)){
// If time and coal pushed
curVal += 2; // Manually adds coal
if(curVal >= 84) curVal = 84; // Stops adding coal
loopTimeSteam = currentTimeSteam;
}
if((curVal < 50) && (digitalRead(autoFireman) == LOW)) autoStok = LOW;
if((currentTimeSteam >= (loopTimeSteam + loop_sample_time)) && (autoStok == LOW)) {
curVal += 2;
sound_playing = coalshovel_s; // Plays sound while coal fills
if(curVal >= 83){
autoStok = HIGH; // Takes autoStok out of the refill loop
}
loopTimeSteam = currentTimeSteam;
}
////////// Limit throttle based on steam level ///////
if((curVal < 50) && (curVal >= 40)) ltemp = ltemp * .75;
if((curVal < 40) && (curVal >= 30)) ltemp = ltemp * .5;
if((curVal < 30) && (curVal >= 20)) ltemp = ltemp * .25;
if(curVal < 20) ltemp = 0;
#ifdef DEBUG
//Serial.print("Speed after steam level limit ");
//Serial.print(ltemp);
//Serial.print(" curVal ");
//Serial.println(curVal);
#endif
///////////////////// Water Loop ////////////////
currentTimeWater = millis();
if((currentTimeWater >= (loopTimeWater + changeWater)) && (digitalRead(waterInj) == HIGH) && (autoFill == HIGH)){
// If time and injector not pushed and water not auto re-filling regardless of autoFireman
vgauge--; // Depletes water
if(vgauge <=1) vgauge = 1; // Stops at empty
loopTimeWater = currentTimeWater; // Reset time for next water depletion
}
if((currentTimeWater >= (loopTimeWater + loop_sample_time)) && (digitalRead(waterInj) == LOW)){
// If time and injector pushed
vgauge+=2; // Manually fills water regardless of autoFireman
tenderLevel--; // Lowers tender level by same amount
if(vgauge >= 95) vgauge = 95; // Stops at full
loopTimeWater = currentTimeWater;
}
if((vgauge < 50) && (digitalRead(autoFireman) == LOW)) autoFill = LOW;
if((currentTimeWater >= (loopTimeWater + loop_sample_time)) && (autoFill == LOW)) {
vgauge+=2; // Fills water while on autoFireman
tenderLevel--; // Lowers tender level by same amount
sound_playing = waterinject_s; // Plays sound while water fills
if(vgauge >= 95) autoFill = HIGH; // Stop autoFill
loopTimeWater = currentTimeWater;
}
tenderVal = (50 - (tenderLevel/10)); // Code to update tender level
display.setColor(DWHITE);
display.fillRect( 110, 10, 16, 54);
display.setColor(DBLACK);
display.fillRect( 111, 11, 14, 52);
display.setColor(DWHITE);
display.fillRect( 113, 13, 10, 50);
display.setColor(DBLACK);
display.fillRect( 112, 12, 12, tenderVal);
display.display();
tender_prev = tenderVal;
/////////////// Refill tender loop ///////
while((digitalRead(tenderFill) == LOW) && (tenderLevel < 500) && (lspeed==0)) {
tenderLevel ++;
tenderVal = (50 - (tenderLevel/10));
display.setColor(DWHITE);
display.fillRect( 110, 10, 16, 54);
display.setColor(DBLACK);
display.fillRect( 111, 11, 14, 52);
display.setColor(DWHITE);
display.fillRect( 113, 13, 10, 50);
display.setColor(DBLACK);
display.fillRect( 112, 12, 12, tenderVal);
display.display();
sound_playing = tenderfill_s; // Plays sound while tender fills
delay(60);
}
if(vgauge < 25) ltemp = 0; // Things to stop the loco
if(brakeVal >= 9) ltemp = 0;
if(tenderLevel <= 0) ltemp = 0;
#ifdef DEBUG
Serial.print("ltemp: ");
Serial.print(ltemp);
Serial.print(" lspeed: ");
Serial.println(lspeed);
#endif
// Periodic Speed update
// if (ltemp != lspeed) { // If speed has changed
lspeed = ltemp;
Test = sound_playing; // DEGMod04
// if((Test==5) || (Test==7) || (Test==8) || (Test==11) || (Test==12)) Bypass = true; // DEGMod04
Bypass = ((Test==5) || (Test==7) || (Test==8) || (Test==11) || (Test==12)); // DEGMod04 try a different way
#ifdef SOUND
Serial.print("sound playing ");
Serial.println(sound_playing);
Serial.print("lspeed ");
Serial.println(lspeed);
Serial.print("sound on ");
Serial.println(sound_on);
Serial.print("Test ");
Serial.println(Test);
Serial.print("Bypass ");
Serial.println(Bypass);
#endif
// if ((lspeed==0) && (sound_on==true) && (sound_playing!=0)) all_sound_off(); //speed went to 0 so turn sounds off DEGMod04
if ((lspeed==0) && (sound_on==true) && (Bypass==true)) all_sound_off(); //speed to zero and selected sounds on DEGMod04
if (speed_repeat_ctr++ > speed_repeat_bump ) { // If time to resend speed
speed_repeat_ctr = 1;
set_speed();
}
/////////////// Function Loop /////////////// flag
for (i=0; i<num_functions; i++) {
switch ( i ) {
//case 0: // Headlight
case 5: // Dim Headlight
ftn_temp = bool( digitalRead(function_pin[5]) );
f0temp = bool( digitalRead(function_pin[0]) );
if (!ftn_temp) { // We're fully down
if (fvalue[0]) { fvalue[0]=false; set_function ( 0 ); }
if (fvalue[5]) { fvalue[5]=false; set_function ( 5 ); }
}
else if (ftn_temp && f0temp) { // We're in the middle setting
if ((!fvalue[0])) { fvalue[0]=true; set_function ( 0 ); }
if ((!fvalue[5])) { fvalue[5]=true; set_function ( 5 ); }
}
else if (!f0temp) { // We're fully up
if (fvalue[5]) { fvalue[5]=false;set_function ( 5 );} // Turn off Dim leave Headlight on
if (!fvalue[0]) { fvalue[0]=true; set_function ( 0 );} //We came quickly from off so turn Headlight on
}
delay(100);
break;
case 2: // Whistle
if ( digitalRead( function_pin[2])== LOW ) { // Whistle is a special case
delay (short_whistle_time); // Let's see if its a short whistle
if (digitalRead( function_pin[2])== HIGH) { // Whistle Pull released in <short_whistle_time 0.4secs xmits = "MTA"+DCCAdr_string+"<;>F13"; // So Short Whistle function is sent
xmits = "MTA"+DCCAdr_string+"<;>F13"; // Signal Short Whistle Function 3
client.println(xmits);
delay (ldelay);
xmits = "MTA"+DCCAdr_string+"<;>F03"; // Signal Short Whistle Function 3
client.println(xmits);
delay (ldelay);
}
else {
display.setColor(DWHITE);
display.drawString(foffset[2], 48, fcs[2]);
display.display();
xmits = "MTA"+DCCAdr_string+"<;>F12";
client.println(xmits);
delay(200);
if (digitalRead( function_pin[2])== HIGH) {
}
while (digitalRead( function_pin[2])== LOW) client.flush();
delay (ldelay);
xmits = "MTA"+DCCAdr_string+"<;>F02";
client.println(xmits);
client.flush();
display.setColor(DBLACK);
display.drawString(foffset[2], 48, fcs[2]);
display.display();
}
}
break;
case 1:
case 3:
case 4:
case 6: {
if (!bool(digitalRead( function_pin[i])) != fvalue[i]) { // If function switch has changed
fvalue[i]= !fvalue[i]; // flip the old state
set_function ( i ); // TOGGLE FUNCTION, see void set_function
delay (100);
}
}
break;
default:
break;
}
}
ftn_temp = false;
if(brakeVal >= 5) ftn_temp = true ;
if(ftn_temp != bvalue) {
bvalue = ftn_temp;
set_brake (); // Toggle the brake
delay (bounce_del);
}
//delay (400);
client.print("*\n\0"); // Send a "Heart Beat" to the WiThrottle Server"
///////////////// DFPlayer Loop ///////////////
if (digitalRead(mute_sound)==HIGH) { // Continue on if the Mute switch is not set
#ifdef SOUND
Serial.println(" In unmute sound play");
Serial.print("Old Sound ");Serial.print(old_sound);Serial.print(" Sound Playing ");Serial.println(sound_playing);
#endif
old_sound = sound_playing;
if (digitalRead(waterInj)==LOW) {sound_playing = waterinject_s; set_volume_level=Throttle_audio_volume;}
if (digitalRead(coalStok)==LOW) {sound_playing = coalshovel_s; set_volume_level=Throttle_audio_volume;}
if ((digitalRead(tenderFill)==LOW)&&(lspeed==0)) {sound_playing = tenderfill_s; set_volume_level=Throttle_audio_volume;}
if (digitalRead(cylcockblow)==LOW) {sound_playing = blowdown_s; set_volume_level=Throttle_audio_volume;}
// background cab sounds play if no sounds playing, 30% of the time, and speed is > threshold setting
if ((sound_playing==0) && (random(1-99)>70) && (lspeed>background_sound_speed)) {
sound_playing=random (4,13);
set_volume_level = background_sounds_volume; // Set the background volume level (1-30)
}
#ifdef SOUND
Serial.print(" Is sound on ");Serial.println(sound_on);
Serial.print("Old Sound ");Serial.print(old_sound);Serial.print(" Sound Playing ");Serial.println(sound_playing);
#endif
if (((sound_on==false) && (sound_playing!=0)) || (sound_on && (sound_playing!=old_sound))) {
sound_on = true;
#ifdef SOUND
Serial.print(" Set sound on ");Serial.println(sound_on);
Serial.print(" Play this sound ");Serial.println(sound_playing);
#endif
// old_sound = sound_playing; //DEGMod03
myDFPlayer.volume (set_volume_level); // Set the default volume level (1-30)
delay(audiocmddelay); // Wait for DFPlayer to process command
myDFPlayer.play(sound_playing); // Start playing sound clip
delay(audiocmddelay);
audio_limit= millis()+audio_track_time[sound_playing];
}
else if (sound_on && (millis()>audio_limit)) all_sound_off (); // Change to silence rapindly ends playing
} else all_sound_off(); // Mute is on so force sound off DEGMod01
/* } else if (sound_on) { // only if this is the DEGM0d01
all_sound_off(); // first time here with Mute ON DEGMod01
}
*/
////////////////// SteamWater Loop ///////////////
g = 1;
tft.drawRect(dshift+4, 17, bar_Width, 97, WHITE); //
currentWaterDraw = millis();
if(currentWaterDraw >= (loopWaterDraw + loop_sample_time)){
st2 = map(vgauge, 0, 100, (96 - 2), 0); //
tft.fillRect(dshift+5, 18, (bar_Width - 2), st2, BLACK); // Empty
tft.fillRect(dshift+5, (st2 + 19), (bar_Width - 2), (96 - 2 - st2), BLUE); // Fill Up
loopWaterDraw = currentWaterDraw;
}
currentSteamDraw = millis();
if(currentSteamDraw >= (loopSteamDraw + loop_sample_time)){
drawNeedle(curVal, 94, 64, 33, BLACK, WHITE);
oldVal = curVal;
loopSteamDraw = currentSteamDraw;
}
} // End of Loop ///////////////////////////////////////////////////
void all_sound_off () {
myDFPlayer.play(silence_s); // Change to silence rapindly ends playing
delay(audiocmddelay);
sound_on = false;
sound_playing = 0;
}
void set_speed () {
#ifdef DEBUG
// Serial.print(" Set Speed ");
// Serial.println(lspeed);
#endif
// set Speed NO update if Neutral
if ((prevSetSpeed > 0)||(setF==0)||(setR==0)) { // if we're in FWD or we are in REV
client.println( "MTA"+DCCAdr_string+"<;>V"+String(lspeed) ); // Set speed in JMRI
display.setColor(DBLACK);
display.fillRect(36, 30, 34, 18);
display.setColor(DWHITE);
display.drawString(36,30, String(lspeed) );
display.display();
prevSetSpeed = lspeed;
delay(40);
}
}
void set_function (byte fi) {
#ifdef DEBUG
Serial.print("toggle F");
Serial.println(fi);
#endif
xmits = "MTA"+DCCAdr_string+"<;>F1"+String(function_map[fi]);
client.println(xmits); // Toggle Function
delay(50);
//xmits = "MTA"+DCCAdr_string+"<;>F0"+String(function_map[fi]);
//client.println(xmits);
if (fvalue [fi]) {
display.setColor(DWHITE);
display.drawString(foffset[fi], 48, fcs[fi]);
display.display();
}
else {
display.setColor(DBLACK);
display.drawString(foffset[fi], 48, fcs[fi]);
display.display();
}
client.flush();
}
void set_brake() { // Toggle Function 4
client.println("MTA"+DCCAdr_string+"<;>F1"+String(brake_sets_DCC_function));
delay(50);
client.println("MTA"+DCCAdr_string+"<;>F0"+String(brake_sets_DCC_function));
client.flush();
}
void set_forward() {
#ifdef DEBUG
Serial.println("forward direction");
#endif
client.println("MTA"+DCCAdr_string+"<;>R1"); // set forward direction
display.setColor(DBLACK);
display.fillRect(70, 30, 30, 18);
display.setColor(DWHITE);
display.drawString(70,30, "Fwd" );
display.display();
}
void set_reverse() {
#ifdef DEBUG
Serial.println("reverse direction");
#endif
client.println("MTA"+DCCAdr_string+"<;>R0"); // set reverse direction
display.setColor(DBLACK);
display.fillRect(70, 30, 30, 18);
display.setColor(DWHITE);
display.drawString(70,30, "Rev" );
display.display();
}
void set_power_on() {
client.println("PPA1\n\0"); // set POWER ON
delay(ldelay);
client.flush();
}
/*
void set_power_off() {
client.println("PPA0\n\0"); // set POWER OFF
delay(ldelay);
client.flush();
}
void dispatch_throttle () {
client.println("MTA"+DCCAdr_string+"<;>d"); // dispatch Throttle
delay(ldelay);
client.flush();
display.setColor(DBLACK);
display.fillRect(42, 12, 36, 18);
display.setColor(DWHITE);
display.drawString(42, 12, "???" );
display.display();
}
void terminate_all () {
#ifdef DEBUG
Serial.println("Power OFF");
#endif
set_power_off (); // set POWER OFF
delay(fdelay);
dispatch_throttle (); // dispatch throttle
delay(fdelay);
while (client.available()) {
char c = client.read();
Serial.write(c);
}
#ifdef DEBUG
Serial.println();
Serial.println("end connection");
#endif
client.stop();
while (true) delay(1000);
}
*/
void drawNeedle(int16_t val, uint8_t x, uint8_t y, uint8_t r, uint16_t color, uint16_t bcolor) {
uint8_t g;
if (curVal > oldVal) {
for (g = oldVal; g <= curVal; g++) {
drawPointerHelper(g - 1, 94, 64, r, bcolor);
drawPointerHelper(g, 94, 64, r, color);
if ((curVal - oldVal) < (84)) delay(5);
}
}
else {
for (g = oldVal; g >= curVal; g--) {
drawPointerHelper(g + 1, 94, 64, r, bcolor);
drawPointerHelper(g, 94, 64, r, color);
if ((oldVal - curVal) >= 84) {
delay(5);
} else {
delay(9);
}
}
}
} // end drawNeedle()
void drawPointerHelper(int16_t val, uint8_t x, uint8_t y, uint8_t r, uint16_t color) {
float dsec, toSecX, toSecY;
int16_t minValue = 0;
int16_t maxValue = 100;
int fromDegree = 132; //start
int toDegree = 271; //end
if (val > maxValue) val = maxValue;
if (val < minValue) val = minValue;
dsec = (((float)(uint16_t)(val - minValue) / (float)(uint16_t)(maxValue - minValue) * toDegree) + fromDegree) * (PI / 180);
toSecX = cos(dsec) * (r / 1.35);
toSecY = sin(dsec) * (r / 1.35);
tft.drawLine(x, y, 1 + x + toSecX, 1 + y + toSecY, color);
tft.fillCircle(x, y, 2, color);
} // end drawPointerHelper()
// Wifi Command Calls
//
// set_function ( xxx ); // Toggle Function xxx
// dispatch_throttle (); // dispatch throttle
// set_forward(); // Set forward direction
// set_reverse(): // Set reverse direction
// set_speed(); // set speed based on global valie lspeed
//
// set_power_on (): //
// set_power_off (); //
// dispatch_throttle (); //
// terminate_all (); //
//