ampel-firmware.ino 7.62 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
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
  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"));
  });

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

99
  pinMode(0, INPUT); // Flash button (used for forced calibration)
100

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

  led_effects::setupRing();

  sensor::initialize();
114

Eric Duminil's avatar
Eric Duminil committed
115
116
117
  Serial.print("JUST A CONFIG TEST : ");
  Serial.println(config::stringParam.value());

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

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

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

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

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

  keepServicesAlive();
161

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

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

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

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

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

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

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

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

void keepServicesAlive() {
Eric Duminil's avatar
Eric Duminil committed
222
#ifdef AMPEL_WIFI
223
  web_config::update();
Eric Duminil's avatar
Eric Duminil committed
224
225
226
227
228
229
  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
230
#endif
231
}