Counts points that hit a LIDAR sensor, and includes threshold sensing so a point doesn't get counted twice, much in the same spirit as debouncing.

Dependencies:   mbed mbed-rtos 4DGL-uLCD-SE X_NUCLEO_53L0A1

The Object Counter counts the number of objects (in our case, ping pong balls) that pass by the sensor. It features a high water mark that ensures only one ball is counted at any given time, and that a ball will only be counted once.

Partner Program

This is intended to be used with Ping Pong Robot, but can be used by itself if you wish

Installation

We use the following components:

All of these can be purchased from SparkFun with the exception of the VL53L0X sensor, wich is from AdaFruit.

To connect these, use the following pin outs:

Class D Amplifier & Speaker

MBEDClass D Audio AmpSpeaker
GndIN-
GndPWR-
5VPWR+
p25IN+
p20S
OUT-Speaker-
OUT+Speaker+

uLCD Screen

MBEDuLCD HeaderuLCD
GndGndGnd
+5V+5V+5V
p13TXRX
p14RXTX
p15RESRES

VL35L0X

MBEDVL35L0X
GndGnd
+3.3VVIN
p26SHDN
p27SDA
p28SCL

Usage

This is intended to be used to track the number of objects that have been collected. Because of this, the software features a "High Water Mark" mentality - after an object has gotten close enough, a flag is tripped - this flag only resets after the distance increases back past the high water mark. In this way, we ensure we count each object once - much in the same spirit as debouncing.

The VL53L0X is a low cost chip that measures distance via "Time of Flight" - basically how long it takes light to travel to the object and back. Because it uses a single laser, it can be quite precise. This makes it ideal for our purposes - tracking a single object as it falls.

Heads Up!

The largest issue is data collection time - while the chip does collect data quickly, if your object is moving at a high speed, it can sometimes miss the object entirely because it didn't "see" the object in the few fractions of a second that the object was close enough!

If you require more precision, there are plenty of higher quality (and higher cost) LIDAR systems - AdaFruit sells many of these.

As a workaround, ensure your object is not moving quickly - this can be accomplished by setting the LIDAR's side on the floor, so its line of sight is directly above the ground. When the object hits the ground, even if it bounces, it should be on the ground enough time for the LIDAR to pick it up. Slightly tilting the ground will make sure your object will roll away after falling.

As with everything else, experience is the best teacher - you might need to tweak the threshold in the code to get optimal results.

Demonstration

The circuit looks like this: /media/uploads/alexhrao/img_9521.jpeg

Committer:
alexhrao
Date:
Sun Apr 14 20:49:13 2019 +0000
Revision:
1:e82997c13013
Parent:
0:fc7ce4e596ff
Child:
2:54e1a301de72
Add working LIDAR code

Who changed what in which revision?

