diff --git a/library.json b/library.json index 93dc914..4d5bbcc 100644 --- a/library.json +++ b/library.json @@ -2,12 +2,17 @@ "name": "DFR_Radar", "keywords": "arduino, millimeter-wave, radar, 24GHz, DFRobot, SEN0395", "description": "Arduino library for working with the DFRobot 24GHz millimeter-wave Human Presence Detection sensor (SEN0395).", - "version": "1.2.0", + "version": "1.2.1", "authors": [ + { + "name": "Tim Logan", + "url": "https://github.com/timtimmahh", + "maintainer": true + }, { "name": "Matthew Clark", "url": "https://github.com/MaffooClock", - "maintainer": true + "maintainer": false }, { "name": "huyujie (DFRobot)", @@ -17,7 +22,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/MaffooClock/DFR_Radar" + "url": "https://github.com/timtimmahh/DFR_Radar" }, "headers": "DFR_Radar.h", "examples": [ diff --git a/library.properties b/library.properties index a53a9ac..e7f42f3 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=DFR_Radar -version=1.2.0 -author=Matthew Clark, huyujie (yujie.hu@dfrobot.com) -maintainer=Matthew Clark +version=1.2.1 +author=Tim Logan, Matthew Clark, huyujie (yujie.hu@dfrobot.com) +maintainer=Tim Logan sentence=Configure and communicate with the DFRobot 24GHz millimeter-wave Human Presence Detection sensor (SEN0395). paragraph=A library is not required to use the sensor, as it will output a signal on pin IO2 when it detects presence, but this library will allow you to configure various parameters via UART. category=Sensors includes=DFR_Radar.h -url=https://github.com/MaffooClock/DFR_Radar +url=https://github.com/timtimmahh/DFR_Radar architectures=* diff --git a/src/DFR_Radar.cpp b/src/DFR_Radar.cpp index f2fac92..d55349f 100644 --- a/src/DFR_Radar.cpp +++ b/src/DFR_Radar.cpp @@ -14,495 +14,765 @@ #include -DFR_Radar::DFR_Radar( Stream *s ) -{ - sensorUART = s; - // isConfigured = false; - stopped = false; - multiConfig = false; -} - -bool DFR_Radar::begin() -{ - /* Not sure if I want to impliment this, keeping it for future consideration... - - unsigned long startTime = millis() + startupDelay; - - // Give the sensor time to start up just in case this method is called too soon. - // - // There's probably a smarter way to do this. Factory default configuration will - // have the sensor dumping out $JYBSS messages once per second, so that could be - // an easy way to tell that it's "ready". But if the sensor is configured to send - // these only when queried, or when an presence event occurs, or if the interval - // is set too long, then this won't really work. - // - // Another way might be to send a `sensorStart` and see if 1) it complains about - // not being ready, or 2) it responds with "sensor started already" and "Error", - // or 3) actually starts? - - while( millis() < startTime ) - yield(); - - if( !stop() ) - return false; +DFR_Radar::DFR_Radar(Stream *s) + : debugSerial(false) { + sensorUART = s; + // isConfigured = false; + stopped = false; + multiConfig = false; +} - // Disable command echoing (less response data that we have to parse through) - sendCommand( comSetEcho ); +bool DFR_Radar::begin() { + /* Not sure if I want to impliment this, keeping it for future consideration... - // Disable periodic $JYBSS messages (we will query for them) - sendCommand( comSetUartOutput ); + unsigned long startTime = millis() + startupDelay; - if( !saveConfig() ) - return false; + // Give the sensor time to start up just in case this method is called too soon. + // + // There's probably a smarter way to do this. Factory default configuration will + // have the sensor dumping out $JYBSS messages once per second, so that could be + // an easy way to tell that it's "ready". But if the sensor is configured to send + // these only when queried, or when an presence event occurs, or if the interval + // is set too long, then this won't really work. + // + // Another way might be to send a `sensorStart` and see if 1) it complains about + // not being ready, or 2) it responds with "sensor started already" and "Error", + // or 3) actually starts? - if( !start() ) - return false; + while( millis() < startTime ) + yield(); - isConfigured = true; + if( !stop() ) + return false; - */ + // Disable command echoing (less response data that we have to parse through) + sendCommand( comSetEcho ); + + // Disable periodic $JYBSS messages (we will query for them) + sendCommand( comSetUartOutput ); + + if( !saveConfig() ) + return false; + + if( !start() ) + return false; + + isConfigured = true; + + */ + + return true; +} + +void DFR_Radar::setStream(Stream *s) { + sensorUART = s; +} - return true; +bool DFR_Radar::isReady() const { + return sensorUART != nullptr; } -size_t DFR_Radar::readLines( char *buffer, size_t lineCount ) -{ - unsigned long timeLimit = millis() + readPacketTimeout; - size_t offset = 0, linesLeft = lineCount; +bool DFR_Radar::setDetectionRange(const float rangeStart, const float rangeEnd) { + if (rangeStart < 0 || rangeStart > 9.45) + return false; - while( linesLeft && millis() < timeLimit ) - { - if( sensorUART->available() <= 0 ) - continue; + if (rangeEnd < 0 || rangeEnd > 9.45) + return false; - char c = sensorUART->read(); + if (rangeEnd < rangeStart) + return false; - if( c == '\r' ) - continue; + char _comSetRange[21] = {0}; - buffer[offset++] = c; +#ifdef __AVR__ +#ifdef _STDLIB_H_ + char _rangeStart[6] = {0}; + dtostrf(rangeStart, 1, 3, _rangeStart); - if( c == '\n' ) - linesLeft--; - } + char _rangeEnd[6] = {0}; + dtostrf(rangeEnd, 1, 3, _rangeEnd); - return strlen( buffer ); + sprintf(_comSetRange, comSetRange, _rangeStart, _rangeEnd); +#else + sprintf(_comSetRange, comSetRange, (uint8_t) rangeStart, (uint16_t) rangeEnd); +#endif +#else + sprintf(_comSetRange, comSetRange, rangeStart, rangeEnd); +#endif + + return setConfig(_comSetRange); } -bool DFR_Radar::checkPresence() -{ - char packet[packetLength] = {0}; +bool DFR_Radar::getDetectionRange(float &rangeStart, float &rangeEnd) { + char _comGetRange[2][7] = {{0}, {0}}; + if (!getConfig<2, 7>(comGetRange, _comGetRange)) { + if (debugSerial) + Serial.println("Error getting range"); + return false; + } - // Factory default settings have $JYBSS messages sent once per second, - // but we won't want to wait; this will prompt for status immediately - serialWrite( comGetOutput ); + rangeStart = strtof(_comGetRange[0], nullptr); + rangeEnd = strtof(_comGetRange[1], nullptr); + return true; +} - /** - * Get the response immediately after sending the command. - * - * If command echoing is enabled, there should be three lines: - * 1. the "getOutput 1" echoed back - * 2. a "Done" status - * 3. the "leapMMW:/>" response followed by the $JYBSS data we want - * - * If command echoing is disabled, there should be two lines: - * 1. a "Done" status - * 2. the $JYBSS data we want - * - * Factory default is command echoing on (might change this in `begin()`) - */ - size_t length = readLines( packet, 3 ); +bool DFR_Radar::setSensitivity(const uint8_t level) { + if (level > 9) + return false; - if( !length ) - return false; + char _comSetSensitivity[17] = {0}; + sprintf(_comSetSensitivity, comSetSensitivity, level); - const size_t expectedLength = 16; - char data[expectedLength] = {0}; - uint8_t offset = 0; - bool startCharacterFound = false, endCharacterFound = false; + return setConfig(_comSetSensitivity); +} - /** - * Parse through the packet until we find a "$", and - * then start capturing characters until we find a "*" - * - * We're expecting to get something like: $JYBSS,1, , , * - */ - for( uint8_t i = 0; i < length; i++ ) - { - char c = packet[i]; +bool DFR_Radar::getSensitivity(uint8_t &level) { + char _comGetSensitivity[1][2] = {{0}}; + if (!getConfig<1, 2>(comGetSensitivity, _comGetSensitivity)) { + if (debugSerial) + Serial.println("Error getting sensitivity"); + return false; + } - if( c == '$' ) - startCharacterFound = true; + level = strtol(_comGetSensitivity[0], nullptr, 10); + return true; +} - if( !startCharacterFound ) - continue; +bool DFR_Radar::setTriggerLatency(const float confirmationDelay, const float disappearanceDelay) { + if (confirmationDelay < 0 || confirmationDelay > 100) + return false; - if( c == '*' ) - endCharacterFound = true; + if (disappearanceDelay < 0 || disappearanceDelay > 1500) + return false; - data[offset++] = c; + char _comSetLatency[28] = {0}; - if( endCharacterFound || offset == expectedLength ) - break; - } +#ifdef __AVR__ +#ifdef _STDLIB_H_ + char _confirmationDelay[8] = {0}; + dtostrf(confirmationDelay, 3, 3, _confirmationDelay); - if( !startCharacterFound || !endCharacterFound ) - return false; + char _disappearanceDelay[9] = {0}; + dtostrf(disappearanceDelay, 4, 3, _disappearanceDelay); - return ( data[7] == '1' ); + sprintf(_comSetLatency, comSetLatency, _confirmationDelay, _disappearanceDelay); +#else + sprintf(_comSetLatency, comSetLatency, (uint8_t) confirmationDelay, (uint16_t) disappearanceDelay); +#endif +#else + sprintf(_comSetLatency, comSetLatency, confirmationDelay, disappearanceDelay); +#endif + + return setConfig(_comSetLatency); } -bool DFR_Radar::setLockout( float time ) -{ - if( time < 0.1 || time > 255 ) - return false; +bool DFR_Radar::getTriggerLatency(float &confirmationDelay, float &disappearanceDelay) { + char _comGetLatency[2][10] = {{0}, {0}}; + if (!getConfig<2, 10>(comGetLatency, _comGetLatency)) { + if (debugSerial) + Serial.println("Error getting latency"); + return false; + } - char _comSetInhibit[19] = {0}; + confirmationDelay = strtof(_comGetLatency[0], nullptr); + disappearanceDelay = strtof(_comGetLatency[1], nullptr); + return true; +} + +bool DFR_Radar::setOutputLatency(const float triggerDelay, const float resetDelay) { + if (triggerDelay < 0 || resetDelay < 0) + return false; + + // Convert seconds into 25ms units + const uint32_t _triggerDelay = triggerDelay * 1000 / 25; + const uint32_t _resetDelay = resetDelay * 1000 / 25; - #ifdef __AVR__ - #ifdef _STDLIB_H_ - char _time[8] = {0}; - dtostrf( time, 3, 3, _time ); + if (_triggerDelay > 65535 || _resetDelay > 65535) + return false; - sprintf( _comSetInhibit, comSetInhibit, _time ); - #else - sprintf( _comSetInhibit, comSetInhibit, (uint8_t)time ); - #endif - #else - sprintf( _comSetInhibit, comSetInhibit, time ); - #endif + char _comOutputLatency[29] = {0}; + sprintf( + _comOutputLatency, + comOutputLatency, + static_cast(_triggerDelay), + static_cast(_resetDelay) + ); - return setConfig( _comSetInhibit ); + return setConfig(_comOutputLatency); } -bool DFR_Radar::setTriggerLevel( uint8_t triggerLevel ) -{ - if( triggerLevel != HIGH || triggerLevel != LOW ) - return false; +bool DFR_Radar::checkPresence() const { + bool presence = false; + readPresence(presence); + return presence; +} - char _comSetGpioMode[16] = {0}; - sprintf( _comSetGpioMode, comSetGpioMode, triggerLevel ); +bool DFR_Radar::readPresence(bool &presence) const { + char packet[packetLength] = {0}; + + // Factory default settings have $JYBSS messages sent once per second, + // but we won't want to wait; this will prompt for status immediately + serialWrite(comGetOutput); + + /** + * Get the response immediately after sending the command. + * + * If command echoing is enabled, there should be three lines: + * 1. the "getOutput 1" echoed back + * 2. a "Done" status + * 3. the "leapMMW:/>" response followed by the $JYBSS data we want + * + * If command echoing is disabled, there should be two lines: + * 1. a "Done" status + * 2. the $JYBSS data we want + * + * Factory default is command echoing on (might change this in `begin()`) + */ + size_t length = readLines(packet, 3); + + if (!length) + return false; + + constexpr size_t expectedLength = 16; + char data[expectedLength] = {0}; + uint8_t offset = 0; + bool startCharacterFound = false, endCharacterFound = false; + + /** + * Parse through the packet until we find a "$", and + * then start capturing characters until we find a "*" + * + * We're expecting to get something like: $JYBSS,1, , , * + */ + for (uint8_t i = 0; i < length; i++) { + const char c = packet[i]; + + if (c == '$') + startCharacterFound = true; + + if (!startCharacterFound) + continue; + + if (c == '*') + endCharacterFound = true; + + data[offset++] = c; + + if (endCharacterFound || offset == expectedLength) + break; + } + + if (!startCharacterFound || !endCharacterFound) { +#ifdef DEBUG + Serial.print("Error: Invalid data "); + Serial.println(packet); +#endif + return false; + } - return setConfig( _comSetGpioMode ); + presence = (data[7] == '1'); + return true; } -bool DFR_Radar::setDetectionRange( float rangeStart, float rangeEnd ) -{ - if( rangeStart < 0 || rangeStart > 9.45 ) - return false; +bool DFR_Radar::setLockout(const float time) { + if (time < 0.1 || time > 255) + return false; - if( rangeEnd < 0 || rangeEnd > 9.45 ) - return false; + char _comSetInhibit[19] = {0}; - if( rangeEnd < rangeStart ) - return false; +#ifdef __AVR__ +#ifdef _STDLIB_H_ + char _time[8] = {0}; + dtostrf(time, 3, 3, _time); + + sprintf(_comSetInhibit, comSetInhibit, _time); +#else + sprintf(_comSetInhibit, comSetInhibit, (uint8_t) time); +#endif +#else + sprintf(_comSetInhibit, comSetInhibit, time); +#endif - char _comSetRange[21] = {0}; + return setConfig(_comSetInhibit); +} - #ifdef __AVR__ - #ifdef _STDLIB_H_ - char _rangeStart[6] = {0}; - dtostrf( rangeStart, 1, 3, _rangeStart ); +bool DFR_Radar::getLockout(float &time) { + char _comGetInhibit[1][10] = {{0}}; + if (!getConfig<1, 10>(comGetInhibit, _comGetInhibit)) { + if (debugSerial) + Serial.println("Error getting inhibit"); + return false; + } - char _rangeEnd[6] = {0}; - dtostrf( rangeEnd, 1, 3, _rangeEnd ); + time = strtof(_comGetInhibit[0], nullptr); + return true; +} - sprintf( _comSetRange, comSetRange, _rangeStart, _rangeEnd ); - #else - sprintf( _comSetRange, comSetRange, (uint8_t)rangeStart, (uint16_t)rangeEnd ); - #endif - #else - sprintf( _comSetRange, comSetRange, rangeStart, rangeEnd ); - #endif +bool DFR_Radar::setTriggerLevel(const uint8_t ioPin, const uint8_t triggerLevel) { + if (triggerLevel != HIGH || triggerLevel != LOW) + return false; - return setConfig( _comSetRange ); + char _comSetGpioMode[16] = {0}; + sprintf(_comSetGpioMode, comSetGpioMode, ioPin, triggerLevel); + + return setConfig(_comSetGpioMode); } -bool DFR_Radar::setTriggerLatency( float confirmationDelay, float disappearanceDelay ) -{ - if( confirmationDelay < 0 || confirmationDelay > 100 ) - return false; +bool DFR_Radar::setTriggerLevel(const uint8_t triggerLevel) { + return setTriggerLevel(2, triggerLevel); +} - if( disappearanceDelay < 0 || disappearanceDelay > 1500 ) - return false; +bool DFR_Radar::getTriggerLevel(const uint8_t ioPin, uint8_t &triggerLevel) { + char _comGetGpioModeCommand[14] = {0}; + sprintf(_comGetGpioModeCommand, comGetGpioMode, ioPin); + + char _comGetGpioMode[2][2] = {{0}}; + if (!getConfig<2, 2>(_comGetGpioModeCommand, _comGetGpioMode)) { + if (debugSerial) + Serial.println("Error getting gpio mode"); + return false; + } + + triggerLevel = strtol(_comGetGpioMode[1], nullptr, 10); + return true; +} - char _comSetLatency[28] = {0}; +bool DFR_Radar::getTriggerLevel(uint8_t &triggerLevel) { + return getTriggerLevel(2, triggerLevel); +} - #ifdef __AVR__ - #ifdef _STDLIB_H_ - char _confirmationDelay[8] = {0}; - dtostrf( confirmationDelay, 3, 3, _confirmationDelay ); +bool DFR_Radar::setUartOutput(const uint8_t messageType, const bool enable, const bool push, const float period) { + if (messageType < 1 || messageType > 3) + return false; - char _disappearanceDelay[9] = {0}; - dtostrf( disappearanceDelay, 4, 3, _disappearanceDelay ); + char _comSetUartOutput[30] = {0}; + sprintf(_comSetUartOutput, comSetUartOutputFull, messageType, enable, push, period); - sprintf( _comSetLatency, comSetLatency, _confirmationDelay , _disappearanceDelay ); - #else - sprintf( _comSetLatency, comSetLatency, (uint8_t)confirmationDelay , (uint16_t)disappearanceDelay ); - #endif - #else - sprintf( _comSetLatency, comSetLatency, confirmationDelay , disappearanceDelay ); - #endif + return setConfig(_comSetUartOutput); +} - return setConfig( _comSetLatency ); +bool DFR_Radar::configureUartDetectionOutput(const bool enable, const bool push, const float period) { + return setUartOutput(1, enable, push, period); } -bool DFR_Radar::setOutputLatency( float triggerDelay, float resetDelay ) -{ - if( triggerDelay < 0 || resetDelay < 0 ) - return false; +bool DFR_Radar::configureUartPointCloudOutput(const bool enable, const bool push, const float period) { + return setUartOutput(2, enable, push, period); +} - // Convert seconds into 25ms units - uint32_t _triggerDelay = triggerDelay * 1000 / 25; - uint32_t _resetDelay = resetDelay * 1000 / 25; +bool DFR_Radar::getUartOutput(const uint8_t messageType, bool &enable, bool &onChange, float &period) { + if (messageType < 1 || messageType > 3) + return false; - if( _triggerDelay > 65535 || _resetDelay > 65535 ) - return false; + char _comGetUartOutput[4][9] = {{0}, {0}, {0}, {0}}; - char _comOutputLatency[29] = {0}; - sprintf( _comOutputLatency, comOutputLatency, (uint16_t)_triggerDelay , (uint16_t)_resetDelay ); + char _comGetUartOutputCommand[16] = {0}; + sprintf(_comGetUartOutputCommand, comGetUartOutput, messageType); + + if (!getConfig<4, 9>(_comGetUartOutputCommand, _comGetUartOutput)) { + if (debugSerial) + Serial.println("Error getting uart output"); + return false; + } - return setConfig( _comOutputLatency ); + enable = strtol(_comGetUartOutput[1], nullptr, 10) == 1; + onChange = strtol(_comGetUartOutput[2], nullptr, 10) == 1; + period = strtof(_comGetUartOutput[3], nullptr); + return true; } -bool DFR_Radar::setSensitivity( uint8_t level ) -{ - if( level > 9 ) - return false; +bool DFR_Radar::getUartDetectionOutput(bool &enable, bool &onChange, float &period) { + return getUartOutput(1, enable, onChange, period); +} - char _comSetSensitivity[17] = {0}; - sprintf( _comSetSensitivity, comSetSensitivity, level ); +bool DFR_Radar::getUartPointCloudOutput(bool &enable, bool &onChange, float &period) { + return getUartOutput(2, enable, onChange, period); +} - return setConfig( _comSetSensitivity ); +bool DFR_Radar::setEcho(const bool enable) { + char _comSetEcho[14] = {0}; + sprintf(_comSetEcho, comSetEcho, enable); + return setConfig(_comSetEcho); } -bool DFR_Radar::disableLED() -{ - return configureLED( true ); +bool DFR_Radar::getEcho(bool &enable) { + char _comGetEcho[1][2] = {{0}}; + if (!getConfig<1, 2>(comGetEcho, _comGetEcho)) { + if (debugSerial) + Serial.println("Error getting echo"); + return false; + } + + enable = strtol(_comGetEcho[0], nullptr, 10) == 1; + return true; } -bool DFR_Radar::enableLED() -{ - return configureLED( false ); +bool DFR_Radar::start() { + if (!stopped) + return true; + + if (sendCommand(comStart, comFailStarted)) { + stopped = false; + return true; + } + + return false; } -bool DFR_Radar::configureLED( bool disabled ) -{ - char _comSetLedMode[15] = {0}; - sprintf( _comSetLedMode, comSetLedMode, disabled ); +bool DFR_Radar::stop() { + if (stopped) + return true; + + if (sendCommand(comStop, comFailStopped)) { + stopped = true; + return true; + } - return setConfig( _comSetLedMode ); + return false; } -bool DFR_Radar::factoryReset() -{ - // if( !stop() ) - // return false; - stop(); +void DFR_Radar::reboot() const { + sendCommand(comResetSystem); +} - bool success = sendCommand( comFactoryReset ); - delay( 2000 ); +bool DFR_Radar::disableLED() { + return configureLED(true); +} - return success; +bool DFR_Radar::enableLED() { + return configureLED(false); } -bool DFR_Radar::configBegin() -{ - if( multiConfig ) +bool DFR_Radar::configureLED(const bool disabled) { + char _comSetLedMode[15] = {0}; + sprintf(_comSetLedMode, comSetLedMode, disabled); + + return setConfig(_comSetLedMode); +} + +bool DFR_Radar::getLEDMode(bool &disabled) { + char _comGetLedMode[2][2] = {{0}}; + if (!getConfig<2, 2>(comGetLedMode, _comGetLedMode)) { + if (debugSerial) + Serial.println("Error getting led mode"); + return false; + } + + disabled = strtol(_comGetLedMode[1], nullptr, 10) == 1; return true; +} - if( !stop() ) - return false; +bool DFR_Radar::configBegin() { + if (multiConfig) + return true; - multiConfig = true; + if (!stop()) + return false; - return true; + multiConfig = true; + + return true; } -bool DFR_Radar::configEnd() -{ - if( !multiConfig ) - return false; +bool DFR_Radar::configEnd() { + if (!multiConfig) + return false; - multiConfig = false; + multiConfig = false; - if( !saveConfig() ) - return false; + if (!saveConfig()) + return false; - if( !start() ) - return false; + if (!start()) + return false; - return true; + return true; } -bool DFR_Radar::setConfig( const char *command ) -{ - if( multiConfig ) - { - return sendCommand( command ); - } - else - { +bool DFR_Radar::factoryReset() { // if( !stop() ) // return false; stop(); - if( !sendCommand( command ) ) - return false; + const bool success = sendCommand(comFactoryReset); + delay(2000); - bool saved = saveConfig(); + return success; +} - if( !start() ) - return false; +bool DFR_Radar::getHWVersion(char *version) { + char _comGetHWVersion[1][32] = {{0}}; + if (!getConfig<1, 32>(comGetHWV, _comGetHWVersion, "")) { + if (debugSerial) + Serial.println("Error getting HW version"); + return false; + } - return saved; - } + strcpy(version, _comGetHWVersion[0]); + return true; } -bool DFR_Radar::saveConfig() -{ - return sendCommand( comSaveCfg ); -} +bool DFR_Radar::getSWVersion(char *version) { + char _comGetSWVersion[1][32] = {{0}}; + if (!getConfig<1, 32>(comGetSWV, _comGetSWVersion, "")) { + if (debugSerial) + Serial.println("Error getting SW version"); + return false; + } -bool DFR_Radar::start() -{ - if( !stopped ) + strcpy(version, _comGetSWVersion[0]); return true; +} - if( sendCommand( comStart, comFailStarted ) ) - { - stopped = false; - return true; - } +size_t DFR_Radar::readLines(char *buffer, const size_t lineCount) const { + const unsigned long timeLimit = millis() + readPacketTimeout; + size_t offset = 0, linesLeft = lineCount; + + while (linesLeft && millis() < timeLimit) { + if (sensorUART->available() <= 0) + continue; - return false; + char c = sensorUART->read(); + + if (c == '\r') + continue; + + buffer[offset++] = c; + + if (c == '\n') + linesLeft--; + } + + return strlen(buffer); } -bool DFR_Radar::stop() -{ - if( stopped ) - return true; +bool DFR_Radar::setConfig(const char *command) { + if (multiConfig) { + return sendCommand(command); + } + // if( !stop() ) + // return false; + stop(); - if( sendCommand( comStop, comFailStopped ) ) - { - stopped = true; - return true; - } + if (!sendCommand(command)) + return false; + + const bool saved = saveConfig(); - return false; + if (!start()) + return false; + + return saved; } -void DFR_Radar::reboot() -{ - sendCommand( comResetSystem ); +bool DFR_Radar::saveConfig() const { + return sendCommand(comSaveCfg); } -size_t DFR_Radar::serialWrite( const char *command ) -{ - static char _command[64] = {0}; - size_t commandLength = strlen( command ) + 3; +size_t DFR_Radar::serialWrite(const char *command) const { + static char _command[64] = {0}; + const size_t commandLength = strlen(command) + 3; - // Ensure the re-usable buffer is empty - memset( &_command[0], 0, sizeof( _command ) ); + // Ensure the re-usable buffer is empty + memset(&_command[0], 0, sizeof(_command)); - // Make a properly-terminated copy of the command - snprintf( _command, commandLength, "%s\r\n", command ); + // Make a properly-terminated copy of the command + snprintf(_command, commandLength, "%s\r\n", command); - // Make sure we have exactly enough time - sensorUART->setTimeout( comTimeout ); + // Make sure we have exactly enough time + sensorUART->setTimeout(comTimeout); - // Clear the receive buffer - while( sensorUART->available() ) - sensorUART->read(); + // Clear the receive buffer + while (sensorUART->available()) + sensorUART->read(); - // Send the command... - sensorUART->write( _command ); - sensorUART->flush(); + if (debugSerial) + Serial.printf("Sending command: '%s'\n", command); - return commandLength; + // Send the command... + sensorUART->write(_command); + sensorUART->flush(); + + return commandLength; } -bool DFR_Radar::sendCommand( const char *command ) -{ - return sendCommand( command, NULL ); +bool DFR_Radar::sendCommand(const char *command) const { + return sendCommand(command, nullptr); } -bool DFR_Radar::sendCommand( const char *command, const char *acceptableResponse ) -{ - bool errorAcceptable = false; - char lineBuffer[64] = {0}; - unsigned long timeout = millis() + comTimeout; +bool DFR_Radar::sendCommand(const char *command, const char *acceptableResponse) const { + bool errorAcceptable = false; + char lineBuffer[64] = {0}; + const unsigned long timeout = millis() + comTimeout; - static const size_t successLength = strlen( comResponseSuccess ); - static const size_t failLength = strlen( comResponseFail ); - static const size_t minResponseLength = min( successLength, failLength ); + static const size_t successLength = strlen(comResponseSuccess); + static const size_t failLength = strlen(comResponseFail); + static const size_t minResponseLength = min(successLength, failLength); - const size_t commandLength = strlen( command ); - const size_t acceptableLength = strlen( acceptableResponse ); - size_t minLength = min( commandLength, minResponseLength ); + const size_t commandLength = strlen(command); + size_t minLength = min(commandLength, minResponseLength); + const size_t acceptableLength = acceptableResponse == nullptr ? minLength : strlen(acceptableResponse); - if( acceptableResponse != NULL ) - minLength = min( acceptableLength, minLength ); + if (acceptableResponse != nullptr) + minLength = min(acceptableLength, minLength); - // Send the command... - serialWrite( command ); + // Send the command... + serialWrite(command); - // ...then wait for a response - while( millis() < timeout ) - { - if( sensorUART->available() <= 0 ) - continue; + // ...then wait for a response + while (millis() < timeout) { + if (sensorUART->available() <= 0) + continue; - // Ensure the buffer is empty - memset( &lineBuffer[0], 0, sizeof( lineBuffer ) ); + // Ensure the buffer is empty + memset(&lineBuffer[0], 0, sizeof(lineBuffer)); - // Read a whole line - size_t responseLength = sensorUART->readBytesUntil( '\n', lineBuffer, sizeof( lineBuffer ) ); + // Read a whole line + size_t responseLength = sensorUART->readBytesUntil('\n', lineBuffer, sizeof(lineBuffer)); - // The sensor is supposed to terminate lines with , and we stopped at , - // so the last character in the line buffer should be a . If so, swap it out - // for a null terminator - uint8_t lastCharacter = strlen( lineBuffer ) - 1; - if( lineBuffer[lastCharacter] == '\r' ) - lineBuffer[lastCharacter] = '\0'; + // The sensor is supposed to terminate lines with , and we stopped at , + // so the last character in the line buffer should be a . If so, swap it out + // for a null terminator + const uint8_t lastCharacter = strlen(lineBuffer) - 1; + if (lineBuffer[lastCharacter] == '\r') + lineBuffer[lastCharacter] = '\0'; - // Update the length and ensure the line we've received is properly terminated - responseLength = strlen( lineBuffer ); - lineBuffer[responseLength] = '\0'; + // Update the length and ensure the line we've received is properly terminated + responseLength = strlen(lineBuffer); + lineBuffer[responseLength] = '\0'; - // We got something shorter than anything we're expecting, so try again - if( responseLength < minLength ) - continue; + if (debugSerial) + Serial.printf("Read line: '%s'\n", lineBuffer); - // Check if that line is the command prompt - if( strncmp( comPrompt, lineBuffer, strlen( comPrompt ) ) == 0 ) - continue; + // We got something shorter than anything we're expecting, so try again + if (responseLength < minLength) + continue; - // ...or if that line is an echo of the original command - if( strncmp( command, lineBuffer, commandLength ) == 0 ) - continue; + // Check if that line is the command prompt + if (strncmp(comPrompt, lineBuffer, strlen(comPrompt)) == 0) + continue; - // ...or if that line contains an expected response - if( acceptableResponse != NULL && strncmp( acceptableResponse, lineBuffer, acceptableLength ) == 0 ) - { - errorAcceptable = true; + // ...or if that line is an echo of the original command + if (strncmp(command, lineBuffer, commandLength) == 0) + continue; - // Even though we got what we want, we can't return yet; we need to go one more round - // so that we get the "Done" or "Error" that follows out of the serial buffer. - continue; - } + // ...or if that line contains an expected response + if (acceptableResponse != nullptr && strncmp(acceptableResponse, lineBuffer, acceptableLength) == 0) { + errorAcceptable = true; - // ...or if that line says "Done" - if( strncmp( comResponseSuccess, lineBuffer, successLength ) == 0 ) - return true; + // Even though we got what we want, we can't return yet; we need to go one more round + // so that we get the "Done" or "Error" that follows out of the serial buffer. + continue; + } - // ...or if that line says "Error" - if( strncmp( comResponseFail, lineBuffer, failLength ) == 0 ) - return errorAcceptable; + // ...or if that line says "Done" + if (strncmp(comResponseSuccess, lineBuffer, successLength) == 0) + return true; - // ...we got nothing we expected, so try again - } + // ...or if that line says "Error" + if (strncmp(comResponseFail, lineBuffer, failLength) == 0) + return errorAcceptable; + + // ...we got nothing we expected, so try again + } + + // We've timed out + return errorAcceptable; +} + +template +bool DFR_Radar::getConfig( + const char *command, + char outParams[NParams][MaxParamLength], + const char *responsePrefix +) { + char lineBuffer[64] = {0}; + const unsigned long timeout = millis() + comTimeout; + + static const size_t successLength = strlen(comResponseSuccess); + static const size_t failLength = strlen(comResponseFail); + static const size_t minResponseLength = min(successLength, failLength); + + const size_t commandLength = strlen(command); + size_t minLength = min(commandLength, minResponseLength); + + const size_t responsePrefixLength = responsePrefix == nullptr ? 0 : strlen(responsePrefix); + + if (responsePrefix != nullptr) + minLength = min(responsePrefixLength, minResponseLength); + + // Send the command... + serialWrite(command); + + uint8_t paramIndex = 0; + + // ...then wait for a response + while (millis() < timeout) { + if (sensorUART->available() <= 0) + continue; + + // Ensure the buffer is empty + memset(&lineBuffer[0], 0, sizeof(lineBuffer)); + + // Read a whole line + size_t responseLength = sensorUART->readBytesUntil('\n', lineBuffer, sizeof(lineBuffer)); + + // The sensor is supposed to terminate lines with , and we stopped at , + // so the last character in the line buffer should be a . If so, swap it out + // for a null terminator + const uint8_t lastCharacter = strlen(lineBuffer) - 1; + if (lineBuffer[lastCharacter] == '\r') + lineBuffer[lastCharacter] = '\0'; + + // Update the length and ensure the line we've received is properly terminated + responseLength = strlen(lineBuffer); + lineBuffer[responseLength] = '\0'; + + if (debugSerial) + Serial.printf("Read line: '%s'\n", lineBuffer); + + // We got something shorter than anything we're expecting, so try again + if (responseLength < minLength) + continue; + + // Check if that line is the command prompt + if (strncmp(comPrompt, lineBuffer, strlen(comPrompt)) == 0) + continue; + + // ...or if that line is an echo of the original command + if (strncmp(command, lineBuffer, commandLength) == 0) + continue; + + // ...or if that line says "Done" + if (strncmp(comResponseSuccess, lineBuffer, successLength) == 0) + continue; + + // ...or if that line says "Error" + if (strncmp(comResponseFail, lineBuffer, failLength) == 0) + continue; + + if (responsePrefix != nullptr && strncmp(responsePrefix, lineBuffer, responsePrefixLength) == 0) { + for (size_t i = responsePrefixLength; i < responseLength && paramIndex < NParams; i++) { + if (isWhitespace(lineBuffer[i])) + continue; + size_t paramCharIndex = 0; + do { + outParams[paramIndex][paramCharIndex++] = lineBuffer[i++]; + } while (i < responseLength && paramCharIndex < MaxParamLength && !isWhitespace(lineBuffer[i])); + outParams[paramIndex++][paramCharIndex] = '\0'; + } + if (debugSerial) { + Serial.printf("getConfig: Got %d/%d params\n", paramIndex, NParams); + for (auto i = 0; i < NParams; i++) { + Serial.printf("getConfig: Param %d: '%s'\n", i, outParams[i]); + } + } + return paramIndex == NParams; + } + } + return false; +} - // We've timed out - return errorAcceptable; +template +bool DFR_Radar::getConfig(const char *command, char outParams[NParams][MaxParamLength]) { + return getConfig(command, outParams, comResponse); } diff --git a/src/DFR_Radar.h b/src/DFR_Radar.h index fd24b05..2b2d795 100644 --- a/src/DFR_Radar.h +++ b/src/DFR_Radar.h @@ -12,28 +12,41 @@ */ -#ifndef __DFR_Radar_H__ -#define __DFR_Radar_H__ +#ifndef DFR_Radar_H_ +#define DFR_Radar_H_ #include -class DFR_Radar -{ - public: - +class DFR_Radar { +public: /** * @brief Constructor - * @param Stream Software serial port interface + * @param s Stream Software serial port interface */ - DFR_Radar( Stream *s ); + explicit DFR_Radar(Stream *s); /** * @brief Not currently implemented * * @return true */ - bool begin( void ); + bool begin(void); + + /** + * @brief Set the serial port to use for communicating with the sensor + * + * @param s The serial port to use + */ + void setStream(Stream *s); + + /** + * @brief Check if the sensor is ready to accept commands + * + * @return true if the sensor is ready; + * false if the sensor is not ready + */ + bool isReady() const; /** * @brief Configure sensor detection range @@ -49,7 +62,23 @@ class DFR_Radar * * @return false if the range values are invalid (no changes made), true otherwise */ - bool setDetectionRange( float rangeStart, float rangeEnd ); + bool setDetectionRange(float rangeStart, float rangeEnd); + + /** + * @brief Get sensor detection range + * + * @note Values are in meters; minimum is 0, maximum is 9.45; end must be greater than start. + * Internally, the range is converted to a level between 0-63, each one being ~15cm. If + * the value isn't a multiple of 15cm, the sensor will round down to the nearest 15cm, + * e.g. 5m = 5 / 0.15 = 33.3, which will be rounded down to 33, so your effective range + * would actually be 4.95m + * + * @param rangeStart Factory default is 0 + * @param rangeEnd Factory default is 6 + * + * @return true if command was successful + */ + bool getDetectionRange(float &rangeStart, float &rangeEnd); /** * @brief Set the sensitivity level @@ -58,7 +87,16 @@ class DFR_Radar * * @return false if the level value is invalid (no changes made), true otherwise */ - bool setSensitivity( uint8_t level ); + bool setSensitivity(uint8_t level); + + /** + * @brief Get the sensitivity level + * + * @param level 0 = Low, 9 = High, 7 = Factory Default + * + * @return true if the command was successful + */ + bool getSensitivity(uint8_t &level); /** * @brief Configure delays that translate actual presence activity to sensor assertion of presence @@ -70,7 +108,16 @@ class DFR_Radar * * @return false if either delay value is invalid (no changes made), true otherwise */ - bool setTriggerLatency( float confirmationDelay, float disappearanceDelay ); + bool setTriggerLatency(float confirmationDelay, float disappearanceDelay); + + /** + * @brief Gets delays that translate actual presence activity to sensor assertion of presence + * + * @note A longer confirmation delay can reduce false positives. A longer disappearance delay can bridge gaps between presence events. + * + * @return true if command was successful + */ + bool getTriggerLatency(float &confirmationDelay, float &disappearanceDelay); /** * @brief Configure delays between state changes on output (IO2) @@ -80,7 +127,7 @@ class DFR_Radar * * @return false if either delay value is invalid (no changes made), true otherwise */ - bool setOutputLatency( float triggerDelay, float resetDelay ); + bool setOutputLatency(float triggerDelay, float resetDelay); /** * @brief Check if the sensor is detecting presence @@ -88,7 +135,19 @@ class DFR_Radar * @return true if presence is currently being detected; * false if no presence or reading sensor failed */ - bool checkPresence( void ); + bool checkPresence(void) const; + + /** + * @brief Read if the sensor is detecting presence, difference from checkPresence it + * will return if sensor reading successful and the presence data is pass by + * reference parameter + * + * @param presence The presence data + * + * @return true if reading sensor successful + * false if reading sensor failed + */ + bool readPresence(bool &presence) const; /** * @brief Sets a delay between when the presence detection resets and when it can trigger again. @@ -100,7 +159,30 @@ class DFR_Radar * * @return false if the value is invalid (no changes made), true otherwise */ - bool setLockout( float time ); + bool setLockout(float time); + + /** + * @brief Gets the delay between when the presence detection resets and when it can trigger again. + * + * @note Used to prevent short-cycling (re-triggering immediately after a reset). + * + * @param time Time in seconds after the presence detection has reset before it can be triggered again. + * Range is 0.1 - 255; factory default is 1 + * + * @return true if command was successful + */ + bool getLockout(float &time); + + /** + * @brief Set whether the IOx pin is HIGH or LOW when triggered. + * + * @param ioPin which GPIO pin number to configure [0|1] (GPIO1 is not currently supported). + * @param triggerLevel HIGH = Vcc when triggered, Ground when idle (factory default) + * LOW = Ground when triggered, Vcc when idle + * + * @return false if the value is invalid (no changes made), true otherwise + */ + bool setTriggerLevel(uint8_t ioPin, uint8_t triggerLevel); /** * @brief Set whether the IO2 pin is HIGH or LOW when triggered. @@ -110,7 +192,128 @@ class DFR_Radar * * @return false if the value is invalid (no changes made), true otherwise */ - bool setTriggerLevel( uint8_t triggerLevel ); + bool setTriggerLevel(uint8_t triggerLevel); + + /** + * @brief Get whether the IOx pin is HIGH or LOW when triggered. + * + * @param ioPin which GPIO pin number to configure [0|1] (GPIO1 is not currently supported). + * @param triggerLevel HIGH = Vcc when triggered, Ground when idle (factory default) + * LOW = Ground when triggered, Vcc when idle + * + * @return true if command was successful + */ + bool getTriggerLevel(uint8_t ioPin, uint8_t &triggerLevel); + + /** + * @brief Get whether the IO2 pin is HIGH or LOW when triggered. + * + * @param triggerLevel HIGH = Vcc when triggered, Ground when idle (factory default) + * LOW = Ground when triggered, Vcc when idle + * + * @return true if command was successful + */ + bool getTriggerLevel(uint8_t &triggerLevel); + + /** + * @brief Enable or disable serial output modes for detection ($JYBSS) or point cloud ($JYRPO). + * + * @note Allows configuration of whether messages actively push updates and the frequency of updates. + * + * @param messageType The types of message to configure. + * 0: detection ($JYBSS); + * 1: point cloud ($JYRPO). + * @param enable False to completely disable the output. + * @param push True if updates should actively push data, false otherwise (factory default is true). + * @param period Update frequency in seconds [0.025, 1500) (factory default is 1.0). + * Larger than 1500 enables passive mode. Allowed range is 1 - 50 Hz. + * + * @return false if configuration values are invalid (no changes made), true otherwise. + */ + bool setUartOutput(uint8_t messageType, bool enable, bool push = false, float period = 1501); + + /** + * @brief Enable or disable serial output modes for detection ($JYBSS). + * + * @note Allows configuration of whether detection state messages ($JYBSS) actively push updates and the frequency of updates. + * + * @param enable False to completely disable the output. + * @param push True if updates should actively push data, false otherwise (factory default is true). + * @param period Update frequency in seconds [0.025, 1500) (factory default is 1.0). + * Larger than 1500 enables passive mode. Allowed range is 1 - 50 Hz. + * + * @return false if configuration values are invalid (no changes made), true otherwise. + */ + bool configureUartDetectionOutput(bool enable, bool push = false, float period = 1501); + + /** + * @brief Enable or disable serial output modes for point cloud ($JYRPO). + * + * @note Allows configuration of whether point cloud messages ($JYRPO) actively push updates and the frequency of updates. + * + * @param enable False to completely disable the output. + * @param push True if updates should actively push data, false otherwise (factory default is true). + * @param period Update frequency in seconds [0.025, 1500) (factory default is 1.0). + * Larger than 1500 enables passive mode. Allowed range is 1 - 50 Hz. + * + * @return false if configuration values are invalid (no changes made), true otherwise. + */ + bool configureUartPointCloudOutput(bool enable, bool push = false, float period = 1501); + + /** + * @brief Gets the state of the serial output modes for detection ($JYBSS) or point cloud ($JYRPO); + * whether they actively push updates, and the frequencies at which they update. + * + * @param messageType the type of message to request. 0: detection ($JYBSS); 1: point cloud ($JYRPO) + * @param enable false if output is completely disabled + * @param onChange whether it outputs immediately upon changes + * @param period the period at which it pushes updates. Active: 0.025-1500; Passive: >1500 + * + * @return true if command was successful + */ + bool getUartOutput(uint8_t messageType, bool &enable, bool &onChange, float &period); + + /** + * @brief Gets the state of the serial output mode for detection ($JYBSS); + * whether it actively pushes updates, and the frequencies at which it outputs. + * + * @param enable false if output is completely disabled + * @param onChange whether it outputs immediately upon changes + * @param period the period at which it pushes updates. Active: 0.025-1500; Passive: >1500 + * + * @return true if command was successful + */ + bool getUartDetectionOutput(bool &enable, bool &onChange, float &period); + + /** + * @brief Gets the state of the serial output mode for point cloud ($JYRPO); + * whether it should actively push updates, and the frequencies at which it outputs. + * + * @param enable false if output is completely disabled + * @param onChange whether it outputs immediately upon changes + * @param period the period at which it pushes updates. Active: 0.025-1500; Passive: >1500 + * + * @return true if command was successful + */ + bool getUartPointCloudOutput(bool &enable, bool &onChange, float &period); + + /** + * Enable or disable prompt printing (leapMMW:/>) and command echo. + * + * @param enable true to enable prompt and command echo + * + * @return true if command was successful + */ + bool setEcho(bool enable); + + /** + * Gets the status of prompt printing (leapMMW:/>) and command echo. + * + * @param enable true if prompot and command echo is enabled + * + * @return true if command was successful + */ + bool getEcho(bool &enable); /** * @brief Start the sensor @@ -118,7 +321,7 @@ class DFR_Radar * @return true if sensor started (or was already started); * false if sensor failed to start */ - bool start( void ); + bool start(void); /** * @brief Stop the sensor @@ -126,27 +329,27 @@ class DFR_Radar * @return true if sensor stopped (or was already stopped); * false if sensor failed to stop */ - bool stop( void ); + bool stop(void); /** * @brief Restart the sensor's internal software (safe; configuration is not lost or changed). * */ - void reboot( void ); + void reboot(void) const; /** * @brief Disable the LED * * @return true if command was successful */ - bool disableLED( void ); + bool disableLED(void); /** * @brief Enable the LED * * @return true if command was successful */ - bool enableLED( void ); + bool enableLED(void); /** * @brief Set whether LED is enabled. @@ -157,7 +360,16 @@ class DFR_Radar * * @return true if command was successful */ - bool configureLED( bool disabled ); + bool configureLED(bool disabled); + + /** + * @brief Get whether LED is enabled. + * + * @param disabled true if LED is disabled, false if enabled + * + * @return true if command was successful + */ + bool getLEDMode(bool &disabled); /** * @brief Allows setting multiple configuration options without @@ -166,7 +378,7 @@ class DFR_Radar * * @return false if the sensor failed to stop (multi-config mode will be disabled), true otherwise */ - bool configBegin( void ); + bool configBegin(void); /** * @brief Allows setting multiple configuration options without @@ -176,7 +388,7 @@ class DFR_Radar * @return false if multi-config mode isn't enabled (forgot to call `configBegin()` first or it failed), * or if saving or re-starting failed; true otherwise */ - bool configEnd( void ); + bool configEnd(void); /** * @brief Restore the sensor configuration to factory default settings. @@ -184,19 +396,43 @@ class DFR_Radar * @return true if command was successful; * false if the sensor failed to stop or if the ecommand failed */ - bool factoryReset( void ); + bool factoryReset(void); + /** + * Gets the sensor's hardware version. + * + * @param version reference to the sensor's hardware version string. + * + * @return true if command was successful + */ + bool getHWVersion(char *version); - private: + /** + * Gets the sensor's software version. + * + * @param version reference to the sensor's software version string. + * + * @return true if command was successful + */ + bool getSWVersion(char *version); + /** + * @brief Enable or disable USB serial debugging output of sensor data. + * + * @param enable Whether to enable printing UART data over serial port + */ + void setDebug(const bool enable) { debugSerial = enable; } + +private: /** * @brief Read a line (or more) from the UART port * * @param buffer Store the read data + * @param lineCount number of lines to read * * @return length of characters captured */ - size_t readLines( char *buffer, size_t lineCount = 1 ); + size_t readLines(char *buffer, size_t lineCount = 1) const; /** * @brief Executes a command string after first stopping the sensor, then afterwards @@ -211,14 +447,14 @@ class DFR_Radar * @return true if command was successful; * false if sensor failed to stop or re-start, command failed, or save failed */ - bool setConfig( const char *command ); + bool setConfig(const char *command); /** * @brief Commits configuration data to flash * * @return true if command was successful */ - bool saveConfig( void ); + bool saveConfig(void) const; /** * @brief Used to ensure commands are terminated before writing to UART @@ -227,7 +463,7 @@ class DFR_Radar * it seems to work without it (sensor MCU probably catches the \0), * but let's just be sure we're doing everything right. */ - size_t serialWrite( const char *command ); + size_t serialWrite(const char *command) const; /** * @brief Writes a command string to the sensor UART port and waits for response @@ -237,7 +473,7 @@ class DFR_Radar * @return true if response was "Done"; * false if "Error" or timeout */ - bool sendCommand( const char *command ); + bool sendCommand(const char *command) const; /** * @brief Writes a command string to the sensor UART port and compares the response to one provided @@ -247,12 +483,46 @@ class DFR_Radar * return `true`. * * @param command A command string generated by one of the other config/command methods - * @param acceptResponse The word or phrase to look for that if found will return `true` + * @param acceptableResponse The word or phrase to look for that if found will return `true` * * @return true if response was "Done" or matched `acceptResponse`; * false if timeout or "Error" (and response didn't already match `acceptResponse`) */ - bool sendCommand( const char *command, const char *acceptResponse ); + bool sendCommand(const char *command, const char *acceptableResponse) const; + + /** + * @brief Request the value of the sensor's setting. + * + * @tparam NParams The maximum number of parameters provided in the response. (i.e. getRange has 2). + * @tparam MaxParamLength The maximum length a parameter can be (i.e. a float with a max value of 1500.000 has 9 including '\0'). + * @param command The command string to request data for. + * @param outParams A 2-D array of size NParams x MaxParamLength to return the parsed parameters back. + * @param responsePrefix An optional response prefix used to determine the line with relevant data. + * + * @return True if command was successful and the requested parameters were parsed. + */ + template + bool getConfig( + const char *command, + char outParams[NParams][MaxParamLength], + const char *responsePrefix + ); + + /** + * @brief Request the value of the sensor's setting. Overloaded with an empty responsePrefix. + * + * @tparam NParams The maximum number of parameters provided in the response. (i.e. getRange has 2). + * @tparam MaxParamLength The maximum length a parameter can be (i.e. a float with a max value of 1500.000 has 9 including '\0'). + * @param command The command string to request data for. + * @param outParams A 2-D array of size NParams x MaxParamLength to return the parsed parameters back. + * + * @return True if command was successful and the requested parameters were parsed. + */ + template + bool getConfig( + const char *command, + char outParams[NParams][MaxParamLength] + ); /** * @brief The serial port (hardware or software) to use for communicating with the sensor @@ -263,51 +533,90 @@ class DFR_Radar // bool isConfigured; bool stopped; bool multiConfig; - - static const uint16_t readPacketTimeout = 100; - static const size_t packetLength = 64; - - static const unsigned long startupDelay = 2000; - - static const unsigned long comTimeout = 1000; - static constexpr const char *comStop = "sensorStop"; - static constexpr const char *comStart = "sensorStart"; - static constexpr const char *comResetSystem = "resetSystem 0"; - static constexpr const char *comSetSensitivity = "setSensitivity %u"; - static constexpr const char *comOutputLatency = "outputLatency -1 %u %u"; - static constexpr const char *comSetGpioMode = "setGpioMode 1 %u"; - static constexpr const char *comGetOutput = "getOutput 1"; - static constexpr const char *comSetLedMode = "setLedMode 1 %u"; - // static constexpr const char *comSetUartOutput = "setUartOutput 1 1 0 1501"; - static constexpr const char *comSetEcho = "setEcho 0"; + bool debugSerial; + + static constexpr uint16_t readPacketTimeout = 100; + static constexpr size_t packetLength = 64; + + static constexpr unsigned long startupDelay = 2000; + + static constexpr unsigned long comTimeout = 1000; + static constexpr const char *comResponse = "Response "; + static constexpr const char *comStop = "sensorStop"; + static constexpr const char *comStart = "sensorStart"; + static constexpr const char *comResetSystem = "resetSystem 0"; + static constexpr const char *comSetSensitivity = "setSensitivity %u"; + static constexpr const char *comGetSensitivity = "getSensitivity"; + static constexpr const char *comOutputLatency = "outputLatency -1 %u %u"; + static constexpr const char *comSetGpioMode = "setGpioMode %u %u"; + static constexpr const char *comGetGpioMode = "getGpioMode %u"; + static constexpr const char *comGetOutput = "getOutput 1"; + static constexpr const char *comSetLedMode = "setLedMode 1 %u"; + static constexpr const char *comGetLedMode = "getLedMode 1"; + /** + * @brief setUartOutput command parameters: + * | Parameter | Value | Description + * ---|-----------|-------|------------ + * 1 | Type | 1-2 | 1=Detection, 2=Point cloud + * 2 | Enabled | 0-1 | 0=Disabled, 1=Enabled + * 3 | Data Mode | 0-1 | 0=Passive(getOutput only), 1=Active + * 4 | Period | 1501 | Frequency of active output + * + * Serial data output mode and period, with the period unit being second: + * (1) When par4 is set between 0.025 and 1500: + * - The value represents the period of actively outputting data. + * - When par3 = 0, data is actively outputted based on the period set by + * par4 (default parameter). + * - When par3 = 1, data is outputted immediately when changes occur, or + * outputted based on the period set by par4 if there's no change. + * (2) When par4 is set to >1500: + * - Data is passively outputted. + * - When par3 = 0, data is not actively outputted and can only be retrieved + * using the `getOutput` query command. + * - When par3 = 1, data is outputted immediately when changes occur, or + * not outputted if there's no change. + * + * @link [DFRobot SEN0521 Manual](https://github.com/user-attachments/files/17264778/sen0521.pdf) + * @link [LeapMMW HS2xx3A v1.3 Manual](https://www.leapmmw.com/wp-content/uploads/1609/47/%E7%94%A8%E6%88%B7%E6%89%8B%E5%86%8CV1.3%EF%BC%9AHS2xx3A%E7%B3%BB%E5%88%97%E4%BA%BA%E5%AD%98%E5%9C%A8%E6%A3%80%E6%B5%8B%E6%A8%A1%E5%9D%97.pdf) + */ + static constexpr const char *comSetUartOutputFull = "setUartOutput %u %u %u %.3f"; + static constexpr const char *comSetUartOutput = "setUartOutput %u %u"; + static constexpr const char *comGetUartOutput = "getUartOutput %u"; + static constexpr const char *comSetEcho = "setEcho %u"; + static constexpr const char *comGetEcho = "getEcho"; + static constexpr const char *comGetHWV = "getHWV"; + static constexpr const char *comGetSWV = "getSWV"; static constexpr const char *comResponseSuccess = "Done"; - static constexpr const char *comResponseFail = "Error"; - static constexpr const char *comFailStopped = "sensor stopped already"; - static constexpr const char *comFailStarted = "sensor started already"; - static constexpr const char *comSaveCfg = "saveConfig"; - static constexpr const char *comFactoryReset = "resetCfg"; - static constexpr const char *comPrompt = "leapMMW:/>"; - - #ifdef __AVR__ - #ifdef _STDLIB_H_ - static constexpr const char *comSetRange = "setRange %s %s"; - static constexpr const char *comSetLatency = "setLatency %s %s"; - static constexpr const char *comSetInhibit = "setInhibit %s"; - #else - #warning Floats in `sprintf()` will be converted to integers - static constexpr const char *comSetRange = "setRange %u %u"; - static constexpr const char *comSetLatency = "setLatency %u %u"; - static constexpr const char *comSetInhibit = "setInhibit %u"; - #endif - #else - // TODO: - // #if !defined( ESP32 ) && !defined( ARDUINO_TEENSY ) && ... - #warning Assuming floats work in `sprintf()` -- it might not! - //#endif - static constexpr const char *comSetRange = "setRange %.3f %.3f"; - static constexpr const char *comSetLatency = "setLatency %.3f %.3f"; - static constexpr const char *comSetInhibit = "setInhibit %.3f"; - #endif + static constexpr const char *comResponseFail = "Error"; + static constexpr const char *comFailStopped = "sensor stopped already"; + static constexpr const char *comFailStarted = "sensor started already"; + static constexpr const char *comSaveCfg = "saveConfig"; + static constexpr const char *comFactoryReset = "resetCfg"; + static constexpr const char *comPrompt = "leapMMW:/>"; + +#ifdef __AVR__ +#ifdef _STDLIB_H_ + static constexpr const char *comSetRange = "setRange %s %s"; + static constexpr const char *comSetLatency = "setLatency %s %s"; + static constexpr const char *comSetInhibit = "setInhibit %s"; +#else +#warning Floats in `sprintf()` will be converted to integers + static constexpr const char *comSetRange = "setRange %u %u"; + static constexpr const char *comSetLatency = "setLatency %u %u"; + static constexpr const char *comSetInhibit = "setInhibit %u"; +#endif +#else + // TODO: + // #if !defined( ESP32 ) && !defined( ARDUINO_TEENSY ) && ... +#warning Assuming floats work in `sprintf()` -- it might not! + //#endif + static constexpr const char *comSetRange = "setRange %.3f %.3f"; + static constexpr const char *comSetLatency = "setLatency %.3f %.3f"; + static constexpr const char *comSetInhibit = "setInhibit %.3f"; +#endif + static constexpr const char *comGetRange = "getRange"; + static constexpr const char *comGetLatency = "getLatency"; + static constexpr const char *comGetInhibit = "getInhibit"; }; #endif