Skyward boardcore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
VNCommonSerial.cpp
Go to the documentation of this file.
1/* Copyright (c) 2024 Skyward Experimental Rocketry
2 * Authors: Matteo Pignataro, Lorenzo Cucchi, Fabrizio Monti
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "VNCommonSerial.h"
24
26#include <utils/Debug.h>
27
28namespace Boardcore
29{
30
32 const char* sensorName, CRCOptions crc,
33 const std::chrono::milliseconds timeout)
34 : usart(usart), baudRate(baudrate), crc(crc),
35 logger(Logging::getLogger(sensorName)), maxTimeout(timeout)
36{
37}
38
40
41uint8_t VNCommonSerial::calculateChecksum8(const uint8_t* message, int length)
42{
43 int i;
44 uint8_t result = 0x00;
45
46 // Iterate and XOR all of the elements
47 for (i = 0; i < length; i++)
48 {
49 //^ = XOR Operation
50 result ^= message[i];
51 }
52
53 return result;
54}
55
56uint16_t VNCommonSerial::calculateChecksum16(const uint8_t* message, int length)
57{
58 int i;
59 uint16_t result = 0x0000;
60
61 // Apply the datasheet definition of CRC16-CCITT
62 for (i = 0; i < length; i++)
63 {
64 result = (uint8_t)(result >> 8) | (result << 8);
65 result ^= message[i];
66 result ^= (uint8_t)(result & 0xff) >> 4;
67 result ^= result << 12;
68 result ^= (result & 0x00ff) << 5;
69 }
70
71 return result;
72}
73
74bool VNCommonSerial::verifyChecksum(char* command, int length)
75{
76 int checksumOffset = 0;
77
78 // I look for the checksum position
79 while (checksumOffset < length && command[checksumOffset] != '*')
80 checksumOffset++;
81
82 if (checksumOffset == length)
83 {
84 // The command doesn't have any checksum
85 TRACE("No checksum in the command!\n");
86 return false;
87 }
88
89 // Check based on the user selected crc type
91 {
92 if (length != checksumOffset + 5) // 4 hex chars + 1 of position
93 {
94 TRACE("16 bit Checksum wrong length: %d != %d --> %s\n", length,
95 checksumOffset + 5, command);
96 return false;
97 }
98
99 // Calculate the checksum and verify (comparison between numerical
100 // checksum to avoid string bugs e.g 0856 != 865)
101 if (strtol(command + checksumOffset + 1, NULL, 16) !=
102 calculateChecksum16((uint8_t*)(command + 1), checksumOffset - 1))
103 {
104 TRACE("Different checksum: %s\n", command);
105 return false;
106 }
107 }
108 else if (crc == CRCOptions::CRC_ENABLE_8)
109 {
110 if (length != checksumOffset + 3) // 2 hex chars + 1 of position
111 {
112 TRACE("8 bit Checksum wrong length: %d != %d --> %s\n", length,
113 checksumOffset + 3, command);
114 return false;
115 }
116
117 // Calculate the checksum and verify (comparison between numerical
118 // checksum to avoid string bugs e.g 0856 != 865)
119 if (strtol(command + checksumOffset + 1, NULL, 16) !=
120 calculateChecksum8((uint8_t*)(command + 1), checksumOffset - 1))
121 {
122 TRACE("Different checksum: %s\n", command);
123 return false;
124 }
125 }
126
127 return true;
128}
129
131{
132 // Command string
133 std::string command = "VNWRG,06,00";
134
135 // Clear the receiving queue
137
138 if (!sendStringCommand(command))
139 return false;
140
141 if (waitResponse)
142 {
144 return false;
145
146 if (checkErrorVN(recvString.data()))
147 return false;
148 }
149
150 return true;
151}
152
153bool VNCommonSerial::setCrc(bool waitResponse)
154{
155 // Command for the crc change
156 std::string command;
157 CRCOptions backup = crc;
158
159 // Check what type of crc is selected
161 {
162 // The 3 inside the command is the 16bit select. The others are default
163 // values
164 command = "VNWRG,30,0,0,0,0,3,0,1";
165 }
166 else
167 {
168 // Even if the CRC is not enabled i put the 8 bit
169 // checksum because i need to know how many 'X' add at the end
170 // of every command sent
171 command = "VNWRG,30,0,0,0,0,1,0,1";
172 }
173
174 // I need to send the command in both crc because i don't know what type
175 // of crc is previously selected. So in order to get the command accepted
176 // i need to do it two times with different crc.
178
179 // Send the command
180 if (!sendStringCommand(command))
181 return false;
182
183 // Read the answer
184 if (waitResponse)
185 {
187 return false;
188 }
189
191
192 // Send the command
193 if (!sendStringCommand(command))
194 return false;
195
196 // Read the answer
197 if (waitResponse)
198 {
200 return false;
201 }
202
203 // Restore the crc
204 crc = backup;
205
206 return true;
207}
208
210{
211 // Initial default settings
213}
214
216{
217 // I format the command to change baud rate
218 std::string command = fmt::format("{}{}", "VNWRG,5,", baudRate);
219
220 // I can send the command
221 if (!sendStringCommand(command))
222 return false;
223
224 // I can open the serial with user's baud rate
226
227 // Check correct serial init
228 return true;
229}
230
231bool VNCommonSerial::verifyModelNumber(const char* expectedModelNumber)
232{
233 // The model number starts from the 10th position
234 const int modelNumberOffset = 10;
235
236 // Clear the receiving queue
238
239 if (!sendStringCommand("VNRRG,01"))
240 {
241 LOG_WARN(logger, "Unable to send string command");
242 return false;
243 }
244
245 miosix::Thread::sleep(100);
246
248 {
249 LOG_WARN(logger, "Unable to receive string command");
250 return false;
251 }
252
253 if (strncmp(expectedModelNumber, recvString.data() + modelNumberOffset,
254 strlen(expectedModelNumber)) != 0)
255 {
256 LOG_ERR(logger, "Model number not corresponding: {} != {}",
257 recvString.data(), expectedModelNumber);
258 return false;
259 }
260
262 {
263 LOG_ERR(logger, "Checksum verification failed: {}", recvString.data());
264 return false;
265 }
266
267 return true;
268}
269
270uint8_t VNCommonSerial::checkErrorVN(const char* message)
271{
272 if (strncmp(message, "$VNERR,", 7) == 0)
273 {
274 // Extract the error code
275 int errorCode = atoi(&message[7]);
276 return errorCode; // Error detected
277 }
278
279 return 0; // No error detected
280}
281
283{
284 // Send the reset command to the VN300
285 if (!sendStringCommand("VNRST"))
286 {
287 LOG_WARN(logger, "Impossible to reset the VN300");
288 return false;
289 }
290
291 isInit = false;
292
293 return true;
294}
295
296bool VNCommonSerial::startHSIEstimator(uint8_t convergeRate)
297{
298 D(assert((convergeRate >= 1 && convergeRate <= 5) &&
299 "convergeRate must be between 1 and 5"));
300
301 // Select HSI_RUN, runs the real-time hard/soft
302 // iron calibration.
303 const uint8_t hsiMode = 1;
304
305 // Select USE_ONBOARD, Onboard HSI is applied to
306 // the magnetic measurements.
307 const uint8_t hsiOutput = 3;
308
309 std::string command =
310 fmt::format("VNWRG,44,{},{},{}", hsiMode, hsiOutput, convergeRate);
311
312 return writeRegister(command);
313}
314
316{
317 // Select HSI_OFF, real-time hard/soft iron calibration
318 // algorithm is turned off.
319 const uint8_t hsiMode = 0;
320
321 // Keep USE_ONBOARD, Onboard HSI is applied to
322 // the magnetic measurements.
323 const uint8_t hsiOutput = 3;
324
325 // Not influent, we are stopping the algorithm
326 const uint8_t convergeRate = 5;
327
328 std::string command =
329 fmt::format("VNWRG,44,{},{},{}", hsiMode, hsiOutput, convergeRate);
330
331 return writeRegister(command);
332}
333
335{
336 // Clear the receiving queue
338
339 if (!sendStringCommand("VNRRG,47"))
340 {
341 LOG_ERR(logger, "getHSIEstimatorValues: unable to send string command");
342 return "";
343 }
344
345 miosix::Thread::sleep(100);
346
348 {
350 "getHSIEstimatorValues: unable to receive string command");
351 return "";
352 }
353
355 {
357 "getHSIEstimatorValues: checksum verification failed: {}",
358 recvString.data());
359 return "";
360 }
361
362 return recvString.data();
363}
364
366 const Eigen::Vector3f& b)
367{
368 std::string command =
369 fmt::format("VNWRG,23,{},{},{},{},{},{},{},{},{},{},{},{}", c(0, 0),
370 c(0, 1), c(0, 2), c(1, 0), c(1, 1), c(1, 2), c(2, 0),
371 c(2, 1), c(2, 2), b(0), b(1), b(2));
372
373 return writeRegister(command);
374}
375
377 const Eigen::Vector3f& b)
378{
379 std::string command =
380 fmt::format("VNWRG,25,{},{},{},{},{},{},{},{},{},{},{},{}", c(0, 0),
381 c(0, 1), c(0, 2), c(1, 0), c(1, 1), c(1, 2), c(2, 0),
382 c(2, 1), c(2, 2), b(0), b(1), b(2));
383
384 return writeRegister(command);
385}
386
387bool VNCommonSerial::setGyroscopeCompensation(const Eigen::Matrix3f& c,
388 const Eigen::Vector3f& b)
389{
390 std::string command =
391 fmt::format("VNWRG,84,{},{},{},{},{},{},{},{},{},{},{},{}", c(0, 0),
392 c(0, 1), c(0, 2), c(1, 0), c(1, 1), c(1, 2), c(2, 0),
393 c(2, 1), c(2, 2), b(0), b(1), b(2));
394
395 return writeRegister(command);
396}
397
399{
400 // Clear the receiving queue
402
403 if (!sendStringCommand("VNWNV"))
404 {
405 LOG_ERR(logger, "saveConfiguration: unable to send string command");
406 return false;
407 }
408
409 // The write settings command takes ~500ms to complete
410 miosix::Thread::sleep(500);
411
413 {
414 LOG_WARN(logger, "saveConfiguration: unable to receive string command");
415 return false;
416 }
417
419 {
420 LOG_ERR(logger, "saveConfiguration: checksum verification failed: {}",
421 recvString.data());
422 return false;
423 }
424
425 return true;
426}
427
429{
430 // Clear the receiving queue
432
433 if (!sendStringCommand("VNRFS"))
434 {
436 "restoreFactorySettings: unable to send string command");
437 return false;
438 }
439
440 miosix::Thread::sleep(100);
441
443 {
445 "restoreFactorySettings: unable to receive string command");
446 return false;
447 }
448
450 {
452 "restoreFactorySettings: checksum verification failed: {}",
453 recvString.data());
454 return false;
455 }
456
457 // Everything is fine, let the sensor restart
458 miosix::Thread::sleep(2000);
459
460 return true;
461}
462
463bool VNCommonSerial::sendStringCommand(std::string command)
464{
466 {
467 char checksum[4]; // 2 hex + \n + \0
468 // I convert the calculated checksum in hex using itoa
469 itoa(calculateChecksum8((uint8_t*)command.c_str(), command.length()),
470 checksum, 16);
471 checksum[2] = '\n';
472 checksum[3] = '\0';
473 // I concatenate
474 command = fmt::format("{}{}{}{}", "$", command, "*", checksum);
475 }
476 else if (crc == CRCOptions::CRC_ENABLE_16)
477 {
478 char checksum[6]; // 4 hex + \n + \0
479 // I convert the calculated checksum in hex using itoa
480 itoa(calculateChecksum16((uint8_t*)command.c_str(), command.length()),
481 checksum, 16);
482 checksum[4] = '\n';
483 checksum[5] = '\0';
484 // I concatenate
485 command = fmt::format("{}{}{}{}", "$", command, "*", checksum);
486 }
487 else
488 {
489 // No checksum, i add only 'XX' at the end and not 'XXXX' because
490 // in cas of CRC_NO the enabled crc is 8 bit
491 command = fmt::format("{}{}{}", "$", command, "*XX\n");
492 }
493
494 // I send the final command
495 usart.writeString(command.c_str());
496
505 float waitTime = command.size() * 8000;
506 waitTime /= baudRate;
507 waitTime = ceil(waitTime);
508 waitTime += 10;
509 miosix::Thread::sleep(waitTime);
510
511 return true;
512}
513
514bool VNCommonSerial::recvStringCommand(char* command, int maxLength)
515{
516 int i = 0;
517 // Read the buffer
518 if (!usart.readBlocking(command, maxLength, maxTimeout))
519 return false;
520
521 // Iterate until i reach the end or i find \n then i substitute it with a \0
522 while (i < maxLength && command[i] != '\n')
523 i++;
524
525 // Terminate the string
526 command[i] = '\0';
527
528 // Assing the length
529 recvStringLength = i - 1;
530
531 return true;
532}
533
534bool VNCommonSerial::writeRegister(const std::string& command)
535{
537 if (!sendStringCommand(command))
538 {
539 LOG_ERR(logger, "Write register failed: could not send the command");
540 return false;
541 }
542
544 {
545 LOG_ERR(logger, "Write register failed: recvStringCommand() failed");
546 return false;
547 }
548
549 if (checkErrorVN(recvString.data()))
550 {
551 LOG_ERR(logger, "Write register failed: {}", recvString.data());
552 return false;
553 }
554
555 return true;
556}
557
558} // namespace Boardcore
#define TRACE(...)
Definition Debug.h:58
#define D(x)
Definition Debug.h:57
#define LOG_WARN(logger,...)
#define LOG_ERR(logger,...)
Driver for STM32F4 low level USART/UART peripheral.
Definition USART.h:170
void writeString(const char *buffer)
Write a string to the serial, comprising the '\0' character.
Definition USART.cpp:564
void clearQueue()
Clears the rxQueue.
Definition USART.cpp:616
void setBaudrate(int baudrate)
Set the baudrate in the BRR register.
Definition USART.cpp:447
virtual bool readBlocking(void *buffer, size_t nBytes, std::chrono::nanoseconds timeout=std::chrono::nanoseconds::zero())
Blocking read operation to read nBytes until the data transfer is complete or the timeout is reached.
Definition USART.h:91
uint8_t checkErrorVN(const char *message)
Check if the message received from the sensor contains an error.
uint16_t calculateChecksum16(const uint8_t *message, int length)
Calculate the 16bit array on the given array.
bool saveConfiguration()
Write the current register settings into non-volatile memory. Once the settings are stored in non-vol...
bool setCrc(bool waitResponse=true)
Sets the user selected crc method.
bool setMagnetometerCompensation(const Eigen::Matrix3f &c, const Eigen::Vector3f &b)
Set custom compensation parameters for the magnetometer.
bool startHSIEstimator(uint8_t convergeRate)
Start the real-time hard/soft iron calibration. The algorithm will continue until stopHSIEstimator() ...
VNCommonSerial(USART &usart, int baudrate, const char *sensorName, CRCOptions crc, const std::chrono::milliseconds timeout)
Constructor.
static const uint8_t recvStringMaxDimension
Maximum size of the receiving string.
bool verifyModelNumber(const char *expectedModelNumber)
Verify the model number of the sensor.
bool stopHSIEstimator()
Real-time hard/soft iron calibration algorithm is turned off.
bool disableAsyncMessages(bool waitResponse)
Disables the async messages that the sensor is default configured to send at 40Hz on startup.
static const int DEFAULT_BAUDRATE
Default baudrate value for the usart communication.
bool closeAndReset()
Method to reset the sensor to default values and to close the connection. Used if you need to close a...
std::array< char, recvStringMaxDimension > recvString
Buffer used to store the string received from the sensor.
bool restoreFactorySettings()
Restore the VN module’s factory default settings and reset the module.
bool recvStringCommand(char *command, int maxLength)
Receives a command from the sensor but swaps the first with a \0 to close the message.
uint8_t recvStringLength
Actual strlen() of the recvString.
bool setGyroscopeCompensation(const Eigen::Matrix3f &c, const Eigen::Vector3f &b)
Set custom compensation parameters for the gyroscope.
bool configUserSerialPort()
Configures the user defined serial communication.
void configDefaultSerialPort()
Configures the default serial communication.
bool verifyChecksum(char *command, int maxLength)
Method to verify the crc validity of a command.
bool sendStringCommand(std::string command)
Sends the command to the sensor with the correct checksum added so '*' symbol is not needed at the en...
bool writeRegister(const std::string &command)
Utility function used to set a register on the sensor. This function relies on sendStringCommand: DO ...
USART & usart
Serial interface that is needed to communicate with the sensor via ASCII codes.
bool setAccelerometerCompensation(const Eigen::Matrix3f &c, const Eigen::Vector3f &b)
Set custom compensation parameters for the accelerometer.
uint8_t calculateChecksum8(const uint8_t *message, int length)
Calculate the 8bit checksum on the given array.
Driver for the VN100S IMU.