diff --git a/src/shared/sensors/Vectornav/VN100/VN100Serial.cpp b/src/shared/sensors/Vectornav/VN100/VN100Serial.cpp index 70f7eaa3dfd73642c744f4cf72459930fd2f3b3c..2b98abb17df9ca7d372ebc3f4fd3d6f9b17c68fe 100644 --- a/src/shared/sensors/Vectornav/VN100/VN100Serial.cpp +++ b/src/shared/sensors/Vectornav/VN100/VN100Serial.cpp @@ -1,5 +1,5 @@ /* Copyright (c) 2021 Skyward Experimental Rocketry - * Author: Matteo Pignataro + * Author: Matteo Pignataro, Fabrizio Monti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,14 +23,13 @@ #include "VN100Serial.h" #include <drivers/timer/TimestampTimer.h> -#include <utils/KernelTime.h> namespace Boardcore { -VN100Serial::VN100Serial(USART& usart, int baudRate, CRCOptions crc, - uint16_t samplePeriod) - : usart(usart), baudRate(baudRate), samplePeriod(samplePeriod), crc(crc) +VN100Serial::VN100Serial(USART& usart, int baud, CRCOptions crc, + std::chrono::milliseconds timeout) + : VNCommonSerial(usart, baud, "vn100-serial", crc, timeout) { } @@ -42,56 +41,58 @@ bool VN100Serial::init() if (isInit) { lastError = SensorErrors::ALREADY_INIT; - LOG_WARN(logger, "Sensor vn100 already initilized"); + LOG_WARN(logger, "Sensor vn100 already initialized"); return true; } - // Allocate the receive vector - recvString = new char[recvStringMaxDimension]; - // Allocate the pre loaded strings based on the user selected crc - if (crc == CRCOptions::CRC_ENABLE_16) - { - preSampleImuString = new string("$VNRRG,15*92EA\n"); - preSampleTempPressString = new string("$VNRRG,54*4E0F\n"); - } - else + switch (this->crc) { - preSampleImuString = new string("$VNRRG,15*77\n"); - preSampleTempPressString = new string("$VNRRG,54*72\n"); + case CRCOptions::CRC_ENABLE_8: + askSampleCommand = "$VNBOM,1*45\n"; + break; + case CRCOptions::CRC_ENABLE_16: + askSampleCommand = "$VNBOM,1*749D\n"; + break; + case CRCOptions::CRC_NO: + askSampleCommand = "$VNBOM,1*XX\n"; + break; } // Set the error to init fail and if the init process goes without problem // i restore it to the last error lastError = SensorErrors::INIT_FAIL; - if (recvString == NULL) + configDefaultSerialPort(); + + if (!setCrc(false)) { - LOG_ERR(logger, "Unable to initialize the receive vn100 string"); + LOG_ERR(logger, "Unable to set the vn100 user selected CRC"); return false; } - if (!configDefaultSerialPort()) + if (!disableAsyncMessages(false)) { - LOG_ERR(logger, "Unable to config the default vn100 serial port"); + LOG_ERR(logger, "Unable to disable async messages from vn100"); return false; } - if (!setCrc(false)) + if (!configUserSerialPort()) { - LOG_ERR(logger, "Unable to set the vn100 user selected CRC"); + LOG_ERR(logger, "Unable to config the user vn100 serial port"); return false; } - if (!disableAsyncMessages(false)) + if (!verifyModelNumber("VN-100")) { - LOG_ERR(logger, "Unable to disable async messages from vn100"); + LOG_ERR(logger, "Error, model number not corresponding"); + lastError = INVALID_WHOAMI; return false; } - if (!configUserSerialPort()) + if (!setBinaryOutput()) { - LOG_ERR(logger, "Unable to config the user vn100 serial port"); + LOG_ERR(logger, "Unable to set binary output register"); return false; } @@ -118,614 +119,134 @@ bool VN100Serial::init() return true; } -void VN100Serial::run() -{ - while (!shouldStop()) - { - long long initialTime = Kernel::getOldTick(); - { - // Sample the data locking the mutex - miosix::Lock<FastMutex> l(mutex); - threadSample = sampleData(); - } - // Sleep for the sampling period - Kernel::Thread::sleepUntil(initialTime + samplePeriod); - } -} - -bool VN100Serial::sampleRaw() -{ - // Sensor not init - if (!isInit) - { - lastError = SensorErrors::NOT_INIT; - LOG_WARN(logger, - "Unable to sample due to not initialized vn100 sensor"); - return false; - } - - // Send the IMU sampling command - usart.writeString(preSampleImuString->c_str()); - - // Wait some time - // TODO dimension the time - miosix::Thread::sleep(1); - - // Receive the string - if (!recvStringCommand(recvString, recvStringMaxDimension)) - { - LOG_WARN(logger, "Unable to sample due to serial communication error"); - return false; - } - - return true; -} - -string VN100Serial::getLastRawSample() -{ - // If not init i return the void string - if (!isInit) - return string(""); - - return string(recvString, recvStringLength); -} - -bool VN100Serial::closeAndReset() -{ - // Sensor not init - if (!isInit) - { - lastError = SensorErrors::NOT_INIT; - LOG_WARN(logger, "Sensor vn100 already not initilized"); - return true; - } - - // Send the reset command to the vn100 - if (!sendStringCommand("VNRST")) - { - LOG_WARN(logger, "Impossible to reset the vn100"); - return false; - } - - isInit = false; - - // Free the recvString memory - delete recvString; - - return true; -} - -bool VN100Serial::selfTest() -{ - if (!selfTestImpl()) - { - lastError = SensorErrors::SELF_TEST_FAIL; - LOG_WARN(logger, "Unable to perform a successful vn100 self test"); - return false; - } - - return true; -} +bool VN100Serial::selfTest() { return true; } VN100SerialData VN100Serial::sampleImpl() { - miosix::Lock<FastMutex> l(mutex); - return threadSample; -} - -VN100SerialData VN100Serial::sampleData() -{ - if (!isInit) - { - lastError = SensorErrors::NOT_INIT; - LOG_WARN(logger, - "Unable to sample due to not initialized vn100 sensor"); - return lastSample; - } - - // Before sampling i check for errors - if (lastError != SensorErrors::NO_ERRORS) - return lastSample; + VN100SerialData data; + BinaryData binData; - // Returns Quaternion, Magnetometer, Accelerometer and Gyro - usart.writeString(preSampleImuString->c_str()); + const uint64_t timestamp = TimestampTimer::getTimestamp(); - // Wait some time - // TODO dimension the time - miosix::Thread::sleep(1); + bool sampleOutcome = + false; // True if a valid sample was retrieved from the sensor - if (!recvStringCommand(recvString, recvStringMaxDimension)) - { - // If something goes wrong i return the last sampled data - return lastSample; - } - - if (!verifyChecksum(recvString, recvStringLength)) - { - LOG_WARN(logger, "Vn100 sampling message invalid checksum"); - // If something goes wrong i return the last sampled data - return lastSample; - } + sampleOutcome = getBinaryOutput<BinaryData>(binData, askSampleCommand); + if (!sampleOutcome) + lastError = NO_NEW_DATA; - // Now i have to parse the data - QuaternionData quat = sampleQuaternion(); - MagnetometerData mag = sampleMagnetometer(); - AccelerometerData acc = sampleAccelerometer(); - GyroscopeData gyro = sampleGyroscope(); + // With binary output the checksum is always calculated with checksum16 + bool validChecksum = + (crc == CRCOptions::CRC_NO) || + (calculateChecksum16(reinterpret_cast<uint8_t*>(&binData), + sizeof(binData)) == 0); - // Returns Magnetometer, Accelerometer, Gyroscope, Temperature and Pressure - // (UNCOMPENSATED) DO NOT USE THESE MAGNETOMETER, ACCELEROMETER AND - // GYROSCOPE VALUES - usart.writeString(preSampleTempPressString->c_str()); + if (sampleOutcome && !validChecksum) + lastError = SensorErrors::BUS_FAULT; - // Wait some time - // TODO dimension the time - miosix::Thread::sleep(1); + // Verify if the sample is valid + sampleOutcome = sampleOutcome && validChecksum; - if (!recvStringCommand(recvString, recvStringMaxDimension)) + if (sampleOutcome) { - // If something goes wrong i return the last sampled data - return lastSample; + buildBinaryData(binData, data, timestamp); + return data; } - - if (!verifyChecksum(recvString, recvStringLength)) + else { - LOG_WARN(logger, "Vn100 sampling message invalid checksum"); - // If something goes wrong i return the last sampled data + // Last error is already set return lastSample; } - - // Parse the data - TemperatureData temp = sampleTemperature(); - PressureData press = samplePressure(); - - return VN100SerialData(quat, mag, acc, gyro, temp, press); } -bool VN100Serial::disableAsyncMessages(bool waitResponse) +void VN100Serial::buildBinaryData(const BinaryData& binData, + VN100SerialData& data, + const uint64_t sampleTimestamp) { - // Command string - std::string command = - "VNWRG,06,00"; // Put 0 in register number 6 (ASYNC Config) - - // Send the command - if (!sendStringCommand(command)) - return false; - - // Read the answer - if (waitResponse) - recvStringCommand(recvString, recvStringMaxDimension); - - return true; -} - -bool VN100Serial::configDefaultSerialPort() -{ - // Initial default settings - usart.setBaudrate(115200); - - // Check correct serial init - return true; -} - -/** - * Even if the user configured baudrate is the default, I want to reset the - * buffer to clean the junk. - */ -bool VN100Serial::configUserSerialPort() -{ - std::string command; - - // I format the command to change baud rate - command = fmt::format("{}{}", "VNWRG,5,", baudRate); - - // I can send the command - if (!sendStringCommand(command)) - return false; - - // I can open the serial with user's baud rate - usart.setBaudrate(baudRate); - - // Check correct serial init - return true; -} - -bool VN100Serial::setCrc(bool waitResponse) -{ - // Command for the crc change - std::string command; - CRCOptions backup = crc; - - // Check what type of crc is selected - if (crc == CRCOptions::CRC_ENABLE_16) - { - // The 3 inside the command is the 16bit select. The others are default - // values - command = "VNWRG,30,0,0,0,0,3,0,1"; - } - else - { - // Even if the CRC is not enabled i put the 8 bit - // checksum because i need to know how many 'X' add at the end - // of every command sent - command = "VNWRG,30,0,0,0,0,1,0,1"; - } - - // I need to send the command in both crc because i don't know what type - // of crc is previously selected. So in order to get the command accepted - // i need to do it two times with different crc. - crc = CRCOptions::CRC_ENABLE_8; + // Acceleration data + data.accelerationTimestamp = sampleTimestamp; + data.accelerationX = binData.accelerationX; + data.accelerationY = binData.accelerationY; + data.accelerationZ = binData.accelerationZ; - // Send the command - if (!sendStringCommand(command)) - return false; + // Angular speed data + data.angularSpeedTimestamp = sampleTimestamp; + data.angularSpeedX = binData.angularX; + data.angularSpeedY = binData.angularY; + data.angularSpeedZ = binData.angularZ; - // Read the answer - if (waitResponse) - recvStringCommand(recvString, recvStringMaxDimension); + // Magnetometer data + data.magneticFieldTimestamp = sampleTimestamp; + data.magneticFieldX = binData.magneticFieldX; + data.magneticFieldY = binData.magneticFieldY; + data.magneticFieldZ = binData.magneticFieldZ; - crc = CRCOptions::CRC_ENABLE_16; + // Quaternion data + data.quaternionTimestamp = sampleTimestamp; + data.quaternionX = binData.quaternionX; + data.quaternionY = binData.quaternionY; + data.quaternionZ = binData.quaternionZ; + data.quaternionW = binData.quaternionW; - // Send the command - if (!sendStringCommand(command)) - return false; + // Temperature data + data.temperatureTimestamp = sampleTimestamp; + data.temperature = binData.temperature; - // Read the answer - if (waitResponse) - recvStringCommand(recvString, recvStringMaxDimension); - - // Restore the crc - crc = backup; - - return true; + // Pressure data + data.pressureTimestamp = sampleTimestamp; + data.pressure = binData.pressure; } -bool VN100Serial::selfTestImpl() +bool VN100Serial::setBinaryOutput() { - char modelNumber[] = "VN-100"; - const int modelNumberOffset = 10; + /** + * This command samples quaternion data, accelerometer, angular rate, + * magnetometer, temperature, and pressure (with asynchronous messages + * disabled). + * + * Refer to the datasheet to modify the sampled data. Note that + * changing the sampled data also requires updating VN100SerialData + * and BinaryData. + */ - // Check the init status - if (!isInit) + const char* setBinarySampleCommand = ""; + switch (this->crc) { - lastError = SensorErrors::NOT_INIT; - LOG_WARN( - logger, - "Unable to perform vn100 self test due to not initialized sensor"); - return false; + case CRCOptions::CRC_ENABLE_8: + setBinarySampleCommand = "$VNWRG,75,0,16,01,0530*44\n"; + break; + case CRCOptions::CRC_ENABLE_16: + setBinarySampleCommand = "$VNWRG,75,0,16,01,0530*9CFA\n"; + break; + case CRCOptions::CRC_NO: + setBinarySampleCommand = "$VNWRG,75,0,16,01,0530*XX\n"; + break; } - // removing junk usart.clearQueue(); - // I check the model number - if (!sendStringCommand("VNRRG,01")) + if (!sendStringCommand(setBinarySampleCommand)) { - LOG_WARN(logger, "Unable to send string command"); - return false; - } - - miosix::Thread::sleep(100); - - if (!recvStringCommand(recvString, recvStringMaxDimension)) - { - LOG_WARN(logger, "Unable to receive string command"); + LOG_WARN(logger, + "sendStringCommand() failed, cannot set binary output"); return false; } - // Now i check that the model number is VN-100 starting from the 10th - // position because of the message structure - if (strncmp(modelNumber, recvString + modelNumberOffset, - strlen(modelNumber)) != 0) + if (!recvStringCommand(recvString.data(), recvStringMaxDimension)) { - LOG_ERR(logger, "VN-100 not corresponding: {} != {}", recvString, - modelNumber); + LOG_WARN(logger, + "recvStringCommand() failed, cannot set binary output"); return false; } - // I check the checksum - if (!verifyChecksum(recvString, recvStringLength)) + if (checkErrorVN(recvString.data()) != 0) { - LOG_ERR(logger, "Checksum verification failed: {}", recvString); + LOG_WARN(logger, "Error while setting binary output: {}", + recvString.data()); return false; } return true; } -QuaternionData VN100Serial::sampleQuaternion() -{ - unsigned int indexStart = 0; - char* nextNumber; - QuaternionData data; - - // Look for the second ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 2; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.quaternionTimestamp = TimestampTimer::getTimestamp(); - data.quaternionX = strtod(recvString + indexStart + 1, &nextNumber); - data.quaternionY = strtod(nextNumber + 1, &nextNumber); - data.quaternionZ = strtod(nextNumber + 1, &nextNumber); - data.quaternionW = strtod(nextNumber + 1, NULL); - - return data; -} - -MagnetometerData VN100Serial::sampleMagnetometer() -{ - unsigned int indexStart = 0; - char* nextNumber; - MagnetometerData data; - - // Look for the sixth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 6; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.magneticFieldTimestamp = TimestampTimer::getTimestamp(); - data.magneticFieldX = strtod(recvString + indexStart + 1, &nextNumber); - data.magneticFieldY = strtod(nextNumber + 1, &nextNumber); - data.magneticFieldZ = strtod(nextNumber + 1, NULL); - - return data; -} - -AccelerometerData VN100Serial::sampleAccelerometer() -{ - unsigned int indexStart = 0; - char* nextNumber; - AccelerometerData data; - - // Look for the ninth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 9; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.accelerationTimestamp = TimestampTimer::getTimestamp(); - data.accelerationX = strtod(recvString + indexStart + 1, &nextNumber); - data.accelerationY = strtod(nextNumber + 1, &nextNumber); - data.accelerationZ = strtod(nextNumber + 1, NULL); - - return data; -} - -GyroscopeData VN100Serial::sampleGyroscope() -{ - unsigned int indexStart = 0; - char* nextNumber; - GyroscopeData data; - - // Look for the twelfth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 12; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.angularSpeedTimestamp = TimestampTimer::getTimestamp(); - data.angularSpeedX = strtod(recvString + indexStart + 1, &nextNumber); - data.angularSpeedY = strtod(nextNumber + 1, &nextNumber); - data.angularSpeedZ = strtod(nextNumber + 1, NULL); - - return data; -} - -TemperatureData VN100Serial::sampleTemperature() -{ - unsigned int indexStart = 0; - TemperatureData data; - - // Look for the eleventh ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 11; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.temperatureTimestamp = TimestampTimer::getTimestamp(); - data.temperature = strtod(recvString + indexStart + 1, NULL); - - return data; -} - -PressureData VN100Serial::samplePressure() -{ - unsigned int indexStart = 0; - PressureData data; - - // Look for the twelfth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 12; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.pressureTimestamp = TimestampTimer::getTimestamp(); - data.pressure = strtod(recvString + indexStart + 1, NULL); - - return data; -} - -bool VN100Serial::sendStringCommand(std::string command) -{ - if (crc == CRCOptions::CRC_ENABLE_8) - { - char checksum[4]; // 2 hex + \n + \0 - // I convert the calculated checksum in hex using itoa - itoa(calculateChecksum8((uint8_t*)command.c_str(), command.length()), - checksum, 16); - checksum[2] = '\n'; - checksum[3] = '\0'; - // I concatenate - command = fmt::format("{}{}{}{}", "$", command, "*", checksum); - } - else if (crc == CRCOptions::CRC_ENABLE_16) - { - char checksum[6]; // 4 hex + \n + \0 - // I convert the calculated checksum in hex using itoa - itoa(calculateChecksum16((uint8_t*)command.c_str(), command.length()), - checksum, 16); - checksum[4] = '\n'; - checksum[5] = '\0'; - // I concatenate - command = fmt::format("{}{}{}{}", "$", command, "*", checksum); - } - else - { - // No checksum, i add only 'XX' at the end and not 'XXXX' because - // in cas of CRC_NO the enabled crc is 8 bit - command = fmt::format("{}{}{}", "$", command, "*XX\n"); - } - - // I send the final command - usart.writeString(command.c_str()); - - // Wait some time - // TODO dimension the time - miosix::Thread::sleep(500); - - return true; -} - -bool VN100Serial::recvStringCommand(char* command, int maxLength) -{ - int i = 0; - // Read the buffer - if (!usart.readBlocking(command, maxLength)) - return false; - - // Iterate until i reach the end or i find \n then i substitute it with a \0 - while (i < maxLength && command[i] != '\n') - i++; - - // Terminate the string - command[i] = '\0'; - - // Assing the length - recvStringLength = i - 1; - - return true; -} - -bool VN100Serial::verifyChecksum(char* command, int length) -{ - int checksumOffset = 0; - - // I look for the checksum position - while (checksumOffset < length && command[checksumOffset] != '*') - checksumOffset++; - - if (checksumOffset == length) - { - // The command doesn't have any checksum - TRACE("No checksum in the command!\n"); - return false; - } - - // Check based on the user selected crc type - if (crc == CRCOptions::CRC_ENABLE_16) - { - if (length != checksumOffset + 5) // 4 hex chars + 1 of position - { - TRACE("16 bit Checksum wrong length: %d != %d --> %s\n", length, - checksumOffset + 5, command); - return false; - } - - // Calculate the checksum and verify (comparison between numerical - // checksum to avoid string bugs e.g 0856 != 865) - if (strtol(command + checksumOffset + 1, NULL, 16) != - calculateChecksum16((uint8_t*)(command + 1), checksumOffset - 1)) - { - TRACE("Different checksum: %s\n", command); - return false; - } - } - else if (crc == CRCOptions::CRC_ENABLE_8) - { - if (length != checksumOffset + 3) // 2 hex chars + 1 of position - { - TRACE("8 bit Checksum wrong length: %d != %d --> %s\n", length, - checksumOffset + 3, command); - return false; - } - - // Calculate the checksum and verify (comparison between numerical - // checksum to avoid string bugs e.g 0856 != 865) - if (strtol(command + checksumOffset + 1, NULL, 16) != - calculateChecksum8((uint8_t*)(command + 1), checksumOffset - 1)) - { - TRACE("Different checksum: %s\n", command); - return false; - } - } - - return true; -} - -uint8_t VN100Serial::calculateChecksum8(uint8_t* message, int length) -{ - int i; - uint8_t result = 0x00; - - // Iterate and XOR all of the elements - for (i = 0; i < length; i++) - { - //^ = XOR Operation - result ^= message[i]; - } - - return result; -} - -uint16_t VN100Serial::calculateChecksum16(uint8_t* message, int length) -{ - int i; - uint16_t result = 0x0000; - - // Apply the datasheet definition of CRC16-CCITT - for (i = 0; i < length; i++) - { - result = (uint8_t)(result >> 8) | (result << 8); - result ^= message[i]; - result ^= (uint8_t)(result & 0xff) >> 4; - result ^= result << 12; - result ^= (result & 0x00ff) << 5; - } - - return result; -} - } // namespace Boardcore diff --git a/src/shared/sensors/Vectornav/VN100/VN100Serial.h b/src/shared/sensors/Vectornav/VN100/VN100Serial.h index 0c1702cf3cff508a9c72c0e1b0478a663b7ceb51..83587ea16c3cd60d73b9fa482a24ad15bd90d183 100644 --- a/src/shared/sensors/Vectornav/VN100/VN100Serial.h +++ b/src/shared/sensors/Vectornav/VN100/VN100Serial.h @@ -1,5 +1,5 @@ /* Copyright (c) 2021 Skyward Experimental Rocketry - * Author: Matteo Pignataro + * Author: Matteo Pignataro, Fabrizio Monti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,41 +25,22 @@ /** * @brief Driver for the VN100S IMU. * - * The VN100S sensor is a calibrated IMU which includes accelerometer, - * magnetometer, gyroscope, barometer and temperature sensor. The device - * provides also a calibration matrix and an anti-drift matrix for the gyroscope - * values. The goal of this driver though is to interface the sensor in its - * basic use. Things like asynchronous data and anti-drift techniques haven't - * been implemented yet. The driver is intended to be used with the "Rugged - * sensor" version (aka only UART communication) although the actual VN100S chip - * is capable also of SPI communication. + * The VN100 sensor is a calibrated IMU which includes accelerometer, + * magnetometer, gyroscope, barometer and temperature sensor. It also provides + * attitude data (yaw, pith, roll, quaternion). The device provides also a + * calibration matrix and an anti-drift matrix for the gyroscope values. * - * The VN100S supports both binary and ASCII encoding for communication but via - * serial and with the asynchronous mode disabled only ASCII is available. The - * protocol also provides two algorithms to verify the integrity of the messages - * (8 bit checksum and 16 bit CRC-CCITT) both selectable by the user using the - * constructor method. The serial communication also can be established with - * various baud rates: - * - 9600 - * - 19200 - * - 38400 - * - 57600 - * - 115200 - * - 128000 - * - 230400 - * - 460800 - * - 921600 + * This driver samples IMU compensated data (accelerometer, gyroscope and + * magnetometer), quaternion data, temperature and pressure data. + * The sampling rate is 400Hz. + * + * This driver only supports binary encoding for communication. */ -#include <ActiveObject.h> -#include <diagnostic/PrintLogger.h> -#include <fmt/format.h> #include <sensors/Sensor.h> -#include <string.h> -#include <utils/Debug.h> +#include <sensors/Vectornav/VNCommonSerial.h> #include "VN100SerialData.h" -#include "drivers/usart/USART.h" namespace Boardcore { @@ -67,53 +48,24 @@ namespace Boardcore /** * @brief Driver class for VN100 IMU. */ -class VN100Serial : public Sensor<VN100SerialData>, public ActiveObject +class VN100Serial : public Sensor<VN100SerialData>, public VNCommonSerial { public: - enum class CRCOptions : uint8_t - { - CRC_NO = 0x00, - CRC_ENABLE_8 = 0x08, - CRC_ENABLE_16 = 0x10 - }; - /** * @brief Constructor. * * @param usart Serial bus used for the sensor. * @param BaudRate different from the sensor's default [9600, 19200, 38400, * 57600, 115200, 128000, 230400, 460800, 921600]. - * @param Redundancy check option. - * @param samplePeriod Sampling period in ms + * @param crc Checksum option. + * @param timeout The maximum time that will be waited when reading from the + * sensor. */ - VN100Serial(USART& usart, int baudrate, - CRCOptions crc = CRCOptions::CRC_ENABLE_8, - uint16_t samplePeriod = 20); + VN100Serial(USART& usart, int baudrate, CRCOptions crc, + std::chrono::milliseconds timeout); bool init() override; - /** - * @brief Method to sample the raw data without parsing. - * - * @return True if operation succeeded. - */ - bool sampleRaw(); - - /** - * @brief Method to get the raw sample. - * - * @return String that represents the sample. - */ - string getLastRawSample(); - - /** - * @brief Method to reset the sensor to default values and to close - * the connection. Used if you need to close and re initialize the sensor. - * - * @return True if operation succeeded. - */ - bool closeAndReset(); - bool selfTest() override; protected: @@ -124,162 +76,51 @@ protected: private: /** - * @brief Active object method, about the thread execution - */ - void run() override; - - /** - * @brief Sampling method used by the thread - * - * @return VN100Data The sampled data - */ - VN100SerialData sampleData(); - - /** - * @brief Disables the async messages that the vn100 is default configured - * to send at 40Hz on startup. - * - * @param waitResponse If true wait for a serial response. - * - * @return True if operation succeeded. - */ - bool disableAsyncMessages(bool waitResponse = true); - - /** - * @brief Configures the default serial communication. - * - * @return True if operation succeeded. - */ - bool configDefaultSerialPort(); - - /** - * @brief Configures the user defined serial communication. - * - * @return True if operation succeeded. - */ - bool configUserSerialPort(); - - /** - * @brief Sets the user selected crc method. - * - * @param waitResponse If true wait for a serial response. - * - * @return True if operation succeeded. - */ - bool setCrc(bool waitResponse = true); - - /** - * @brief Method implementation of self test. - * - * @return True if operation succeeded. - */ - bool selfTestImpl(); - - QuaternionData sampleQuaternion(); - - MagnetometerData sampleMagnetometer(); - - AccelerometerData sampleAccelerometer(); - - GyroscopeData sampleGyroscope(); - - TemperatureData sampleTemperature(); - - PressureData samplePressure(); - - /** - * @brief Sends the command to the sensor with the correct checksum added - * so '*' symbol is not needed at the end of the string as well as the '$' - * at the beginning of the command. - * - * @param command Command to send. - * - * @return True if operation succeeded. + * @brief Struct used to store the binary data received from the sensor. */ - bool sendStringCommand(std::string command); + struct __attribute__((packed)) BinaryData + { + uint8_t group; + uint16_t group1; + float quaternionX; + float quaternionY; + float quaternionZ; + float quaternionW; + float angularX; + float angularY; + float angularZ; + float accelerationX; + float accelerationY; + float accelerationZ; + float magneticFieldX; + float magneticFieldY; + float magneticFieldZ; + float temperature; + float pressure; + uint16_t crc; + }; /** - * @brief Receives a command from the VN100 serialInterface->recv() but - * swaps the first \n with a \0 to close the message. - * - * @param command The char array which will be filled with the command. - * @param maxLength Maximum length for the command array. + * @brief Build output data packet starting from raw binary data received + * from the sensor. * - * @return True if operation succeeded. + * @param rawData The raw data received from the sensor. + * @param data The structure that will contain the output. + * @param timestamp The timestamp of the extracted data. */ - bool recvStringCommand(char* command, int maxLength); + void buildBinaryData(const BinaryData& binData, VN100SerialData& data, + const uint64_t sampleTimestamp); /** - * @brief Method to verify the crc validity of a command. - * - * @param command The char array which contains the command. - * @param maxLength Maximum length for the command array. + * @brief Set the binary output register. * * @return True if operation succeeded. */ - bool verifyChecksum(char* command, int maxLength); + bool setBinaryOutput(); /** - * @brief Calculate the 8bit checksum on the given array. - * - * @param command Command on which compute the crc. - * @param length Array length. - * - * @return The 8 bit checksum. + * @brief Pre computed command used to ask a binary sample to the sensor. */ - uint8_t calculateChecksum8(uint8_t* message, int length); - - /** - * @brief Calculate the 16bit array on the given array. - * - * @param command Command on which compute the crc. - * @param length Array length. - * - * @return The 16 bit CRC16-CCITT error check. - */ - uint16_t calculateChecksum16(uint8_t* message, int length); - - /** - * @brief Serial interface that is needed to communicate - * with the sensor via ASCII codes. - */ - USART& usart; - int baudRate; - - uint16_t samplePeriod; - CRCOptions crc; - bool isInit = false; - - /** - * @brief IMU pre-elaborated sample string for efficiency reasons. - */ - string* preSampleImuString = nullptr; - - /** - * @brief Temperature and pressure pre-elaborated sample string for - * efficiency reasons. - */ - string* preSampleTempPressString = nullptr; - - /** - * @brief Pointer to the received string by the sensor. Allocated 1 time - * only (200 bytes). - */ - char* recvString = nullptr; - - /** - * @brief Actual strlen() of the recvString. - */ - unsigned int recvStringLength = 0; - - /** - * @brief Mutex to synchronize the reading and writing of the threadSample - */ - mutable miosix::FastMutex mutex; - VN100SerialData threadSample; - - PrintLogger logger = Logging::getLogger("vn100-serial"); - - static const unsigned int recvStringMaxDimension = 200; + const char* askSampleCommand = ""; }; } // namespace Boardcore diff --git a/src/shared/sensors/Vectornav/VN100/VN100SerialData.h b/src/shared/sensors/Vectornav/VN100/VN100SerialData.h index dc1fa4dbf87112c38342fa6af17ba426762dc78a..91050d0d835a46863b45267b2b211fc01508148d 100644 --- a/src/shared/sensors/Vectornav/VN100/VN100SerialData.h +++ b/src/shared/sensors/Vectornav/VN100/VN100SerialData.h @@ -1,5 +1,5 @@ /* Copyright (c) 2021 Skyward Experimental Rocketry - * Author: Matteo Pignataro + * Author: Matteo Pignataro, Fabrizio Monti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/shared/sensors/Vectornav/VNCommonSerial.cpp b/src/shared/sensors/Vectornav/VNCommonSerial.cpp index d271510142d11d5d16d9f167e8343c9814d7ab79..d5696d9c2b4e2d99177fa178f9554e1119ab678c 100644 --- a/src/shared/sensors/Vectornav/VNCommonSerial.cpp +++ b/src/shared/sensors/Vectornav/VNCommonSerial.cpp @@ -267,110 +267,6 @@ bool VNCommonSerial::verifyModelNumber(const char* expectedModelNumber) return true; } -QuaternionData VNCommonSerial::sampleQuaternion() -{ - unsigned int indexStart = 0; - char* nextNumber; - QuaternionData data; - - // Look for the second ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 2; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.quaternionTimestamp = TimestampTimer::getTimestamp(); - data.quaternionX = strtod(recvString.data() + indexStart + 1, &nextNumber); - data.quaternionY = strtod(nextNumber + 1, &nextNumber); - data.quaternionZ = strtod(nextNumber + 1, &nextNumber); - data.quaternionW = strtod(nextNumber + 1, NULL); - - return data; -} - -MagnetometerData VNCommonSerial::sampleMagnetometer() -{ - unsigned int indexStart = 0; - char* nextNumber; - MagnetometerData data; - - // Look for the sixth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 6; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.magneticFieldTimestamp = TimestampTimer::getTimestamp(); - data.magneticFieldX = - strtod(recvString.data() + indexStart + 1, &nextNumber); - data.magneticFieldY = strtod(nextNumber + 1, &nextNumber); - data.magneticFieldZ = strtod(nextNumber + 1, NULL); - - return data; -} - -AccelerometerData VNCommonSerial::sampleAccelerometer() -{ - unsigned int indexStart = 0; - char* nextNumber; - AccelerometerData data; - - // Look for the ninth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 9; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.accelerationTimestamp = TimestampTimer::getTimestamp(); - data.accelerationX = - strtod(recvString.data() + indexStart + 1, &nextNumber); - data.accelerationY = strtod(nextNumber + 1, &nextNumber); - data.accelerationZ = strtod(nextNumber + 1, NULL); - - return data; -} - -GyroscopeData VNCommonSerial::sampleGyroscope() -{ - unsigned int indexStart = 0; - char* nextNumber; - GyroscopeData data; - - // Look for the twelfth ',' in the string - // I can avoid the string control because it has already been done in - // sampleImpl - for (int i = 0; i < 12; i++) - { - while (indexStart < recvStringLength && recvString[indexStart] != ',') - indexStart++; - indexStart++; - } - - // Parse the data - data.angularSpeedTimestamp = TimestampTimer::getTimestamp(); - data.angularSpeedX = - strtod(recvString.data() + indexStart + 1, &nextNumber); - data.angularSpeedY = strtod(nextNumber + 1, &nextNumber); - data.angularSpeedZ = strtod(nextNumber + 1, NULL); - - return data; -} - uint8_t VNCommonSerial::checkErrorVN(const char* message) { if (strncmp(message, "$VNERR,", 7) == 0) diff --git a/src/shared/sensors/Vectornav/VNCommonSerial.h b/src/shared/sensors/Vectornav/VNCommonSerial.h index 6fa6c14f2f1a46956b0470560e1073938eabf0ba..53f807e93246f3c0cb500b6510334070af4e8149 100644 --- a/src/shared/sensors/Vectornav/VNCommonSerial.h +++ b/src/shared/sensors/Vectornav/VNCommonSerial.h @@ -222,30 +222,6 @@ protected: template <typename T> bool getBinaryOutput(T& binaryData, const char* const sampleCommand); - /** - * @brief Utility function used to extract quaternion data from the - * receiving string. - */ - QuaternionData sampleQuaternion(); - - /** - * @brief Utility function used to extract magnetometer data from the - * receiving string. - */ - MagnetometerData sampleMagnetometer(); - - /** - * @brief Utility function used to extract accelerometer data from the - * receiving string. - */ - AccelerometerData sampleAccelerometer(); - - /** - * @brief Utility function used to extract gyroscope data from the receiving - * string. - */ - GyroscopeData sampleGyroscope(); - /** * @brief Check if the message received from the sensor contains an error. * diff --git a/src/tests/sensors/test-vn100-serial.cpp b/src/tests/sensors/test-vn100-serial.cpp index 9d11d0f125ad99680e0adb1b6713cb6ba3e96e8c..c94541399ca5ba554aba5ac399cec8fbd4bbbf9a 100644 --- a/src/tests/sensors/test-vn100-serial.cpp +++ b/src/tests/sensors/test-vn100-serial.cpp @@ -1,5 +1,5 @@ /* Copyright (c) 2021 Skyward Experimental Rocketry - * Author: Matteo Pignataro + * Author: Matteo Pignataro, Fabrizio Monti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,6 @@ * THE SOFTWARE. */ -#include <drivers/timer/TimestampTimer.h> #include <inttypes.h> #include <sensors/Vectornav/VN100/VN100Serial.h> @@ -30,7 +29,6 @@ using namespace Boardcore; int main() { VN100SerialData sample; - string sampleRaw; GpioPin u2tx1(GPIOA_BASE, 2); GpioPin u2rx1(GPIOA_BASE, 3); @@ -41,7 +39,8 @@ int main() u2tx1.mode(Mode::ALTERNATE); USART usart(USART2, 115200); - VN100Serial sensor{usart, 115200, VN100Serial::CRCOptions::CRC_ENABLE_16}; + VN100Serial sensor{usart, 115200, VNCommonSerial::CRCOptions::CRC_ENABLE_16, + std::chrono::seconds(5)}; // Let the sensor start up Thread::sleep(1000); @@ -60,15 +59,6 @@ int main() return 0; } - if (!sensor.start()) - { - printf("Unable to start the sampling thread\n"); - return 0; - } - - printf("Sensor sampling thread started!\n"); - - // Sample and print 100 samples for (int i = 0; i < 100; i++) { sensor.sample(); @@ -78,12 +68,14 @@ int main() sample.accelerationY, sample.accelerationZ); printf("ang: %.3f, %.3f, %.3f\n", sample.angularSpeedX, sample.angularSpeedY, sample.angularSpeedZ); + printf("mag: %.3f, %.3f, %.3f\n", sample.magneticFieldX, + sample.magneticFieldY, sample.magneticFieldZ); + printf("quat: %.3f, %.3f, %.3f, %.3f\n", sample.quaternionX, + sample.quaternionY, sample.quaternionZ, sample.quaternionW); + printf("temp: %.3f\n", sample.temperature); + printf("press: %.3f\n\n", sample.pressure); - sensor.sampleRaw(); - sampleRaw = sensor.getLastRawSample(); - printf("%s\n", sampleRaw.c_str()); - // Thread::sleep(100); - printf("\n"); + Thread::sleep(500); } sensor.closeAndReset();