ampel-firmware.ino 8.32 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
/***
 *       ____ ___ ____       _                         _
 *      / ___/ _ \___ \     / \   _ __ ___  _ __   ___| |
 *     | |  | | | |__) |   / _ \ | '_ ` _ \| '_ \ / _ \ |
 *     | |__| |_| / __/   / ___ \| | | | | | |_) |  __/ |
 *      \____\___/_____| /_/__ \_\_| |_| |_| .__/ \___|_|         _
 *     | | | |/ _|_   _| / ___|| |_ _   _| |_| |_ __ _  __ _ _ __| |_
 *     | |_| | |_  | |   \___ \| __| | | | __| __/ _` |/ _` | '__| __|
 *     |  _  |  _| | |    ___) | |_| |_| | |_| || (_| | (_| | |  | |_
 *     |_| |_|_|   |_|   |____/ \__|\__,_|\__|\__\__, |\__,_|_|   \__|
 *                                               |___/
 */

14
#include "ampel-firmware.h"
15
16
17
18
19

/*****************************************************************
 * GPL License                                                   *
 *****************************************************************/
/*
Eric Duminil's avatar
Eric Duminil committed
20
21
 * This file is part of the "CO2 Ampel" project ( https://transfer.hft-stuttgart.de/gitlab/co2ampel and
 * https://transfer.hft-stuttgart.de/gitlab/co2ampel/ampel-firmware )
Eric Duminil's avatar
Eric Duminil committed
22
 * Copyright (c) 2022 HfT Stuttgart.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 *
 * This program 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, version 3.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*****************************************************************
 * Authors                                                       *
 *****************************************************************/
/*
 * Eric Duminil
 * Robert Otto
 * Myriam Guedey
 * Tobias Gabriel Erhart
 * Jonas Stave
46
 * Michael Käppler
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 */

/*****************************************************************
 * Configuration                                                 *
 *****************************************************************/
/*
 * Please define settings in 'config.h'.
 * There's an example config file called 'config.example.h'.
 * You can copy 'config.public.h' (stored in Git) to 'config.h' (not stored in Git),
 * and define your credentials and parameters in 'config.h'.
 */

/*****************************************************************
 * Setup                                                         *
 *****************************************************************/
void setup() {
63
64
  led_effects::setupOnBoardLED();
  led_effects::onBoardLEDOff();
65

Eric Duminil's avatar
Eric Duminil committed
66
  Serial.begin(config::bauds);
67

68
69
  web_config::initialize();

70
  web_config::setWifiConnectingCallback(wifiConnecting);
Eric Duminil's avatar
Eric Duminil committed
71
72
  web_config::setWifiConnectionCallback(wifiConnected);
  web_config::setWifiFailCallback(wifiFailed);
73
  web_config::setApModeCallback(apModeStarts);
Eric Duminil's avatar
Eric Duminil committed
74

75
  pinMode(0, INPUT); // Flash button (used for forced calibration)
76

Eric Duminil's avatar
Eric Duminil committed
77
  Serial.println();
78
  Serial.print(F("Sensor ID: "));
79
  Serial.println(ampel.sensorId);
Eric Duminil's avatar
Eric Duminil committed
80
81
  Serial.print(F("Name     : "));
  Serial.println(config::ampel_name());
82
83
  Serial.print(F("MAC      : "));
  Serial.println(ampel.macAddress);
84
  Serial.print(F("Board    : "));
Eric Duminil's avatar
Eric Duminil committed
85
  Serial.println(ampel.board);
Eric Duminil's avatar
Eric Duminil committed
86
87
88
89
90
91
  Serial.print(F("Firmware : "));
  Serial.println(ampel.version);

  led_effects::setupRing();

  sensor::initialize();
92

Eric Duminil's avatar
Eric Duminil committed
93
  csv_writer::initialize(config::ampel_name());
Eric Duminil's avatar
Eric Duminil committed
94

Eric Duminil's avatar
Eric Duminil committed
95
96
  ntp::initialize();

Eric Duminil's avatar
Eric Duminil committed
97
  if (config::is_wifi_on) {
Eric Duminil's avatar
Eric Duminil committed
98
99
    wifi::defineCommands();
    web_server::definePages();
Eric Duminil's avatar
Eric Duminil committed
100
    wifi::tryConnection();
Eric Duminil's avatar
Eric Duminil committed
101
  }
Eric Duminil's avatar
Eric Duminil committed
102

Eric Duminil's avatar
Eric Duminil committed
103
#if defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
104
  if (config::is_lorawan_active()) {
Eric Duminil's avatar
Eric Duminil committed
105
106
    lorawan::initialize();
  }
Eric Duminil's avatar
Eric Duminil committed
107
#endif
108
109
110
111
112
113
}

