Introduction: DrumCube, an Arduino Robot Drummer

Live music gigs are always a very special thing, but to produce it, you need humans, and humans are not always available. I occasionally play gigs equipped by my guitar and my voice, but music generally features many others instruments, like percussion, and these instruments tend to only sound when played by humans. This problem could be solved by just using a recording and playing over it, but that would somehow feel against the idea of the “live” concept.

So, I decided to get rid of the human element entirely, and build a robot drummer... The idea would be to make something portable, which could move and play sounds in different rhythms with no use of samples/recordings, that could be played or stopped at will, and that its sound could be amplified in a gig/band situation.

Disclaimer 1: As I said in my previous project, I have no formal electronics education, so I'm sure that many of the circuits described in this tutorial could be arranged in a better and more efficient/effective way. I'm open to suggestions. Btw, the same applies for all the code I wrote in this project.

Disclaimer 2: In order to keep this tutorial as short, easy and useful as possible, I decided to focus in the main functions of the projects. These being, how the sound is produced, how is captured and amplified, and how the arduino keeps everything under control. With this in mind, I simplified many aspect of the project, like the use of a simple tact switch instead of a rotary encoder.

Step 1: Materials

**I did all the wooden pieces by hand, so I don't really have any exact plans for them. I've drawn and explained everything as best as I could though, and I don't think it would be much hard to replicate them, more so if you have a cnc or a 3d printer.

Step 2: The General Idea

In first place, it is necessary to define what the drumkit elements will be. Following the logic of the frequency range, the standard drum-kit is usually conformed by three basic elements; the kick (low frequency range), the snare (mid range), and the hi-hat (high range). After a couple weeks of experimentation, I decided to replicate this sounds with the following arrangements:

Kick/LowRange: 2 piezos, covered with a small piece of sponge material, which are hit by a servo controlled stick with a soft rubber end.

Snare/MidRange: A can of pringles, with a small square sheet of tin for resonation, which is also hit by servo controlled sticks with a rubber end. The sound is then captured by an electret microphone.

HiHat/HighRange: A transistor-based white noise generator circuit, which is controlled directly by the arduino.

All of the above mentioned systems are set in time by the arduino board, and the audio signal generated by them is then amplified to an instrument level signal by three simple amplifier circuits for each one of them.

Step 3: The Kick Drum

This works by taking advantage of the low frequency sound which is produced when a piezo pickup is directly touched or hit with a soft object. To make this work, first we need to automatise the hitting of the piezo, and secondly, we need to amplify the audio signal generated by said piezo.

As I mentioned above, I used a servo controlled stick to hit two different piezos located at each end of the servo's movement range (in order to make use of all the movement). To help damp high frequencies, the piezos are covered with sponges and the stick has a soft rubber end.

It is important, at this point, to define the angle at which the servo will be when it is hitting each end of its range of movement. This information will be necessary later, and It can be easily obtained using a basic piece of arduino code (like the one described here) and the Serial.print(); command.

The audio signal from the piezos is then amplified by the circuit described on the image above, powered by 5v - 7v from a voltage regulator, which is fed from the main power supply (not the arduino board). This is important because, although the amplifier itself would be able to work with the 5v provided by the arduino, it is ideal to maintain the analog audio signal as separated as possible from the digital circuitry, in order to avoid any digital noise interference making its way into the final audio signal.

Again, I have no formal (or any lol) electronics education, and I've struggle a lot with this last problem. Noise is virtually impossible to get absolutely rid of in any audio application, but I've found that the filtering in the voltage regulator (as well as the analogue and digital separation, and a proper electrical shielding of the project) reduces noise to very acceptable levels.

Step 4: The Snare

This is a very complicated sound to replicate, even now I'm still not absolutely happy with the sound I’ve achieved. To pickup this sound, I decided to use an electret microphone, as they're usually more sensitive, and the unique acoustic nature of a snare sound makes this necessary.

As we did with the kick system, we need a mechanical part, and an audio one. I've attached a drawing of the mechanical system I designed. Yes, I used a Pringles can upside down, it was the perfect shape, had the best sound I could find, and the crisps were delicious.

Just like we did with the kick, we need to get the angle at which each servo will be when resting and when hitting. Again, tinkering with the Potentiometer+Servo tutorial, we can get that information.

