{"id":96,"date":"2025-11-03T00:05:30","date_gmt":"2025-11-03T05:05:30","guid":{"rendered":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/?p=96"},"modified":"2025-11-30T19:15:02","modified_gmt":"2025-12-01T00:15:02","slug":"robotics-project-gesture-drum-bot","status":"publish","type":"post","link":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/projects\/robotics-project-gesture-drum-bot\/","title":{"rendered":"Robotics Project &#8211; Gesture Drum Bot"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<p>Music has always been an important part of my life, and it&#8217;s something I listen to everyday, whether I need to relax or find motivation. The rhythm and energy of beats have a way of bringing focus and emotions together, and inspired me to incorporate a music element into this project. For this project, I wanted to design a simple robot that could detect hand movements, respond dynamically, and perform an expressive action by drumming to the rhythm of my hand movements. <\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>The Gesture Drum bot uses the user&#8217;s hand as a virtual conductor. And when their hand moves toward or away from the ultrasonic sensor in rhythm, the Arduino measures these distance changes and creates a beat.<\/p>\n\n\n\n<div style=\"height:6px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Each time a beat is detected:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The servo taps like a drumstick<\/li>\n\n\n\n<li>The buzzer plays a short musical note with a random <\/li>\n\n\n\n<li>The LCD screen briefly flashes &#8220;BEAT!&#8221; and then updates the BPM<\/li>\n<\/ul>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Tinkercad Design<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"631\" src=\"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-1024x631.png\" alt=\"\" class=\"wp-image-97\" srcset=\"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-1024x631.png 1024w, https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-300x185.png 300w, https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-768x473.png 768w, https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-1536x947.png 1536w, https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Screenshot-2025-11-02-at-17.08.58-2048x1262.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center has-medium-font-size\">Image of Circuit Design in Tinkercad<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I started by designing and simulating a prototype in Tinkercad. This process helped me to first verify the logic of the code and wiring for different components.<\/p>\n\n\n\n<div style=\"height:6px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>The wires in the circuit design are also color coded to make it more accessible:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Green &#8211; PINs<\/p>\n\n\n\n<p>Yellow &#8211; Ground<\/p>\n\n\n\n<p>Red &#8211; Power (5V)<\/p>\n\n\n\n<p>Blue &#8211; SDA \/ SCL<\/p>\n<\/blockquote>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Circuit Wiring<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Component<\/td><td>PIN connection<\/td><td>Function<\/td><\/tr><tr><td>Ultrasonic Sensor<\/td><td>VCC -&gt; 5V, <br>GND -&gt; GND, <br>SIG -&gt; D9<\/td><td>Measures hand distance<\/td><\/tr><tr><td>Servo Motor<\/td><td>SIG -&gt; D6, <br>VCC -&gt; 5V, <br>GND -&gt; GND<\/td><td>Moves motor based on the distance<\/td><\/tr><tr><td>Piezo Buzzer<\/td><td>(+) -&gt; D3, <br>(-) -&gt; GND<\/td><td>Plays random tone when beat triggered<\/td><\/tr><tr><td>LCD<\/td><td>SDA -&gt; A4, <br>SCL -&gt; A5, <br>VCC -&gt; 5V,<br>GND -&gt; GND<\/td><td>Displace distance and BPM.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div style=\"height:16px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Arduino-Gesture-Drum-Bot-1.pdf\" type=\"application\/pdf\" style=\"width:100%;height:460px\" aria-label=\"Embed of Arduino Gesture Drum Bot (1).\"><\/object><a id=\"wp-block-file--media-d0bc4128-357f-47f0-a4fc-8af61eeb1ed6\" href=\"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Arduino-Gesture-Drum-Bot-1.pdf\">Arduino Gesture Drum Bot (1)<\/a><a href=\"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-content\/uploads\/sites\/33\/2025\/11\/Arduino-Gesture-Drum-Bot-1.pdf\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-d0bc4128-357f-47f0-a4fc-8af61eeb1ed6\">Download<\/a><\/div>\n\n\n\n<p>Above is the schematic diagram of the circuit design. Where it illustrates how all the components are connected to each other and the Arduino board.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Bill of Materials (BOM)<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Component<\/td><td>Quantity<\/td><td>Description<\/td><\/tr><tr><td>Arduino UNO<\/td><td>1<\/td><td>Main board<\/td><\/tr><tr><td>Ultrasonic Distance Sensor<\/td><td>1<\/td><td>Detects distance of hand movement<\/td><\/tr><tr><td>Servo Motor<\/td><td>1<\/td><td>acts as drumstick arm<\/td><\/tr><tr><td>Piezo Buzzer<\/td><td>1<\/td><td>plays a tone for each  beat<\/td><\/tr><tr><td>PCF8574-based, 32 (0x20) LCD 16 x 2 (I2C)<\/td><td>1<\/td><td>displays data<\/td><\/tr><tr><td>LCBG LED RGB<\/td><td>1<\/td><td>LED that can display any color<\/td><\/tr><tr><td>1 k\u03a9  Resistor<\/td><td>3<\/td><td>resistor for LED<\/td><\/tr><tr><td>BreadBoard + Jumper Wires<\/td><td>&#8211;<\/td><td>for connections<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div style=\"height:0px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code Logic<\/h2>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Entire Source Code<\/summary>\n<pre class=\"wp-block-code\"><code>#include &lt;Servo.h&gt;\n#include &lt;LiquidCrystal_I2C.h&gt;\n\nLiquidCrystal_I2C lcd(0x20, 16, 2);\nServo arm;\n\n\/\/pins\nconst int ultrasonic_pin = 9;\nconst int servo_pin = 6;\nconst int buzzer_pin = 3;\n\n\/\/window\nconst int min_dist = 60;\nconst int max_dist = 150;\n\n\/\/beat\nfloat lastDistance = 0;\nfloat lastVelocity = 0;\nbool approaching = false;\n\nunsigned long lastBeat = 0;\nconst unsigned long min_beat_gap = 250;\n\n\/\/BPM\nunsigned long history&#091;4] = {0};\nint Pos = 0;\nbool Full = false;\nfloat bpm = 0;\n\n\/\/Notes\nconst int notes&#091;] = {196,220,247,294,330,392,440,494};\nconst int note_count = sizeof(notes) \/ sizeof(int);\n\nvoid setup() {\n  Serial.begin(115200);\n  \n  pinMode(buzzer_pin, OUTPUT);\n  digitalWrite(buzzer_pin, LOW);\n  \n  \/\/setup lcd\n  lcd.init();\n  lcd.backlight();\n  lcd.clear();\n  \n  \/\/reset servo position\n  arm.attach(servo_pin);\n  arm.write(90);\n  \n  randomSeed(analogRead(A0));\n  \/\/start timer\n  lastBeat = millis();\n\n}\n\nvoid loop(){\n  unsigned long now = millis();\n  \n  \/\/Read distance\n  float dist = readDistanceCM();\n  \/\/positive value, continues\n  bool valid = (dist&gt;0);\n  \n  if (valid){\n    \/\/servo follows hand\n    arm.write(distanceToAngle(dist));\n  }\n  \n  \/\/Detect beat\n  float velocity = lastDistance - dist;\n  \/\/within the window that we defined\n  bool inRange = (dist &gt;= min_dist &amp;&amp; dist &lt;= max_dist);\n\n  \/\/valid hand movement, continues\n  if (inRange &amp;&amp; velocity &gt; 0.4) {\n    approaching = true;\n  }\n  \n  \/\/for detecting hand movement coming back\n  bool reversed = (lastVelocity &gt; 0 &amp;&amp; velocity &lt; 0);\n  \n  if (valid &amp;&amp; approaching &amp;&amp; reversed &amp;&amp; (now - lastBeat &gt; min_beat_gap)){\n    triggerBeat();\n    record(now);\n    approaching = false;\n  }\n  \n  lastVelocity = velocity;\n  lastDistance = dist;\n  \n  \/\/lcd\n  static unsigned long lastLCD = 0;\n  if (now - lastLCD &gt; 200) {\n    lastLCD = now;\n    updateLCD(valid ? dist : -1, bpm);\n  }\n    \n  delay(15);\n\n}\n\n\nfloat readDistanceCM() {\n  pinMode(ultrasonic_pin, OUTPUT);\n  digitalWrite(ultrasonic_pin, LOW);\n  delayMicroseconds(2);\n  digitalWrite(ultrasonic_pin, HIGH);\n  delayMicroseconds(10);\n  digitalWrite(ultrasonic_pin, LOW);\n  \n  \/\/echo\n  pinMode(ultrasonic_pin, INPUT);\n  unsigned long dur = pulseIn(ultrasonic_pin, HIGH, 15000);\n  \n  if (dur==0) return -1;\n  \n  float cm = dur \/ 58.0;\n  if (cm &lt; 2 || cm &gt; 400) return -1;\n  \n  return cm;\n}\n\n\nint distanceToAngle(float cm) {\n  \/\/detect distance inside the window\n  if (cm &lt; min_dist) cm = min_dist;\n  if (cm &gt; max_dist) cm = max_dist;\n  \n  \/\/convert distance to servo angle\n  float t = (cm - min_dist) \/ (max_dist - min_dist);\n  return 35 + (int)(t*(120-35));\n}\n\nvoid triggerBeat() {\n  int note = notes&#091;random(note_count)];\n  tone(buzzer_pin, note, 120);\n  lastBeat = millis();\n}\n\nvoid record(unsigned long now) {\n  unsigned long x = now - lastBeat;\n  history&#091;Pos] = x;\n  Pos = (Pos + 1) % 4;\n  \n  \/\/true once we have stored 4 intervals\n  if (Pos == 0) Full = true;\n  \n  if (Full) {\n    unsigned long sum = 0;\n    for (int i=0; i&lt;4; i++) {\n      sum += history&#091;i];\n    }\n    \n    float avg = sum \/ 4.0;\n    bpm = 60000.0 \/ avg;\n    \n  }\n}\n\nvoid updateLCD(float dist, float bpm) {\n  \/\/print distance\n  lcd.setCursor(0, 0);\n  if (dist &lt; 0) {\n    lcd.print(\"Dist: --       \");\n  } else {\n    lcd.print(\"Dist: \");\n    lcd.print((int)dist);\n    lcd.print(\"cm    \");\n  }\n\n  lcd.setCursor(0, 1);\n  lcd.print(\"BPM: \");\n  if (bpm &gt; 0) lcd.print((int)bpm);\n  else lcd.print(\"--\");\n  lcd.print(\"       \");\n}<\/code><\/pre>\n<\/details>\n\n\n\n<ol class=\"wp-block-list is-style-default\">\n<li>Read distance: The ultrasonic sensor measures hand distance.<\/li>\n\n\n\n<li>Filter Noise: A smoothing algorithm for the ultrasonic sensor to remove inconsistent values that might affect consistency<\/li>\n\n\n\n<li>Detect Beat: If the hand moves toward the sensor and then reverses direction quickly, then it&#8217;s considered to a beat.<\/li>\n\n\n\n<li>Play Beat: After a beat is detected, the system will react to it by playing a random musical note using tone(), and move the servo slightly as a drum tap.<\/li>\n\n\n\n<li>Calculate BPM: The program stores the last few beat intervals and averages them.<\/li>\n\n\n\n<li>Display Data: The LCD continuously updates to show distance and BPM.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Code Explanation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>void setup() {\n  Serial.begin(115200);\n  \n  pinMode(buzzer_pin, OUTPUT);\n  digitalWrite(buzzer_pin, LOW);\n  \n  \/\/setup lcd\n  lcd.init();\n  lcd.backlight();\n  lcd.clear();\n  \n  \/\/reset servo position\n  arm.attach(servo_pin);\n  arm.write(90);\n  \n  randomSeed(analogRead(A0));\n  \/\/start timer\n  lastBeat = millis();\n\n}<\/code><\/pre>\n\n\n\n<p>The program starts with the setup() function, which runs once when the program is uploaded to Arduino. It includes all of the libraries needed and initializes all the variables.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>This line starts a serial port at 115200, which acts as the console and allows Arduino to send debugging messages. In addition, pinMode() sets up the buzzer&#8217;s pin so it can send sound signals. The parameters of it are the pin number and the mode (output \/ input).<\/p>\n\n\n\n<p>diginalWrite(&lt;pin&gt;, LOW) turns off the buzzer at the start so it doesn&#8217;t make any sound when the program starts. With digitalWrite(), you are able to either set it as High (5V) or Low (0V).<\/p>\n\n\n\n<p>Afterwards, the LCD display get initialized. And the servo is configured at a specific pin.<\/p>\n\n\n\n<p>The code now proceeds to the loop() function which runs over and over again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned long nowMs = millis();<\/code><\/pre>\n\n\n\n<p>millis() is a built in function that returns the number of milliseconds since the program started running. And in this case, it is assigned and stored in a variable called nowMs, and is used throughout the loop for timing things like detecting the beat and stuff.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  \/\/Read distance\n  float dist = readDistanceCM();\n  \/\/positive value, continues\n  bool valid = (dist&gt;0);\n\n\/\/read distance function\nfloat readDistanceCM() {\n  pinMode(ultrasonic_pin, OUTPUT);\n  digitalWrite(ultrasonic_pin, LOW);\n  delayMicroseconds(2);\n  digitalWrite(ultrasonic_pin, HIGH);\n  delayMicroseconds(10);\n  digitalWrite(ultrasonic_pin, LOW);\n  \n  \/\/echo\n  pinMode(ultrasonic_pin, INPUT);\n  unsigned long dur = pulseIn(ultrasonic_pin, HIGH, 15000);\n  \n  if (dur==0) return -1;\n  \n  float cm = dur \/ 58.0;\n  if (cm &lt; 2 || cm &gt; 400) return -1;\n  \n  return cm;\n}<\/code><\/pre>\n\n\n\n<p>This section is in charge of reading the distance from the ultrasonic sensor and smoothening the data received. This is achieved by first creating a floating variable and calling the readDistanceCM() function to store the distance returned inside it. Afterwards, a boolean variable is created to check if the reading make sense. And if valid, the distance is passed on to convert it to an angle.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  \/\/Servo follows\n  if (valid) {\n    int targetAngle = distanceToAngle(emaDist);\n    arm.write(targetAngle);\n  }\n\n\/\/function to convert distance to angle\nint distanceToAngle(float cm) {\n  \/\/detect distance inside the window\n  if (cm &lt; min_dist) cm = min_dist;\n  if (cm &gt; max_dist) cm = max_dist;\n  \n  \/\/convert distance to servo angle\n  float t = (cm - min_dist) \/ (max_dist - min_dist);\n  return 35 + (int)(t*(120-35));\n}<\/code><\/pre>\n\n\n\n<p>If the distance is valid, it converts it to an angle that can be passed on to the servo using the distanceToAngle function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> \/\/Detect beat\n  float velocity = lastDistance - dist;\n  \/\/within the window that we defined\n  bool inRange = (dist &gt;= min_dist &amp;&amp; dist &lt;= max_dist);\n\n  \/\/valid hand movement, continues\n  if (inRange &amp;&amp; velocity &gt; 0.4) {\n    approaching = true;\n  }<\/code><\/pre>\n\n\n\n<p>This section is responsible for detecting whether the hand is moving toward the sensor fast enough to count as a beat gesture.<\/p>\n\n\n\n<p>dist is the distance from the ultrasonic sensor. lastDistance is the previous loop&#8217;s distance, and is stored to compare how much the distance changed since then. In other words, if the hand moves quickly, the difference between those two values becomes bigger.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  velocity = lastDistance - dist;<\/code><\/pre>\n\n\n\n<p>Therefore, the formula above gives an estimate of how fast the hand is moving. If the hand moves closer to the sensor, then emaDist (current distance) becomes smaller, lastDistance &#8211; dist becomes positive, and this represents positive velocity, vice versa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/within the window that we defined\n  bool inRange = (dist &gt;= min_dist &amp;&amp; dist &lt;= max_dist);\n\n  \/\/valid hand movement, continues\n  if (inRange &amp;&amp; velocity &gt; 0.4) {\n    approaching = true;\n  }<\/code><\/pre>\n\n\n\n<p>The window min and max defines the detection zone that we actually consider about, if the hand isn&#8217;t in this zone, we ignore the movement, and this is used to prevent false triggers. And approaching is simply a boolean variable that is true when the hand is inside this distance zone.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (valid &amp;&amp; approaching &amp;&amp; reversed &amp;&amp; (now - lastBeat &gt; min_beat_gap)){\n    triggerBeat();\n    record(now);\n    approaching = false;\n  }<\/code><\/pre>\n\n\n\n<p>Finally, the if statement considers whether the hand is inside the valid distance zone and is moving toward the sensor fast enough, if so, it triggers a beat by calling that function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float readDistanceCM() {\n  pinMode(ultrasonic_pin, OUTPUT);\n  digitalWrite(ultrasonic_pin, LOW);\n  delayMicroseconds(2);\n  digitalWrite(ultrasonic_pin, HIGH);\n  delayMicroseconds(10);\n  digitalWrite(ultrasonic_pin, LOW);\n  \n  \/\/echo\n  pinMode(ultrasonic_pin, INPUT);\n  unsigned long dur = pulseIn(ultrasonic_pin, HIGH, 15000);\n  \n  if (dur==0) return -1;\n  \n  float cm = dur \/ 58.0;\n  if (cm &lt; 2 || cm &gt; 400) return -1;\n  \n  return cm;\n}<\/code><\/pre>\n\n\n\n<p>Next up, this function is in charge of calculating the distance from the hand. The logic of it is to first send a short ultrasonic pulse, and wait for the echo to return. And by using this data, it is able to convert that time into a distance measurement.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  float dist = readDistanceCM();<\/code><\/pre>\n\n\n\n<p>We have actually mentioned it previously, and called it to store it inside the dist variable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pinMode(ULTRA_PIN, OUTPUT);\ndigitalWrite(ULTRA_PIN, LOW);\ndelayMicroseconds(2);\ndigitalWrite(ULTRA_PIN, HIGH);\ndelayMicroseconds(10);\ndigitalWrite(ULTRA_PIN, LOW);<\/code><\/pre>\n\n\n\n<p>This line first allows assigns a pin and mode for the sensor. And create a pulse starting with low for 2 microseconds, high for 10 microseconds, and back to low again. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pinMode(ULTRA_PIN, INPUT);<\/code><\/pre>\n\n\n\n<p>After that, it immediately switched to input mode to listen for the echo returning.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned long dur = pulseIn(ULTRA_PIN, HIGH, 15000UL);\nfloat cm = dur \/ 58.0;<\/code><\/pre>\n\n\n\n<p>Finally, this standard formula converts the echo received into distance in centimeters.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Above, I have explained all the essential parts of the code. Most of the program\u2019s logic is organized inside the main loop, but to keep the code cleaner and easier, I moved any repeated or commonly used operations into separate functions.<br><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Fusion Robotics Project Demo\" width=\"500\" height=\"375\" src=\"https:\/\/www.youtube.com\/embed\/9brHa3_FkCA?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"height:26px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Physical Prototype:<\/h2>\n\n\n\n<p>While I really want to build the project out in real life on a real Arduino board, my computer unfortunately couldn&#8217;t detect the port connected to it. I plan on fixing this issue, and once it&#8217;s revolved, I&#8217;ll update this post with a demonstration of the physical prototype.<\/p>\n\n\n\n<p>For now, the Tinkercad prototype functions perfectly, and as a reader, you are able to replicate the project with the wiring and code provided. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Benefits of making a physical prototype:<\/h3>\n\n\n\n<p>Right now, when running the code online in Tinkercad, I&#8217;ve noticed some lag and unresponsiveness. And by compiling the code on a real Arduino board, this issue can be eliminated. Moreover, building a physical prototype provides a more engaging and realistic experience, as you are able to interact with it directly and can have a better user experience.<\/p>\n\n\n\n<div style=\"height:26px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Reflections on AI<\/h2>\n\n\n\n<p>I used GPT-5 as my AI assistant throughout this project. I chose to use GPT not only because I am more familiar with it, but also because it knows me better in terms of my working style and preferences. Over time, it has learned about my personality and requirements stored as a memory, allowing it to provide to responses that I feel more suitable for me.<\/p>\n\n\n\n<p>And in the project, I used AI for a lot of several tasks. This includes: debugging, suggesting better algorithms, and polishing my wording of this blog post to make it sound a bit smoother. In addition, since it had been a while since I last programmed with arduino, I have asked it for guidance on parts of the code.<\/p>\n\n\n\n<p>I chose to use AI because it helps me to save time, while still allowing active learning for myself through problem solving, thinking creatively, and deepen my understanding of coding.  <\/p>\n\n\n\n<div style=\"height:26px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Source Code<\/summary>\n<pre class=\"wp-block-code\"><code>#include &lt;Servo.h&gt;\n#include &lt;LiquidCrystal_I2C.h&gt;\n\nLiquidCrystal_I2C lcd(0x20, 16, 2);\nServo arm;\n\n\/\/pins\nconst int ultrasonic_pin = 9;\nconst int servo_pin = 6;\nconst int buzzer_pin = 3;\n\n\/\/window\nconst int min_dist = 60;\nconst int max_dist = 150;\n\n\/\/beat\nfloat lastDistance = 0;\nfloat lastVelocity = 0;\nbool approaching = false;\n\nunsigned long lastBeat = 0;\nconst unsigned long min_beat_gap = 250;\n\n\/\/BPM\nunsigned long history&#091;4] = {0};\nint Pos = 0;\nbool Full = false;\nfloat bpm = 0;\n\n\/\/Notes\nconst int notes&#091;] = {196,220,247,294,330,392,440,494};\nconst int note_count = sizeof(notes) \/ sizeof(int);\n\nvoid setup() {\n  Serial.begin(115200);\n  \n  pinMode(buzzer_pin, OUTPUT);\n  digitalWrite(buzzer_pin, LOW);\n  \n  \/\/setup lcd\n  lcd.init();\n  lcd.backlight();\n  lcd.clear();\n  \n  \/\/reset servo position\n  arm.attach(servo_pin);\n  arm.write(90);\n  \n  randomSeed(analogRead(A0));\n  \/\/start timer\n  lastBeat = millis();\n\n}\n\nvoid loop(){\n  unsigned long now = millis();\n  \n  \/\/Read distance\n  float dist = readDistanceCM();\n  \/\/positive value, continues\n  bool valid = (dist&gt;0);\n  \n  if (valid){\n    \/\/servo follows hand\n    arm.write(distanceToAngle(dist));\n  }\n  \n  \/\/Detect beat\n  float velocity = lastDistance - dist;\n  \/\/within the window that we defined\n  bool inRange = (dist &gt;= min_dist &amp;&amp; dist &lt;= max_dist);\n\n  \/\/valid hand movement, continues\n  if (inRange &amp;&amp; velocity &gt; 0.4) {\n    approaching = true;\n  }\n  \n  \/\/for detecting hand movement coming back\n  bool reversed = (lastVelocity &gt; 0 &amp;&amp; velocity &lt; 0);\n  \n  if (valid &amp;&amp; approaching &amp;&amp; reversed &amp;&amp; (now - lastBeat &gt; min_beat_gap)){\n    triggerBeat();\n    record(now);\n    approaching = false;\n  }\n  \n  lastVelocity = velocity;\n  lastDistance = dist;\n  \n  \/\/lcd\n  static unsigned long lastLCD = 0;\n  if (now - lastLCD &gt; 200) {\n    lastLCD = now;\n    updateLCD(valid ? dist : -1, bpm);\n  }\n    \n  delay(15);\n\n}\n\n\nfloat readDistanceCM() {\n  pinMode(ultrasonic_pin, OUTPUT);\n  digitalWrite(ultrasonic_pin, LOW);\n  delayMicroseconds(2);\n  digitalWrite(ultrasonic_pin, HIGH);\n  delayMicroseconds(10);\n  digitalWrite(ultrasonic_pin, LOW);\n  \n  \/\/echo\n  pinMode(ultrasonic_pin, INPUT);\n  unsigned long dur = pulseIn(ultrasonic_pin, HIGH, 15000);\n  \n  if (dur==0) return -1;\n  \n  float cm = dur \/ 58.0;\n  if (cm &lt; 2 || cm &gt; 400) return -1;\n  \n  return cm;\n}\n\n\nint distanceToAngle(float cm) {\n  \/\/detect distance inside the window\n  if (cm &lt; min_dist) cm = min_dist;\n  if (cm &gt; max_dist) cm = max_dist;\n  \n  \/\/convert distance to servo angle\n  float t = (cm - min_dist) \/ (max_dist - min_dist);\n  return 35 + (int)(t*(120-35));\n}\n\nvoid triggerBeat() {\n  int note = notes&#091;random(note_count)];\n  tone(buzzer_pin, note, 120);\n  lastBeat = millis();\n}\n\nvoid record(unsigned long now) {\n  unsigned long x = now - lastBeat;\n  history&#091;Pos] = x;\n  Pos = (Pos + 1) % 4;\n  \n  \/\/true once we have stored 4 intervals\n  if (Pos == 0) Full = true;\n  \n  if (Full) {\n    unsigned long sum = 0;\n    for (int i=0; i&lt;4; i++) {\n      sum += history&#091;i];\n    }\n    \n    float avg = sum \/ 4.0;\n    bpm = 60000.0 \/ avg;\n    \n  }\n}\n\nvoid updateLCD(float dist, float bpm) {\n  \/\/print distance\n  lcd.setCursor(0, 0);\n  if (dist &lt; 0) {\n    lcd.print(\"Dist: --       \");\n  } else {\n    lcd.print(\"Dist: \");\n    lcd.print((int)dist);\n    lcd.print(\"cm    \");\n  }\n\n  lcd.setCursor(0, 1);\n  lcd.print(\"BPM: \");\n  if (bpm &gt; 0) lcd.print((int)bpm);\n  else lcd.print(\"--\");\n  lcd.print(\"       \");\n}\n<\/code><\/pre>\n<\/details>\n\n\n\n<div style=\"height:26px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-a89b3969 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-text-align-center wp-element-button\" href=\"https:\/\/www.tinkercad.com\/things\/jgSHn0rcyv5-arduino-gesture-drum-bot\/editel?sharecode=6npUCJ5cCJ0d1pdJn8PYvAmO7uKpDYGgKM9_9q--GNQ\">Tinkercad Design<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/chatgpt.com\/share\/6908300e-73b4-800b-9d94-e8513bb578af\">AI Transcript<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Music has always been an important part of my life, and it&#8217;s something I listen to everyday, whether I need to relax or find motivation. The rhythm and energy of beats have a way of bringing focus and emotions together, and inspired me to incorporate a music element into this project. For this project, I [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,6],"tags":[],"class_list":["post-96","post","type-post","status-publish","format-standard","hentry","category-intro","category-projects"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/posts\/96","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/comments?post=96"}],"version-history":[{"count":26,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/posts\/96\/revisions"}],"predecessor-version":[{"id":157,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/posts\/96\/revisions\/157"}],"wp:attachment":[{"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/media?parent=96"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/categories?post=96"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/michaelh\/wp-json\/wp\/v2\/tags?post=96"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}