/*****************************************************************
 * Main loop                                                     *
 *****************************************************************/
void loop() {
Eric Duminil's avatar
Eric Duminil committed
114
#if defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
115
  if (config::is_lorawan_active()) {
Eric Duminil's avatar
Eric Duminil committed
116
117
118
119
120
121
122
    //LMIC Library seems to be very sensitive to timing issues, so run it first.
    lorawan::process();

    if (lorawan::waiting_for_confirmation) {
      // If node is waiting for join confirmation from Gateway, nothing else should run.
      return;
    }
Eric Duminil's avatar
Eric Duminil committed
123
124
  }
#endif
125
  //NOTE: Loop should never take more than 1000ms. Split in smaller methods and logic if needed.
Eric Duminil's avatar
Notes    
Eric Duminil committed
126
  //NOTE: Only use millis() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over.
127
128
129
  uint32_t t0 = millis();

  keepServicesAlive();
130

131
132
133
  // Short press for night mode, Long press for calibration.
  checkFlashButton();

Eric Duminil's avatar
Eric Duminil committed
134
  sensor_console::checkSerialInput();
135

Eric Duminil's avatar
Eric Duminil committed
136
  if (sensor::processData()) {
Eric Duminil's avatar
Eric Duminil committed
137
    if (config::is_csv_active()) {
138
139
      csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
    }
Eric Duminil's avatar
Eric Duminil committed
140

Eric Duminil's avatar
Eric Duminil committed
141
    if (config::is_wifi_on && config::is_mqtt_active()) {
Eric Duminil's avatar
Eric Duminil committed
142
143
      mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
    }
Eric Duminil's avatar
Eric Duminil committed
144

Eric Duminil's avatar
Eric Duminil committed
145
#if defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
146
    if (config::is_lorawan_active()) {
Eric Duminil's avatar
Eric Duminil committed
147
148
      lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity);
    }
Eric Duminil's avatar
Eric Duminil committed
149
150
#endif
  }
151
152

  uint32_t duration = millis() - t0;
Eric Duminil's avatar
Eric Duminil committed
153
154
  if (duration > ampel.max_loop_duration) {
    ampel.max_loop_duration = duration;
Eric Duminil's avatar
Eric Duminil committed
155
    Serial.print(F("Debug - Max loop duration : "));
Eric Duminil's avatar
Eric Duminil committed
156
    Serial.print(ampel.max_loop_duration);
157
    Serial.println(F(" ms."));
158
159
160
  }
}

Eric Duminil's avatar
Eric Duminil committed
161
162
163
/*****************************************************************
 * Callbacks                                                     *
 *****************************************************************/
164
165
166
void wifiConnecting() {
  Serial.print(F("WiFi - Trying to connect to "));
  Serial.print(config::selected_ssid());
Eric Duminil's avatar
Note    
Eric Duminil committed
167
  Serial.print(F(" (max "));
168
  Serial.print(config::wifi_timeout);
Eric Duminil's avatar
Note    
Eric Duminil committed
169
  Serial.println(F("s)."));
170
171
172
  led_effects::showRainbowWheel();
}