The audio signal from the electret is amplified by another instance of the same circuit used in the kick's amplifier, although in this case with a few modifications to power the electret microphone, and without the low-pass filter used in the kick.

Step 5: The HiHat

I used a different approach to this sound, a transistor-based white noise generator circuit, and a led indicator, are activated by the arduino when it is required. White noise is a random signal with equal intensity at different frequencies. Roughly speaking, just as white light contains all colours, white noise contains all frequencies, and it sound like a nonexistent radio station (static). This is a noise commonly used in electronic music and audio synthesis, I read that it was even used to make the classic sound of the 80's snares and cymbals.

As the diagram shows, after the noise is generated, it then goes through a high-pass filter to eliminate the unwanted frequencies and to accentuate the drum cymbal sensation.

The white noise generator needs 12v or higher to function, that are provided by a step up voltage elevator, which itself is powered directly by an arduino pin. This is the only instance in which an audio system is attached directly to the arduino, because noise has to be generated only in certain times, and noise seems not to be an issue in a circuit that generates noise. You can't make white more white can you?

Step 6: The Signal Mix & Complete Diagram

After all of three drum-kit sounds are generated, each signal goes through a potentiometer and a basic passive mixer circuit, which mixes the final audio signal.

We also need a way to select what drumbeat to play and when to start and finish playing. For this instructable, we will use a tact button connected and a simple switch for each function respectively (connected to the arduino pins 2 and 3, as the image above shows)

Step 7: The Code I - Servo Time Calibration

Now, given that this project is a musical instrument, it needs to generate sound in the right time. So, before we start coding different rhythms and functionalities, specially in the case of the kick and the snare, we need to program the movement of the servos to start some milliseconds before their turn to sound, in order to compensate the time the stick takes to go from resting to hitting, and make the sticks hit their destination just in time.

To solve this problem, I improvised a simple system to measure the time it took every servo to go from its resting position to the exact point of hitting its destination. I covered the sticks and the areas where they hit with aluminum foil, and attached alligator clips to the foil, creating a connection only measurable by the arduino when the stick hits its target. Then I wrote a simple bit of code, which did two things: firstly, make the servo sticks hit repeatedly their target, going from resting angle, to hitting angle. And secondly, print the time when the servo command was sent, and the time when the stick close the connection with its target. Subtracting this two values will give us the time it takes the sticks to make a sound, from resting to hitting.

After doing this enough times, you'll end up with a lot of roughly similar numbers, which you can then average, and obtain a pretty much usable time of anticipation for every servo.

Step 8: The Code II - Drum Combinations

Now, we define a number code for every combination of kick, snare, and hihat in a beat. We assign the first odd numbers to each of our three drumkit elements:

Kick	Snare	HiHat
1 3 5

And then, if we need to play more than one element at the same time, we simply add the numbers of each element. Kick and snare would be 4, kick and HiHat 6, and so on. Nought/zero is a silence. Now we have a number for every possible drum elements combination, and we can store them in a variable (cycleNumber variable).

3 HiHat		x 	-	x	x
2 Snare - x x x 1 Kick x x - x _________________________________________ COMBINATION: 4 3 5 6

Step 9: The Code III - Semiquaver Cycle (hitting on Time)

We now know that is necessary to anticipate the commands for the kick and snare servos, but we also have to play the hi-hat on time, which is a circuit that work almost instantly.

To approach this problem, which might get a bit confusing down the road, I decided to establish a system of checkpoints inside every semiquaver (or sixteenth note) of every bar. I chose a sixteenth because in the tempo of most common rhythms, a sixteenth of a note last about 100 to 150 milliseconds, which is also the speed at which the slower servo (in my case) can go from rest to hitting, or vice verse. We can change the speed of the tempo by simply changing the amount of milliseconds assigned to the timeTotalCycle variable. Here is a useful table of tempo, notes, and milliseconds equivalence. If we tried to play something faster, and we reduce the total time of the cycle lower than the time the slower servo takes to move, this servo might be unable to reach hitting position from rest, and therefore no sound would be produced. A more detailed look at the diagram I've posted, will explain this better.

As shown in the diagram, at the start of what I called the semiquaver-cycle we send the command for positioning the snare servos on their rest position, in case that they had been commanded to hit in a previous cycle. Also at this point, we begin counting the milliseconds that has been set for the duration of the entire semiquaver cycle.

