ampel-firmware.ino 7.51 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
#ifdef AMPEL_WIFI
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
  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
96
#endif
Eric Duminil's avatar
Eric Duminil committed
97

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
TODO    
Eric Duminil committed
121
  web_server::definePages();
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
    //TODO: Check if not in AP mode. Lines get written with 1970 otherwise.
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
  if (wifi::connected()) {
Eric Duminil's avatar
Eric Duminil committed
222
223
224
225
226
    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
}