Gbox 4.20
Grow box automation and monitoring - <a href='https://sites.google.com/site/growboxguy/'>https://sites.google.com/site/growboxguy/</a>
 
Loading...
Searching...
No Matches
Gbox420_Mega_Main.ino
Go to the documentation of this file.
1
9#include "Arduino.h"
10#include "avr/wdt.h" // Watchdog timer for detecting a crash and automatically resetting the board
11#include "avr/boot.h" // Watchdog timer related bug fix
12#include "printf.h" // Printing the wireless status message from nRF24L01
13#include "TimerThree.h" // Interrupt handling for webpage
14#include "ELClient.h" // ESP-link
15#include "ELClientWebServer.h" // ESP-link - WebServer API
16#include "ELClientCmd.h" // ESP-link - Get current time from the internet using NTP
17#include "ELClientRest.h" // ESP-link - REST API
18#include "ELClientMqtt.h" // ESP-link - MQTT protocol for sending and receiving IoT messages
19#include "Thread.h" // Splitting functions to threads for timing
20#include "StaticThreadController.h" // Grouping threads
21#include "SerialLog.h" // Logging to the Serial console and to ESP-link's console
22#include "Settings.h" // EEPROM stored settings for every component
23#include "src/Modules_Web/MainModule_Web.h" // Represents a complete box with all feautres
24#include "SPI.h" // allows you to communicate with SPI devices, with the Arduino as the master device
25#include "RF24.h" // https://github.com/maniacbug/RF24
26
27// Global variable initialization
31
32// Component initialization
33HardwareSerial &ArduinoSerial = Serial;
34HardwareSerial &ESPSerial = Serial3;
35ELClient ESPLink(&ESPSerial);
36ELClientWebServer WebServer(&ESPLink);
37ELClientCmd ESPCmd(&ESPLink);
38ELClientRest PushingBoxRestAPI(&ESPLink);
39ELClientRest HomeAssistantRestAPI(&ESPLink);
40ELClientMqtt MqttAPI(&ESPLink);
42bool &Debug = *new bool;
43bool &Metric = *new bool;
44bool MqttConnected = false;
45MainModule *Main1;
46
47RF24 Wireless(WirelessCEPin, WirelessCSNPin);
48
49// Thread initialization
50Thread OneSecThread = Thread();
51Thread FiveSecThread = Thread();
52Thread MinuteThread = Thread();
53StaticThreadController<3> ThreadControl(&OneSecThread, &FiveSecThread, &MinuteThread);
54
55void setup()
56{
57 ArduinoSerial.begin(115200);
58 ESPSerial.begin(115200);
59 pinMode(LED_BUILTIN, OUTPUT);
60 printf_begin();
61 logToSerials(F(""), true, 0);
62 logToSerials(F("Main module initializing"), true, 0);
63 wdt_enable(WDTO_8S);
64 boot_rww_enable();
65
66 // Loading settings from EEPROM
67 logToSerials(F("Loading settings"), true, 0);
71
72 logToSerials(F("Setting up ESP-link connection"), true, 0);
73 ESPLink.resetCb = &resetWebServer;
75 setSyncProvider(getNtpTime);
76 setSyncInterval(86400);
77 if ((ModuleSettings->Main1).ReportToMqtt)
78 {
79 setupMqtt(); // MQTT message relay setup. Logs "ConnectedCB is XXXX" to serial if successful
80 }
81 // Threads - Setting up how often threads should be triggered and what functions to call when the trigger fires
82 logToSerials(F("Setting up refresh threads"), false, 0);
83 OneSecThread.setInterval(1000);
84 OneSecThread.onRun(run1sec);
85 FiveSecThread.setInterval(5000);
87 MinuteThread.setInterval(60000);
88 MinuteThread.onRun(run1min);
89 logToSerials(F("done"), true, 3);
90
91 // Start interrupts to handle request from ESP-link firmware
92 logToSerials(F("Setting up interrupt handler"), false, 0);
93 Timer3.initialize(500);
94 Timer3.attachInterrupt(processTimeCriticalStuff);
95 Timer3.start();
96 logToSerials(F("done"), true, 3);
97
98 // Initialize wireless communication with remote Modules
99 logToSerials(F("Setting up wireless transceiver"), false, 0);
100 Wireless.begin();
101 Wireless.setDataRate(RF24_250KBPS);
102 Wireless.setCRCLength(RF24_CRC_16);
103 Wireless.setPALevel(RF24_PA_MAX);
104 Wireless.setPayloadSize(WirelessPayloadSize);
105 Wireless.enableDynamicPayloads();
106 Wireless.enableAckPayload();
107 Wireless.setRetries(WirelessDelay, WirelessRetry);
108 Wireless.stopListening();
109 logToSerials(F("done"), true, 3);
110
111 // Create the Module objects
112 logToSerials(F("Creating Main module"), true, 0);
113 Main1 = new MainModule(F("Main1"), &ModuleSettings->Main1, &Wireless);
114
115 // sendEmailAlert(F("Grow%20box%20(re)started"));
116 logToSerials(F("Setup ready, starting loops:"), true, 0);
117}
118
119void loop()
120{
121 ThreadControl.run();
122}
123
125{
126 ESPLink.Process();
127}
128
129// Threads
130
132{
133 wdt_reset();
134 heartBeat();
135 Main1->run1sec();
136}
137
139{
140 wdt_reset();
141 Main1->run5sec();
142}
143
145{
146 wdt_reset();
147 Main1->run1min();
149}
150
155{
156 static bool ledStatus;
157 ledStatus = !ledStatus;
158 digitalWrite(LED_BUILTIN, ledStatus);
159}
160
161// Website related functions
162
167{
168 logToSerials(F("(re)Connecting ESP-link"), false, 1);
169 while (!ESPLink.Sync())
170 {
171 logToSerials(F("."), false, 0);
172 delay(1000);
173 };
174 logToSerials(F(""), true, 0);
175 if (PushingBoxRestAPI.begin("api.pushingbox.com") == 0)
176 {
177 logToSerials(F("PushingBox RestAPI ready"), true, 2);
178 }
179 else
180 logToSerials(F("PushingBox RestAPI failed"), true, 2);
181 /*
182 if (HomeAssistantRestAPI.begin(HomeAssistantServerIP, HomeAssistantServerPort, true) == 0) ///< Pre-setup RestAPI with Home Assistant (Not actual connection)
183 {
184 HomeAssistantRestAPI.setContentType("application/json");
185 HomeAssistantRestAPI.setHeader(HomeAssistantServerToken);
186
187 logToSerials(F("HomeAssistant RestAPI ready"), true, 2);
188 }
189 else
190 logToSerials(F("HomeAssistant RestAPI failed"), true, 2); ///< If begin returns a negative number the initialization failed
191 */
192 WebServer.setup();
193 URLHandler *GrowBoxHandler = WebServer.createURLHandler("/Main.html.json");
194 GrowBoxHandler->loadCb.attach(&loadCallback);
195 GrowBoxHandler->refreshCb.attach(&refreshCallback);
196 GrowBoxHandler->buttonCb.attach(&buttonCallback);
197 GrowBoxHandler->setFieldCb.attach(&fieldCallback);
198 URLHandler *SettingsHandler = WebServer.createURLHandler("/Settings.html.json");
199 SettingsHandler->loadCb.attach(&settingsLoadCallback);
200 SettingsHandler->refreshCb.attach(&settingsRefreshCallback);
201 SettingsHandler->buttonCb.attach(&settingsButtonCallback);
202 SettingsHandler->setFieldCb.attach(&settingsFieldCallback);
203 URLHandler *HempyHandler = WebServer.createURLHandler("/Hempy.html.json");
204 HempyHandler->loadCb.attach(&ignoreCallback);
205 HempyHandler->refreshCb.attach(&ignoreCallback);
206
207 logToSerials(F("ESP-link ready"), true, 1);
208}
209
214{
215
216 MqttAPI.connectedCb.attach(mqttConnected);
217 MqttAPI.disconnectedCb.attach(mqttDisconnected);
218 MqttAPI.publishedCb.attach(mqttPublished);
219 MqttAPI.dataCb.attach(mqttReceived);
220
221 memset(&ShortMessage[0], 0, MaxShotTextLength); // reset variable to store the Publish to path
222 strcat(ShortMessage, MqttLwtTopic);
223 MqttAPI.lwt(ShortMessage, MqttLwtMessage, 0, 1); //(topic,message,qos,retain) declares what message should be sent on it's behalf by the broker after Gbox420 has gone offline.
224 MqttAPI.setup();
225}
226
227void mqttConnected(__attribute__((unused)) void *response)
228{
229 MqttAPI.subscribe(MqttSubTopic);
230 MqttConnected = true;
231 // if(Debug) logToSerials(F("MQTT connected"), true);
232}
233
234void mqttDisconnected(__attribute__((unused)) void *response)
235{
236 // if(Debug) logToSerials(F("MQTT disconnected"), true);
237 MqttConnected = false;
238}
239
240void mqttPublished(__attribute__((unused)) void *response)
241{
242 // if(Debug) logToSerials(F("MQTT published"), true);
243}
244
245void mqttReceived(void *response)
246{
247 static uint8_t MqttSubTopicLength = strlen(MqttSubTopic) - 1; // Get length of the command topic
248 static char command[MaxShotTextLength];
249 static char data[MaxShotTextLength];
250 ELClientResponse *res = (ELClientResponse *)response;
251 String mqttTopic = (*res).popString();
252 String mqttData = (*res).popString();
253 logToSerials(F("MQTT"), false, 0);
254 logToSerials(&mqttTopic, false, 1);
255 mqttTopic.remove(0, MqttSubTopicLength); // Cut the known command topic from the arrived topic
256 mqttTopic.toCharArray(command, MaxShotTextLength);
257 mqttData.toCharArray(data, MaxShotTextLength);
258 Main1->commandEventTrigger(command, data);
259 Main1->reportToMqttTrigger(true); // send out a fresh report
260}
261
262static bool SyncInProgress = false;
263
268{
269 time_t NTPResponse = 0;
270 if (!SyncInProgress)
271 { // block calling the sync again inside an interrupt while already waiting for a sync to finish
272 SyncInProgress = true;
273 uint32_t LastRefresh = millis();
274 logToSerials(F("Waiting for NTP time"), false, 0);
275 while (NTPResponse == 0 && millis() - LastRefresh < 15000)
276 {
277 NTPResponse = ESPCmd.GetTime();
278 delay(1000);
279 logToSerials(F("."), false, 0);
280 wdt_reset();
281 }
282 SyncInProgress = false;
283 if (NTPResponse == 0)
284 {
285 logToSerials(F("sync failed"), true, 3);
286 }
287 else
288 logToSerials(F("synchronized"), true, 3);
289 }
290 return NTPResponse;
291}
292
297void loadCallback(__attribute__((unused)) char *Url)
298{
299 Main1->websiteLoadEventTrigger(Url); // Runs through all components that are subscribed to this event
300}
301
306void refreshCallback(__attribute__((unused)) char *Url)
307{
308 Main1->websiteRefreshEventTrigger(Url);
309}
310
315void buttonCallback(char *Button)
316{
317 logToSerials(F("ESP button:"), false, 0);
318 if (strcmp_P(Button, (PGM_P)F("RestoreDef")) == 0)
319 {
321 }
322 else
323 {
324 Main1->commandEventTrigger(Button, NULL);
325 }
327}
328
333void fieldCallback(char *Field)
334{
335 logToSerials(F("ESP field:"), false, 0);
336 Main1->commandEventTrigger(Field, WebServer.getArgString());
338}
339
343void settingsLoadCallback(__attribute__((unused)) char *Url)
344{
345 Main1->settingsEvent_Load(Url); // Runs through all components that are subscribed to this event
346}
347
351void settingsRefreshCallback(__attribute__((unused)) char *Url)
352{
353 Main1->settingsEvent_Refresh(Url);
354}
355
360void settingsButtonCallback(char *Button)
361{
362 if (Debug)
363 {
364 logToSerials(F("Settings button:"), false, 0);
365 logToSerials(&Button, true, 1);
366 }
367 if (strcmp_P(Button, (PGM_P)F("RestoreDef")) == 0)
368 {
370 }
371 else
372 {
373 Main1->settingsEvent_Command(Button, NULL);
374 }
376}
377
382void settingsFieldCallback(char *Field)
383{
384 if (Debug)
385 {
386 logToSerials(F("Settings field:"), false, 0);
387 logToSerials(&Field, true, 1);
388 }
389 Main1->settingsEvent_Command(Field, WebServer.getArgString());
391}
392
397{
398 if (Debug)
399 {
400 logToSerials(F("Wireless report:"), true, 0);
401 Wireless.printPrettyDetails();
402 logToSerials(F(""), true, 0);
403 }
404}
405
409void ignoreCallback(__attribute__((unused)) char *Url)
410{
411}
void logToSerials(const __FlashStringHelper *ToPrint, bool BreakLine, uint8_t Indent)
< Logging
Definition SerialLog.cpp:5
void restoreDefaults()
Load sketch default settings into EEPROM.
Definition Settings.cpp:45
void saveSettings(Settings *ToSave)
Store settings in EEPROM - Only updates changed bits.
Definition Settings.cpp:10
Settings * loadSettings(bool ResetEEPROM)
Load settings from EEPROM.
Definition Settings.cpp:20
#define MqttSubTopic
Subscribe to messages of this topic and all sub-topic.
Definition Settings.h:43
#define MqttLwtMessage
Subscribers will get this message under the topic specified by MqttLwtTopic when the MQTT client goes...
Definition Settings.h:45
#define MqttLwtTopic
When the connection is lost the MQTT broker will publish a final message to this topic....
Definition Settings.h:44
void settingsButtonCallback(char *Button)
Called when a button is pressed on the /Settings.html web page.
void run5sec()
RF24 Wireless(WirelessCEPin, WirelessCSNPin)
Wireless communication with Modules over nRF24L01+.
void resetWebServer()
(re-)Initialize the ESP-link connection
void settingsRefreshCallback(__attribute__((unused)) char *Url)
Called when the /Settings.html website is refreshing on the ESP-link webserver.
void fieldCallback(char *Field)
Called when a field on the website is submitted.
void mqttDisconnected(__attribute__((unused)) void *response)
char CurrentTime[MaxWordLength]
Buffer for storing current time in text format.
char LongMessage[MaxLongTextLength]
Temp storage for assembling long messages (REST API, MQTT reporting)
Thread MinuteThread
Thread OneSecThread
void mqttConnected(__attribute__((unused)) void *response)
void processTimeCriticalStuff()
time_t getNtpTime()
Update the time over ESP-link using NTP (Network Time Protocol)
HardwareSerial & ESPSerial
Reference to the ESP Link Serial output : Mega 2560 Rev3 Serial:0(RX) 1(TX), Serial1:19(RX1) 18(TX1),...
void buttonCallback(char *Button)
Called when a button is pressed.
void setup()
Thread FiveSecThread
void mqttReceived(void *response)
void mqttPublished(__attribute__((unused)) void *response)
char ShortMessage[MaxShotTextLength]
Temp storage for assembling short text messages (Log entries, Error messages,etc)
bool MqttConnected
Track the connection state to the MQTT broker configured on the ESP-link's REST/MQTT tab.
void run1min()
void settingsLoadCallback(__attribute__((unused)) char *Url)
Called when the /Settings.html website is loading on the ESP-link webserver.
MainModule * Main1
Represents a Grow Box with all components (Lights, DHT sensors, Power sensor, Aero/Hempy/Reservoir wi...
Settings * ModuleSettings
This object will store the settings loaded from the EEPROM. Persistent between reboots.
HardwareSerial & ArduinoSerial
Reference to the Arduino Serial output : Mega 2560 Rev3 Serial:0(RX) 1(TX) also showed on USB,...
ELClientWebServer WebServer & ESPLink
ESP-link - WebServer API.
bool & Debug
True - Turns on extra debug messages on the Serial Output.
void settingsFieldCallback(char *Field)
Called when a field on the /Settings.html website is submitted.
void heartBeat()
Turns the integrated LED on the Arduino board ON/OFF.
void refreshCallback(__attribute__((unused)) char *Url)
Called when a website is refreshing on the ESP-link webserver.
void loadCallback(__attribute__((unused)) char *Url)
Called when a website is loading on the ESP-link webserver.
void setupMqtt()
Sets up the MQTT relay.
bool & Metric
True - Use metric units, False - Use imperial units.
void run1sec()
void ignoreCallback(__attribute__((unused)) char *Url)
Ignores the incoming load/refresh event. Used when embedding another module's web interface that alre...
void getWirelessStatus()
Prints the nRF24L01+ wireless transceiver's status to the Serial log.
void loop()
constexpr uint8_t WirelessPayloadSize
Size of the wireless packages exchanged with the Main module. Max 32 bytes are supported on nRF24L01+...
Definition Settings.h:31
constexpr uint8_t MaxShotTextLength
Default char * buffer length for storing mutiple words. Memory intense!
Definition Settings.h:18
constexpr uint8_t MaxWordLength
Default char * buffer length for storing a word + null terminator. Memory intense!
Definition Settings.h:17
constexpr uint16_t MaxLongTextLength
Default char * buffer length for storing a long text. Memory intense!
Definition Settings.h:19
bool Metric
Switch between Imperial/Metric units. If changed update the default temp and pressure values below to...
Definition Settings.h:61
struct MainModuleSettings Main1
Definition Settings.h:134
bool Debug
Logs debug messages to serial and web outputs.
Definition Settings.h:60