## **Lab 3** | Working with Sensors in Practice ### Design of Autonomous Systems ### CSCI 6907/4907 - Section 86 ### Prof. **Sibin Mohan** --- ## **Lab Overview** you will implement, - a simple but realistic **sensor–decision–actuation loop** - similar to those used in embedded and autonomous systems --- **this task involves:** - continuously reading data from ultrasonic distance sensor - in real time - making a decision based on that data - flash LED - measured distance → falls below a specified threshold --- ## **what's new in this lab** - does adding LED toggle code to previous implementation work? - this lab requires LED to flash **continuously** - not just turn on or off --- ## the challenge: - flashing requires → repeatedly toggling LED with fixed delay - at the same time → distance measurements continue uninterrupted --- single-threaded, blocking implementation, prevents one task from running while other sleeps/waits --- **solution** → system performs concurrent operations (_e.g.,_ using threads) --- ## concurrency and shared data concurrency introduces → **shared state** --- ## In this lab - one task updates the shared distance value - another task reads that value to control the LED --- uncoordinated access → **race conditions** and inconsistent behavior --- traditional solutions → **synchronization primitives** - locks or mutexes (covered in MP-4) --- ## using atomic variables we take a simpler approach → storing distance in an **atomic variable** --- ## why atomic variables? - guarantee that reads and writes → **single**, **indivisible** operations - preventing partial updates and race conditions - without explicit locking --- ## How Atomics Work - atomic operations → are enforced at the **hardware level** - ensuring that updates either complete fully or not at all - fast and predictable for small shared values --- ## **System Design**
- diagram illustrates a simple multithreaded system design - two threads run **concurrently** and **share data safely** --- ## **Code Overview** code is split into **4 main parts**: 1. setting up shared atomic variable and PIN IDs 2. reading from the Ultrasonic Sensor 3. flashing the LED 4. main function to start threads --- ## Part 1: Setting Up Atomic Variable ```cpp #include
// Shared variable for distance measurement std::atomic
distance(0.0); ``` declare a shared atomic variable, - named `distance` - type `float` - initialized to `0.0` --- this variable will be: - updated by the sensor-reading thread - read by the LED-flashing thread - thread-safe without the need for locks --- ## Part 2: Reading from Ultrasonic Sensor code for reading ultrasonic sensor → similar to Lab 2, but in C++ --- **key points** - set up the GPIO pins - use a loop to continuously read distance measurements - all code wrapped in a function for use in a thread --- ## Part 2: Sensor Reading Code (1/2) ```cpp void readSensor() { while (true) { // Trigger the ultrasonic sensor write_pin(ULTRASONIC_TRIG, 1); usleep(10); // Trigger pulse write_pin(ULTRASONIC_TRIG, 0); // Wait for echo and calculate distance auto startTime = std::chrono::high_resolution_clock::now(); auto endTime = startTime; while (!read_pin(ULTRASONIC_ECHO)) { // Wait for echo to go high startTime = std::chrono::high_resolution_clock::now(); } ``` --- ## Part 2: Sensor Reading Code (2/2) ```cpp while (read_pin(ULTRASONIC_ECHO)); { // Wait for echo to go low endTime = std::chrono::high_resolution_clock::now(); } // Calculate distance and update atomic variable auto delta = endTime - startTime; float distanceValue = std::chrono::duration_cast
(delta).count() * 0.034 / 2; distance.store(distanceValue); // Update the shared atomic variable } } ``` --- ## Part 3: Flashing the LED LED flashing logic → implemented in a **separate thread** --- **behavior:** - continuously checks the shared distance variable - toggles the LED on and off with a delay - when distance is below threshold --- ## Part 3: LED Flashing Code ```cpp void flashLED() { const float threshold = 20.0; // Distance threshold in cm while (true) { float currentDistance = distance.load(); // Read the shared atomic variable if (currentDistance < threshold) { // Flash the LED write_pin(GPIO_RED, 1); // Turn on red LED usleep(500000); // Wait for 500ms write_pin(GPIO_RED, 0); // Turn off red LED usleep(500000); // Wait for 500ms } } } ``` --- ## Part 4: Main Function to Start Threads in the `main` function, you will create and start both threads: ```cpp int main() { // Initialize GPIO pins and any necessary setup // Start the sensor reading thread std::thread sensorThread(readSensor); // Start the LED flashing thread std::thread ledThread(flashLED); // Join threads (optional, as they run indefinitely) sensorThread.join(); ledThread.join(); return 0; } ``` --- ## How It All Works Together - reads from the ultrasonic sensor continuously - controls the LED based on distance measurement - uses atomic variables for thread-safe access - both threads → safely access/update shared distance - without conflicts --- ## Full Code and Compilation [Lab 3 Full Code](https://gist.github.com/ubdussamad/2ab5a127e43ad72f8dd1cea329dcf614) **to compile:** ```bash g++ -std=c++11 -o lab3 lab3_full_code.cpp -lpthread ``` **to run:** ```bash ./lab3 ``` --- ## Testing Your Implementation when running the full code: 1. play with the distance threshold value 2. observe how the LED responds to changes in measured distance 3. move objects closer and farther from the sensor 4. verify that sensor readings continue while LED flashes --- **expected behavior:** - LED should flash continuously - when an object is within the threshold distance --- ## Mini Homework [not graded] **challenge 1:** - modify the code to use **fewer threads** - example above uses 3 threads (main + 2 worker threads) --- **challenge 2:** add a signal catcher to gracefully exit the program when, - a specific signal (_e.g.,_ `SIGINT`) is received, OR - a key (say `Q`) is pressed