After that, the arduino will start checking if the amount of time left for the cycle to end, is equal to any of the times that each servo needs to produce sound. The idea here, is to detect the point in time at which we have to send the hitting command so that every servo reach and hit their target (and make their sound) at the exact end of the cycle. Every element will therefore have to start its movement at different checkpoints in the cycle, depending on how fast the servo and its mechanism are. The hihat white noise circuit, on the other hand, can be just turned on at the very end of the cycle or a few milliseconds earlier (20 recommended). Its ending will be commanded on the next cycle in a checkpoint defined by you, how long after the start of the cycle you set it, will be how long the hihat sound is (I set mine between 30 and 40 milliseconds, but that is up to you).

Every time one of these checkpoints are met, the arduino will check if its corresponding element is intended to action, by comparing it with the drum combination number stored on the cycleNumber variable (the number code system explained earlier). Example, if the snare's checkpoint is met, the snare's number is 3, so the hitting command will only be sent if the drum combination has the numbers 3, 4 (3+1), 8 (3+5) or 9 (3+1+5).

3 HiHat		x 	-	x	x
2 Snare - x x x 1 Kick x x - x
_________________________________________
COMBINATION: 4 3 5 6


Why using two sets of sticks and servo in the snare mechanism?

Well, if you think about it, if only one servo would have been used, and lets say its rest-to-hitting time was 100 milliseconds, the shortest amount of time that our semiquaver cycle could last would be 200 milliseconds. 100 to allow going back from hitting to rest, and then 100 more from rest to hiting. 200 millisecond for each sixteenth note equals to a tempo of around 75 bpm (“dream on” by aerosmith), which is pretty slow and limiting in terms of possible rhythms.

Using two separate sets of sticks and servos, and alternating their use, making one go back from hitting to rest in its 100 milliseconds, while the other goes hitting in the same amount of time, reduces the minimum semiquaver cycle time from around 200 to 100 milliseconds approximately (“Call me” by blondie), much faster. In other words, the time of the slower servo, is also the minimum time the semiquaver cycle can last.

Step 10: The Code IV - Making the Drum Beat

We now have a way to define exactly what sounds to generate, and also, to do it all at the exact same time. We just need to play 16 of these sixteenth note cycles, one after another, with the desired drum combinations for each one, and we have a bar. We repeat that bar as long as we want, and voliá, we have a musical drum beat.

With a simple bit of code, we store our bar-sequence of 16 semiquaver in an array, and make the arduino go through over and over. Different drum beats can be stored and played by setting different arrays of 16 numbers, as shown in this diagram:

3 HiHat		x-x-x-x-x-x-x-x-
2 Snare		----x-------x---
1 Kick		x-------x-------
ARRAY:		6050805060508050
3 HiHat		x-x-x-x-x-x-x-x-
2 Snare		----x-------x---
1 Kick		x--x--x--xx---x- 
ARRAY:		6051806051608060

To switch between these arrays, we'll use the tact switch button connected to pin 3.

Step 11: The Code V - Putting It All Together

// DrumCube, an arduino robot drummer
// by Franco Molina @artefrancomolina

// Setting servos #include <Servo.h>
Servo servo1;
Servo servo2;
Servo servo3;

// Servo positions // this will differ depending on your servos, please test yours to find out the values that best suit you byte restServo1 = 12; byte hitServo1 = 21; byte restServo2 = 123; byte hitServo2 = 114; byte restServo3 = 4; byte hitServo3 = 19;

// Setting the HiHat white noise generator #define noiseGenerator 12 #define noiseLed 13 //turn on led as a visual representation of the noise being generated

// Setting servo times // this will also differ depending on your servos, please test yours to find out the values that best suit you byte timeSnare1 = 110; byte timeSnare2 = 108; byte timeKick = 71; byte timeHihat = 20; byte sustainTimeHihat = 70;

// Setting previous Snare and Kick byte previousSnare = 2; byte previousKick = 0;

// Setting cycle times static unsigned long timeStartCycle; static unsigned long timeTotalCycle; // changing this, change how fast/slow the rhythm is, you can set a specific value for each drumbeat in the selectDrumBeat() function

// Bar and Cycles variables int cycleNumber; int cycleNumbers[] = {6, 0, 5, 0, 8, 0, 5, 0, 6, 0, 5, 0, 8, 0, 5, 0}; // this array stores the drum elements combination for each cycle (semiquaver or sixteenth) of the drumbeat byte timeSignature = 44; int drumbeatID = 0; // ID number for each drumbeat

