-
Notifications
You must be signed in to change notification settings - Fork 1
/
LampColorControler.ino
221 lines (185 loc) · 5.87 KB
/
LampColorControler.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include <Arduino.h>
#include <bluefruit.h>
#include <Wire.h>
#include "src/compile.h"
#include "src/system/alerts.h"
#include "src/system/behavior.h"
#include "src/system/charger/charger.h"
#include "src/system/physical/battery.h"
#include "src/system/physical/bluetooth.h"
#include "src/system/physical/button.h"
#include "src/system/physical/IMU.h"
#include "src/system/physical/fileSystem.h"
#include "src/system/physical/MicroPhone.h"
#include "src/system/physical/led_power.h"
#include "src/system/utils/serial.h"
#include "src/system/utils/utils.h"
#include "src/user/functions.h"
void set_watchdog(const uint32_t timeoutDelaySecond)
{
// Configure WDT
NRF_WDT->CONFIG = 0x01; // Configure WDT to run when CPU is asleep
NRF_WDT->CRV = timeoutDelaySecond * 32768 + 1; // set timeout
NRF_WDT->RREN = 0x01; // Enable the RR[0] reload register
NRF_WDT->TASKS_START = 1; // Start WDT
}
// timestamp of the system wake up
static uint32_t turnOnTime = 0;
void setup()
{
// start by resetting the led driver
ledpower::write_current(0);
// set turn on time
turnOnTime = millis();
// set watchdog (reset the soft when the program crashes)
// Should be long enough to flash the microcontroler !!!
set_watchdog(5); // second timeout
// necessary for all i2c communications
Wire.setClock(400000); // 400KHz clock
Wire.setTimeout(100); // ms timout
Wire.begin();
// setup serial
serial::setup();
analogReference(AR_INTERNAL_3_0); // 3v reference
analogReadResolution(ADC_RES_EXP);
// setup charger
charger::setup();
// start the file system
fileSystem::setup();
behavior::read_parameters();
// check if we are in first boot mode (no first boot flag stored)
const bool isFirstBoot = !fileSystem::doKeyExists(behavior::isFirstBootKey);
const auto startReason = readResetReason();
// POWER_RESETREAS_RESETPIN_Msk: reset from pin-reset detected
// POWER_RESETREAS_DOG_Msk: reset from watchdog
// POWER_RESETREAS_SREQ_Msk: reset via soft reset
// POWER_RESETREAS_LOCKUP_Msk: reset from cpu lockup
// POWER_RESETREAS_OFF_Msk: wake up from pin interrupt
// POWER_RESETREAS_LPCOMP_Msk: wake up from analogic pin detect (LPCOMP)
// POWER_RESETREAS_DIF_Msk: wake up from debug interface
// POWER_RESETREAS_NFC_Msk: wake from NFC field detection
// POWER_RESETREAS_VBUS_Msk: wake from vbus high signal
bool shouldAlertUser = false;
// handle start flags
if (!isFirstBoot)
{
// started after reset, clear all code and go to bootloader mode
if ((startReason & POWER_RESETREAS_RESETPIN_Msk) != 0x00)
{
enterSerialDfu();
}
if ((startReason & POWER_RESETREAS_DOG_Msk) != 0x00)
{
// POWER_USBREGSTATUS_OUTPUTRDY_Msk : debounce time passed
// POWER_USBREGSTATUS_VBUSDETECT_Msk : vbus is detected on usb
// power detected on the USB, reset the program
if ((NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0x00)
{
// system will reset & shutdown after that
enterSerialDfu();
}
else
{
// alert the user that the lamp was resetted by watchdog
shouldAlertUser = true;
}
}
}
// set up button colors and callbacks
button::init();
if (shouldAlertUser)
{
for (int i = 0; i < 5; i++)
{
button::set_color(utils::ColorSpace::WHITE);
delay(300);
button::set_color(utils::ColorSpace::BLACK);
delay(300);
}
}
const bool wasPoweredyByUserInterrupt = (startReason & POWER_RESETREAS_OFF_Msk) != 0x00;
// any wake up from something that is not an interrupt should be considered as vbus voltage
behavior::set_woke_up_from_vbus(not wasPoweredyByUserInterrupt);
// let the user start in unpowered mode
user::power_off_sequence();
// use the charging thread !
Scheduler.startLoop(charging_thread);
// user requested another thread, spawn it
if (user::should_spawn_thread())
{
Scheduler.startLoop(secondary_thread);
}
}
void charging_thread()
{
if (behavior::is_shuting_down())
return;
// run the charger loop (all the time)
charger::loop();
delay(2);
}
void secondary_thread()
{
if (behavior::is_shuting_down())
return;
if (not behavior::is_user_code_running())
return;
user::user_thread();
}
void check_loop_runtime(const uint32_t runTime)
{
static constexpr uint8_t maxAlerts = 5;
static uint32_t alarmRaisedTime = 0;
// check the loop duration
static uint8_t isOnSlowLoopCount = 0;
if (runTime > LOOP_UPDATE_PERIOD + 1)
{
isOnSlowLoopCount = min(isOnSlowLoopCount + 1, maxAlerts);
if (runTime > 500)
{
// if loop time is too long, go back to flash mode
enterSerialDfu();
}
}
else if (isOnSlowLoopCount > 0)
{
isOnSlowLoopCount--;
}
if (isOnSlowLoopCount >= maxAlerts)
{
alarmRaisedTime = millis();
AlertManager.raise_alert(Alerts::LONG_LOOP_UPDATE);
}
// lower the alert (after 5 seconds)
else if (isOnSlowLoopCount <= 1 and millis() - alarmRaisedTime > 3000)
{
AlertManager.clear_alert(Alerts::LONG_LOOP_UPDATE);
};
}
/**
* \brief Run the main program loop
*/
void loop()
{
uint32_t start = millis();
// update watchdog (prevent crash)
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
// loop is not ran in shutdown mode
button::handle_events(behavior::button_clicked_callback, behavior::button_hold_callback);
// handle user serial events
serial::handleSerialEvents();
// loop the behavior
behavior::loop();
// wait for a delay if we are faster than the set refresh rate
uint32_t stop = millis();
const uint32_t loopDuration = (stop - start);
if (loopDuration < LOOP_UPDATE_PERIOD)
{
delay(LOOP_UPDATE_PERIOD - loopDuration);
}
stop = millis();
check_loop_runtime(stop - start);
// automatically deactivate sensors if they are not used for a time
microphone::disable_after_non_use();
imu::disable_after_non_use();
}