Eric Duminil's avatar
Eric Duminil committed
173
174
void wifiConnected() {
  Serial.println();
Eric Duminil's avatar
Eric Duminil committed
175
176
177
  Serial.print(F("WiFi - Connected to "));
  Serial.print(WiFi.SSID());
  Serial.print(F(", IP address: "));
Eric Duminil's avatar
Eric Duminil committed
178
179
180
  IPAddress address = WiFi.localIP();
  snprintf(wifi::local_ip, sizeof(wifi::local_ip), "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
  Serial.println(wifi::local_ip);
181
  led_effects::showKITTWheel(color::green);
Eric Duminil's avatar
Eric Duminil committed
182

Eric Duminil's avatar
Eric Duminil committed
183
  ntp::connect();
Eric Duminil's avatar
Eric Duminil committed
184

Eric Duminil's avatar
Eric Duminil committed
185
  if (config::is_mqtt_active()) {
Eric Duminil's avatar
Eric Duminil committed
186
187
188
189
    mqtt::initialize(ampel.sensorId);
  }

  Serial.print(F("You can access this sensor via http://"));
Eric Duminil's avatar
Eric Duminil committed
190
  Serial.print(config::ampel_name());
Eric Duminil's avatar
Eric Duminil committed
191
192
193
194
195
  Serial.print(F(".local (might be unstable) or http://"));
  Serial.println(WiFi.localIP());
}

void wifiFailed() {
196
  // Ampel will go back to Access Point mode for AP_TIMEOUT seconds, and try connection again after
197
  Serial.println();
Eric Duminil's avatar
Eric Duminil committed
198
  Serial.print(F("WiFi - Could not connect to "));
Eric Duminil's avatar
Done.    
Eric Duminil committed
199
  Serial.println(config::selected_ssid());
Eric Duminil's avatar
Eric Duminil committed
200
201
202
  led_effects::showKITTWheel(color::red);
}

203
204
205
206
207
208
209
void apModeStarts() {
  Serial.print(F("WiFi - Starting Access Point mode ("));
  Serial.print(config::ampel_name());
  Serial.println(F(")."));
  led_effects::alert(color::turquoise);
}

Eric Duminil's avatar
Eric Duminil committed
210
211
212
213
/*****************************************************************
 * Helper functions                                              *
 *****************************************************************/

214
215
216
217
218
219
220
221
/**
 * Checks if flash button has been pressed:
 *   If not, do nothing.
 *   If short press, toggle LED display.
 *   If long press, start calibration process.
 */
void checkFlashButton() {
  if (!digitalRead(0)) { // Button has been pressed
222
    led_effects::onBoardLEDOn();
223
224
225
    delay(300);
    if (digitalRead(0)) {
      Serial.println(F("Flash has been pressed for a short time. Should toggle night mode."));
226
      led_effects::toggleNightMode();
Eric Duminil's avatar
Eric Duminil committed
227
      //NOTE: Start Access Point instead?
228
229
    } else {
      Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration."));
230
      if (led_effects::countdownToZero()) {
231
        Serial.println(F("You can now release the button."));
232
        sensor::startCalibrationProcess();
233
        led_effects::showKITTWheel(color::red, 2);
234
235
      }
    }
236
    led_effects::onBoardLEDOff();
237
238
239
240
  }
}

void keepServicesAlive() {
Eric Duminil's avatar
Eric Duminil committed
241
  if (config::is_wifi_on) {
Eric Duminil's avatar
Eric Duminil committed
242
243
244
    web_config::update();
    if (wifi::connected()) {
      ntp::update(); // NTP client has its own timer. It will connect to NTP server every 60s.
Eric Duminil's avatar
Eric Duminil committed
245
      if (config::is_mqtt_active()) {
Eric Duminil's avatar
Eric Duminil committed
246
247
        mqtt::keepConnection(); // MQTT client has its own timer. It will keep alive every 15s.
      }
Eric Duminil's avatar
Eric Duminil committed
248
    }
Eric Duminil's avatar
Eric Duminil committed
249
  }
250
}