// int i; // for counting the time in the "for loop" of the cyclePlayer function int b; // for the "for loop" that counts the number of cycles

// control interface #define switchPlayStop 2 // switch bettween playing and stop #define buttonSelect 3 // tact button that select the drumbeat to play

void setup(void) {

// define pins for each servo pinMode (5, OUTPUT); pinMode (6, OUTPUT); pinMode (9, OUTPUT);

// attach servos to those pins servo1.attach(5); //caja 1 servo2.attach(6); //caja 2 servo3.attach(9); //bombo delay(150);

// put all servos on rest position servo1.write(restServo1); servo2.write(restServo2); servo3.write(restServo3); delay(300);

// Setting hihat and led pinmodes pinMode (noiseGenerator, OUTPUT); digitalWrite(noiseGenerator, LOW); pinMode (noiseLed, OUTPUT); digitalWrite(noiseLed, LOW);

// Setting control interface pinmodes pinMode (switchPlayStop, INPUT_PULLUP); pinMode (buttonSelect, INPUT_PULLUP);

}

void loop(void) { if (switchPlayStop == LOW) { //if the play/stop switch is ON (logical LOW) //We start a "for loop" to play a semiquaver cycle a maximum of 16 times, for every bar for (b = 0; b < 16; b = b + 1) { selectDrumBeat(); // select the drumbeat based on the drumbeatID selected cyclePlayer(cycleNumbers[b]); //play the semiquaver cycle for each number stored in the cycleNumbers array

// if we reach the maximum number of cycles (16 for 4/4, 12 for 6/8, 12/8 or 3/4) we start again if (b == 11 && (timeSignature == 68 || timeSignature == 128)) { b = -1; } if (b == 15) { b = -1; } } } // If the play/stop switch is OFF (logical HIGH) we enter settingMode else { settingMode(); // function that lets you choose between different drumbeats and wait for the play switch to be activated } }

// CYCLE PLAYER // this functions runs a semiquaver cycle every time is called. void cyclePlayer(int cycleNumber) { // we store every single value of the cycleNumbers array into the cycleNumber variable timeStartCycle = millis(); // we save the starting time, to compare it later with the actual time and get the time past

//set both snare servos to rest position in case they were on hit position servo1.write(restServo1); servo2.write(restServo2);

//we star a "for loop" for the entire duration of every semiquaver cycle for (i = 0; i < timeTotalCycle; i++) {

//now we send the hitting commands on time so they reach their destination on time (the end of the semiquaver cycle)

// if we reach the time to send the command to the snare on servo1. We start checking the element that takes the longest time to move, in this case is servo1, in yours could be different. if ((millis() - timeStartCycle >= timeTotalCycle - timeSnare1)) { // we check if this is the one snare of the two, that has to be played, // and also, if in this cycle a snare has to be played at, all based on the cycleNumber number (this number define the cobination of drum elements) if ((previousSnare == 2) && ((cycleNumber == 9) || (cycleNumber == 8) || (cycleNumber == 4) || (cycleNumber == 3)) ) { servo1.write(hitServo1 + 5); // we send the servo command } // if we reach the time to send the command to the snare on servo2 if (millis() - timeStartCycle >= timeTotalCycle - timeSnare2) { // we check if this is the one snare of the two, that has to be played, and also if in this cycle a snare has to be played at all if ((previousSnare == 1) && ((cycleNumber == 9) || (cycleNumber == 8) || (cycleNumber == 4) || (cycleNumber == 3)) ) { servo2.write(hitServo2 - 5); // we send the servo command } // we now check if we reached the time to send the command to the third servo, the kick if ((millis() - timeStartCycle >= timeTotalCycle - timeKick)) { // we check if in this cycle a snare has to be played if ( (cycleNumber == 9) || (cycleNumber == 4) || (cycleNumber == 6) || (cycleNumber == 1) ) { // we check on what position was previosly the servo, either hiting the left or the right piezo, this state will be saved later if (previousKick == 0) { servo3.write(hitServo3); } else if (previousKick == 1) { servo3.write(restServo3); } } // finally, we check if the time to turn on the white noise generator was reached, for the hihat if (millis() - timeStartCycle >= (timeTotalCycle - timeHihat)) { // we check if in this cycle the hihat has to be played if ( (cycleNumber == 9) || (cycleNumber == 8) || (cycleNumber == 6) || (cycleNumber == 5) ) { digitalWrite(noiseLed, HIGH); digitalWrite(noiseGenerator, HIGH); } } } } } // This is where the semiquaver cycle ends.

// HIHAT DURATION // turn off hi-hat noise generator, but only under the following conditions: // //if the noise-generator was ON //the time past exceed the time set as sustain //the time past does not reach yet the point where a new noise could start if ( (digitalRead(noiseGenerator) == HIGH) && (millis() - timeStartCycle >= sustainTimeHihat) && (millis() - timeStartCycle < (timeTotalCycle - (timeHihat + 10))) ) { digitalWrite(noiseGenerator, LOW); digitalWrite(noiseLed, LOW); }

// Check if the play/stop switch is switched OFF if (digitalRead(switchPlayStop) == HIGH) { i = timeTotalCycle; //reset time counting digitalWrite(noiseGenerator, LOW); //turn off noise digitalWrite(noiseLed, LOW); //turn off noise servo1.write(restServo1); //stop servos servo2.write(restServo2); //stop servos }

delay(1); }

// If one of the Snares was hit, then declare it as the previous one, in order to hit the other one the next time if ((cycleNumber == 9) || (cycleNumber == 8) || (cycleNumber == 4) || (cycleNumber == 3)) { switch (previousSnare) { case 1: previousSnare = 2; break; case 2: previousSnare = 1; break; default: break; } }

// If one of the piezo kick was hit, then declare it as the previous one, in order to hit the other one the next time if ( (cycleNumber == 9) || (cycleNumber == 4) || (cycleNumber == 6) || (cycleNumber == 1) ) { switch (previousKick) { case 0: previousKick = 1; break; case 1: previousKick = 0; break; default: break; } } }

// SETTING MODE // this mode is activated when the play/stop switch is swiched OFF (logical LOW) void settingMode() { while (digitalRead(switchPlayStop) == HIGH) { // keep doing the following actions while the play/stop switch is OFF if (digitalRead(buttonSelect) == LOW) { // if the selection button is pressed (logical LOW), we change the drumbeat drumbeatID ++; // we select the next drumbeat each time the button is pressed selectDrumBeat(); // select the drumbeat based on the drumbeatID // OPTIONAL // Here you can add whatever piece of code to indicate which drumbeat is being selected, // this could be turning in a led, sending a serial.print(), etc. if (drumbeatID > 7) { //in case we exceed the total number of drumbeats availabe (), we go back to 0. drumbeatID = 0; } delay(100); //delay to avoid detecting button twice when pressed once } } }

// SELECT DRUMBEAT // this functions uses the value stored in drumbeatID, to modify all the variables needed in order to choose between drumbeats. void selectDrumBeat() { switch (drumbeatID) { case 1: // DiscoBasic timeTotalCycle = 124; timeSignature = 44; cycleNumbers[0] = 1; cycleNumbers[1] = 0; cycleNumbers[2] = 5; cycleNumbers[3] = 0; cycleNumbers[4] = 4; cycleNumbers[5] = 0; cycleNumbers[6] = 5; cycleNumbers[7] = 0; cycleNumbers[8] = 1; cycleNumbers[9] = 0; cycleNumbers[10] = 5; cycleNumbers[11] = 0; cycleNumbers[12] = 4; cycleNumbers[13] = 0; cycleNumbers[14] = 5; cycleNumbers[15] = 0; break; case 2: // NewBugalú timeTotalCycle = 155; timeSignature = 44; cycleNumbers[0] = 6; cycleNumbers[1] = 0; cycleNumbers[2] = 5; cycleNumbers[3] = 0; cycleNumbers[4] = 8; cycleNumbers[5] = 0; cycleNumbers[6] = 5; cycleNumbers[7] = 3; cycleNumbers[8] = 5; cycleNumbers[9] = 3; cycleNumbers[10] = 6; cycleNumbers[11] = 0; cycleNumbers[12] = 8; cycleNumbers[13] = 0; cycleNumbers[14] = 5; cycleNumbers[15] = 0; break; case 3: // HiHat16 timeTotalCycle = 134; timeSignature = 44; cycleNumbers[0] = 6; cycleNumbers[1] = 5; cycleNumbers[2] = 5; cycleNumbers[3] = 5; cycleNumbers[4] = 8; cycleNumbers[5] = 5; cycleNumbers[6] = 5; cycleNumbers[7] = 5; cycleNumbers[8] = 6; cycleNumbers[9] = 5; cycleNumbers[10] = 5; cycleNumbers[11] = 5; cycleNumbers[12] = 8; cycleNumbers[13] = 5; cycleNumbers[14] = 5; cycleNumbers[15] = 5; break; case 4: // SwingGroove timeTotalCycle = 153; timeSignature = 128; // (12/8) cycleNumbers[0] = 6; cycleNumbers[1] = 0; cycleNumbers[2] = 0; cycleNumbers[3] = 6; cycleNumbers[4] = 0; cycleNumbers[5] = 5; cycleNumbers[6] = 6; cycleNumbers[7] = 0; cycleNumbers[8] = 0; cycleNumbers[9] = 6; cycleNumbers[10] = 0; cycleNumbers[11] = 5; break; case 5: // BossaNova timeTotalCycle = 200; timeSignature = 44; cycleNumbers[0] = 6; cycleNumbers[1] = 5; cycleNumbers[2] = 5; cycleNumbers[3] = 9; cycleNumbers[4] = 6; cycleNumbers[5] = 5; cycleNumbers[6] = 8; cycleNumbers[7] = 6; cycleNumbers[8] = 6; cycleNumbers[9] = 5; cycleNumbers[10] = 8; cycleNumbers[11] = 6; cycleNumbers[12] = 9; cycleNumbers[13] = 5; cycleNumbers[14] = 5; cycleNumbers[15] = 6; break; case 6: // ShuffleGroove timeTotalCycle = 134; timeSignature = 128; // (12/8) cycleNumbers[0] = 6; cycleNumbers[1] = 0; cycleNumbers[2] = 5; cycleNumbers[3] = 9; cycleNumbers[4] = 0; cycleNumbers[5] = 5; cycleNumbers[6] = 6; cycleNumbers[7] = 0; cycleNumbers[8] = 5; cycleNumbers[9] = 9; cycleNumbers[10] = 0; cycleNumbers[11] = 5; break; case 7: // Franco's beat timeTotalCycle = 142; timeSignature = 44; cycleNumbers[0] = 6; cycleNumbers[1] = 0; cycleNumbers[2] = 5; cycleNumbers[3] = 1; cycleNumbers[4] = 8; cycleNumbers[5] = 0; cycleNumbers[6] = 6; cycleNumbers[7] = 0; cycleNumbers[8] = 5; cycleNumbers[9] = 1; cycleNumbers[10] = 6; cycleNumbers[11] = 0; cycleNumbers[12] = 8; cycleNumbers[13] = 0; cycleNumbers[14] = 6; cycleNumbers[15] = 0; break; default: // Basic 4/4 timeTotalCycle = 144; timeSignature = 44; cycleNumbers[0] = 6; cycleNumbers[1] = 0; cycleNumbers[2] = 5; cycleNumbers[3] = 0; cycleNumbers[4] = 8; cycleNumbers[5] = 0; cycleNumbers[6] = 5; cycleNumbers[7] = 0; cycleNumbers[8] = 6; cycleNumbers[9] = 0; cycleNumbers[10] = 5; cycleNumbers[11] = 0; cycleNumbers[12] = 8; cycleNumbers[13] = 0; cycleNumbers[14] = 5; cycleNumbers[15] = 0; break; } }

Step 12: Play Music With Your Robot Drummer!

That's it, pickup your instrument of preference, plug in your robot drummer, switch on the play/stop switch, and start playing.

Step 13: What's Next?

Alternative line level output
This is, right now, the most needed feature for me, as it would improve a lot the machine's portability. I could simply plug in it into a common bluetooth portable speaker.

MIDI input
I'm currently working on making this thing MIDI controlled, to make it more versatile on live situations. The main challenge to this idea, is the anticipation time needed by this drummer that the midi protocol doesn't function with.

Battery operated
This is the thing that most headache gives me. I haven't been able to figured out how batteries work. I need a reliable and rechargeable way of providing the 12v needed for this thing to function, but nothing has worked so far. In my foolish attempts, I tried a couple of 18650 with a TP4056 module and a voltage elevator module… failed spectacularly. Any ideas?

Arduino Contest 2019

First Prize in the
Arduino Contest 2019