forked from DCC-EX/CommandStation-EX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CommandStation-EX.ino
231 lines (201 loc) · 7.23 KB
/
CommandStation-EX.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
////////////////////////////////////////////////////////////////////////////////////
// DCC-EX CommandStation-EX Please see https://DCC-EX.com
//
// This file is the main sketch for the Command Station.
//
// CONFIGURATION:
// Configuration is normally performed by editing a file called config.h.
// This file is NOT shipped with the code so that if you pull a later version
// of the code, your configuration will not be overwritten.
//
// If you used the automatic installer program, config.h will have been created automatically.
//
// To obtain a starting copy of config.h please copy the file config.example.h which is
// shipped with the code and may be updated as new features are added.
//
// If config.h is not found, config.example.h will be used with all defaults.
////////////////////////////////////////////////////////////////////////////////////
#if __has_include ( "config.h")
#include "config.h"
#ifndef MOTOR_SHIELD_TYPE
#error Your config.h must include a MOTOR_SHIELD_TYPE definition. If you see this warning in spite not having a config.h, you have a buggy preprocessor and must copy config.example.h to config.h
#endif
#else
#warning config.h not found. Using defaults from config.example.h
#include "config.example.h"
#endif
/*
* © 2021 Neil McKechnie
* © 2020-2021 Chris Harlow, Harald Barth, David Cutting,
* Fred Decker, Gregor Baues, Anthony W - Dayton
* © 2023 Nathan Kellenicki
* All rights reserved.
*
* This file is part of CommandStation-EX
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#include "DCCEX.h"
#include "Display_Implementation.h"
#ifdef ARDUINO_ARCH_ESP32
#include <ArduinoOTA.h>
#endif // ARDUINO_ARCH_ESP32
#ifdef CPU_TYPE_ERROR
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH THE ARCHITECTURES LISTED IN defines.h
#endif
#ifdef WIFI_WARNING
#warning You have defined that you want WiFi but your hardware has not enough memory to do that, so WiFi DISABLED
#endif
#ifdef ETHERNET_WARNING
#warning You have defined that you want Ethernet but your hardware has not enough memory to do that, so Ethernet DISABLED
#endif
#ifdef EXRAIL_WARNING
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
#endif
void setup()
{
// The main sketch has responsibilities during setup()
// Responsibility 1: Start the usb connection for diagnostics
// This is normally Serial but uses SerialUSB on a SAMD processor
SerialManager::init();
DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com"));
// Initialise HAL layer before reading EEprom or setting up MotorDrivers
IODevice::begin();
// As the setup of a motor shield may require a read of the current sense input from the ADC,
// let's make sure to initialise the ADCee class!
ADCee::begin();
// Set up MotorDrivers early to initialize all pins
TrackManager::Setup(MOTOR_SHIELD_TYPE);
DISPLAY_START (
// This block is still executed for DIAGS if display not in use
LCD(0,F("DCC-EX v%S"),F(VERSION));
LCD(1,F("Lic GPLv3"));
);
// Responsibility 2: Start all the communications before the DCC engine
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
// Start Ethernet if it exists
#ifndef ARDUINO_ARCH_ESP32
#if WIFI_ON
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
#endif // WIFI_ON
#else
// ESP32 needs wifi on always
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
#if OTA_AUTO_INIT
Diag::OTA = true;
#endif // OTA_AUTO_INIT
#endif // ARDUINO_ARCH_ESP32
#if ETHERNET_ON
EthernetInterface::setup();
#endif // ETHERNET_ON
// Responsibility 3: Start the DCC engine.
DCC::begin();
// Start RMFT aka EX-RAIL (ignored if no automnation)
RMFT::begin();
// Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h.
// This can be used to create turnouts, outputs, sensors etc. through the normal text commands.
#if __has_include ( "mySetup.h")
#define SETUP(cmd) DCCEXParser::parse(F(cmd))
#include "mySetup.h"
#undef SETUP
#endif
#if defined(LCN_SERIAL)
LCN_SERIAL.begin(115200);
LCN::init(LCN_SERIAL);
#endif
LCD(3, F("Ready"));
CommandDistributor::broadcastPower();
}
void loop()
{
// The main sketch has responsibilities during loop()
// Responsibility 1: Handle DCC background processes
// (loco reminders and power checks)
DCC::loop();
// Responsibility 2: handle any incoming commands on USB connection
SerialManager::loop();
// Responsibility 3: Optionally handle any incoming WiFi traffic
#ifndef ARDUINO_ARCH_ESP32
#if WIFI_ON
WifiInterface::loop();
#endif //WIFI_ON
#else //ARDUINO_ARCH_ESP32
#ifndef WIFI_TASK_ON_CORE0
WifiESP::loop();
#endif
// Responsibility 4: Optionally handle Arduino OTA updates
if (Diag::OTA) {
static bool otaInitialised = false;
// Initialise OTA if not already done
if (!otaInitialised) {
ArduinoOTA.setHostname(WIFI_HOSTNAME);
// Prevent locos from moving during OTA
ArduinoOTA.onStart([]() {
// Emergency stop all locos
DCC::setThrottle(0,1,1);
// Disable tracks power
TrackManager::setMainPower(POWERMODE::OFF);
TrackManager::setProgPower(POWERMODE::OFF);
// Broadcast power status
CommandDistributor::broadcastPower();
DISPLAY_START (
LCD(0,F("OTA update"));
LCD(1,F("In progress..."));
);
});
ArduinoOTA.onEnd([]() {
DISPLAY_START (
LCD(0,F("OTA update"));
LCD(1,F("Complete"));
);
});
ArduinoOTA.onError([](ota_error_t error) {
DISPLAY_START (
LCD(0,F("OTA update"));
LCD(1,F("Error: %d"), error);
);
});
// Set OTA password if defined
#ifdef OTA_AUTH
ArduinoOTA.setPassword(OTA_AUTH);
#endif // OTA_AUTH
ArduinoOTA.begin();
otaInitialised = true;
}
// Handle OTA if initialised
else {
ArduinoOTA.handle();
}
}
#endif //ARDUINO_ARCH_ESP32
#if ETHERNET_ON
EthernetInterface::loop();
#endif
RMFT::loop(); // ignored if no automation
#if defined(LCN_SERIAL)
LCN::loop();
#endif
// Display refresh
DisplayInterface::loop();
// Handle/update IO devices.
IODevice::loop();
Sensor::checkAll(); // Update and print changes
// Report any decrease in memory (will automatically trigger on first call)
static int ramLowWatermark = __INT_MAX__; // replaced on first loop
int freeNow = DCCTimer::getMinimumFreeMemory();
if (freeNow < ramLowWatermark) {
ramLowWatermark = freeNow;
LCD(3,F("Free RAM=%5db"), ramLowWatermark);
}
}