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

14
#include "ampel-firmware.h"
Eric Duminil's avatar
TODO    
Eric Duminil committed
15
//TODO: Move as many includes into cpp as possible
16
17
18
19
20

/*****************************************************************
 * GPL License                                                   *
 *****************************************************************/
/*
Eric Duminil's avatar
Eric Duminil committed
21
22
 * 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 )
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 * Copyright (c) 2020 HfT Stuttgart.
 *
 * 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
47
 * Michael Käppler
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 */

/*****************************************************************
 * 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() {
64
65
  led_effects::setupOnBoardLED();
  led_effects::onBoardLEDOff();
66
67
68

  Serial.begin(BAUDS);

69
70
  web_config::initialize();

Eric Duminil's avatar
Eric Duminil committed
71
#ifdef AMPEL_WIFI
Eric Duminil's avatar
Eric Duminil committed
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
  web_config::setWifiConnectionCallback([]() {
    //TODO: Move somewhere else
    led_effects::showKITTWheel(color::green);
    Serial.println();
    Serial.print(F("WiFi - Connected! IP address: "));
    IPAddress address = WiFi.localIP();
    snprintf(wifi::local_ip, sizeof(wifi::local_ip), "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);

    ntp::initialize();

#  ifdef AMPEL_MQTT
    mqtt::initialize(ampel.sensorId);
#  endif

    Serial.println(wifi::local_ip);
    Serial.print(F("You can access this sensor via http://"));
    Serial.print(ampel.sensorId);
    Serial.print(F(".local (might be unstable) or http://"));
    Serial.println(WiFi.localIP());
  });

  web_config::setWifiConnectionFailedCallback([]() {
    led_effects::showKITTWheel(color::red);
    Serial.println(F("Connection to WiFi failed"));
  });
Eric Duminil's avatar
Eric Duminil committed
97
#endif
Eric Duminil's avatar
Eric Duminil committed
98
99
100

  void setWifiConnectionFailedCallback(void (*function)());

101
  pinMode(0, INPUT); // Flash button (used for forced calibration)
102

Eric Duminil's avatar
Eric Duminil committed
103
  Serial.println();
104
  Serial.print(F("Sensor ID: "));
105
  Serial.println(ampel.sensorId);
106
107
  Serial.print(F("MAC      : "));
  Serial.println(ampel.macAddress);
108
  Serial.print(F("Board    : "));
Eric Duminil's avatar
Eric Duminil committed
109
  Serial.println(ampel.board);
Eric Duminil's avatar
Eric Duminil committed
110
111
112
113
114
115
  Serial.print(F("Firmware : "));
  Serial.println(ampel.version);

  led_effects::setupRing();

  sensor::initialize();
116

Eric Duminil's avatar
Eric Duminil committed
117
118
119
120
#ifdef AMPEL_CSV
  csv_writer::initialize(ampel.sensorId);
#endif

Eric Duminil's avatar
Eric Duminil committed
121
#ifdef AMPEL_WIFI // Structure doesn't make sense anymore
Eric Duminil's avatar
Eric Duminil committed
122
  wifi::defineCommands();
Eric Duminil's avatar
Eric Duminil committed
123
#  ifdef AMPEL_HTTP
Eric Duminil's avatar
TODO    
Eric Duminil committed
124
  web_server::definePages();
Eric Duminil's avatar
Eric Duminil committed
125
  //TODO: Rename. Not just web_server
126
  // web_server::initialize();
Eric Duminil's avatar
Eric Duminil committed
127
128
#  endif
#endif
Eric Duminil's avatar
Eric Duminil committed
129

Eric Duminil's avatar
Eric Duminil committed
130
#if defined(AMPEL_LORAWAN) && defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
131
132
  lorawan::initialize();
#endif
133
134
}

Eric Duminil's avatar
Eric Duminil committed
135
136
137
138
139
140
141
/*****************************************************************
 * Helper functions                                              *
 *****************************************************************/
void keepServicesAlive();
void checkFlashButton();
void checkSerialInput();

142
143
144
145
/*****************************************************************
 * Main loop                                                     *
 *****************************************************************/
void loop() {
Eric Duminil's avatar
Eric Duminil committed
146
#if defined(AMPEL_LORAWAN) && defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
147
148
149
150
151
152
153
154
  //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;
  }
#endif
155
  //NOTE: Loop should never take more than 1000ms. Split in smaller methods and logic if needed.
Eric Duminil's avatar
Notes    
Eric Duminil committed
156
  //NOTE: Only use millis() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over.
157
158
159
  uint32_t t0 = millis();

  keepServicesAlive();
160

161
162
163
  // Short press for night mode, Long press for calibration.
  checkFlashButton();

Eric Duminil's avatar
Eric Duminil committed
164
  checkSerialInput();
165

Eric Duminil's avatar
Eric Duminil committed
166
  if (sensor::processData()) {
Eric Duminil's avatar
Eric Duminil committed
167
#ifdef AMPEL_CSV
Eric Duminil's avatar
Eric Duminil committed
168
169
170
    csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
#endif

Eric Duminil's avatar
Eric Duminil committed
171
#if defined(AMPEL_WIFI) && defined(AMPEL_MQTT)
Eric Duminil's avatar
Eric Duminil committed
172
173
174
    mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
#endif

Eric Duminil's avatar
Eric Duminil committed
175
#if defined(AMPEL_LORAWAN) && defined(ESP32)
Eric Duminil's avatar
Eric Duminil committed
176
    lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity);
Eric Duminil's avatar
Eric Duminil committed
177
178
#endif
  }
179
180

  uint32_t duration = millis() - t0;
Eric Duminil's avatar
Eric Duminil committed
181
182
  if (duration > ampel.max_loop_duration) {
    ampel.max_loop_duration = duration;
Eric Duminil's avatar
Eric Duminil committed
183
    Serial.print(F("Debug - Max loop duration : "));
Eric Duminil's avatar
Eric Duminil committed
184
    Serial.print(ampel.max_loop_duration);
185
    Serial.println(F(" ms."));
186
187
188
  }
}

Eric Duminil's avatar
Eric Duminil committed
189
190
191
192
193
194
void checkSerialInput() {
  while (Serial.available() > 0) {
    sensor_console::processSerialInput(Serial.read());
  }
}

195
196
197
198
199
200
201
202
/**
 * 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
203
    led_effects::onBoardLEDOn();
204
205
206
    delay(300);
    if (digitalRead(0)) {
      Serial.println(F("Flash has been pressed for a short time. Should toggle night mode."));
207
      led_effects::toggleNightMode();
208
209
    } else {
      Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration."));
210
      if (led_effects::countdownToZero()) {
211
        Serial.println(F("You can now release the button."));
212
        sensor::startCalibrationProcess();
213
        led_effects::showKITTWheel(color::red, 2);
214
215
      }
    }
216
    led_effects::onBoardLEDOff();
217
218
219
220
  }
}

void keepServicesAlive() {
Eric Duminil's avatar
Eric Duminil committed
221
#ifdef AMPEL_WIFI
222
  web_config::update();
Eric Duminil's avatar
Eric Duminil committed
223
224
225
226
227
228
  if (WiFi.status() == WL_CONNECTED) {
    ntp::update(); // NTP client has its own timer. It will connect to NTP server every 60s.
#  ifdef AMPEL_MQTT
    mqtt::keepConnection(); // MQTT client has its own timer. It will keep alive every 15s.
#  endif
  }
Eric Duminil's avatar
Eric Duminil committed
229
#endif
230
}