ampel-firmware.ino 7.49 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 )
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 * 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
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
66
67

  Serial.begin(BAUDS);

68
69
  web_config::initialize();

Eric Duminil's avatar
Eric Duminil committed
70
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
  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)());

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

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

  led_effects::setupRing();

  sensor::initialize();
113

Eric Duminil's avatar
Eric Duminil committed
114
115
116
117
#ifdef AMPEL_CSV
  csv_writer::initialize(ampel.sensorId);
#endif

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

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

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

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

  keepServicesAlive();
157

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

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

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

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

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

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

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

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

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