-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathRBCXManager.cpp
More file actions
357 lines (301 loc) · 10.6 KB
/
RBCXManager.cpp
File metadata and controls
357 lines (301 loc) · 10.6 KB
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#include <driver/i2c.h>
#include <driver/uart.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "RBCXBattery.h"
#include "RBCXManager.h"
#include "rbcx.pb.h"
#define TAG "RBCXManager"
#define MOTORS_FAILSAFE_PERIOD_MS 300
#define MAX_COPROC_IDLE_MS 75
namespace rb {
Manager::Manager()
: m_keepaliveTask(nullptr)
, m_coprocFwVersion(CoprocStat_VersionStat_init_zero) {}
Manager::~Manager() {}
void Manager::install(
ManagerInstallFlags flags, BaseType_t managerLoopStackSize) {
if (m_keepaliveTask != nullptr) {
ESP_LOGE(TAG,
"The manager has already been installed, please make sure to "
"only call install() once!");
abort();
}
for (int i = 0; i < UltrasoundsCount; ++i) {
m_ultrasounds[i].init(i);
}
for (MotorId id = MotorId::M1; id < MotorId::MAX; ++id) {
m_motors[size_t(id)].setId(id);
}
m_motors_last_set = 0;
if (!(flags & MAN_DISABLE_MOTOR_FAILSAFE)) {
schedule(MOTORS_FAILSAFE_PERIOD_MS,
std::bind(&Manager::motorsFailSafe, this));
}
const uart_config_t uart_config = {
.baud_rate = 921600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, GPIO_NUM_2, GPIO_NUM_0,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, 1024, 0, 0, NULL, 0));
m_coprocSemaphore = xSemaphoreCreateBinary();
// The esp_timer (-> rb::Timers) task runs pinned on core 0, which gets stalled
// for ~700 ms(?!) when connecting to a WiFi network, so it can't be used
// for this watchdog.
xTaskCreate(&Manager::keepaliveRoutine, "rbmanager_keepalive", 1536, this,
10, &m_keepaliveTask);
monitorTask(m_keepaliveTask);
TaskHandle_t task;
xTaskCreate(&Manager::consumerRoutineTrampoline, "rbmanager_loop",
managerLoopStackSize, this, 5, &task);
monitorTask(task);
// One day, when the version of FW with CoprocReq_coprocStartupMessage_tag
// is everywhere, we can drop this one.
sendToCoproc(CoprocReq { .which_payload = CoprocReq_versionReq_tag } );
if (xSemaphoreTake(m_coprocSemaphore, pdMS_TO_TICKS(300)) != pdTRUE) {
ESP_LOGE(TAG,
"failed to acquire FW version from STM32, message not received in "
"300ms.\n");
}
if(m_coprocFwVersion.number >= 0x010200) {
sendToCoproc(CoprocReq {
.which_payload = CoprocReq_coprocStartupMessage_tag,
.payload = {
.coprocStartupMessage = {
.getButtons = true,
.getVersion = false,
.getRtc = false,
.has_espWatchdogSettings = true,
.espWatchdogSettings = {
.disable = (flags & MAN_DISABLE_ESP_WATCHDOG) != 0,
},
},
},
});
} else {
sendToCoproc(CoprocReq { .which_payload = CoprocReq_getButtons_tag } );
}
#ifdef RB_DEBUG_MONITOR_TASKS
schedule(10000, [&]() { return printTasksDebugInfo(); });
#endif
}
void Manager::consumerRoutineTrampoline(void* cookie) {
((Manager*)cookie)->consumerRoutine();
}
void Manager::consumerRoutine() {
CoprocLinkParser<CoprocStat, &CoprocStat_msg> parser(m_codec);
while (true) {
uint8_t byte;
if (uart_read_bytes(UART_NUM_2, &byte, 1, portMAX_DELAY) != 1) {
ESP_LOGE(TAG, "Invalid uart read\n");
continue;
}
if (!parser.add(byte))
continue;
const auto& msg = parser.lastMessage();
switch (msg.which_payload) {
case CoprocStat_buttonsStat_tag:
m_buttons.setState(msg.payload.buttonsStat);
break;
case CoprocStat_ultrasoundStat_tag: {
const auto& p = msg.payload.ultrasoundStat;
if (p.utsIndex >= 0 && p.utsIndex < UltrasoundsCount)
m_ultrasounds[p.utsIndex].onMeasuringDone(p);
break;
}
case CoprocStat_powerAdcStat_tag:
m_battery.setState(msg.payload.powerAdcStat);
break;
case CoprocStat_versionStat_tag:
m_coprocFwVersion = msg.payload.versionStat;
xSemaphoreGive(m_coprocSemaphore);
break;
case CoprocStat_motorStat_tag: {
const auto& p = msg.payload.motorStat;
if (p.motorIndex < (uint32_t)MotorId::MAX) {
m_motors[p.motorIndex].onMotorStat(p);
}
break;
}
case CoprocStat_smartServoStat_tag:
m_smartServoBusBackend.onCoprocStat(msg.payload.smartServoStat);
break;
case CoprocStat_ledsStat_tag:
case CoprocStat_stupidServoStat_tag:
// Ignore
break;
case CoprocStat_mpuStat_tag:
m_mpu.setState(msg.payload.mpuStat);
break;
case CoprocStat_mpuCalibrationDone_tag:
m_mpu.onCalibrationDone(msg.payload.mpuCalibrationDone);
break;
case CoprocStat_faultStat_tag:
fault(msg.payload.faultStat);
break;
default:
printf("Received message of unknown type from stm32: %d\n",
msg.which_payload);
break;
}
}
}
void Manager::keepaliveRoutine(void* cookie) {
auto& man = *((Manager*)cookie);
while (true) {
if (xTaskNotifyWait(0, 0, NULL, pdMS_TO_TICKS(MAX_COPROC_IDLE_MS))
== pdFALSE) {
man.sendToCoproc(CoprocReq {
.which_payload = CoprocReq_keepalive_tag,
});
}
}
}
void Manager::sendToCoproc(const CoprocReq& msg) {
m_codecTxMutex.lock();
const auto len = m_codec.encodeWithHeader(
&CoprocReq_msg, &msg, m_txBuf, sizeof(m_txBuf));
if (len > 0) {
uart_write_bytes(UART_NUM_2, (const char*)m_txBuf, len);
}
m_codecTxMutex.unlock();
xTaskNotify(m_keepaliveTask, 0, eNoAction);
}
bool Manager::coprocFwAtLeastVersion(uint32_t minVersion) {
// Ignore zero version number and pass. Might be the cmd was not received yet,
// but better than to crash in that case.
if (m_coprocFwVersion.number == 0)
return true;
// Ignore dirty versions for development
if (m_coprocFwVersion.dirty)
return true;
return minVersion <= m_coprocFwVersion.number;
}
void Manager::coprocFwVersionAssert(uint32_t minVersion, const char* name) {
if (!coprocFwAtLeastVersion(minVersion)) {
printf("\n\nERROR: Please update your STM32 FW, '%s' requires version "
"0x%06x and you have 0x%06x!\n\n",
name, minVersion, m_coprocFwVersion.number);
abort();
}
}
SmartServoBusBackend &Manager::smartServoBusBackend() {
coprocFwVersionAssert(0x010200, "smart servos tunnel");
return m_smartServoBusBackend;
}
StupidServo& Manager::stupidServo(uint8_t index) {
static StupidServo stupidServos[StupidServosCount] = {
{0},
{1},
{2},
{3},
};
return stupidServos[index];
}
void Manager::resetMotorsFailSafe() { m_motors_last_set = xTaskGetTickCount(); }
bool Manager::motorsFailSafe() {
if (m_motors_last_set != 0) {
const auto now = xTaskGetTickCount();
if (now - m_motors_last_set
> pdMS_TO_TICKS(MOTORS_FAILSAFE_PERIOD_MS)) {
ESP_LOGE(TAG, "Motor failsafe triggered, stopping all motors!");
for (auto& m : m_motors) {
m.power(0);
}
m_motors_last_set = 0;
}
}
return true;
}
MotorChangeBuilder Manager::setMotors() { return MotorChangeBuilder(); }
void Manager::monitorTask(TaskHandle_t task) {
#ifdef RB_DEBUG_MONITOR_TASKS
m_tasks_mutex.lock();
m_tasks.push_back(task);
m_tasks_mutex.unlock();
#endif
}
#ifdef RB_DEBUG_MONITOR_TASKS
bool Manager::printTasksDebugInfo() {
std::lock_guard<std::mutex> lock(m_tasks_mutex);
printf("%16s %5s %5s\n", "Name", "prio", "stack");
printf("==========================================\n");
for (auto task : m_tasks) {
auto stackMark = uxTaskGetStackHighWaterMark(task);
auto prio = uxTaskPriorityGet(task);
printf("%16s %5d %5d\n", pcTaskGetTaskName(task), (int)prio,
(int)stackMark);
}
return true;
}
#endif
void Manager::fault(CoprocStat_FaultStat faultStat) {
switch (faultStat.which_fault) {
case CoprocStat_FaultStat_oledFault_tag:
ESP_LOGE(TAG, "Oled not connected");
break;
case CoprocStat_FaultStat_mpuFault_tag:
ESP_LOGE(TAG, "MPU6050 not connected");
break;
default:
ESP_LOGE(TAG, "CoprocStat - non specific error");
}
}
MotorChangeBuilder::MotorChangeBuilder() {}
MotorChangeBuilder::MotorChangeBuilder(MotorChangeBuilder&& o)
: m_calls(std::move(o.m_calls)) {}
MotorChangeBuilder::~MotorChangeBuilder() {}
MotorChangeBuilder& MotorChangeBuilder::power(MotorId id, int16_t value) {
m_calls.emplace_back(
std::move([=]() { Manager::get().motor(id).power(value); }));
return *this;
}
MotorChangeBuilder& MotorChangeBuilder::speed(
MotorId id, int16_t ticksPerSecond) {
m_calls.emplace_back(
std::move([=]() { Manager::get().motor(id).speed(ticksPerSecond); }));
return *this;
}
MotorChangeBuilder& MotorChangeBuilder::pwmMaxPercent(
MotorId id, int8_t percent) {
m_calls.emplace_back(
std::move([=]() { Manager::get().motor(id).pwmMaxPercent(percent); }));
return *this;
}
MotorChangeBuilder& MotorChangeBuilder::brake(
MotorId id, uint16_t brakingPower) {
m_calls.emplace_back(
std::move([=]() { Manager::get().motor(id).brake(brakingPower); }));
return *this;
}
MotorChangeBuilder& MotorChangeBuilder::drive(MotorId id,
int32_t positionRelative, int16_t speedTicksPerSecond,
Motor::callback_t callback) {
m_calls.emplace_back(std::move([=]() {
Manager::get().motor(id).drive(
positionRelative, speedTicksPerSecond, std::move(callback));
}));
return *this;
}
MotorChangeBuilder& MotorChangeBuilder::driveToValue(MotorId id,
int32_t positionAbsolute, int16_t speedTicksPerSecond,
Motor::callback_t callback) {
m_calls.emplace_back(std::move([=]() {
Manager::get().motor(id).driveToValue(
positionAbsolute, speedTicksPerSecond, std::move(callback));
}));
return *this;
}
void MotorChangeBuilder::set() {
for (const auto& c : m_calls) {
c();
}
m_calls.clear();
}
};