Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
co2ampel
ampel-firmware
Commits
36bd5ec7
Commit
36bd5ec7
authored
Apr 20, 2021
by
Eric Duminil
Browse files
Merge branch 'feature/sensor_console' into develop
parents
58daf119
d2b43f65
Pipeline
#2943
passed with stage
in 1 minute and 33 seconds
Changes
19
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
ampel-firmware/ampel-firmware.ino
View file @
36bd5ec7
...
...
@@ -83,12 +83,12 @@ void setup() {
sensor
::
initialize
();
Serial
.
print
(
F
(
"Sensor ID: "
));
Serial
.
println
(
SENSOR_ID
);
Serial
.
println
(
ampel
.
sensorId
);
Serial
.
print
(
F
(
"Board : "
));
Serial
.
println
(
BOARD
);
Serial
.
println
(
ampel
.
board
);
#ifdef AMPEL_WIFI
WiFiConnect
(
SENSOR_ID
);
WiFiConnect
(
ampel
.
sensorId
);
Serial
.
print
(
F
(
"WiFi - Status: "
));
Serial
.
println
(
WiFi
.
status
());
...
...
@@ -100,7 +100,7 @@ void setup() {
ntp
::
initialize
();
if
(
MDNS
.
begin
(
SENSOR_ID
.
c_str
()))
{
// Start the mDNS responder for SENSOR_ID.local
if
(
MDNS
.
begin
(
ampel
.
sensorId
.
c_str
()))
{
// Start the mDNS responder for SENSOR_ID.local
MDNS
.
addService
(
"http"
,
"tcp"
,
80
);
Serial
.
println
(
F
(
"mDNS responder started"
));
}
else
{
...
...
@@ -108,7 +108,7 @@ void setup() {
}
# ifdef AMPEL_MQTT
mqtt
::
initialize
(
"CO2sensors/"
+
SENSOR_ID
);
mqtt
::
initialize
(
"CO2sensors/"
+
ampel
.
sensorId
);
# endif
}
#endif
...
...
@@ -162,10 +162,10 @@ void loop() {
}
uint32_t
duration
=
millis
()
-
t0
;
if
(
duration
>
max_loop_duration
)
{
max_loop_duration
=
duration
;
if
(
duration
>
ampel
.
max_loop_duration
)
{
ampel
.
max_loop_duration
=
duration
;
Serial
.
print
(
F
(
"Debug - Max loop duration : "
));
Serial
.
print
(
max_loop_duration
);
Serial
.
print
(
ampel
.
max_loop_duration
);
Serial
.
println
(
F
(
" ms."
));
}
}
...
...
ampel-firmware/co2_sensor.cpp
View file @
36bd5ec7
...
...
@@ -14,7 +14,7 @@ namespace config {
#else
const
float
temperature_offset
=
-
3.0
;
// [K] Temperature measured by sensor is usually at least 3K too high.
#endif
const
bool
auto_calibrate_sensor
=
AUTO_CALIBRATE_SENSOR
;
// [true / false]
bool
auto_calibrate_sensor
=
AUTO_CALIBRATE_SENSOR
;
// [true / false]
}
namespace
sensor
{
...
...
@@ -22,7 +22,7 @@ namespace sensor {
uint16_t
co2
=
0
;
float
temperature
=
0
;
float
humidity
=
0
;
String
timestamp
=
""
;
char
timestamp
[
23
]
;
int16_t
stable_measurements
=
0
;
uint32_t
waiting_color
=
color
::
blue
;
bool
should_calibrate
=
false
;
...
...
@@ -71,17 +71,15 @@ namespace sensor {
Serial
.
print
(
F
(
"Auto-calibration is "
));
Serial
.
println
(
config
::
auto_calibrate_sensor
?
"ON."
:
"OFF."
);
sensor_console
::
defineIntCommand
(
"co2"
,
setCO2forDebugging
,
" 1500 (Sets co2 level, for debugging purposes)"
);
sensor_console
::
defineIntCommand
(
"timer"
,
setTimer
,
" 30 (Sets measurement interval, in s)"
);
sensor_console
::
defineCommand
(
"calibrate"
,
startCalibrationProcess
,
" (Starts calibration process)"
);
sensor_console
::
defineIntCommand
(
"co2"
,
setCO2forDebugging
,
F
(
" 1500 (Sets co2 level, for debugging purposes)"
)
)
;
sensor_console
::
defineIntCommand
(
"timer"
,
setTimer
,
F
(
" 30 (Sets measurement interval, in s)"
)
)
;
sensor_console
::
defineCommand
(
"calibrate"
,
startCalibrationProcess
,
F
(
" (Starts calibration process)"
)
)
;
sensor_console
::
defineIntCommand
(
"calibrate"
,
calibrateSensorToSpecificPPM
,
" 600 (Starts calibration process, to given ppm)"
);
F
(
" 600 (Starts calibration process, to given ppm)"
)
)
;
sensor_console
::
defineIntCommand
(
"calibrate!"
,
calibrateSensorRightNow
,
" 600 (Calibrates right now, to given ppm)"
);
sensor_console
::
defineCommand
(
"reset"
,
[]()
{
ESP
.
restart
();
},
" (Restarts the sensor)"
);
sensor_console
::
defineCommand
(
"night_mode"
,
led_effects
::
toggleNightMode
,
" (Toggles night mode on/off)"
);
F
(
" 600 (Calibrates right now, to given ppm)"
));
sensor_console
::
defineIntCommand
(
"auto_calibrate"
,
setAutoCalibration
,
F
(
" 0/1 (Disables/enables autocalibration)"
));
}
//NOTE: should timer deviation be used to adjust measurement_timestep?
...
...
@@ -168,7 +166,7 @@ namespace sensor {
if
(
freshData
)
{
// checkTimerDeviation();
timestamp
=
ntp
::
getLocalTime
();
ntp
::
getLocalTime
(
timestamp
);
co2
=
scd30
.
getCO2
();
temperature
=
scd30
.
getTemperature
();
humidity
=
scd30
.
getHumidity
();
...
...
@@ -212,6 +210,13 @@ namespace sensor {
Serial
.
println
(
co2
);
}
void
setAutoCalibration
(
int32_t
autoCalibration
)
{
config
::
auto_calibrate_sensor
=
autoCalibration
;
scd30
.
setAutoSelfCalibration
(
autoCalibration
);
Serial
.
print
(
F
(
"Setting auto-calibration to : "
));
Serial
.
println
(
autoCalibration
?
F
(
"On."
)
:
F
(
"Off."
));
}
void
setTimer
(
int32_t
timestep
)
{
if
(
timestep
>=
2
&&
timestep
<=
1800
)
{
Serial
.
print
(
F
(
"Setting Measurement Interval to : "
));
...
...
ampel-firmware/co2_sensor.h
View file @
36bd5ec7
...
...
@@ -12,7 +12,7 @@
namespace
config
{
extern
uint16_t
measurement_timestep
;
// [s] Value between 2 and 1800 (range for SCD30 sensor)
extern
const
bool
auto_calibrate_sensor
;
// [true / false]
extern
bool
auto_calibrate_sensor
;
// [true / false]
extern
uint16_t
co2_calibration_level
;
// [ppm]
extern
const
float
temperature_offset
;
// [K] Sign isn't relevant.
}
...
...
@@ -22,7 +22,7 @@ namespace sensor {
extern
uint16_t
co2
;
extern
float
temperature
;
extern
float
humidity
;
extern
String
timestamp
;
extern
char
timestamp
[
23
]
;
void
initialize
();
bool
processData
();
...
...
@@ -32,5 +32,6 @@ namespace sensor {
void
setTimer
(
int32_t
timestep
);
void
calibrateSensorToSpecificPPM
(
int32_t
calibrationLevel
);
void
calibrateSensorRightNow
(
int32_t
calibrationLevel
);
void
setAutoCalibration
(
int32_t
autoCalibration
);
}
#endif
ampel-firmware/config.public.h
View file @
36bd5ec7
...
...
@@ -144,7 +144,7 @@
*/
# define NTP_SERVER "pool.ntp.org"
# define UTC_OFFSET_IN_SECONDS
36
00 // [s] 3600 for UTC+1
# define UTC_OFFSET_IN_SECONDS
72
00 // [s] 3600 for UTC+1
, 7200 for UTC+1 and daylight saving time
/**
* Others
...
...
ampel-firmware/csv_writer.cpp
View file @
36bd5ec7
...
...
@@ -6,7 +6,7 @@ namespace config {
}
namespace
csv_writer
{
unsigned
long
last_written_at
=
0
;
String
last_successful_write
=
""
;
char
last_successful_write
[
23
]
;
#if defined(ESP8266)
/**
...
...
@@ -78,7 +78,7 @@ namespace csv_writer {
}
#endif
const
String
filename
=
"/"
+
SENSOR_ID
+
".csv"
;
const
String
filename
=
"/"
+
ampel
.
sensorId
+
".csv"
;
int
getAvailableSpace
()
{
return
getTotalSpace
()
-
getUsedSpace
();
...
...
@@ -115,9 +115,9 @@ namespace csv_writer {
showFilesystemContent
();
Serial
.
println
();
sensor_console
::
defineIntCommand
(
"csv"
,
setCSVinterval
,
" 60 (Sets CSV writing interval, in s)"
);
sensor_console
::
defineCommand
(
"format_filesystem"
,
formatFilesystem
,
" (Deletes the whole filesystem)"
);
sensor_console
::
defineCommand
(
"show_csv"
,
showCSVContent
,
" (Displays the complete CSV file on Serial)"
);
sensor_console
::
defineIntCommand
(
"csv"
,
setCSVinterval
,
F
(
" 60 (Sets CSV writing interval, in s)"
)
)
;
sensor_console
::
defineCommand
(
"format_filesystem"
,
formatFilesystem
,
F
(
" (Deletes the whole filesystem)"
)
)
;
sensor_console
::
defineCommand
(
"show_csv"
,
showCSVContent
,
F
(
" (Displays the complete CSV file on Serial)"
)
)
;
}
File
openOrCreate
()
{
...
...
@@ -132,11 +132,11 @@ namespace csv_writer {
return
csv_file
;
}
void
log
(
const
String
&
time
S
tamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
)
{
void
log
(
const
char
*
time
s
tamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
)
{
led_effects
::
onBoardLEDOn
();
File
csv_file
=
openOrCreate
();
char
csv_line
[
42
];
snprintf
(
csv_line
,
sizeof
(
csv_line
),
"%s;%d;%.1f;%.1f
\r\n
"
,
time
S
tamp
.
c_str
()
,
co2
,
temperature
,
humidity
);
snprintf
(
csv_line
,
sizeof
(
csv_line
),
"%s;%d;%.1f;%.1f
\r\n
"
,
time
s
tamp
,
co2
,
temperature
,
humidity
);
if
(
csv_file
)
{
size_t
written_bytes
=
csv_file
.
print
(
csv_line
);
csv_file
.
close
();
...
...
@@ -145,7 +145,7 @@ namespace csv_writer {
}
else
{
Serial
.
print
(
F
(
"CSV - Wrote : "
));
Serial
.
print
(
csv_line
);
last_successful_write
=
ntp
::
getLocalTime
(
);
ntp
::
getLocalTime
(
last_successful_write
);
}
updateFsInfo
();
delay
(
50
);
...
...
@@ -156,7 +156,7 @@ namespace csv_writer {
led_effects
::
onBoardLEDOff
();
}
void
logIfTimeHasCome
(
const
String
&
timeStamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
)
{
void
logIfTimeHasCome
(
const
char
*
timeStamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
)
{
unsigned
long
now
=
seconds
();
if
(
now
-
last_written_at
>
config
::
csv_interval
)
{
last_written_at
=
now
;
...
...
ampel-firmware/csv_writer.h
View file @
36bd5ec7
...
...
@@ -20,9 +20,9 @@ namespace config {
extern
uint16_t
csv_interval
;
// [s]
}
namespace
csv_writer
{
extern
String
last_successful_write
;
extern
char
last_successful_write
[
23
]
;
void
initialize
();
void
logIfTimeHasCome
(
const
String
&
time
S
tamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
);
void
logIfTimeHasCome
(
const
char
*
time
s
tamp
,
const
int16_t
&
co2
,
const
float
&
temperature
,
const
float
&
humidity
);
int
getAvailableSpace
();
extern
const
String
filename
;
...
...
ampel-firmware/led_effects.cpp
View file @
36bd5ec7
...
...
@@ -74,6 +74,7 @@ namespace led_effects {
pixels
.
begin
();
pixels
.
setBrightness
(
config
::
max_brightness
);
LEDsOff
();
sensor_console
::
defineCommand
(
"night_mode"
,
toggleNightMode
,
F
(
" (Toggles night mode on/off)"
));
}
void
toggleNightMode
()
{
...
...
ampel-firmware/led_effects.h
View file @
36bd5ec7
...
...
@@ -2,6 +2,7 @@
#define LED_EFFECTS_H_INCLUDED
#include
<Arduino.h>
#include
"config.h"
#include
"sensor_console.h"
// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip)
// https://github.com/adafruit/Adafruit_NeoPixel
...
...
ampel-firmware/lorawan.cpp
View file @
36bd5ec7
...
...
@@ -33,7 +33,7 @@ void os_getDevKey(u1_t *buf) {
namespace
lorawan
{
bool
waiting_for_confirmation
=
false
;
bool
connected
=
false
;
String
last_transmission
=
""
;
char
last_transmission
[
23
]
=
""
;
void
initialize
()
{
Serial
.
println
(
F
(
"Starting LoRaWAN. Frequency plan : "
LMIC_FREQUENCY_PLAN
" MHz."
));
...
...
@@ -47,7 +47,7 @@ namespace lorawan {
LMIC_reset
();
// Join, but don't send anything yet.
LMIC_startJoining
();
sensor_console
::
defineIntCommand
(
"lora"
,
setLoRaInterval
,
" 300 (Sets LoRaWAN sending interval, in s)"
);
sensor_console
::
defineIntCommand
(
"lora"
,
setLoRaInterval
,
F
(
" 300 (Sets LoRaWAN sending interval, in s)"
)
)
;
}
// Checks if OTAA is connected, or if payload should be sent.
...
...
@@ -65,8 +65,10 @@ namespace lorawan {
}
void
onEvent
(
ev_t
ev
)
{
char
current_time
[
23
];
ntp
::
getLocalTime
(
current_time
);
Serial
.
print
(
"LoRa - "
);
Serial
.
print
(
ntp
::
getLocalT
ime
()
);
Serial
.
print
(
current_t
ime
);
Serial
.
print
(
" - "
);
switch
(
ev
)
{
case
EV_JOINING
:
...
...
@@ -113,7 +115,7 @@ namespace lorawan {
Serial
.
println
(
F
(
"EV_REJOIN_FAILED"
));
break
;
case
EV_TXCOMPLETE
:
last_transmission
=
ntp
::
getLocalTime
();
ntp
::
getLocalTime
(
last_transmission
);
Serial
.
println
(
F
(
"EV_TXCOMPLETE"
));
break
;
case
EV_TXSTART
:
...
...
ampel-firmware/lorawan.h
View file @
36bd5ec7
...
...
@@ -39,7 +39,7 @@ namespace config {
namespace
lorawan
{
extern
bool
waiting_for_confirmation
;
extern
bool
connected
;
extern
String
last_transmission
;
extern
char
last_transmission
[]
;
void
initialize
();
void
process
();
void
preparePayloadIfTimeHasCome
(
const
int16_t
&
co2
,
const
float
&
temp
,
const
float
&
hum
);
...
...
ampel-firmware/mqtt.cpp
View file @
36bd5ec7
...
...
@@ -25,7 +25,7 @@ namespace mqtt {
String
publish_topic
;
const
char
*
json_sensor_format
;
String
last_successful_publish
=
""
;
char
last_successful_publish
[
23
]
=
""
;
void
initialize
(
String
&
topic
)
{
json_sensor_format
=
PSTR
(
"{
\"
time
\"
:
\"
%s
\"
,
\"
co2
\"
:%d,
\"
temp
\"
:%.1f,
\"
rh
\"
:%.1f}"
);
...
...
@@ -36,22 +36,22 @@ namespace mqtt {
// mqttClient.setSocketTimeout(config::mqtt_timeout); //NOTE: somehow doesn't seem to have any effect on connect()
mqttClient
.
setServer
(
config
::
mqtt_server
,
config
::
mqtt_port
);
sensor_console
::
defineIntCommand
(
"mqtt"
,
setMQTTinterval
,
" 60 (Sets MQTT sending interval, in s)"
);
sensor_console
::
defineIntCommand
(
"mqtt"
,
setMQTTinterval
,
F
(
" 60 (Sets MQTT sending interval, in s)"
)
)
;
sensor_console
::
defineCommand
(
"local_ip"
,
sendInfoAboutLocalNetwork
,
" (Sends local IP and SSID via MQTT. Can be useful to find sensor)"
);
F
(
" (Sends local IP and SSID via MQTT. Can be useful to find sensor)"
)
)
;
}
void
publish
(
const
String
&
timestamp
,
int16_t
co2
,
float
temperature
,
float
humidity
)
{
void
publish
(
const
char
*
timestamp
,
int16_t
co2
,
float
temperature
,
float
humidity
)
{
if
(
WiFi
.
status
()
==
WL_CONNECTED
&&
mqttClient
.
connected
())
{
led_effects
::
onBoardLEDOn
();
Serial
.
print
(
F
(
"MQTT - Publishing message ... "
));
char
payload
[
75
];
// Should be enough for json...
snprintf
(
payload
,
sizeof
(
payload
),
json_sensor_format
,
timestamp
.
c_str
()
,
co2
,
temperature
,
humidity
);
snprintf
(
payload
,
sizeof
(
payload
),
json_sensor_format
,
timestamp
,
co2
,
temperature
,
humidity
);
// Topic is the same as clientID. e.g. 'CO2sensors/ESP3d03da'
if
(
mqttClient
.
publish
(
publish_topic
.
c_str
(),
payload
))
{
Serial
.
println
(
F
(
"OK"
));
last_successful_publish
=
ntp
::
getLocalTime
(
);
ntp
::
getLocalTime
(
last_successful_publish
);
}
else
{
Serial
.
println
(
F
(
"Failed."
));
}
...
...
@@ -120,12 +120,12 @@ namespace mqtt {
}
}
void
publishIfTimeHasCome
(
const
String
&
time
S
tamp
,
const
int16_t
&
co2
,
const
float
&
temp
,
const
float
&
hum
)
{
void
publishIfTimeHasCome
(
const
char
*
time
s
tamp
,
const
int16_t
&
co2
,
const
float
&
temp
,
const
float
&
hum
)
{
// Send message via MQTT according to sending interval
unsigned
long
now
=
seconds
();
if
(
now
-
last_sent_at
>
config
::
mqtt_sending_interval
)
{
last_sent_at
=
now
;
publish
(
time
S
tamp
,
co2
,
temp
,
hum
);
publish
(
time
s
tamp
,
co2
,
temp
,
hum
);
}
}
...
...
ampel-firmware/mqtt.h
View file @
36bd5ec7
...
...
@@ -11,11 +11,11 @@ namespace config {
extern
uint16_t
mqtt_sending_interval
;
// [s]
}
namespace
mqtt
{
extern
String
last_successful_publish
;
extern
char
last_successful_publish
[]
;
extern
bool
connected
;
void
initialize
(
String
&
topic
);
void
keepConnection
();
void
publishIfTimeHasCome
(
const
String
&
time
S
tamp
,
const
int16_t
&
co2
,
const
float
&
temp
,
const
float
&
hum
);
void
publishIfTimeHasCome
(
const
char
*
time
s
tamp
,
const
int16_t
&
co2
,
const
float
&
temp
,
const
float
&
hum
);
void
setMQTTinterval
(
int32_t
sending_interval
);
void
sendInfoAboutLocalNetwork
();
...
...
ampel-firmware/sensor_console.cpp
View file @
36bd5ec7
...
...
@@ -19,7 +19,8 @@ namespace sensor_console {
Command
commands
[
MAX_COMMANDS
];
//NOTE: Probably possible to DRY (with templates?)
void
defineCommand
(
const
char
*
name
,
void
(
*
function
)(
void
),
const
char
*
doc
)
{
void
defineCommand
(
const
char
*
name
,
void
(
*
function
)(
void
),
const
__FlashStringHelper
*
doc_fstring
)
{
const
char
*
doc
=
(
const
char
PROGMEM
*
)
doc_fstring
;
if
(
commands_count
<
MAX_COMMANDS
)
{
commands
[
commands_count
].
name
=
name
;
commands
[
commands_count
].
voidFunction
=
function
;
...
...
@@ -31,7 +32,8 @@ namespace sensor_console {
}
}
void
defineIntCommand
(
const
char
*
name
,
void
(
*
function
)(
int32_t
),
const
char
*
doc
)
{
void
defineIntCommand
(
const
char
*
name
,
void
(
*
function
)(
int32_t
),
const
__FlashStringHelper
*
doc_fstring
)
{
const
char
*
doc
=
(
const
char
PROGMEM
*
)
doc_fstring
;
if
(
commands_count
<
MAX_COMMANDS
)
{
commands
[
commands_count
].
name
=
name
;
commands
[
commands_count
].
intFunction
=
function
;
...
...
@@ -50,13 +52,11 @@ namespace sensor_console {
uint8_t
parseCommand
(
const
char
*
command
,
char
*
function_name
,
int32_t
&
argument
)
{
char
split_command
[
MAX_COMMAND_SIZE
];
strlcpy
(
split_command
,
command
,
MAX_COMMAND_SIZE
);
Serial
.
print
(
F
(
"Received : '"
));
Serial
.
print
(
command
);
Serial
.
println
(
"'"
);
char
*
arg
;
char
*
part1
;
part1
=
strtok
(
split_command
,
" "
);
if
(
!
part1
)
{
Serial
.
println
(
F
(
"Received empty command"
));
// Empty string
return
1
;
}
...
...
@@ -126,7 +126,6 @@ namespace sensor_console {
Serial
.
print
(
commands
[
i
].
doc
);
Serial
.
println
(
"."
);
}
led_effects
::
showKITTWheel
(
color
::
red
,
1
);
}
/*
...
...
@@ -143,18 +142,20 @@ namespace sensor_console {
Serial
.
print
(
F
(
"Calling : "
));
Serial
.
print
(
function_name
);
if
(
has_argument
)
{
Serial
.
print
(
"("
);
Serial
.
print
(
F
(
"("
)
)
;
Serial
.
print
(
argument
);
Serial
.
println
(
")"
);
Serial
.
println
(
F
(
")"
)
)
;
commands
[
i
].
intFunction
(
argument
);
}
else
{
Serial
.
println
(
"()"
);
Serial
.
println
(
F
(
"()"
)
)
;
commands
[
i
].
voidFunction
();
}
return
;
}
}
Serial
.
println
(
F
(
"Message not supported. Available commands :"
));
Serial
.
print
(
F
(
"'"
));
Serial
.
print
(
command
);
Serial
.
println
(
F
(
"' not supported. Available commands :"
));
listAvailableCommands
();
}
}
ampel-firmware/sensor_console.h
View file @
36bd5ec7
#ifndef SENSOR_CONSOLE_H_INCLUDED
#define SENSOR_CONSOLE_H_INCLUDED
#include
<Arduino.h>
#include
"led_effects.h"
/** Other scripts can use this namespace, in order to define commands, via callbacks.
* Those callbacks can then be used to send commands to the sensor (reset, calibrate, night mode, ...)
...
...
@@ -9,6 +10,8 @@
namespace
sensor_console
{
void
processSerialInput
(
const
byte
in_byte
);
void
runCommand
(
const
char
*
command
);
void
defineIntCommand
(
const
char
*
command
,
void
(
*
function
)(
int32_t
),
const
char
*
doc
);
void
defineCommand
(
const
char
*
command
,
void
(
*
function
)(
void
),
const
char
*
doc
);
void
defineIntCommand
(
const
char
*
command
,
void
(
*
function
)(
int32_t
),
const
__FlashStringHelper
*
ifsh
);
void
defineCommand
(
const
char
*
command
,
void
(
*
function
)(
void
),
const
__FlashStringHelper
*
ifsh
);
}
#endif
ampel-firmware/src/lib/NTPClient-master/NTPClient.cpp
View file @
36bd5ec7
...
...
@@ -152,19 +152,17 @@ int NTPClient::getSeconds() {
return
(
this
->
getEpochTime
()
%
60
);
}
String
NTPClient
::
getFormattedTime
(
unsigned
long
secs
)
{
void
NTPClient
::
getFormattedTime
(
char
*
formatted_time
,
unsigned
long
secs
)
{
unsigned
long
rawTime
=
secs
?
secs
:
this
->
getEpochTime
();
unsigned
int
hours
=
(
rawTime
%
86400L
)
/
3600
;
unsigned
int
minutes
=
(
rawTime
%
3600
)
/
60
;
unsigned
int
seconds
=
rawTime
%
60
;
char
formatted_time
[
9
];
snprintf
(
formatted_time
,
sizeof
(
formatted_time
),
"%02d:%02d:%02d"
,
hours
,
minutes
,
seconds
);
return
String
(
formatted_time
);
snprintf
(
formatted_time
,
9
,
"%02d:%02d:%02d"
,
hours
,
minutes
,
seconds
);
}
// Based on https://github.com/PaulStoffregen/Time/blob/master/Time.cpp
String
NTPClient
::
getFormattedDate
(
unsigned
long
secs
)
{
void
NTPClient
::
getFormattedDate
(
char
*
formatted_date
,
unsigned
long
secs
)
{
unsigned
long
rawTime
=
(
secs
?
secs
:
this
->
getEpochTime
())
/
86400L
;
// in days
unsigned
long
days
=
0
,
year
=
1970
;
uint8_t
month
;
...
...
@@ -187,11 +185,9 @@ String NTPClient::getFormattedDate(unsigned long secs) {
month
++
;
// jan is month 1
rawTime
++
;
// first day is day 1
char
formatted_date
[
23
];
snprintf
(
formatted_date
,
sizeof
(
formatted_date
),
"%4lu-%02d-%02lu %s%+03d"
,
year
,
month
,
rawTime
,
this
->
getFormattedTime
(
secs
).
c_str
(),
this
->
_timeOffset
/
3600
);
return
String
(
formatted_date
);
char
formatted_time
[
9
];
this
->
getFormattedTime
(
formatted_time
,
secs
);
snprintf
(
formatted_date
,
23
,
"%4lu-%02d-%02lu %s%+03d"
,
year
,
month
,
rawTime
,
formatted_time
,
this
->
_timeOffset
/
3600
);
}
void
NTPClient
::
end
()
{
...
...
ampel-firmware/src/lib/NTPClient-master/NTPClient.h
View file @
36bd5ec7
...
...
@@ -80,7 +80,7 @@ class NTPClient {
/**
* @return secs argument (or 0 for current time) formatted like `hh:mm:ss`
*/
String
getFormattedTime
(
unsigned
long
secs
=
0
);
void
getFormattedTime
(
char
*
formatted_time
,
unsigned
long
secs
=
0
);
/**
* @return time in seconds since Jan. 1, 1970
...
...
@@ -91,7 +91,7 @@ class NTPClient {
* @return secs argument (or 0 for current date) formatted to ISO 8601
* like `2004-02-12T15:19:21+00:00`
*/
String
getFormattedDate
(
unsigned
long
secs
=
0
);
void
getFormattedDate
(
char
*
formatted_date
,
unsigned
long
secs
=
0
);
/**
* Stops the underlying UDP client
...
...
ampel-firmware/util.cpp
View file @
36bd5ec7
...
...
@@ -5,6 +5,14 @@ namespace config {
const
long
utc_offset_in_seconds
=
UTC_OFFSET_IN_SECONDS
;
// UTC+1
}
#if defined(ESP8266)
const
char
*
current_board
=
"ESP8266"
;
#elif defined(ESP32)
const
char
*
current_board
=
"ESP32"
;
#else
const
char
*
current_board
=
"UNKNOWN"
;
#endif
// Get last 3 bytes of ESP MAC (worldwide unique)
String
macToID
()
{
uint8_t
mac
[
6
];
...
...
@@ -24,22 +32,51 @@ String macToID() {
namespace
ntp
{
WiFiUDP
ntpUDP
;
NTPClient
timeClient
(
ntpUDP
,
config
::
ntp_server
,
config
::
utc_offset_in_seconds
,
60000UL
);
bool
connected_at_least_once
=
false
;
void
initialize
()
{
timeClient
.
begin
();
}
void
update
()
{
timeClient
.
update
();
connected_at_least_once
|=
timeClient
.
update
();
}
void
getLocalTime
(
char
*
timestamp
)
{
timeClient
.
getFormattedDate
(
timestamp
);
}
String
getLocalTime
()
{
return
timeClient
.
getFormattedDate
();
void
setLocalTime
(
int32_t
unix_seconds
)
{
char
time
[
23
];
timeClient
.
getFormattedDate
(
time
);
Serial
.
print
(
F
(
"Current time : "
));
Serial
.
println
(
time
);
if
(
connected_at_least_once
)
{
Serial
.
println
(
F
(
"NTP update already happened. Not changing anything."
));
return
;
}
Serial
.
print
(
F
(
"Setting UNIX time to : "
));
Serial
.
println
(
unix_seconds
);
timeClient
.
setEpochTime
(
unix_seconds
-
seconds
());
timeClient
.
getFormattedDate
(
time
);
Serial
.
print
(
F
(
"Current time : "
));
Serial
.
println
(
time
);
}
}
uint32_t
max_loop_duration
=
0
;
void
Ampel
::
showFreeSpace
()
{
Serial
.
print
(
F
(
"Free heap space : "
));
Serial
.
print
(
get_free_heap_size
());
Serial
.
println
(
F
(
" bytes."
));
}
Ampel
::
Ampel
()
:
board
(
current_board
),
sensorId
(
"ESP"
+
macToID
()),
max_loop_duration
(
0
)
{
sensor_console
::
defineIntCommand
(
"set_time"
,
ntp
::
setLocalTime
,
F
(
" 1618829570 (Sets time to the given UNIX time)"
));
sensor_console
::
defineCommand
(
"free"
,
Ampel
::
showFreeSpace
,
F
(
" (Displays available heap space)"
));
sensor_console
::
defineCommand
(
"reset"
,
[]()
{
ESP
.
restart
();
},
F
(
" (Restarts the sensor)"
));
}
//FIXME: Remove every instance of Strings, to avoid heap fragmentation problems. (Start: "Free heap space : 17104 bytes")
// See more https://cpp4arduino.com/2020/02/07/how-to-format-strings-without-the-string-class.html
const
String
SENSOR_ID
=
"ESP"
+
macToID
();
Ampel
ampel
;
ampel-firmware/util.h
View file @
36bd5ec7
...
...
@@ -2,26 +2,23 @@
#define AMPEL_UTIL_H_INCLUDED
#include
<Arduino.h>
#include
"config.h"
#include
"sensor_console.h"
#include
<WiFiUdp.h>
// required for NTP
#include
"src/lib/NTPClient-master/NTPClient.h"
// NTP
#if defined(ESP8266)
# define BOARD "ESP8266"
# include <ESP8266WiFi.h> // required to get MAC address
# define get_free_heap_size() system_get_free_heap_size()
#elif defined(ESP32)
# define BOARD "ESP32"
# include <WiFi.h> // required to get MAC address
# define get_free_heap_size() esp_get_free_heap_size()
#else
# define BOARD "Unknown"
#endif
namespace
ntp
{
void
initialize
();
void
update
();
String
getLocalTime
();
void
getLocalTime
(
char
*
timestamp
);
}
namespace
util
{
...
...
@@ -35,10 +32,18 @@ namespace util {
return
b
>
a
?
b
:
a
;
}
}
class
Ampel
{
private:
static
void
showFreeSpace
();
public:
const
char
*
board
;
const
String
sensorId
;
uint32_t
max_loop_duration
;
Ampel
();
};
//NOTE: Only use seconds() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over.
#define seconds() (millis() / 1000UL)
extern
uint32_t
max_loop_duration
;
const
extern
String
SENSOR_ID
;
extern
Ampel
ampel
;
#endif
ampel-firmware/web_server.cpp
View file @
36bd5ec7
...
...
@@ -184,7 +184,7 @@ namespace web_server {
http
.
begin
();
Serial
.
print
(
F
(
"You can access this sensor via http://"
));
Serial
.
print
(
SENSOR_ID
);
Serial
.
print
(
ampel
.
sensorId
);
Serial
.
print
(
F
(
".local (might be unstable) or http://"
));
Serial
.
println
(
WiFi
.
localIP
());
}
...
...
@@ -212,7 +212,7 @@ namespace web_server {
char
content
[
2000
];
// Update if needed
// INFO - Header size : 1767 - Body size : 1812 - Script size : 1909
snprintf_P
(
content
,
sizeof
(
content
),
header_template
,
sensor
::
co2
,
SENSOR_ID
.
c_str
(),
snprintf_P
(
content
,
sizeof
(
content
),
header_template
,
sensor
::
co2
,
ampel
.
sensorId
.
c_str
(),
WiFi
.
localIP
().
toString
().
c_str
()
#ifdef AMPEL_CSV
,
csv_writer
::
filename
.
c_str
()
...
...
@@ -225,21 +225,21 @@ namespace web_server {
http
.
send_P
(
200
,
PSTR
(
"text/html"
),
content
);
// Body
snprintf_P
(
content
,
sizeof
(
content
),
body_template
,
SENSOR_ID
.
c_str
(),
sensor
::
co2
,
sensor
::
temperature
,
sensor
::
humidity
,
sensor
::
timestamp
.
c_str
()
,
config
::
measurement_timestep
,
snprintf_P
(
content
,
sizeof
(
content
),
body_template
,
ampel
.
sensorId
.
c_str
(),
sensor
::
co2
,
sensor
::
temperature
,
sensor
::
humidity
,
sensor
::
timestamp
,
config
::
measurement_timestep
,
#ifdef AMPEL_CSV
csv_writer
::
last_successful_write
.
c_str
()
,
config
::
csv_interval
,
csv_writer
::
getAvailableSpace
()
/
1024
,
csv_writer
::
last_successful_write
,
config
::
csv_interval
,
csv_writer
::
getAvailableSpace
()
/
1024
,
#endif
#ifdef AMPEL_MQTT
mqtt
::
connected
?
"Yes"
:
"No"
,
mqtt
::
last_successful_publish
.
c_str
()
,
config
::
mqtt_sending_interval
,
mqtt
::
connected
?
"Yes"
:
"No"
,
mqtt
::
last_successful_publish
,
config
::
mqtt_sending_interval
,
#endif
#if defined(AMPEL_LORAWAN) && defined(ESP32)
lorawan
::
connected
?
"Yes"
:
"No"
,
LMIC_FREQUENCY_PLAN
,
lorawan
::
last_transmission
.
c_str
()
,
lorawan
::
connected
?
"Yes"
:
"No"
,
LMIC_FREQUENCY_PLAN
,
lorawan
::
last_transmission
,
config
::
lorawan_sending_interval
,
#endif
config
::
temperature_offset
,
config
::
auto_calibrate_sensor
?
"Yes"
:
"No"
,
SENSOR_ID
.
c_str
(),
SENSOR_ID
.
c_str
(),
WiFi
.
localIP
().
toString
().
c_str
(),
WiFi
.
localIP
().
toString
().
c_str
(),
get_free_heap_size
(),
max_loop_duration
,
BOARD
,
dd
,
hh
,
mm
,
ss
);
config
::
temperature_offset
,
config
::
auto_calibrate_sensor
?
"Yes"
:
"No"
,
ampel
.
sensorId
.
c_str
(),
ampel
.
sensorId
.
c_str
(),
WiFi
.
localIP
().
toString
().
c_str
(),
WiFi
.
localIP
().
toString
().
c_str
(),
get_free_heap_size
(),
ampel
.
max_loop_duration
,
ampel
.
board
,
dd
,
hh
,
mm
,
ss
);
Serial
.
print
(
F
(
" - Body size : "
));
http
.
sendContent
(
content
);
...
...
@@ -248,7 +248,7 @@ namespace web_server {
// Script
snprintf_P
(
content
,
sizeof
(
content
),
script_template
#ifdef AMPEL_CSV
,
csv_writer
::
filename
.
c_str
(),
SENSOR_ID
.
c_str
()
,
csv_writer
::
filename
.
c_str
(),
ampel
.
sensorId
.
c_str
()
#endif
);
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment