├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── CHANGELOG.md ├── README.md ├── Sweep ├── Examples │ ├── MegaDistanceBlink │ │ └── MegaDistanceBlink.ino │ └── MegaSerialPrinter │ │ └── MegaSerialPrinter.ino ├── ScanPacket.cpp ├── ScanPacket.h ├── Sweep.cpp ├── Sweep.h └── keywords.txt └── wiring_diagrams └── MegaSerialPrinter.png /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Help 2 | The [issues](https://github.com/scanse/sweep-arduino/issues) section is for bug reports and feature requests. If you need help with a project, please use the [community](http://community.scanse.io/) forum. 3 | 4 | --- 5 | # Bugs 6 | #### Before reporting a bug 7 | 8 | 1. Search [issue tracker](https://github.com/scanse/sweep-arduino/issues) for similar issues. 9 | 2. Make sure you are using the latest version of the code, and that you have thoroughly reviewed all provided documentation. 10 | 3. Make sure you are using the latest version of the sweep firmware that is compatible with the arduino code. 11 | 12 | #### How to report a bug 13 | 14 | 1. Describe the problem in detail. Explain what happened, and what you expected would happen. 15 | 2. If it makes things more clear, include a code snipper or an annotated screenshot. 16 | 17 | --- 18 | # Contribution 19 | The `sweep-arduino` repository will evolve much more quickly with community contribution! We will work hard to integrate valuable changes, improvements and features. To make the process more efficient, here are some general steps to contribute. 20 | 21 | #### How to contribute 22 | 23 | 1. Login or create a GitHub account. 24 | 2. Fork the repository on GitHub. 25 | 3. Make changes to your fork of the repository. 26 | 4. Check the [Contribution Guidelines](PULL_REQUEST_TEMPLATE.md) for pull requests and make sure your modifications adhere to the guidelines. 27 | 5. Submit your modifications as a new pull request [here](https://github.com/scanse/sweep-arduino/pulls). -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | #### Scope of changes 16 | 21 | 22 | #### Known Limitations 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 9/7/2017 4 | - Updated `README` 5 | - Updated `keywords.txt` 6 | - Expanded `ScanPacket` struct into full class 7 | - `ScanPacket` is now immutable, and its data is accessed through getter methods 8 | - Obtaining a reading now requires a separate boolean variable for the success flag 9 | - Angle data is efficiently stored in raw 16-bit fixed point value 10 | - Getter methods provide an interface to obtain the angle as a raw value or as a float in degrees 11 | - Removed unused method `angle_raw_to_deg` from `Sweep.h` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sweep-arduino 2 | Arduino Library for Scanse Sweep LiDAR 3 | 4 | # Compatibility 5 | ### Arduino 6 | Currently the library has only been tested with an `Arduino Mega 2560`, an `Arduino Mega ADK`, and an `Arduino Micro`. 7 | 8 | ### Sweep Firmware 9 | | sweep firmware | compatibility | 10 | | :------------: | :-----------: | 11 | | > v1.4 | untested | 12 | | v1.4 | yes | 13 | 14 | The library is designed to support the most recent Sweep firmware version. The communication protocol used by earlier firmware versions may not work properly with this library. A guide to updating the Sweep's firmware is available [here](https://support.scanse.io/hc/en-us/articles/224557908-Upgrading-Firmware). 15 | 16 | # Installation 17 | Copy the entire `Sweep/` folder to your `.../Arduino/libraries/` directory. 18 | 19 | # Use 20 | Checkout the provided `Examples/` directory for 2 full examples. 21 | 22 | For best results, you should provide dedicated external 5V power to the Sweep rather than using power from the Arduino. Just be sure to connect the ground from the power source and the arduino. If you are just experimenting, you can run the sweep off the 5V power from the Arduino with the Arduino receiving power over USB. However, it is possible that using a low power USB port (ex: laptop) to power the arduino + sweep will result in unexpected behavior. 23 | 24 | ![Alt text](wiring_diagrams/MegaSerialPrinter.png?raw=true "Title") 25 | 26 | 27 | 28 | Include the Sweep library in your sketch: 29 | ```arduino 30 | #include 31 | ... 32 | ``` 33 | 34 | Create a Sweep device with a Stream Object: 35 | 36 | ```c 37 | // Ex: assuming sweep is connected to Serial #1 (RX1 & TX1) on the Mega 38 | Sweep device(Serial1); 39 | ... 40 | 41 | ``` 42 | 43 | Adjust device settings: 44 | ```c++ 45 | // Set the motor speed to 5HZ (codes available from 1->10 HZ) 46 | bool bSuccess = device.setMotorSpeed(MOTOR_SPEED_CODE_5_HZ); 47 | ... 48 | // Set the sample rate to 500HZ (codes available for 500, 750 and 1000 HZ) 49 | bool bSuccess = device.setSampleRate(SAMPLE_RATE_CODE_500_HZ); 50 | ``` 51 | 52 | Retrieve current device settings: 53 | ```c 54 | // Read the current motor speed 55 | int32_t currentMotorSpeed = device.getMotorSpeed(); 56 | if (currentMotorSpeed < 0) 57 | // Failed to get current motor speed 58 | ... 59 | 60 | // Read the current sample rate 61 | int32_t currentSampleRate = device.getSampleRate(); 62 | if (currentSampleRate < 0) 63 | // Failed to get current sample rate 64 | ... 65 | ``` 66 | 67 | Data collection: 68 | ```c 69 | 70 | // Start scanning 71 | bool bSuccess = device.startScanning(); 72 | ... 73 | 74 | // read 10 individual sensor readings 75 | for (int i = 0; i < 10; i++) { 76 | // Create a scan packet to populate with the sensor reading 77 | bool success = false; 78 | ScanPacket reading = device.getReading(success); 79 | if(success) { 80 | bool bFirstOfNewScan = reading.isSync(); 81 | float angle = reading.getAngleDegrees(); 82 | uint16_t range = reading.getDistanceCentimeters(); 83 | uint8_t confidence = reading.getSignalStrength(); 84 | ... 85 | 86 | // stop scanning 87 | bool bSuccess = device.stopScanning(); 88 | 89 | ``` 90 | 91 | 92 | # API 93 | 94 | ### Sweep 95 | 96 | ```c++ 97 | Sweep(Stream &serial) 98 | ``` 99 | 100 | Constructs a sweep device on the provided Stream object. 101 | 102 | ```c++ 103 | bool isScanning() 104 | ``` 105 | 106 | Returns true if the device is currently scanning. 107 | 108 | ```c++ 109 | bool startScanning() 110 | ``` 111 | Blocks until the sweep device is ready, then signals the device to start scanning. Initiates an indefinite stream of individual sensor readings until `stopScanning()` is called. 112 | During an active data stream, any attempt to communicate with the device other than `stopScanning()` will fail. 113 | 114 | To avoid blocking, you can use the `getMotorReady()` command to query the current state of the device , and do work in the meantime if it isn't ready. 115 | 116 | 117 | ```c++ 118 | bool stopScanning() 119 | ``` 120 | 121 | Signals the sweep device to stop scanning. Will block for ~500ms to flush leftover data stream from the buffer and validate a second response from the sensor. 122 | 123 | ```c++ 124 | ScanPacket getReading(bool &success) 125 | ``` 126 | 127 | Reads a single sensor reading from the serial buffer. Must be called frequently enough to keep up with the data stream. 128 | 129 | ```c++ 130 | class ScanPacket 131 | ``` 132 | 133 | Class representing a single sensor reading (ranging). ie: a full 360deg scan would be composed of many such readings. 134 | 135 | ```c++ 136 | bool getMotorReady() 137 | ``` 138 | 139 | Returns true if the device is ready. A device is ready when the motor speed has stabilized to the current setting and the calibration routine is complete. If a device was just powered on, or the motor speed was just changed, it can take up to 9 seconds for the device to get ready. For visual confirmation, the blue LED on the face of the sweep will blink until the device is ready. 140 | 141 | ```c++ 142 | bool waitUntilMotorReady() 143 | ``` 144 | 145 | Blocks until the device is ready. Returns true if the device is ready, and false if the check timed out (max 10 seconds). 146 | 147 | 148 | ```c++ 149 | int32_t getMotorSpeed() 150 | ``` 151 | 152 | Returns the current motor speed setting in HZ 153 | 154 | ```c++ 155 | bool setMotorSpeed(const uint8_t motorSpeedCode[2]) 156 | 157 | // Available Codes 158 | MOTOR_SPEED_CODE_0_HZ 159 | MOTOR_SPEED_CODE_1_HZ 160 | MOTOR_SPEED_CODE_2_HZ 161 | MOTOR_SPEED_CODE_3_HZ 162 | MOTOR_SPEED_CODE_4_HZ 163 | MOTOR_SPEED_CODE_5_HZ 164 | MOTOR_SPEED_CODE_6_HZ 165 | MOTOR_SPEED_CODE_7_HZ 166 | MOTOR_SPEED_CODE_8_HZ 167 | MOTOR_SPEED_CODE_9_HZ 168 | MOTOR_SPEED_CODE_10_HZ 169 | ``` 170 | 171 | Blocks until the device is ready, then adjusts the motor speed setting to the provided code. Recommend users pass one of the const codes defined by library. 172 | 173 | To avoid blocking, you can use the `getMotorReady()` command to query the current state of the device , and do work in the meantime if it isn't ready. 174 | 175 | ```c++ 176 | int32_t getSampleRate() 177 | ``` 178 | 179 | Returns the current sample rate setting in HZ. 180 | 181 | ```c++ 182 | bool setSampleRate(const uint8_t sampleRateCode[2]) 183 | 184 | // Available Codes 185 | SAMPLE_RATE_CODE_500_HZ 186 | SAMPLE_RATE_CODE_750_HZ 187 | SAMPLE_RATE_CODE_1000_HZ 188 | ``` 189 | 190 | Adjusts the sample rate setting to the provided code. Recommend users pass one of the const codes defined by library. 191 | 192 | 193 | ```c++ 194 | void reset() 195 | ``` 196 | 197 | Resets the device. Attempting to communicate with the device while it is resetting will cause issues. While the device is booting the LED on the device will blink green. The device is capable of communication when the LED turns from green to blue, although the motor may still be adjusting until the blue LED stops blinking as well. 198 | 199 | ### ScanPacket 200 | 201 | ```c++ 202 | ScanPacket(const bool bIsSync, const uint16_t rawAngle, const uint16_t distance, const uint8_t signalStrength) 203 | ``` 204 | 205 | Constructs a ScanPacket object with the given parameters. The construction of the ScanPacket object for a reading is handled by the Sweep class's getReading() method; you should not need to use this constructor. 206 | 207 | ```c++ 208 | bool isSync() const 209 | ``` 210 | 211 | Returns true if this reading was the first reading of a new scan; otherwise, returns false. 212 | 213 | ```c++ 214 | float getAngleDegrees() const 215 | ``` 216 | 217 | Returns the angle of this reading as a float, in degrees. 218 | 219 | ```c++ 220 | uint16_t getAngleRaw() const 221 | ``` 222 | 223 | Returns the angle of this reading as the raw 16-bit fixed point value. The scaling factor of this value is 16; this means that the angle in degrees is obtained by dividing the raw value by 16. 224 | 225 | ```c++ 226 | uint16_t getDistanceCentimeters() const 227 | ``` 228 | 229 | Returns the distance of this reading, in centimeters. 230 | 231 | ```c++ 232 | uint8_t getSignalStrength() const 233 | ``` 234 | 235 | Returns the signal strength of this reading as an integer value between 0 and 255. Signal strength can be thought of as a confidence metric. A low value indicates low strength, and a high value indicates high strength. 236 | 237 | > **Note:** The signal strenth value is affected by light conditions, material properties etc. For more information, see the article on the [Theory of Operation](https://support.scanse.io/hc/en-us/articles/115006333327-Theory-of-Operation). 238 | 239 | ```c++ 240 | float getNormalizedSignalStrength() const 241 | ``` 242 | 243 | Returns the signal strength of this reading as a float value normalized between 0 and 1. ie: ints [0:255] map to floats [0.0f:1.0f]. 244 | > **Note:** this method only normalizes a raw byte value. It does NOT normalize the signal strength of this reading in the context of the min and max signal strengths from all readings in a scan, as the reading class has no knowledge of a scan. 245 | -------------------------------------------------------------------------------- /Sweep/Examples/MegaDistanceBlink/MegaDistanceBlink.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Scanse Sweep Arduino Library Examples 4 | 5 | MegaDistanceBlink: 6 | - Example sketch for using the Scanse Sweep with the Arduino Mega 2560. 7 | - Blinks the onboard LED at a frequency based on the distance of the 8 | closest object in the angular window 330-360 degrees (ie: the 30 9 | degrees before the azimuth 0 degree mark). For reference, the thin 10 | groove and LED on the face of the sweep sensor represent the 0 degree 11 | mark. 12 | - Expected behavior: 13 | 1. Resets the device and waits a while to guarantee sweep has time to reset. 14 | During this time the LED onboard the arduino will blink at ~1Hz 15 | 2. Adjusts device settings and verifies the parameters. During this 16 | step the LED on the face of the sweep should blink blue. 17 | 3. Waits for the motor speed to stabilize/calibrate and then starts data 18 | acquisition. 19 | 4. Checks for the closest object within 1 meter, within the angular range 20 | [330:360] degrees. Stand in front of this angular region and walk back 21 | and forth from about 25cm to 100cm away from the device. You should see 22 | LED onboard the arduino blink rapidly when you are 1m away from the sweep, 23 | and slowly when you are close to the sweep. 24 | 5. Continues indefinitely, and never stops the data stream. Sensor will have to 25 | be powered cycled to rerun the script. 26 | - Although the sweep is not meant to produce reliable readings below 27 | 1 meter, for this example the bounds of considered range are 0 to 100 cm. 28 | This is meant to allow the frequency adjustment to be obvious as you 29 | move near the sweep. 30 | - Assumes Sweep sensor is physically connected to Serial #1 (RX1 & TX1) 31 | - For the sweep's power, ground, RX & TX pins, follow the connector 32 | pinouts in the sweep user manual located here: 33 | http://scanse.io/downloads 34 | - Be sure to connect RX_device -> TX_arduino & TX_device -> RX_arduino 35 | - You should provide dedicated external 5V power to the Sweep... NOT using 36 | the arduino's power. Be sure to connect the ground from the power source 37 | and the arduino. 38 | 39 | Created by Scanse LLC, June 7th, 2017. 40 | Released into the public domain. 41 | */ 42 | 43 | #include 44 | 45 | const uint16_t DIST_CM_MIN = 1; // (cm) Clip range values on the low end 46 | const uint16_t DIST_CM_MAX = 100; // (cm) Clip range values on the high end 47 | const uint16_t DIST_CM_RANGE = DIST_CM_MAX - DIST_CM_MIN; 48 | const uint16_t BLINK_INTERVAL_MS_MIN = 100; // (milliseconds) Min blink interval, indicates max distance 49 | const uint16_t BLINK_INTERVAL_MS_MAX = 2000; // (milliseconds) Max blink interval, indicates min distance 50 | const uint16_t BLINK_INTERVAL_MS_RANGE = BLINK_INTERVAL_MS_MAX - BLINK_INTERVAL_MS_MIN; 51 | 52 | // the angular range (Field of View). The distance of the shortest valid ranging in the 53 | // angular window between [360-FOV: 0] degrees will be used. 54 | // (0 deg corresponds to the thin groove + LED on the face of the sweep device) 55 | const uint8_t FOV = 30; 56 | 57 | // Finite States for the program sequence 58 | const uint8_t STATE_WAIT_ON_RESET = 0; 59 | const uint8_t STATE_ADJUST_DEVICE_SETTINGS = 1; 60 | const uint8_t STATE_VERIFY_CURRENT_DEVICE_SETTINGS = 2; 61 | const uint8_t STATE_BEGIN_DATA_ACQUISITION = 3; 62 | const uint8_t STATE_GATHER_DATA = 4; 63 | const uint8_t STATE_UPDATE_BLINK_FREQUENCY = 5; 64 | const uint8_t STATE_RESET = 6; 65 | const uint8_t STATE_ERROR = 7; 66 | 67 | // Create a Sweep device using Serial #1 (RX1 & TX1) 68 | Sweep device(Serial1); 69 | 70 | uint16_t closestDistanceInSpecifiedFOV = 4000; // the distance (in cm) of the closest object in the specified angular range 71 | uint16_t interval = 1000; // interval at which to blink onboard LED (milliseconds) 72 | int ledState = LOW; // ledState used to set the LED 73 | unsigned long previousMillis = 0; // time of the last LED update 74 | 75 | // Current state in the program sequence 76 | uint8_t currentState = STATE_WAIT_ON_RESET; 77 | 78 | void setup() 79 | { 80 | // initialize digital pin LED_BUILTIN as an output. 81 | pinMode(LED_BUILTIN, OUTPUT); 82 | digitalWrite(LED_BUILTIN, LOW); 83 | 84 | // initialize serial1 for the sweep device 85 | Serial1.begin(115200); // sweep device 86 | 87 | // initialize counter variables and reset the current state 88 | reset(); 89 | } 90 | 91 | // Loop functions as an FSM (finite state machine) 92 | void loop() 93 | { 94 | switch (currentState) 95 | { 96 | case STATE_WAIT_ON_RESET: 97 | currentState = waitOnReset() ? STATE_ADJUST_DEVICE_SETTINGS : STATE_ERROR; 98 | break; 99 | case STATE_ADJUST_DEVICE_SETTINGS: 100 | currentState = adjustDeviceSettings() ? STATE_VERIFY_CURRENT_DEVICE_SETTINGS : STATE_ERROR; 101 | break; 102 | case STATE_VERIFY_CURRENT_DEVICE_SETTINGS: 103 | currentState = verifyCurrentDeviceSettings() ? STATE_BEGIN_DATA_ACQUISITION : STATE_ERROR; 104 | break; 105 | case STATE_BEGIN_DATA_ACQUISITION: 106 | // Attempt to start scanning (will wait for motor speed to stabilize and calibration routine to complete...) 107 | currentState = device.startScanning() ? STATE_GATHER_DATA : STATE_ERROR; 108 | break; 109 | case STATE_GATHER_DATA: 110 | if (gatherDistanceInfo()) 111 | currentState = STATE_UPDATE_BLINK_FREQUENCY; 112 | break; 113 | case STATE_UPDATE_BLINK_FREQUENCY: 114 | updateBlinkFrequency(); 115 | // reset the closest distance 116 | closestDistanceInSpecifiedFOV = 4000; 117 | currentState = STATE_GATHER_DATA; 118 | break; 119 | case STATE_RESET: 120 | reset(); 121 | currentState = STATE_WAIT_ON_RESET; 122 | break; 123 | default: // there was some error 124 | // DO NOTHING 125 | interval = 10; // short enough interval to make the LED appear as if it is ON 126 | break; 127 | } 128 | 129 | updateLED(); 130 | } 131 | 132 | // Waits ~8 seconds for the device to reset and verifies it can communicate 133 | bool waitOnReset() 134 | { 135 | int tempState = LOW; 136 | for (int i = 0; i < 16; i++) 137 | { 138 | delay(500); 139 | // toggle the LED 140 | tempState = (tempState == LOW) ? HIGH : LOW; 141 | digitalWrite(LED_BUILTIN, tempState); 142 | } 143 | 144 | // to verify communication, check a random command response that doesn't require motor is ready 145 | return device.getSampleRate() > 0; 146 | } 147 | 148 | // Adjusts the device settings 149 | bool adjustDeviceSettings() 150 | { 151 | // Set the motor speed to 2HZ (codes available from 1->10 HZ) 152 | bool bSuccess = device.setMotorSpeed(MOTOR_SPEED_CODE_2_HZ); 153 | 154 | /* 155 | * Device will always default to 500HZ scan rate when it is powered on. 156 | * Snippet below is left for reference. 157 | */ 158 | // Set the sample rate to 500HZ (codes available for 500, 750 and 1000 HZ) 159 | //bool bSuccess = device.setSampleRate(SAMPLE_RATE_CODE_500_HZ); 160 | return bSuccess; 161 | } 162 | 163 | // Querries the current device settings (motor speed and sample rate) 164 | // and prints them to the console 165 | bool verifyCurrentDeviceSettings() 166 | { 167 | // Read the current motor speed and sample rate 168 | int32_t currentMotorSpeed = device.getMotorSpeed(); 169 | if (currentMotorSpeed < 0) 170 | return false; 171 | int32_t currentSampleRate = device.getSampleRate(); 172 | if (currentSampleRate < 0) 173 | return false; 174 | return true; 175 | } 176 | 177 | // Gathers distance info, for the angular region [360-FOV: 0] degree 178 | // returns true if distances for the complete range [360-FOV: 0] have been collected 179 | bool gatherDistanceInfo() 180 | { 181 | // attempt to get the next scan packet 182 | // Note: getReading() will return a ScanPacket object 183 | bool success = false; 184 | ScanPacket reading = device.getReading(success); 185 | if (success) 186 | { 187 | // marks the end of the angular range, so report true 188 | if (reading.isSync()) 189 | return true; 190 | 191 | // consider a Field of View in the angular range [360-FOV: 0] 192 | if (reading.getAngleDegrees() > 360 - FOV) 193 | { 194 | // only consider valid readings (sensor will report distance of 1 for failed readings) 195 | uint16_t dist = reading.getDistanceCentimeters(); 196 | if (dist > 1) 197 | { 198 | // check if this reading is closer than anything seen so far 199 | if (dist < closestDistanceInSpecifiedFOV) 200 | closestDistanceInSpecifiedFOV = dist; 201 | } 202 | } 203 | } 204 | return false; 205 | } 206 | 207 | // Updates the blink frequency based off the average distance around the azimuth 0 degree mark 208 | void updateBlinkFrequency() 209 | { 210 | // clip the distance 211 | uint16_t dist = max(closestDistanceInSpecifiedFOV, DIST_CM_MIN); 212 | dist = min(dist, DIST_CM_MAX); 213 | 214 | // normalize the average 215 | float normalized = (1.0 * DIST_CM_MAX - dist) / DIST_CM_RANGE; 216 | 217 | // calculate a blink interval (in ms) from the normalized distance 218 | interval = (uint16_t)(BLINK_INTERVAL_MS_MIN + normalized * BLINK_INTERVAL_MS_RANGE); 219 | } 220 | 221 | // update the LED if necessary (if the difference between the current 222 | // time and last update time is greater thant the desired interval) 223 | void updateLED() 224 | { 225 | unsigned long currentMillis = millis(); 226 | 227 | if (currentMillis - previousMillis >= interval) 228 | { 229 | // store the timestamp 230 | previousMillis = currentMillis; 231 | 232 | // determine the new State 233 | ledState = (ledState == LOW) ? HIGH : LOW; 234 | 235 | // toggle the LED 236 | digitalWrite(LED_BUILTIN, ledState); 237 | } 238 | } 239 | 240 | // Resets the variables and state so the sequence can be repeated 241 | void reset() 242 | { 243 | currentState = STATE_WAIT_ON_RESET; 244 | closestDistanceInSpecifiedFOV = 4000; 245 | ledState = LOW; 246 | digitalWrite(LED_BUILTIN, ledState); // turn the LED off by making the voltage LOW 247 | device.reset(); 248 | } 249 | -------------------------------------------------------------------------------- /Sweep/Examples/MegaSerialPrinter/MegaSerialPrinter.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Scanse Sweep Arduino Library Examples 4 | 5 | MegaSerialPrinter: 6 | - Example sketch for using the Scanse Sweep with the Arduino Mega 2560. 7 | Collects 3 complete scans, and then prints the sensor readings 8 | - Assumes Sweep sensor is physically connected to Serial #1 (RX1 & TX1) 9 | - For the sweep's power, ground, RX & TX pins, follow the connector 10 | pinouts in the sweep user manual located here: 11 | http://scanse.io/downloads 12 | - Be sure to connect RX_device -> TX_arduino & TX_device -> RX_arduino 13 | - For best results, you should provide dedicated external 5V power to the Sweep 14 | rather than using power from the Arduino. Just be sure to connect the ground 15 | from the power source and the arduino. If you are just experimenting, you can 16 | run the sweep off the 5V power from the Arduino with the Arduino receiving power 17 | over USB. However this has only been tested with an external powered USB hub. 18 | It is possible that using a low power USB port (ex: laptop) to power the 19 | arduino & sweep together will result in unexpected behavior. 20 | - Note that running off of USB power is not entirely adequate for the sweep, 21 | so the quantity and qaulity of sensor readings will drop. This is OK for 22 | this example, as it is only meant to provide some visual feedback over 23 | the serial monitor. 24 | - In your own projects, be sure to use dedicated power instead of the USB. 25 | 26 | Created by Scanse LLC, February 21, 2017. 27 | Released into the public domain. 28 | */ 29 | 30 | #include 31 | 32 | // Create a Sweep device using Serial #1 (RX1 & TX1) 33 | Sweep device(Serial1); 34 | 35 | // keeps track of how many scans have been collected 36 | uint8_t scanCount = 0; 37 | // keeps track of how many samples have been collected 38 | uint16_t sampleCount = 0; 39 | 40 | // Arrays to store attributes of collected scans 41 | bool syncValues[500]; // 1 -> first reading of new scan, 0 otherwise 42 | float angles[500]; // in degrees (accurate to the millidegree) 43 | uint16_t distances[500]; // in cm 44 | uint8_t signalStrengths[500]; // 0:255, higher is better 45 | 46 | // Finite States for the program sequence 47 | const uint8_t STATE_WAIT_FOR_USER_INPUT = 0; 48 | const uint8_t STATE_ADJUST_DEVICE_SETTINGS = 1; 49 | const uint8_t STATE_VERIFY_CURRENT_DEVICE_SETTINGS = 2; 50 | const uint8_t STATE_BEGIN_DATA_ACQUISITION = 3; 51 | const uint8_t STATE_GATHER_DATA = 4; 52 | const uint8_t STATE_STOP_DATA_ACQUISITION = 5; 53 | const uint8_t STATE_REPORT_COLLECTED_DATA = 6; 54 | const uint8_t STATE_RESET = 7; 55 | const uint8_t STATE_ERROR = 8; 56 | 57 | // Current state in the program sequence 58 | uint8_t currentState; 59 | 60 | // String to collect user input over serial 61 | String userInput = ""; 62 | 63 | void setup() 64 | { 65 | // Initialize serial 66 | Serial.begin(9600); // serial terminal on the computer 67 | Serial1.begin(115200); // sweep device 68 | 69 | // reserve space to accumulate user message 70 | userInput.reserve(50); 71 | 72 | // initialize counter variables and reset the current state 73 | reset(); 74 | } 75 | 76 | // Loop functions as an FSM (finite state machine) 77 | void loop() 78 | { 79 | switch (currentState) 80 | { 81 | case STATE_WAIT_FOR_USER_INPUT: 82 | if (listenForUserInput()) 83 | currentState = STATE_ADJUST_DEVICE_SETTINGS; 84 | break; 85 | case STATE_ADJUST_DEVICE_SETTINGS: 86 | currentState = adjustDeviceSettings() ? STATE_VERIFY_CURRENT_DEVICE_SETTINGS : STATE_ERROR; 87 | break; 88 | case STATE_VERIFY_CURRENT_DEVICE_SETTINGS: 89 | currentState = verifyCurrentDeviceSettings() ? STATE_BEGIN_DATA_ACQUISITION : STATE_ERROR; 90 | break; 91 | case STATE_BEGIN_DATA_ACQUISITION: 92 | currentState = beginDataCollectionPhase() ? STATE_GATHER_DATA : STATE_ERROR; 93 | break; 94 | case STATE_GATHER_DATA: 95 | gatherSensorReading(); 96 | if (scanCount > 3) 97 | currentState = STATE_STOP_DATA_ACQUISITION; 98 | break; 99 | case STATE_STOP_DATA_ACQUISITION: 100 | currentState = stopDataCollectionPhase() ? STATE_REPORT_COLLECTED_DATA : STATE_ERROR; 101 | break; 102 | case STATE_REPORT_COLLECTED_DATA: 103 | printCollectedData(); 104 | currentState = STATE_RESET; 105 | break; 106 | case STATE_RESET: 107 | Serial.println("\n\nAttempting to reset and run the program again..."); 108 | reset(); 109 | currentState = STATE_WAIT_FOR_USER_INPUT; 110 | break; 111 | default: // there was some error 112 | Serial.println("\n\nAn error occured. Attempting to reset and run program again..."); 113 | reset(); 114 | currentState = STATE_WAIT_FOR_USER_INPUT; 115 | break; 116 | } 117 | } 118 | 119 | // checks if the user has communicated anything over serial 120 | // looks for the user to send "start" 121 | bool listenForUserInput() 122 | { 123 | while (Serial.available()) 124 | { 125 | userInput += (char)Serial.read(); 126 | } 127 | if (userInput.indexOf("start") != -1) 128 | { 129 | Serial.println("Registered user start."); 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | // Adjusts the device settings 136 | bool adjustDeviceSettings() 137 | { 138 | // Set the motor speed to 5HZ (codes available from 1->10 HZ) 139 | bool bSuccess = device.setMotorSpeed(MOTOR_SPEED_CODE_5_HZ); 140 | Serial.println(bSuccess ? "\nSuccessfully set motor speed." : "\nFailed to set motor speed"); 141 | 142 | /* 143 | // Device will always default to 500HZ scan rate when it is powered on. 144 | // Snippet below is left for reference. 145 | // Set the sample rate to 500HZ (codes available for 500, 750 and 1000 HZ) 146 | bool bSuccess = device.setSampleRate(SAMPLE_RATE_CODE_500_HZ); 147 | Serial.println(bSuccess ? "\nSuccessfully set sample rate." : "\nFailed to set sample rate."); 148 | */ 149 | return bSuccess; 150 | } 151 | 152 | // Querries the current device settings (motor speed and sample rate) 153 | // and prints them to the console 154 | bool verifyCurrentDeviceSettings() 155 | { 156 | // Read the current motor speed and sample rate 157 | int32_t currentMotorSpeed = device.getMotorSpeed(); 158 | if (currentMotorSpeed < 0) 159 | { 160 | Serial.println("\nFailed to get current motor speed"); 161 | return false; 162 | } 163 | int32_t currentSampleRate = device.getSampleRate(); 164 | if (currentSampleRate < 0) 165 | { 166 | Serial.println("\nFailed to get current sample rate"); 167 | return false; 168 | } 169 | 170 | // Report the motor speed and sample rate to the computer terminal 171 | Serial.println("\nMotor Speed Setting: " + String(currentMotorSpeed) + " HZ"); 172 | Serial.println("Sample Rate Setting: " + String(currentSampleRate) + " HZ"); 173 | 174 | return true; 175 | } 176 | 177 | // Initiates the data collection phase (begins scanning) 178 | bool beginDataCollectionPhase() 179 | { 180 | // Attempt to start scanning 181 | Serial.println("\nWaiting for motor speed to stabilize and calibration routine to complete..."); 182 | bool bSuccess = device.startScanning(); 183 | Serial.println(bSuccess ? "\nSuccessfully initiated scanning..." : "\nFailed to start scanning."); 184 | if (bSuccess) 185 | Serial.println("\nGathering 3 scans..."); 186 | return bSuccess; 187 | } 188 | 189 | // Gathers individual sensor readings until 3 complete scans have been collected 190 | void gatherSensorReading() 191 | { 192 | // attempt to get the next scan packet 193 | // Note: getReading() will write values into the "reading" variable 194 | bool success = false; 195 | ScanPacket reading = device.getReading(success); 196 | if (success) 197 | { 198 | // check if this reading was the very first reading of a new 360 degree scan 199 | if (reading.isSync()) 200 | scanCount++; 201 | 202 | // don't collect more than 3 scans 203 | if (scanCount > 3) 204 | return; 205 | 206 | // store the info for this sample 207 | syncValues[sampleCount] = reading.isSync(); 208 | angles[sampleCount] = reading.getAngleDegrees(); 209 | distances[sampleCount] = reading.getDistanceCentimeters(); 210 | signalStrengths[sampleCount] = reading.getSignalStrength(); 211 | 212 | // increment sample count 213 | sampleCount++; 214 | } 215 | } 216 | 217 | // Terminates the data collection phase (stops scanning) 218 | bool stopDataCollectionPhase() 219 | { 220 | // Attempt to stop scanning 221 | bool bSuccess = device.stopScanning(); 222 | 223 | Serial.println(bSuccess ? "\nSuccessfully stopped scanning." : "\nFailed to stop scanning."); 224 | return bSuccess; 225 | } 226 | 227 | // Prints the collected data to the console 228 | // (only prints the complete scans, ignores the first partial) 229 | void printCollectedData() 230 | { 231 | Serial.println("\nPrinting info for the collected scans (NOT REAL-TIME):"); 232 | 233 | int indexOfFirstSyncReading = 0; 234 | // don't print the trailing readings from the first partial scan 235 | while (!syncValues[indexOfFirstSyncReading]) 236 | { 237 | indexOfFirstSyncReading++; 238 | } 239 | // print the readings for all the complete scans 240 | for (int i = indexOfFirstSyncReading; i < sampleCount; i++) 241 | { 242 | if (syncValues[i]) 243 | { 244 | Serial.println("\n----------------------NEW SCAN----------------------"); 245 | } 246 | Serial.println("Angle: " + String(angles[i], 3) + ", Distance: " + String(distances[i]) + ", Signal Strength: " + String(signalStrengths[i])); 247 | } 248 | } 249 | 250 | // Resets the variables and state so the sequence can be repeated 251 | void reset() 252 | { 253 | scanCount = 0; 254 | sampleCount = 0; 255 | // reset the sensor 256 | device.reset(); 257 | delay(50); 258 | Serial.flush(); 259 | userInput = ""; 260 | Serial.println("\n\nWhenever you are ready, type \"start\" to to begin the sequence..."); 261 | currentState = 0; 262 | } 263 | -------------------------------------------------------------------------------- /Sweep/ScanPacket.cpp: -------------------------------------------------------------------------------- 1 | #include "ScanPacket.h" 2 | 3 | ScanPacket::ScanPacket(const bool bIsSync, const uint16_t rawAngle, 4 | const uint16_t distance, const uint8_t signalStrength): 5 | _bIsSync(bIsSync), _rawAngle(rawAngle), _distance(distance), 6 | _signalStrength(signalStrength) {} 7 | 8 | bool ScanPacket::isSync() const 9 | { 10 | return _bIsSync; 11 | } 12 | 13 | float ScanPacket::getAngleDegrees() const 14 | { 15 | return _rawAngle / SCALING_FACTOR; 16 | } 17 | 18 | uint16_t ScanPacket::getAngleRaw() const 19 | { 20 | return _rawAngle; 21 | } 22 | 23 | uint16_t ScanPacket::getDistanceCentimeters() const 24 | { 25 | return _distance; 26 | } 27 | 28 | uint8_t ScanPacket::getSignalStrength() const 29 | { 30 | return _signalStrength; 31 | } 32 | 33 | float ScanPacket::getNormalizedSignalStrength() const 34 | { 35 | return _signalStrength / 255; 36 | } 37 | -------------------------------------------------------------------------------- /Sweep/ScanPacket.h: -------------------------------------------------------------------------------- 1 | #ifndef _SCANPACKET_H 2 | #define _SCANPACKET_H 3 | 4 | #include 5 | 6 | /** 7 | * Class representing the data block returned from the Sweep. 8 | */ 9 | class ScanPacket 10 | { 11 | public: 12 | // Constructor 13 | ScanPacket(const bool bIsSync, const uint16_t rawAngle, 14 | const uint16_t distance, const uint8_t signalStrength); 15 | 16 | // Returns true if this reading is the first of a new scan 17 | bool isSync() const; 18 | 19 | // Returns the angle in degrees as a float 20 | float getAngleDegrees() const; 21 | 22 | // Returns the angle as a raw fixed point value 23 | uint16_t getAngleRaw() const; 24 | 25 | // Returns the range measurement in centimeters 26 | uint16_t getDistanceCentimeters() const; 27 | 28 | // Returns the signal strength as an integer value between 0 and 255 29 | uint8_t getSignalStrength() const; 30 | 31 | // Returns the signal strength as a float normalized between 0 and 1 32 | float getNormalizedSignalStrength() const; 33 | 34 | private: 35 | // Scaling factor of raw angle 36 | static constexpr float SCALING_FACTOR = 16.0f; 37 | 38 | bool _bIsSync; // 1 -> first reading of new scan, 0 otherwise 39 | uint16_t _rawAngle; // fixed point value: (degrees * 16) 40 | uint16_t _distance; // cm 41 | uint8_t _signalStrength; // 0:255, higher is better 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /Sweep/Sweep.cpp: -------------------------------------------------------------------------------- 1 | #include "Sweep.h" 2 | 3 | Sweep::Sweep(Stream &serial) : _serial(serial), bIsScanning(false) {} 4 | 5 | Sweep::~Sweep() 6 | { 7 | _serial.flush(); 8 | } 9 | 10 | bool Sweep::isScanning() 11 | { 12 | return bIsScanning; 13 | } 14 | 15 | bool Sweep::startScanning() 16 | { 17 | if (bIsScanning) 18 | return false; 19 | 20 | // wait until the device is ready (calibration complete + motor stabilized) 21 | if (!waitUntilMotorReady()) 22 | return false; 23 | 24 | _writeCommand(_DATA_ACQUISITION_START); 25 | // wait for the receipt (possible timeout) 26 | if (_readResponseHeader()) 27 | { 28 | // TODO: validate receipt 29 | bIsScanning = true; 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | bool Sweep::stopScanning() 36 | { 37 | _writeCommand(_DATA_ACQUISITION_STOP); 38 | 39 | // wait for the device to stop sending packets 40 | delay(500); 41 | 42 | // then flush the buffer and send STOP again to check for a receipt 43 | _flushInputBuffer(); 44 | _writeCommand(_DATA_ACQUISITION_STOP); 45 | 46 | // wait for the receipt (possible timeout) 47 | if (_readResponseHeader()) 48 | { 49 | // TODO: validate receipt 50 | bIsScanning = false; 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | ScanPacket Sweep::getReading(bool &success) 57 | { 58 | success = false; 59 | if (!bIsScanning) 60 | { 61 | return ScanPacket(false, 0, 0, 0); 62 | } 63 | 64 | // wait for the receipt (possible timeout) 65 | if (_readResponseScanPacket()) 66 | { 67 | // TODO: validate receipt 68 | uint8_t i = 0; 69 | 70 | bool bIsSync = _responseScanPacket[i++] & _SYNC_MASK; 71 | 72 | // read raw fixed point azimuth value 73 | uint16_t rawAngle_lsb = _responseScanPacket[i++]; 74 | uint16_t rawAngle_msb = _responseScanPacket[i++] << 8; 75 | uint16_t rawAngle = rawAngle_lsb + rawAngle_msb; 76 | 77 | // read distance value 78 | uint16_t distance_lsb = _responseScanPacket[i++]; 79 | uint16_t distance_msb = _responseScanPacket[i++] << 8; 80 | uint16_t distance = distance_lsb + distance_msb; 81 | 82 | // read signal strength value 83 | uint8_t signalStrength = _responseScanPacket[i++]; 84 | 85 | success = true; 86 | return ScanPacket(bIsSync, rawAngle, distance, signalStrength); 87 | } 88 | 89 | return ScanPacket(false, 0, 0, 0); 90 | } 91 | 92 | bool Sweep::getMotorReady() 93 | { 94 | if (bIsScanning) 95 | return false; 96 | _writeCommand(_MOTOR_READY); 97 | if (_readResponseInfoSetting()) 98 | { 99 | // TODO: validate receipt (hold off until performance hit is determined) 100 | const uint8_t readyCode[2] = {_responseInfoSetting[2], _responseInfoSetting[3]}; 101 | // readyCode == 0 indicates device is ready 102 | return _ascii_bytes_to_integer(readyCode) == 0; 103 | } 104 | return false; 105 | } 106 | 107 | bool Sweep::waitUntilMotorReady() 108 | { 109 | if (bIsScanning) 110 | return false; 111 | // only check for 10 seconds (20 iterations with 500ms pause) 112 | for (uint8_t i = 0; i < 20; ++i) 113 | { 114 | if (getMotorReady()) 115 | return true; 116 | delay(500); 117 | } 118 | // timeout after 10 seconds 119 | return false; 120 | } 121 | 122 | int32_t Sweep::getMotorSpeed() 123 | { 124 | if (bIsScanning) 125 | return false; 126 | 127 | _writeCommand(_MOTOR_INFORMATION); 128 | if (_readResponseInfoSetting()) 129 | { 130 | // TODO: validate receipt (hold off until performance hit is determined) 131 | const uint8_t speedCode[2] = {_responseInfoSetting[2], _responseInfoSetting[3]}; 132 | return _ascii_bytes_to_integer(speedCode); 133 | } 134 | return -1; 135 | } 136 | 137 | bool Sweep::setMotorSpeed(const uint8_t motorSpeedCode[2]) 138 | { 139 | if (bIsScanning) 140 | return false; 141 | 142 | // wait until the device is ready (calibration complete + motor stabilized) 143 | if (!waitUntilMotorReady()) 144 | return false; 145 | 146 | _writeCommandWithArgument(_MOTOR_SPEED_ADJUST, motorSpeedCode); 147 | // wait for the receipt (possible timeout) 148 | if (_readResponseParam()) 149 | { 150 | // TODO: validate receipt 151 | return true; 152 | } 153 | return false; 154 | } 155 | 156 | int32_t Sweep::getSampleRate() 157 | { 158 | if (bIsScanning) 159 | return false; 160 | 161 | _writeCommand(_SAMPLE_RATE_INFORMATION); 162 | if (_readResponseInfoSetting()) 163 | { 164 | // TODO: validate receipt (hold off until performance hit is determined) 165 | const uint8_t sampleRateCode[2] = {_responseInfoSetting[2], _responseInfoSetting[3]}; 166 | switch (_ascii_bytes_to_integer(sampleRateCode)) 167 | { 168 | case 1: 169 | return 500; 170 | case 2: 171 | return 750; 172 | case 3: 173 | return 1000; 174 | default: 175 | break; 176 | } 177 | } 178 | return -1; 179 | } 180 | 181 | bool Sweep::setSampleRate(const uint8_t sampleRateCode[2]) 182 | { 183 | if (bIsScanning) 184 | return false; 185 | 186 | _writeCommandWithArgument(_SAMPLE_RATE_ADJUST, sampleRateCode); 187 | // wait for the receipt (possible timeout) 188 | if (_readResponseParam()) 189 | { 190 | // TODO: validate receipt 191 | return true; 192 | } 193 | return false; 194 | } 195 | 196 | void Sweep::reset() 197 | { 198 | _writeCommand(_RESET_DEVICE); 199 | } 200 | 201 | void Sweep::_writeCommand(const uint8_t cmd[2]) 202 | { 203 | const uint8_t command[3] = {cmd[0], cmd[1], _COMMAND_TERMINATION}; 204 | 205 | _serial.write(command, 3); 206 | } 207 | 208 | void Sweep::_writeCommandWithArgument(const uint8_t cmd[2], const uint8_t arg[2]) 209 | { 210 | const uint8_t command[5] = {cmd[0], cmd[1], arg[0], arg[1], _COMMAND_TERMINATION}; 211 | 212 | _serial.write(command, 5); 213 | } 214 | 215 | bool Sweep::_readResponseHeader() 216 | { 217 | // determine the expected number of bytes to read 218 | uint8_t len = sweepArrLen(_responseHeader); 219 | 220 | // set a timeout on the read 221 | _serial.setTimeout(1000); 222 | 223 | // attempt to read (can timeout) 224 | int numBytesRead = _serial.readBytes(_responseHeader, len); 225 | 226 | // return true if the expected num of bytes were read 227 | return numBytesRead == len; 228 | } 229 | 230 | bool Sweep::_readResponseParam() 231 | { 232 | // determine the expected number of bytes to read 233 | uint8_t len = sweepArrLen(_responseParam); 234 | 235 | // set a timeout on the read 236 | _serial.setTimeout(1000); 237 | 238 | // attempt to read (can timeout) 239 | int numBytesRead = _serial.readBytes(_responseParam, len); 240 | 241 | // return true if the expected num of bytes were read 242 | return numBytesRead == len; 243 | } 244 | 245 | bool Sweep::_readResponseScanPacket() 246 | { 247 | // determine the expected number of bytes to read 248 | uint8_t len = sweepArrLen(_responseScanPacket); 249 | 250 | // set a timeout on the read 251 | _serial.setTimeout(1000); 252 | 253 | // attempt to read (can timeout) 254 | int numBytesRead = _serial.readBytes(_responseScanPacket, len); 255 | 256 | // return true if the expected num of bytes were read 257 | return numBytesRead == len; 258 | } 259 | 260 | bool Sweep::_readResponseInfoDevice() 261 | { 262 | // determine the expected number of bytes to read 263 | uint8_t len = sweepArrLen(_responseInfoDevice); 264 | 265 | // set a timeout on the read 266 | _serial.setTimeout(1000); 267 | 268 | // attempt to read (can timeout) 269 | int numBytesRead = _serial.readBytes(_responseInfoDevice, len); 270 | 271 | // return true if the expected num of bytes were read 272 | return numBytesRead == len; 273 | } 274 | 275 | bool Sweep::_readResponseInfoVersion() 276 | { 277 | // determine the expected number of bytes to read 278 | uint8_t len = sweepArrLen(_responseInfoVersion); 279 | 280 | // set a timeout on the read 281 | _serial.setTimeout(1000); 282 | 283 | // attempt to read (can timeout) 284 | int numBytesRead = _serial.readBytes(_responseInfoVersion, len); 285 | 286 | // return true if the expected num of bytes were read 287 | return numBytesRead == len; 288 | } 289 | 290 | bool Sweep::_readResponseInfoSetting() 291 | { 292 | // determine the expected number of bytes to read 293 | uint8_t len = sweepArrLen(_responseInfoSetting); 294 | 295 | // set a timeout on the read 296 | _serial.setTimeout(1000); 297 | 298 | // attempt to read (can timeout) 299 | int numBytesRead = _serial.readBytes(_responseInfoSetting, len); 300 | 301 | // return true if the expected num of bytes were read 302 | return numBytesRead == len; 303 | } 304 | 305 | void Sweep::_flushInputBuffer() 306 | { 307 | while (_serial.available() > 0) 308 | { 309 | _serial.read(); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /Sweep/Sweep.h: -------------------------------------------------------------------------------- 1 | /* 2 | Sweep.h - Library for communicating with Scanse Sweep scanning LiDAR device. 3 | Created by David C. Young, February 21, 2017. 4 | Released into the public domain. 5 | */ 6 | #ifndef _SWEEP_H 7 | #define _SWEEP_H 8 | 9 | #include 10 | #include "ScanPacket.h" 11 | 12 | // Returns the num of elements in an array 13 | #define sweepArrLen(x) (sizeof(x) / sizeof(*x)) 14 | 15 | // Available Motor Speed Codes for the setMotorSpeed method 16 | const uint8_t MOTOR_SPEED_CODE_0_HZ[2] = {'0', '0'}; 17 | const uint8_t MOTOR_SPEED_CODE_1_HZ[2] = {'0', '1'}; 18 | const uint8_t MOTOR_SPEED_CODE_2_HZ[2] = {'0', '2'}; 19 | const uint8_t MOTOR_SPEED_CODE_3_HZ[2] = {'0', '3'}; 20 | const uint8_t MOTOR_SPEED_CODE_4_HZ[2] = {'0', '4'}; 21 | const uint8_t MOTOR_SPEED_CODE_5_HZ[2] = {'0', '5'}; 22 | const uint8_t MOTOR_SPEED_CODE_6_HZ[2] = {'0', '6'}; 23 | const uint8_t MOTOR_SPEED_CODE_7_HZ[2] = {'0', '7'}; 24 | const uint8_t MOTOR_SPEED_CODE_8_HZ[2] = {'0', '8'}; 25 | const uint8_t MOTOR_SPEED_CODE_9_HZ[2] = {'0', '9'}; 26 | const uint8_t MOTOR_SPEED_CODE_10_HZ[2] = {'1', '0'}; 27 | 28 | // Available Sample Rate Codes for the setSampleRate method 29 | const uint8_t SAMPLE_RATE_CODE_500_HZ[2] = {'0', '1'}; 30 | const uint8_t SAMPLE_RATE_CODE_750_HZ[2] = {'0', '2'}; 31 | const uint8_t SAMPLE_RATE_CODE_1000_HZ[2] = {'0', '3'}; 32 | 33 | /** 34 | * Class representing a sweep device 35 | */ 36 | class Sweep 37 | { 38 | public: 39 | // Constructor 40 | Sweep(Stream &serial); 41 | 42 | // Destructor 43 | ~Sweep(); 44 | 45 | // Returns true if the device is currently scanning 46 | bool isScanning(); 47 | 48 | /** 49 | * Initiates an active data stream that sends scan packets 50 | * (each representing a single reading) over the _serial 51 | * stream. This data stream continues indefinitely until 52 | * "stopScanning()" is called. 53 | * 54 | * @return True if start was successful 55 | */ 56 | bool startScanning(); 57 | 58 | /** 59 | * Terminates an active data stream. 60 | * 61 | * @return True if stop was successful 62 | */ 63 | bool stopScanning(); 64 | 65 | /** 66 | * Reads a single sensor reading from the serial buffer. 67 | * (must be called frequently to keep up with the data stream) 68 | * 69 | * @param success Whether a reading was successfully retrieved. 70 | * @return The reading as a ScanPacket object. 71 | */ 72 | ScanPacket getReading(bool &success); 73 | 74 | /** 75 | * Check if the device is ready. A device is ready if the 76 | * calibration routine is complete and the motor speed has 77 | * stabilized to the current speed setting. 78 | * 79 | * @return True if the device is ready 80 | */ 81 | bool getMotorReady(); 82 | 83 | /** 84 | * Waits (blocks) until the device is ready, or the wait 85 | * times out after 8 seconds. See getMotorReady for details. 86 | * 87 | * @return True if motor is ready, false if the wait timed out 88 | */ 89 | bool waitUntilMotorReady(); 90 | 91 | // Returns the current motor speed setting in HZ 92 | int32_t getMotorSpeed(); 93 | 94 | /** 95 | * Adjusts the motor speed setting to the provided code. 96 | * (Recommend users pass const codes defined by library) 97 | * 98 | * @param motorSpeedCode ASCII code between '00' and '10' 99 | * @return True if the setting was successfully applied. 100 | */ 101 | bool setMotorSpeed(const uint8_t motorSpeedCode[2]); 102 | 103 | // Returns the current sample rate setting in HZ 104 | int32_t getSampleRate(); 105 | 106 | /** 107 | * Adjusts the sample rate setting to the provided code. 108 | * (Recommend users pass const codes defined by library) 109 | * 110 | * @param sampleRateCode ASCII code between '01' and '03' 111 | * @return True if the setting was successfully applied. 112 | */ 113 | bool setSampleRate(const uint8_t sampleRateCode[2]); 114 | 115 | /** 116 | * Resets the device. 117 | * Warning: attempting to communicate with the device while 118 | * it is resetting will cause issues. Device is ready 119 | * when the LED turns blue. 120 | */ 121 | void reset(); 122 | 123 | private: 124 | // The stream object connected to the sweep device. 125 | Stream &_serial; 126 | 127 | // True if the device is currently scanning 128 | bool bIsScanning; 129 | 130 | // Command Prefixes (See Sweep User Manual for CommProtocol) 131 | const uint8_t _COMMAND_TERMINATION = '\n'; 132 | const uint8_t _DATA_ACQUISITION_START[2] = {'D', 'S'}; 133 | const uint8_t _DATA_ACQUISITION_STOP[2] = {'D', 'X'}; 134 | const uint8_t _MOTOR_READY[2] = {'M', 'Z'}; 135 | const uint8_t _MOTOR_SPEED_ADJUST[2] = {'M', 'S'}; 136 | const uint8_t _MOTOR_INFORMATION[2] = {'M', 'I'}; 137 | const uint8_t _SAMPLE_RATE_ADJUST[2] = {'L', 'R'}; 138 | const uint8_t _SAMPLE_RATE_INFORMATION[2] = {'L', 'I'}; 139 | const uint8_t _VERSION_INFORMATION[2] = {'I', 'V'}; 140 | const uint8_t _DEVICE_INFORMATION[2] = {'I', 'D'}; 141 | const uint8_t _RESET_DEVICE[2] = {'R', 'R'}; 142 | 143 | // Sync/Error byte bit masks 144 | const uint8_t _E6_MASK = 0x80; 145 | const uint8_t _E5_MASK = 0x40; 146 | const uint8_t _E4_MASK = 0x20; 147 | const uint8_t _E3_MASK = 0x10; 148 | const uint8_t _E2_MASK = 0x08; 149 | const uint8_t _E1_MASK = 0x04; 150 | const uint8_t _E0_MASK = 0x02; 151 | const uint8_t _SYNC_MASK = 0x01; 152 | 153 | // Arrays to hold responses 154 | uint8_t _responseHeader[6]; 155 | uint8_t _responseParam[9]; 156 | uint8_t _responseScanPacket[7]; 157 | uint8_t _responseInfoDevice[18]; 158 | uint8_t _responseInfoVersion[21]; 159 | uint8_t _responseInfoSetting[5]; 160 | 161 | // Write command without any argument 162 | void _writeCommand(const uint8_t cmd[2]); 163 | // Write command with a 2 character argument code 164 | void _writeCommandWithArgument(const uint8_t cmd[2], const uint8_t arg[2]); 165 | 166 | // Read various types of responses 167 | bool _readResponseHeader(); 168 | bool _readResponseParam(); 169 | bool _readResponseScanPacket(); 170 | bool _readResponseInfoDevice(); 171 | bool _readResponseInfoVersion(); 172 | bool _readResponseInfoSetting(); 173 | void _flushInputBuffer(); 174 | 175 | // converts a pair of ascii code (between '00':'10') into an integer 176 | inline int32_t _ascii_bytes_to_integer(const uint8_t bytes[2]) 177 | { 178 | const uint8_t ASCIINumberBlockOffset = 48; 179 | 180 | const uint8_t num1 = bytes[0] - ASCIINumberBlockOffset; 181 | const uint8_t num2 = bytes[1] - ASCIINumberBlockOffset; 182 | 183 | if (num1 > 9 || num2 > 9 || num1 < 0 || num2 < 0) 184 | return -1; 185 | 186 | return (num1 * 10) + (num2 * 1); 187 | } 188 | }; 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /Sweep/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | Sweep KEYWORD1 9 | ScanPacket KEYWORD1 10 | 11 | ####################################### 12 | # Methods and functions (KEYWORD2) 13 | ####################################### 14 | isScanning KEYWORD2 15 | startScanning KEYWORD2 16 | stopScanning KEYWORD2 17 | getReading KEYWORD2 18 | getMotorReady KEYWORD2 19 | waitUntilMotorReady KEYWORD2 20 | getMotorSpeed KEYWORD2 21 | setMotorSpeed KEYWORD2 22 | getSampleRate KEYWORD2 23 | setSampleRate KEYWORD2 24 | reset KEYWORD2 25 | 26 | isSync KEYWORD2 27 | getAngleDegrees KEYWORD2 28 | getAngleRaw KEYWORD2 29 | getDistanceCentimeters KEYWORD2 30 | getSignalStrength KEYWORD2 31 | 32 | ####################################### 33 | # Constants (LITERAL1) 34 | ####################################### 35 | MOTOR_SPEED_CODE_0_HZ LITERAL1 36 | MOTOR_SPEED_CODE_1_HZ LITERAL1 37 | MOTOR_SPEED_CODE_2_HZ LITERAL1 38 | MOTOR_SPEED_CODE_3_HZ LITERAL1 39 | MOTOR_SPEED_CODE_4_HZ LITERAL1 40 | MOTOR_SPEED_CODE_5_HZ LITERAL1 41 | MOTOR_SPEED_CODE_6_HZ LITERAL1 42 | MOTOR_SPEED_CODE_7_HZ LITERAL1 43 | MOTOR_SPEED_CODE_8_HZ LITERAL1 44 | MOTOR_SPEED_CODE_9_HZ LITERAL1 45 | MOTOR_SPEED_CODE_10_HZ LITERAL1 46 | 47 | SAMPLE_RATE_CODE_500_HZ LITERAL1 48 | SAMPLE_RATE_CODE_750_HZ LITERAL1 49 | SAMPLE_RATE_CODE_1000_HZ LITERAL1 50 | 51 | _E6_MASK LITERAL1 52 | _E5_MASK LITERAL1 53 | _E4_MASK LITERAL1 54 | _E3_MASK LITERAL1 55 | _E2_MASK LITERAL1 56 | _E1_MASK LITERAL1 57 | _E0_MASK LITERAL1 58 | _SYNC_MASK LITERAL1 59 | 60 | SCALING_FACTOR LITERAL1 61 | -------------------------------------------------------------------------------- /wiring_diagrams/MegaSerialPrinter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scanse/sweep-arduino/edc49e98eece5dd6d0ce80479840bdbf8ebe04d2/wiring_diagrams/MegaSerialPrinter.png --------------------------------------------------------------------------------