Recently, I have ordered a VL53l0X sensor. A VL53l0X sensor is a distance sensor similar to the HC-SR04 ultrasonic sensor which can also be configured to
measure distance by using the travel time of sound and its reflection off an object. The difference between these two sensors is that
the VL53l0X sensor uses a laser to judge distance. Lasers are inherently more accurate of a distance measuring tool as sound may bounce or
return at an angle which can highly vary the return time of sound while light is a bit less variable. Lasers are also more focused than
sound waves, this allows the sensor to pick up on a more narrow cone of vision, which ultimately helps the accuracy of measurements.
I initially purchased this sensor to improve my proximity light, as the ultrasonic sensor sometimes malfunctions and is hard to use to test the
code with. I finished this change very quickly, which improved the behavior of the proximity light so I decided to begin another project.
^A VL53l0X sensor. A time of flight laser ranging module.
I was going to create a workout rep counter for pushups and pullups for an upcoming large project but opted to create something a little more complicated
as a simple rep counter just requires a display and a sensor with basically no advanced techniques. I continued on this thought process of some type of exercise
related project and thought about recreating dance dance revolution.
This idea is a bit advanced but seems plausible, it requires 4 strips of LED which I can create by cutting sections off my LED strip from the proximity distance sensor. It also needs 2 distance sensors with a combined filed of view in a cross shape in order to map the position of
a users feet. I quickly found out through this link that my sensor uses the SDA and SCL
pins of the Arduino. My Arduino Uno has a single port of both, meaning that attaching 2 sensors would require a pointer/address workaround which is doable but this
realization caused me to decide to make a single track and sensor work before the entire DDR project. But a single line DDR is basically just a convoluted form of jump rope,
so that is what I am going to do, a jump rope game, where the LED strips indicate jump timings and the sensor tracks these timings.
For this project, you can find my code here
First of all, this project requires multitasking from the arduino. There is a constant updating of the lights while checking if the user has jumped or not by using the sensor.
Combine this with a buzzer that I wanted to include to notify if the user has missed a jump and some type of timing management of the arduino is required. Using
the millis() function, we can keep track of the milliseconds that have passed since the program has been set up. Using this function to decide to check or update conditions
at programmed timings allows the Arduino to jump between updating and measuring while doing it efficiently enough to seem like multitasking to the human brain.
^A flag updater method that will be elaborated on later. Notice how chunkCheck, widthCheck, and printData only run if enough time has passed to be greater than the flagUpdate
requirement. When the flag is updated, the time is saved in order to time the next flag update correctly.
This project has the LED strip wrapped in a loop on the ground around the player, a green LED is left in a static position. This position is used as the reference point for
the user to know where to jump. Chunks of red lights will periodically rotate through the strip. When a red light passes through the green reference point, the user is intended
to jump. If they do not jump, a buzzer will sense their lack of distance from the sensor and buzz. This project so far is pretty simple in of itself, but in order to increase the complexity, I
attached 3 potentiometers, which control the travel speed of the red lights, the number of red light segments, and the width of these segments. I also wanted these options to be changable in real time, meaning that if
a user turns a knob, the repespective changes occur almost immediately without resetting the Arduino.
I first wanted to be able to generate a static green light while moving a one width one segment red setting with speed customization. Through this process, I created the first large flaw in my program.
the optimal procedure is to update the light formation only when a speed, width or chunk potentiometer value is changed. Shifting it otherwise. Instead, I foolishly programmed the WS2812B strip
to always update the light formation regardless of a potentiometer change in addition to shifting it. The reason I did this is because it seemed easier to program the static green light if I am constantly updating
which is true, since the moving red lights can be handled with a for loop while the green can be left out. Though programming the static light with the optimal strategy is not too complicated either, but definitely a programming dillema I have never seen before.
This optimal code shifts the LEDs through the strip using currentTime and tick to determine the shift rate. It erases the green light right before the shift and puts it back in
after the shift to keep it static
^The Optimal and Suboptimal sequence of procedures. Apologies for the poor flowchart shape choices. Notice the optimal code will check to see if any potentiometers have changed (uses a flag to reconfigure lights) whereas the suboptimal code will always reconfigure lights regardless of if the potentiometers have changed.
So after programming the code in the suboptimal structure I got curious and decided to see how quickly I could move the red light. Using basic division and a stopwatch, I found the WS2812B strip
can update at a rate at about once every 5 milliseconds which was surprisingly fast. I decided to mess with the code to see if I could make several long trains of Red LEDs travel and partially succeeded.
I found that at any speed past 30ms tick rate, when a red light crosses the green reference, the entire system slows down, causing the lights to move jittery. This phenomenon is what caused me to think to
optimize my code. Even with several optimizations though, I failed to solve this problem. Until I found a fix that introduced a new problem.
This is the code used to display a single red train of lights:
It is quite simple to follow, since the color Black denotes turning off a light, a trail of reds that are “width” wide is travelling and their trail is
constantly getting erased by a black light. Using the modulo, the light can wrap around smoothly. This code caused the slow down problem.
The following code manages to evade the slow down problem and still somehow maintains the width I call this method the teleporting method (I am not sure why/how it maintains width visually though):
This code is a product of copy + pasting my own previous code and accidentally changing a variable incorrectly. Never have I accidentally fixed a problem while programming but theres a first time for everything I guess!
Except it introduces new problem: As the lights reach the end of the strip, the lights will momentarily vanish and teleport to the start of the strip with the full width immediately
visible. This is not too much of a problem for small widths as the teleport is barely noticeable but looks terrible for widths more than 5 segments at any
“jumpable” speed. In hindsight, the reasoning for the teleporting method being able to handle high speeds is due to the code being a essentially a single red light at start + width (that somehow looks like a wide red light)
going through the green light. No matter the width setting, the arduino actually only checks the sensor a single time since there is only 1 actual red light. If I had this realization when programming the teleporting method, I would have figured
that the reason for delay was either in the sensor or buzzer activating due to a red light passing a green light. I hadn’t realized this yet so a potential solution I thought of was to write 2 versions of every light update method. A method that used the teleporting lights strategy top handle fast speeds and another to use the wrapping train
which visually looks better for slower speeds. This solution fails to manage simultaneously fast and wide segments. Because of this, I scrapped the idea in the middle of programming the secondary methods looking for another solution. My next suspicion
was that somehow the red light wrapping around to the start of the strip somehow slows the data transfer of the Arduino to the WS2812B strip. I have used this strip before, but never at the update frequency in this project. I tested this by changing the position of the green light. Since it was initially
always at the front. I found that it was the overlap of Red passing green lights that caused the slow down. At this point I had finally narrowed the slow down to the buzzer or the sensor.
I found a a forum post discussing how to increase the speed of VL53l0X measurment rate. It turns out the rate can be reliably adjusted to around 20ms which is a 33% speed improvement. Seeing that this is was as far I can push the sensor, that meant the speed problem persisted though I now knew the cause of the problem and that It wasnt fully fixable. 20ms is quite fast though so I was quite happy with the improvements.
^only 1 line of code to (mostly) fix my problem :(
So thats about it for the details in coding this project, It has been a long, long time since I have written such long code for a single project. I hope to add a few more features and will update if I do. Perhaps a health system? The green can change to different colors based on how many buzzes you get. Maybe it can trigger some punishment if you run out of health?
Update: 8/26/2023
I have decided to add a togglable health system in which you can turn on or off a remaining health system. I thought this would make my program a little more interesting and use a bit more of the potential colors that can be created by the WS281B light strip. There were a few ideas I had in mind to implement a health system:
1. Health Bar:
The first idea was a health “bar” in which a string of lights after the reference point are a visualization of your health. This method is really useful as you can simply look at your remaining health bar and tell how much health you have. In videogames, a bar is probably the most straightforward way to show health though for this case, I can identify a couple flaws. Firstly, if your health exceeds the Number of LEDs, how is this going to be represented? It is possible you could make the health bar “double layered” for example, you health bar in the overlapping section could be for example blue which could represent you still having a health bar underneath as well, but this is perhaps overly complicated, especially given that red trails of lights are also constantly being calculated and these red trails would have to go on top of all the other health bars so they can be seen. And all this must be done while keeping all positions of static reference points and health bars the same. Also, a health bar with overlaps on itself would make it a bit challenging to find the reference point to jump at. Overall, this is a surprisingly complicated and potnetially system to implement so I decided on not going with a health bar.
2. Display:
Another idea is to use some sort of pixel or segment display to show health. This is quite straightforward and requires minimal programming as the complexity of using a display is usually in the circuitry. An LCD display usually looks pretty cool to add to any project but what I realized is that my LCD display has an only 16 x 2 display. Meaning a maximum of 32 characters can be shown. This is not a problem if all I want to do is display health, but there are many other statistics in this program that are also useful to show that would be left out such as width, tick, and chunks which should either all be displayed or be on the serial monitor. It is also possible to implement the previoius “health bar” idea on the LCD screen but this detracts from the idea of using the WS281B to represent health which I would prefer to do.
3. Changing the Reference Light:
The method I decided to implement is to only change the reference light. This eliminates the problem of having a confusing light strip with potentially multiple layers of health bars and has a more straightforward behavior that is easier to program than an overlapping health bar. It also does not use a display that could cause me to sacrifice certain statistics that I would want to show. The idea is to first create an array. This array will be of a size set to the user’s initial health. The array will be filled with values that linearly change between an upper and lower range. This range is determined by the user and will be used to change the hue value of the reference light. For example, selecting an upper range of 127 (cyan) and a lower range of 45 (yellow) will cause the user’s reference light to begin at cyan and gradually change to yellow as their health approaches 0. Though once health bypasses around 10 for this range, it begins to get difficult for a user to estimate their health exactly. To combat this problem, the user may enter a criticalHue value, which breaks the linear change pattern at 1 health remaining, setting the light to a hue of the user’s choosing to notify them that their health is critically low. This idea was fairly straightforward to implement after some obervation of the pattern. Below are the notes I wrote while finding the algorithm:
^You can see the equation I found to calculate the hue when Max Health is greater than 2.
This method does have a constant bug that I am unable to fix. At max health, the light will periodically cycle through colors at a constant rising Hue value of around 300. I do not know why this happens though I believe to be a problem that is out of my scope of expertise to solve as I have spent hours trying to find what is causing this periodic increase to no avail. I have asked fairly qualified individuals as well as chatGPT to diagnose these problems and it seems this may be a problem in the Arduino’s memory management as I cannot find a way to perfectly generalize the rate the hue value rises or at what period other than that it has some proportionality to the tick rate. This bug is not all too bad though since it is a clear indicator of having full health. I decided not to pursue fixing this bug since the result is still quite cool to watch and useful as well. Another small issue with the VL53L0X sensor is that it seems to be very sensitive. If I put the sensor close to a carpet ground, there will frequently be readings of close distances despit there not being any objects nearby. It seems that because dust collects very easily in carpets, even small adjacent movements to the sensor can cause it to detect dust particles in the air. While it is incredibly how sensitive this sensor is, this is a potential problem that needs to be taken into account for this particular project.
^Implementation of the main behavior for hue[] array which is used to change the color of the reference light
The final addition I had to this project was some method of signalling that the played has ran out of health. To do this, I made the buzzer play the start of Beethoven’s 5th Symphony (which is pretty funny to me)
and show the entire LED strip as a rainbow gradient. I will attach the code again here as well as a youtube video demonstration:
This project was a big multitasking and intensive Arduino refresher project. At almost 350 lines of code, this is definitely the most technically complicated endeavor I have worked on as a personal project. I am very proud of this project because every idea from the very start was basically inspired by myself with the culmination being something quite unique. You will find plenty of GLSL programs, robotic arms, and reactive distance sensors on the internet maybe even with full tutorials, but I have never seen a project similar to my own with so many features. I remember first learning Arduino and immediately recognising the power of the tool and its IDE to make useful real-life systems and circuits but never have I made something so close to a “product”. I feel like this project with the power of some pretty packaging, design, marketing, and more customizability could be an actual consumer product that people would be interested in. With every passing project, I am feeling more and more like the “engineer” that you would fantasize yourself being when you first sign up for an Electical Engineering degree. This project has been an absolute blast!
P.S. : Seriously considering buying an electronic BB gun that will shoot you if you run out of health
^ Demo of Jump Rope game
^COde explanation