ampel-firmware.ino 7.53 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
118
#ifdef AMPEL_CSV
  csv_writer::initialize(ampel.sensorId);
#endif

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

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

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

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

  keepServicesAlive();
158

159
160
161
  // Short press for night mode, Long press for calibration.
  checkFlashButton();

Eric Duminil's avatar
Eric Duminil committed
162
  checkSerialInput();
163

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

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

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

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

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

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

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