UserRevisionLine numberNew contents of line
alexhrao 0:fc7ce4e596ff 1 #include "mbed.h"
alexhrao 0:fc7ce4e596ff 2 #include "rtos.h"
alexhrao 0:fc7ce4e596ff 3 #include <stdio.h>
alexhrao 0:fc7ce4e596ff 4 #include "uLCD_4DGL.h"
alexhrao 0:fc7ce4e596ff 5 #include "XNucleo53L0A1.h"
alexhrao 1:e82997c13013 6 #include <time.h>
alexhrao 0:fc7ce4e596ff 7
alexhrao 0:fc7ce4e596ff 8 #define VL53L0_I2C_SDA p28
alexhrao 0:fc7ce4e596ff 9 #define VL53L0_I2C_SCL p27
alexhrao 0:fc7ce4e596ff 10
alexhrao 0:fc7ce4e596ff 11 // Light Thread
alexhrao 0:fc7ce4e596ff 12 // Only shine light when
alexhrao 0:fc7ce4e596ff 13 // Sound Thread
alexhrao 0:fc7ce4e596ff 14
alexhrao 0:fc7ce4e596ff 15 // Game
alexhrao 1:e82997c13013 16 volatile int num_points = 0;
alexhrao 0:fc7ce4e596ff 17 volatile int time_left = 0;
alexhrao 1:e82997c13013 18 int threshold = 300; // ???
alexhrao 0:fc7ce4e596ff 19 volatile bool is_point = false;
alexhrao 0:fc7ce4e596ff 20
alexhrao 0:fc7ce4e596ff 21 // LIDAR
alexhrao 0:fc7ce4e596ff 22 static XNucleo53L0A1* lidar = NULL;
alexhrao 0:fc7ce4e596ff 23 uint32_t dist;
alexhrao 0:fc7ce4e596ff 24 DigitalOut shdn(p26);
alexhrao 0:fc7ce4e596ff 25
alexhrao 0:fc7ce4e596ff 26 // LCD
alexhrao 0:fc7ce4e596ff 27 Mutex lcd_lock;
alexhrao 1:e82997c13013 28 // NOTE: do NOT use p9, p10, p11
alexhrao 1:e82997c13013 29 // There is a bug in LIDAR system and uLCD system - they can't BOTH BE ON
alexhrao 1:e82997c13013 30 // I2C! It just so happens, p9 and p10 are I2C, so the systems will get
alexhrao 1:e82997c13013 31 // confused, and you'll get weird hangs with the uLCD screen!!
alexhrao 1:e82997c13013 32 uLCD_4DGL uLCD(p13, p14, p15); // serial tx, serial rx, reset pin;
alexhrao 0:fc7ce4e596ff 33
alexhrao 0:fc7ce4e596ff 34 // Speaker
alexhrao 0:fc7ce4e596ff 35 PwmOut speaker(p25);
alexhrao 0:fc7ce4e596ff 36
alexhrao 0:fc7ce4e596ff 37 // LEDs
alexhrao 0:fc7ce4e596ff 38 PwmOut point_led(p21);
alexhrao 0:fc7ce4e596ff 39
alexhrao 1:e82997c13013 40 // Button
alexhrao 1:e82997c13013 41 DigitalIn starter(p19);
alexhrao 0:fc7ce4e596ff 42 // Threads
alexhrao 0:fc7ce4e596ff 43 Thread sound_thread;
alexhrao 0:fc7ce4e596ff 44 Thread time_thread;
alexhrao 0:fc7ce4e596ff 45 Thread points_thread;
alexhrao 0:fc7ce4e596ff 46
alexhrao 0:fc7ce4e596ff 47 // Debugging
alexhrao 0:fc7ce4e596ff 48 DigitalOut err_led(LED1);
alexhrao 0:fc7ce4e596ff 49 DigitalOut status_led(LED2);
alexhrao 0:fc7ce4e596ff 50 Serial pc(USBTX, USBRX);
alexhrao 0:fc7ce4e596ff 51
alexhrao 1:e82997c13013 52
alexhrao 0:fc7ce4e596ff 53 void time(void) {
alexhrao 0:fc7ce4e596ff 54 while (1) {
alexhrao 0:fc7ce4e596ff 55 // every second, change LED and update screen
alexhrao 0:fc7ce4e596ff 56 if (time_left > 0) {
alexhrao 0:fc7ce4e596ff 57 lcd_lock.lock();
alexhrao 1:e82997c13013 58 uLCD.locate(4, 5);
alexhrao 0:fc7ce4e596ff 59 uLCD.text_height(2);
alexhrao 0:fc7ce4e596ff 60 uLCD.text_width(2);
alexhrao 1:e82997c13013 61 uLCD.color(RED);
alexhrao 1:e82997c13013 62 uLCD.printf("%02d", time_left);
alexhrao 0:fc7ce4e596ff 63 lcd_lock.unlock();
alexhrao 0:fc7ce4e596ff 64 Thread::wait(1000);
alexhrao 1:e82997c13013 65 time_left--;
alexhrao 0:fc7ce4e596ff 66 } else {
alexhrao 0:fc7ce4e596ff 67 break;
alexhrao 0:fc7ce4e596ff 68 }
alexhrao 0:fc7ce4e596ff 69 }
alexhrao 0:fc7ce4e596ff 70 lcd_lock.lock();
alexhrao 1:e82997c13013 71 uLCD.locate(4, 5);
alexhrao 0:fc7ce4e596ff 72 uLCD.text_height(2);
alexhrao 0:fc7ce4e596ff 73 uLCD.text_width(2);
alexhrao 1:e82997c13013 74 uLCD.color(RED);
alexhrao 1:e82997c13013 75 uLCD.printf("00");
alexhrao 0:fc7ce4e596ff 76 lcd_lock.unlock();
alexhrao 0:fc7ce4e596ff 77 }
alexhrao 0:fc7ce4e596ff 78 void sound(void) {
alexhrao 0:fc7ce4e596ff 79 int is_new = 1;
alexhrao 0:fc7ce4e596ff 80 speaker.period(1.0 / 900.0);
alexhrao 0:fc7ce4e596ff 81 while (1) {
alexhrao 0:fc7ce4e596ff 82 // if we have a point, sound?
alexhrao 1:e82997c13013 83 if (is_point && is_new == 1) {
alexhrao 0:fc7ce4e596ff 84 is_new = 0;
alexhrao 0:fc7ce4e596ff 85 // sound that
alexhrao 1:e82997c13013 86 speaker = 0.5;
alexhrao 1:e82997c13013 87 Thread::wait(100);
alexhrao 1:e82997c13013 88 speaker = 0;
alexhrao 0:fc7ce4e596ff 89
alexhrao 0:fc7ce4e596ff 90 } else if (!is_point) {
alexhrao 0:fc7ce4e596ff 91 is_new = 1;
alexhrao 0:fc7ce4e596ff 92 }
alexhrao 0:fc7ce4e596ff 93 Thread::wait(10);
alexhrao 0:fc7ce4e596ff 94 }
alexhrao 0:fc7ce4e596ff 95 }
alexhrao 0:fc7ce4e596ff 96
alexhrao 1:e82997c13013 97 void points(void) {
alexhrao 1:e82997c13013 98 int prev_points = -1;
alexhrao 0:fc7ce4e596ff 99 while (1) {
alexhrao 1:e82997c13013 100 if (time_left <= 0)
alexhrao 1:e82997c13013 101 break;
alexhrao 1:e82997c13013 102 if (num_points != prev_points) {
alexhrao 0:fc7ce4e596ff 103 lcd_lock.lock();
alexhrao 1:e82997c13013 104 uLCD.locate(4, 2);
alexhrao 0:fc7ce4e596ff 105 uLCD.text_width(2);
alexhrao 0:fc7ce4e596ff 106 uLCD.text_height(2);
alexhrao 0:fc7ce4e596ff 107 uLCD.color(GREEN);
alexhrao 1:e82997c13013 108 uLCD.printf("%d", num_points);
alexhrao 0:fc7ce4e596ff 109 lcd_lock.unlock();
alexhrao 1:e82997c13013 110 prev_points = num_points;
alexhrao 0:fc7ce4e596ff 111 }
alexhrao 0:fc7ce4e596ff 112 Thread::wait(100);
alexhrao 0:fc7ce4e596ff 113 }
alexhrao 0:fc7ce4e596ff 114 }
alexhrao 0:fc7ce4e596ff 115
alexhrao 0:fc7ce4e596ff 116 int main() {
alexhrao 0:fc7ce4e596ff 117 // Tell the user we're spinning up
alexhrao 0:fc7ce4e596ff 118 uLCD.cls();
alexhrao 0:fc7ce4e596ff 119 uLCD.text_height(2);
alexhrao 0:fc7ce4e596ff 120 uLCD.text_width(2);
alexhrao 0:fc7ce4e596ff 121 uLCD.locate(1, 2);
alexhrao 0:fc7ce4e596ff 122 uLCD.color(RED);
alexhrao 0:fc7ce4e596ff 123 uLCD.printf("SPINNING UP");
alexhrao 1:e82997c13013 124 starter.mode(PullUp);
alexhrao 0:fc7ce4e596ff 125
alexhrao 1:e82997c13013 126 time_left = 15;
alexhrao 0:fc7ce4e596ff 127
alexhrao 0:fc7ce4e596ff 128 // Normally, we should worry about deleting this object. However, it will
alexhrao 0:fc7ce4e596ff 129 // survive for the lifetime of the program!
alexhrao 1:e82997c13013 130
alexhrao 0:fc7ce4e596ff 131 DevI2C* device_i2c = new DevI2C(VL53L0_I2C_SDA, VL53L0_I2C_SCL);
alexhrao 0:fc7ce4e596ff 132 lidar = XNucleo53L0A1::instance(device_i2c, A2, D8, D2);
alexhrao 1:e82997c13013 133
alexhrao 0:fc7ce4e596ff 134 // Reset shdn (?):
alexhrao 0:fc7ce4e596ff 135 shdn = 0;
alexhrao 0:fc7ce4e596ff 136 wait(0.1);
alexhrao 0:fc7ce4e596ff 137 shdn = 1;
alexhrao 0:fc7ce4e596ff 138 wait(0.1);
alexhrao 0:fc7ce4e596ff 139
alexhrao 0:fc7ce4e596ff 140 int status = lidar->init_board();
alexhrao 0:fc7ce4e596ff 141 while (status) {
alexhrao 0:fc7ce4e596ff 142 pc.printf("Initializing Board...\r\n");
alexhrao 0:fc7ce4e596ff 143 status = lidar->init_board();
alexhrao 0:fc7ce4e596ff 144 status_led = !status_led;
alexhrao 0:fc7ce4e596ff 145 wait(1);
alexhrao 0:fc7ce4e596ff 146 }
alexhrao 0:fc7ce4e596ff 147 pc.printf("Finished Initialization\r\n");
alexhrao 1:e82997c13013 148 uLCD.cls();
alexhrao 1:e82997c13013 149 uLCD.locate(0, 0);
alexhrao 1:e82997c13013 150 uLCD.color(GREEN);
alexhrao 1:e82997c13013 151 uLCD.text_height(2);
alexhrao 1:e82997c13013 152 uLCD.text_width(2);
alexhrao 1:e82997c13013 153 uLCD.printf("Wanna\nPlay?");
alexhrao 1:e82997c13013 154 while (starter != 0);
alexhrao 1:e82997c13013 155 uLCD.cls();
alexhrao 1:e82997c13013 156 uLCD.text_width(2);
alexhrao 1:e82997c13013 157 uLCD.text_height(2);
alexhrao 1:e82997c13013 158 uLCD.locate(1, 1);
alexhrao 1:e82997c13013 159 uLCD.color(GREEN);
alexhrao 1:e82997c13013 160 uLCD.printf("POINTS");
alexhrao 1:e82997c13013 161 uLCD.locate(2, 4);
alexhrao 1:e82997c13013 162 uLCD.color(RED);
alexhrao 1:e82997c13013 163 uLCD.printf("TIME");
alexhrao 1:e82997c13013 164 time_thread.start(time);
alexhrao 1:e82997c13013 165 points_thread.start(points);
alexhrao 1:e82997c13013 166 sound_thread.start(sound);
alexhrao 1:e82997c13013 167
alexhrao 0:fc7ce4e596ff 168 // Continually measure distance
alexhrao 0:fc7ce4e596ff 169 while (1) {
alexhrao 1:e82997c13013 170 status = lidar->sensor_centre->get_distance(&dist);
alexhrao 1:e82997c13013 171 if (status == VL53L0X_ERROR_NONE) {
alexhrao 1:e82997c13013 172 //pc.printf("Distance: %dmm\r\n", dist);
alexhrao 1:e82997c13013 173 err_led = 0;
alexhrao 1:e82997c13013 174 // Determine if we've hit the threshold (4 cases):
alexhrao 1:e82997c13013 175 // 1. NOT in the threshold, current distance is above
alexhrao 1:e82997c13013 176 // 2. NOT in the threshold, current distance is below
alexhrao 1:e82997c13013 177 // 3. IN the threshold, current distance is above
alexhrao 1:e82997c13013 178 // 4. IN the threshold, current distance is below
alexhrao 1:e82997c13013 179 //pc.printf("D=%ld mm\r\n", dist);
alexhrao 1:e82997c13013 180 if (!is_point && dist <= threshold) {
alexhrao 1:e82997c13013 181 if (time_left == 0)
alexhrao 1:e82997c13013 182 break;
alexhrao 1:e82997c13013 183 // increment our points
alexhrao 1:e82997c13013 184 ++num_points;
alexhrao 1:e82997c13013 185 is_point = true;
alexhrao 1:e82997c13013 186 point_led = 1;
alexhrao 1:e82997c13013 187 } else if (is_point && dist > threshold) {
alexhrao 1:e82997c13013 188 is_point = false;
alexhrao 1:e82997c13013 189 point_led = 0;
alexhrao 1:e82997c13013 190 }
alexhrao 1:e82997c13013 191 } else {
alexhrao 0:fc7ce4e596ff 192 is_point = false;
alexhrao 0:fc7ce4e596ff 193 point_led = 0;
alexhrao 1:e82997c13013 194 //err_led = 1;
alexhrao 1:e82997c13013 195 //pc.printf("ERROR: %d\r\n", status);
alexhrao 0:fc7ce4e596ff 196 }
alexhrao 0:fc7ce4e596ff 197 }
alexhrao 1:e82997c13013 198 point_led = 1;
alexhrao 1:e82997c13013 199 while (1);
alexhrao 0:fc7ce4e596ff 200 }