I started out the final project push by putting together a project schedule:
Used Trello to outline my remaining tasks:
Developed a series of spirals to aim for:
On Thursday morning, I visited Micro Center to determine which of the remaining components I needed to collect were there. To my surprise, they had quite a wide selection of items.
I found a 2000mAh Lithium Ion battery that was small enough to fit within a 3-4 inch juggling ball, and would likely provide more than enough power to the board for long enough to last an entire demo day.
I ran the ESP32-WROOM32 attached to my USB cell phone charger brick and it ran successfully while cycling the RGB LED color for 4 hours. It ran without any issues, and didn't heat up at all.
Pouring beads into a plastic bag:
With one RGB LED in the light:
With one RGB LED in the dark:
The board held up surprisingly well for being encased in a bunch of beads. Even the bridge wires seem to have survived.
To see if it would stay on, I got a rubber band and tried throwing the bag:
One surprising thing I'm finding is that the beads are very slippery. A negative is that they likely can't be relied on for positioning things within the bags. A positive is that they will allow for easy inserting/moving components around in the bags.
One thing that excited me about using a fabric-based ball enclosure is that it would enable using capacitive touch as an input mechanism. I haven't seen this done on other juggling balls before (and maybe for good reason!)
The ESP32-WROOM-32 has built-in capacitive touch pins:
So I tried hooking up to them. When I first heard about the conductive thread, for some reason I assumed you could just solder it to pads. Wrong! This is why the lilypad and other sewable Arduinos tend to have pads with cutout circles in them.
Just to prototype whether this approach might work, I used a metallic intermediate connector to go from the ESP32 pin to a soldered wire to an alligator clip to the thread and then tested touching it.
The pad-soldered wire required a bit of babying since it was free-standing. Holding up the thread would have caused the
So I took a pair of pliers with insulated handles, held up the thread, and tried touching the thread:
Success! This approach may work. The next step is to see whether a weighted bean bag with thread sewn throughout it would trigger a noticeable cap touch change.
But... I've never sewn before.
I tried sewing for the first time. Not bad! Just went up and down a bunch, and then tried to tie a knot. The final knot was hard! I'll have to ask Graham how to do this properly during Friday's introduction to sewing.
Just to get an end to end input to output going, I decided to use the cap touch value to drive the color LED.
The range is typically 70 (fabric test) or 40 (pliers test) down to 14-15 when an extreme grab occurs. So I want to do something significant when 15 is hit.
I decided to try modulating the LED brightness by the cap touch value.
hueToRGB(color, max(0, min(255, touchRead(T3) - 15)));
Success!
Now I want to see if the Adafruit 2000mAh Lithium Ion Battery I have will work for these balls.
I'm trying manually hooking up the Adafruit PowerBoost 500mA Lipo Charger. This is a bit expensive ($15/per), but will let me hit a baseline and gives USB charging for free. A SMD JST might be better / cheaper later.
I used a couple of carefully placed aligator clips and it worked!
Weirdly I got a real big static shock when I touched my laptop after pressing on stuff.
First I want to get the power connected well enough so it won't definitely fall off / short. I found some M/F jumper cables I could use to go from my FTDI header power/ground to the power board.
I soldered the M part of the cable into the holes in the PowerBoost for +5v and GND.
It worked! And everything is so self-contained!
If I'm going to continue using the PowerBoost, I'll need to do something about that pesky blue-light-while-on! It makes it really hard to see our single, less powerful LED.
Success on the first few loop goals of the ball:
I'm going to test leaving the whole thing connected and assembled overnight (with me) and see how long it lasts.
The ball stayed lit and running overnight! (Update: it lasted about 18 hours.) The 2000mAh battery is probably overkill, but if I can program it wirelessly (which is apparently possible with the ESP32, this may make it easier to iterate later on.
Today I am learning to sew with Graham with the goal of creating a nice package for the ball.
I'm starting out by doing by doing some research into how juggling balls can be sewn, what patterns are required, etc.
One approach is a 4-panel juggling ball. It looks like 4 orange slices, and you sew them all edge to edge. It looks like it would be hard to do with much machine assistance.
Stuffing the ball seems to be done by leaving a small hole in the final connection (via Jeremy Shafer):
Since for my ball I will likely be keeping the pellets within a plastic baggy, I might need to do the whole final stitch (or maybe velcro?) at the way end.
Jeremy Shafer also has a guide for creating them and templates for a few sizes.
These patterns have up to 65mm. Someone online suggested they like the two-panel approach as it's the easiest to sew and "for not having any seams crossing", but "they tend a little more to become unround, cubic even, if sewn inaccurately."
The two-piece ball pattern has a postscript file you can modify. I notice it has a constant at the top that corresponds to the size of the ball. I will try modifying that to be closer to the 3.5 inches (99mm) I'd like to use.
It seems to have worked, but it gets cut off due to the orientation of the PS file. I'll need to figure out how to rotate it (inserting rotate 90 various places didn't seem to do it).
Graham was kind enough to introduce me to how to use the sewing machine. Threading the machine initially was complicated! Fortunately the guide on the Singer manual is a helpful reminder.
One tip: if you leave the needle in and the feet up, you can rotate around the needle:
Graham's attempt came out quite nice! We left a hole at the top to fill with Poly Pellets.
I filled the ball with Poly Pellets:
And weighed the result.
209.6g is 0.462lb, which is $1.53 worth of poly pellets.
The ball feels really nice! The nylon and the pellets are slick, and the nylon is super thin so you can feel the pellets.
When stood up on a corner, it sort of droops like a parallelogram, losing a bit of its ball-like feeling:
Some open questions will be: how will the ball feel with electronics and plastic around everything? How will I fill and re-seal it? Will the lights shine through enough with this larger amount?
This weekend I will be continuing to create these balls, so want to take a stab at one while Graham is here.
I began by drawing lines to cut out two sides of the ball (later I may want to use a lighter marker).
Then I pinned up the cut out.
Sewing went OK for a little while! What I learned is I should leave more of a seam allowance (Graham suggested about a quarter of an inch.)
When I got too close to the edge, things started bunching up, and the ball started looking like the fabric version of Zalgo text.
This required un-jamming the sewing machine. That was mostly a process of cutting the threads, pulling things out, and re-threading everything. Graham helped with the bit of fabric that ended up inside of the lower part of the needle receptacle.
I used the seam ripper to remove the terrible part.
Did a bit more repair, and the ball was complete enough (just barely!) to hold pellets.
I want to figure out what types (and geometry) of lights will be best within the balls.
I have a variety of lights I can test:
It seems like for the most part they all use the same 5050 LEDs.
(Power-related note: I started re-charging around 8:15pm? The blue and orange lights turn on when I do that.
In thinking about how to test these lights on my board, I looked up how to deal with through-hole connections. This StackExchange guide had a bunch of nice photos.
Grabber test clips or alligator clips seem like good options. I couldn't find grabber test clips so I will use alligator clips and jumper wires.
Alternatively, I bet I could use conductive thread.
I ended up doing the following:
I poured a bunch of the pellets into a translucent plastic cup, and shimmied the nylon fabric we're using to the outside of it to simulate how it will look.
The 16 LED ring lights up fantastically, and surprisingly you can't even tell from the outside which way the ring is pointing. I hope this means the beads are doing enough diffusion to get beyond the issue Jiri had mentioned some light up juggling balls had where two hemispheres of lights would cause a ring of un-even un-lit-ness on the outside.
Suddenly only 7 of the 16 pins were lighting up. Odd.
The 7 pin NeoPixel (RGBW) was quite nice and worked fine, but not quite enough power to hit a half-cup full of the beads.
There's a line Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGBW); //NEO_GRBW + NEO_KHZ800); that controls the controller type for the LEDs. I will try just NEO_RGBW instead of NEO_GRBW which was the default.
Nothing happened when I changed it to NEO_RGBW, so trying going back to the combination -- nothing again.
The WROOM is starting to heat up a bit. I wonder if there is a short somewhere.
I guess it is GRBW not RGBW. Weird!
Nevermind - it turned out I had the wrong pin selected already.
With _JUST_ RGBW, it has a bit of a flicker when it lights up. With both NEO_RGBW + NEO_KHZ800 set, it is very smooth. The judder reminds me of when I had it hooked up to the capacitive touch test pin.
Testing the NeoPixel Ring 24 - originally I had the wrong setting for this and the cycle was very RGB-y. I switched from NEO_RGBW to NEO_RGB and it worked beautifully. Though I had to also up the # of LEDs!
Next I wanted to test the brightness difference. (Interesting to note, it seems like sometimes in the pattern the LEDs stutter when they should be off.)
This morning I worked on sewing a conductive thread ball at my house with the assistance of my wonderful sewing expert mom!
I now have a ball shell that works well for experiments.
Some notes:
Now that I know what new features I'll need for this:
In doing some more research about driving speakers with the ESP32, I decided to ditch audio in favor of focusing on accelerometer, capacitive touch and lights.
Two big pieces of advice on the NeoPixel website:
I will look at adding 3 5v and 3 ground, and 3 output breakout pins. I will test that those output pins work for NeoPixels too.
I quite like Pin 24 (Touch 0) for this. It is close to the outside of the board already. Unfortunately right now, though, that pin is not exposed to the outside because it is blocked by RX and TX.
RX and TX could go around the other side of the board, though.
Eyal's board had a nice approach
Eyal had 3 SMD JST pins. I will mount those down.
I took a look at the Lilypad Fabduino for inspiration.
I love the idea of using a pad like this for cap touch pins.
I'm having fun making the board look cool!
Extra pinouts I will need:
I realized what may be my pinch point may be accelerometers!
They all seem to require Vin, Gnd, SCL, SDA for i2c.
According to here:
The hardware I2C pins for the ESP32 are GPIO 22 (SCL) and GPIO 21 (SDA). The pins with a blue rectangle in the figure below.
Of the types I have:
LSM90S1: Vin, 3v3, GND, SCL, SDA- can have 2 (one more at Microcenter tomorrow)
MPU9250: XXX, VCC, GND, SCL, SDA, i1, SCL, SDA- 3 coming
BNO055: Vin, 3vo, GND, SDA, SCL, RST
MMA8451: Vin, GND, 3Vo, i2, i1, SCL, SDA
VIN == 5v, 3v == 3v
The LSM and MPU can be made to work together if you treat the MPU VCC as 3v3, since according to its Amazon description:
* Supply Voltage: 4.4 to 6.5 V or 3.3V if you solder the solder jumper near the on-board voltage regulator
So I could have 4 pin FTDI style header hanging off the side.
Which pins could work for the NeoPixels? Above I said 25 worked well, and I see that right now. But it's down at the bottom of the board.
Is it any GPIO? Let's try 4 and 16 since they are close to our pinout.
GPI04 (second to last) works well
GPI016 (third to last) barely works...
GPI017 (fourth to last) also barely works...
Maybe they need to be ADC?
Wonder if we can share the programming one (GPIO2)
How about 5. It works well! That will be close enough to where I need it.
Finally have my EAGLE board all designed! A few things of note:
The milling and stuffing went well! I got in to a groove of stuffing as my next board was being milled.
"What is this mess? Oh it's a graphic!" -Eyal
Yesterday I got all of my boards milled and together!
Today I am going to be programming the boards to have configurable lights and possibly start in on pattern generation. Tomorrow will be about getting the interface going.
I'm going to start out by testing capacitive touch driving the NeoPixel colors.
I originally picked the wrong pin!
I had set a #DEFINE TOUCH_PIN but not used the variable when I called touchRead(TOUCH_PIN)
It works! Some things of note:
In Networking week, I initially experimented using bluetooth to communicate from a browser to the board. Now I'm thinking Firebase using Firebase-ESP32 might be a more flexible option.
I'm interested in exploring PlatformIO + Clion
brew install platformio
One error I got:
# unauthorized, Could not parse auth token.
They hid the API key for the (legacy) realtime DB, here is how to get it.
It worked!!
Now to have it stream the current cap touch value. This would be really helpful for telling what thresholds to configure on the balls for cap touch catch detection and possibly accelerometer as a fall-back.
I don't need to store historical numbers, I can have a given client do that (or graph that in the browser for example).
I hooked up a real-time graph in the browser using Chart.js:
I really want the ability to edit light patterns so I was running up against the limits of jQuery a bit. I'm exploring creating a React app (tried using babel + react-dom + react in the browser but it didn't seem to support modules without a build system the way cuttlefish does).
In the mean time I am going to continue working on getting throw detection tight and a single light animation playing based on that. Then a dropdown for modes!
First a field that sends the min touch value to the ball.
I periodically get "SPI Reset" loops when trying to program the board where the board seems to be stuck in a boot loop. This Q&A doesn't seem related
When trying to stream data from Firebase, I keep getting:
http connection was used by other processes
The data does eventually come in.
Worth seeing if I can get those parameters to become the color anyway.
Next things I will try:
One interesting clue, the data comes in when the stream times out:
connection refused Stream not available connection refused Stream not available connection refused Stream timeout, resume streaming... Stream not available not connected Other Data type: 68.575783235 Stream not available
I worked a bit on a logo! I played around with text shapes in Adobe Illustrator.
I'm trying to run just sending cap touch data again. We will see how that goes.
It turns out just making a bunch of requests works pretty well.
About conductive thread interfacing options
For these config and feedback parameters, data rates have been important. I have been throttling the data requests since I'm concerned they may interrupt normal logic, so should only be used for debugging / should be disable-able outside of a heartbeat.
if (millis() > nextConfigGetTime) { nextConfigGetTime = millis() + configUpdateInterval; currentColorR = updateIntFromPath("/config/" + BALL_GUID + "/color/r", currentColorR); currentColorG = updateIntFromPath("/config/" + BALL_GUID + "/color/g", currentColorG); currentColorB = updateIntFromPath("/config/" + BALL_GUID + "/color/b", currentColorB); currentBrightness = updateIntFromPath("/config/" + BALL_GUID + "/intensity", currentBrightness); } if (millis() > nextLowPriConfigGetTime) { nextLowPriConfigGetTime = millis() + LOW_PRIORITY_CONFIG_INTERVAL; catchBelow = updateIntFromPath("/config/" + BALL_GUID + "/catchBelow", catchBelow); configUpdateInterval = updateIntFromPath("/config/" + BALL_GUID + "/configUpdateInterval", configUpdateInterval); capUpdateInterval = updateIntFromPath("/config/" + BALL_GUID + "/capUpdateInterval", capUpdateInterval); loopDelay = updateIntFromPath("/config/" + BALL_GUID + "/loopDelay", loopDelay); } if (millis() > nextCapPushTime) { nextCapPushTime = millis() + capUpdateInterval; setData("capTouch", String(touchRead(TOUCH_PIN))); }
My vision for this project was to have juggling balls that could play defined animations at the moment of each throw, since I hadn't seen that before.
I'm now looking to implement that in the Arduino code. It's a bit tricky since I'm not used to working with multidimensional arrays in C++.
I added a few variables:
const int throwAnimation[][4] = {{100, 255, 0, 0}, {200, 0, 255, 0}, {300, 0, 0, 255}, {400, 0, 0, 0} }; int currentAnimationStep = 0; bool isAnimating = false; int animationStartTime = 0;
And I step through it. It may be the case that 100ms between each is too fast! And setting the color to 0,0,0 at the end with intensity 100 leaves it white?
Or it's possible that the way I check the array length is inaccurate:
if (sizeof(throwAnimation) / sizeof(throwAnimation[0]) >= currentAnimationStep) {
I tried increasing the duration of each step, and using a simple pre-defined length.
It was the length issue! Time to juggle this!
It works!! It is pretty cool! I want to try it untethered as well, first I want to make a more interesting animation.
I just realized the animation was happening faster than the timing I had set (one second each). That is because I don't initialize animationStartTime when the throw starts.
I'm now playing with making it play a rainbow animation.
Rainbow animation is cool! It needs to aggressively stop if the cap touch threshold is hit again, though.
Light-on-throw is so cool in the dark!
I wonder if I can arrange to demo in a dark spot tomorrow. I posted to GitLab Issues to see if anyone else was interested / if that might be a possibility.
Kristen had an idea of idea of making the Ls on programmaballs juggling balls. I will give that a quick shot.
After some polling, this one emerged the victor:
Now to go from 1 ball demo to 3 balls.
First to inventory the parts I have.
Think it makes sense to just pick the brightest/largest rings, solder jumpers for them. This time I might make smaller ones.
I counted and laid out all the parts I had. I will be using different types of NeoPixels for each ball for now.
I soldered up the remaining 4 NeoPixel rings going from jumper (F) direct to the holes on the pixels.
And I soldered F to solder-wire-loops for attaching conductive thread to the pinout:
And I tied the conductive thread to that loop:
Then I assembled the three balls!
Interestingly I got an unexpected power-draw the first time I programmed it. It didn't happen again.
The colors I was seeing on the pixels didn't match what I had in the UI. I saw a note on the NeoPixel guide about "(some RGBW strips use NEO_GRBW, so try that if you're getting unexpected results!)", so am trying that.
I got a wicked static-electrical shock when I hit the light after experimenting.
One half of one of my 16 GRBW NeoPixel rings is busted. That happened earlier, before I had even soldered it in. Dang! Fortunately I have another 16-er, but it may be a different color temperature.
The replacement strip works great! It seems to be the same color, too.
I then disassembled the balls to go charge the batteries for tomorrow.