电子说
步骤1:材料
1x Arduino UNO
3x伺服电机
2x电压调节器模块
1x电压升降器模块
1x驻极体微型电脑
2x压电传感器
1x Tact按钮
1x任何ON/OFF开关
5x 2n3904晶体管
3x 500k电位器
3x 100k电阻器
3x 10k电阻器
3x 47k电阻器
1x 1/4“单声道插孔
1x 9v至12v电源
各种电阻器(可能需要进行实验)
各种电容器(可能需要进行实验)
PCB穿孔板,或者面包板。
一些MDF **
**我手工制作了所有木制件,所以我真的没有任何确切的计划我尽可能地画出并解释了所有的东西,我认为复制它们并不困难,莫如果您有cnc或3d打印机,请重新进行操作。
步骤2:一般理念
地方,有必要定义drumkit元素将是什么。遵循频率范围的逻辑,标准鼓组通常由三个基本元素组成;踢(低频范围),小鼓(中距离)和踩镲(高音)。经过几个星期的实验,我决定按照以下安排复制这些声音:
Kick/LowRange: 2个压电,用一小块海绵材料覆盖,它们是用一个柔软的橡胶末端的伺服控制棒击中。
Snare/MidRange:一罐带有小方形锡片的小齿轮,用于共振,也被击中伺服控制棒,橡胶端。然后由驻极体麦克风捕获声音。
HiHat/HighRange:基于晶体管的白噪声发生器电路,由arduino直接控制。
所有上述系统均由arduino板,然后由它们产生的音频信号通过三个简单的放大器电路放大到仪器电平信号。
步骤3:踢鼓
这可以利用直接触碰或击打压电拾音器时产生的低频声音柔软的物体。为了使这个工作,首先我们需要自动匹配压电器,其次,我们需要放大由所述压电器产生的音频信号。
如上所述,我使用伺服控制杆来击中位于伺服运动范围两端的两个不同的压电(为了利用所有的运动)。为了帮助抑制高频,压电器用海绵覆盖,并且杆上有一个柔软的橡胶端。
此时,重要的是要确定伺服器击打时的角度其运动范围的每一端。稍后将需要此信息,并且可以使用基本的arduino代码(如此处所述的代码)和 Serial.print(); 命令轻松获取。
来自压电的音频信号然后由上图所示的电路放大,由来自电源调节器的5v-7v供电,电压调节器由主电源(不是arduino板)供电。这很重要,因为尽管放大器本身能够与arduino提供的5v一起工作,但理想的是保持模拟音频信号尽可能与数字电路分开,以避免任何数字噪声干扰进入最终音频信号的方式。
同样,我没有正式(或任何lol)电子学教育,而且我在这最后一个问题上遇到了很多困难。在任何音频应用中几乎不可能完全消除噪声,但我发现电压调节器中的滤波(以及模拟和数字分离,以及项目的适当电屏蔽)将噪声降低到非常可接受的水平。第2步:小军团
这是一个非常复杂的复制声音,即使现在我仍然对我所取得的声音并不十分满意。为了拾取这个声音,我决定使用驻极体麦克风,因为它们通常更敏感,并且小鼓声的独特声学特性使这成为必要。
正如我们对踢腿系统所做的那样,我们需要一个机械部件和一个音响部件。我附上了我设计的机械系统的图纸。是的,我用Pringles可以颠倒,它是完美的形状,有我能找到的最好的声音,薯片很美味。
就像我们做的一样,我们需要得到每个伺服在休息和击球时的角度。再次,修补电位计+伺服教程,我们可以获得该信息。
来自驻极体的音频信号被踢放大器中使用的同一电路的另一个实例放大,尽管在这种情况下有一个很少修改驻极体麦克风的电源,也没有使用低通滤波器。
步骤5:HiHat
我用这种声音的不同方法,一个基于晶体管的白噪声发生器电路和一个LED指示灯,在需要时由arduino激活。白噪声是在不同频率下具有相等强度的随机信号。粗略地说,就像白光包含所有颜色一样,白噪声包含所有频率,它听起来像一个不存在的无线电台(静态)。这是一种常用于电子音乐和音频合成的噪音,我读到它甚至用来制作80年代小鼓和钹的经典声音。
如图所示,在产生噪音之后,然后它通过一个高通滤波器来消除不需要的频率,并强调鼓钹的感觉。
白噪声发生器需要12v或更高功能,由升压电梯提供,它本身由arduino引脚直接供电。这是音频系统直接连接到arduino的唯一情况,因为噪声必须仅在特定时间产生,并且噪声似乎不是产生噪声的电路中的问题。你不能让白色更白吗?
第6步:信号混合和完整图
在产生所有三个鼓组音色之后,每个信号都通过一个电位器和一个基本的无源混音器电路,它混合了最终的音频信号。
我们还需要一种方法来选择要播放的鼓声以及何时开始和结束播放。对于这个指示,我们将分别使用连接的按钮和一个简单的开关(连接到arduino引脚2和3,如上图所示)
步骤7:代码I - 伺服时间校准
现在,鉴于此项目是一种乐器,它需要在合适的时间产生声音。因此,在我们开始编码不同的节奏和功能之前,特别是在踢球和小鼓的情况下,我们需要对舵机的运动进行编程,以便在它们转向声音之前的几毫秒开始,以便补偿杖的时间。为了解决这个问题,我想出了一个简单的系统来测量每个伺服从静止位置到达击中目的地的确切点。我用铝箔覆盖了棍棒和它们所在的区域,并将鳄鱼夹连接到箔片上,创造了一个只有当棍子击中目标时才能用arduino测量的连接。然后我写了一段简单的代码,它做了两件事:首先,让伺服棒反复击中目标,从静止角度到击球角度。其次,打印发送伺服命令的时间,以及棒与目标关闭连接的时间。减去这两个值将给我们时间棒发出声音所需的时间,从休息到击打。
这样做了足够多次之后,你会得到很多大致相似的数字,然后你可以平均,并获得每个伺服的预期相当可用的时间。
步骤8:代码II - 鼓组合
现在,我们为节拍中的kick,snare和hihat的每个组合定义一个数字代码。我们将第一个奇数分配给我们的三个drumkit元素:
Kick Snare HiHat
1 3 5
然后,如果我们需要同时播放多个元素,我们只需添加每个元素的数字元件。 Kick和Snare将是4,kick和HiHat 6,依此类推。零/零是沉默。现在我们为每个可能的鼓元素组合都有一个数字,我们可以将它们存储在变量(cycleNumber变量)中。
3 HiHat x - x x
2 Snare - x x x
1 Kick x x - x
_________________________________________
COMBINATION: 4 3 5 6
步骤9:Code III - Semiquaver循环(按时间命中)
我们现在知道有必要预测kick和snare伺服系统的命令,但我们还必须玩这些命令帽子准时,这是一个几乎立即工作的电路。
为了解决这个问题,这可能会让我感到有些混乱,我决定在每个半音符(或十六分音符)内建立一个检查点系统每个酒吧。我选择了第16个,因为在最常见节奏的节奏中,音符的十六分之一持续约100到150毫秒,这也是较慢的伺服(在我的情况下)从静止到击打的速度,或反之亦然。我们可以通过简单地改变分配给timeTotalCycle变量的毫秒数来改变速度。这是一个有用的速度,音符和毫秒等效表。如果我们试图更快地发挥作用,并且我们将周期的总时间减少到慢于伺服移动的时间,则该伺服可能无法从静止到达击中位置,因此不会产生声音。更详细地看一下我发布的图表,将更好地解释这一点。
如图所示,在我称之为semiquaver-cycle的开始,我们发送用于定位圈套伺服器的命令在他们的休息位置,如果他们被命令在前一个周期击中。此时,我们开始计算在整个半切割周期内设置的毫秒数。
之后,arduino将开始检查循环结束剩余的时间是否等于每个伺服需要产生声音的任何时间。这里的想法是检测我们必须发送命中命令的时间点,以便每个伺服到达并在周期的确切结束时击中它们的目标(并发出它们的声音)。因此,每个元素都必须在循环中的不同检查点开始运动,具体取决于伺服及其机构的速度。另一方面,hihat白噪声电路可以在循环结束时或几毫秒之前打开(推荐20)。它的结尾将在你定义的检查点的下一个循环中被命令,在你设置它的循环开始后多久,将是hihat声音的时间长度(我设置在30到40毫秒之间,但是这个结束了
每当满足其中一个检查点时,arduino将通过将其与存储在cycleNumber变量上的鼓组合号(数字代码)进行比较来检查其相应的元素是否有效。系统先前解释过)。例如,如果圈套的检查点满足,则圈套的数量为3,因此只有当鼓组合具有数字3,4(3 + 1),8(3 + 5)或9(3+)时才会发送命中命令1 + 5)。
3 HiHat x - x x
2 Snare - x x x
1 Kick x x - x
_________________________________________
COMBINATION: 4 3 5 6
为什么在圈套机制中使用两组木棍和伺服?
嗯,如果你考虑一下,如果只使用一个伺服,并且让它的休息时间为100毫秒,那么我们的半定时器周期可以持续的最短时间是200毫秒。 100允许从打击到休息,然后再从休息到休息100。每十六分音符200毫秒相当于约75 bpm的速度(由航空匠“梦想”),这是非常缓慢的并且限制了可能的节奏。
使用两组独立的棍棒和舵机,并交替使用它们,让其中一个在100毫秒内从击中休息,而另一个在相同的时间内击中,将最小的半定时器周期时间从大约200毫秒减少到100毫秒(“给我打电话”)金发女郎),快得多。换句话说,较慢的伺服时间也是半续半周期可以持续的最短时间。
步骤10:代码IV - 使鼓节拍
我们现在有办法确切地定义要生成的声音,并且还可以在同一时间完成所有声音。我们只需要一个接一个地播放这些十六分音符周期中的16个,每个音符组合所需的鼓组合,我们有一个小节。只要我们想要,我们就重复那个吧,而且,我们有一个鼓声。
用简单的代码,我们将16个半序列的条形序列存储在一个数组中,然后制作arduino经历了一遍又一遍。通过设置16个数字的不同数组,可以存储和播放不同的鼓节拍,如下图所示:
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
要在这些数组之间切换,我们将使用轻触开关按钮连接到引脚3。
步骤11:代码V - 全部放在一起
// DrumCube, an arduino robot drummer
// by Franco Molina @artefrancomolina
// Setting servos
#include
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;
}
}
第12步:与机器人鼓手一起播放音乐!
就是这样,拿起您的首选乐器,插上机器人鼓手,打开播放/停止开关,然后开始播放。
步骤13:下一步是什么?
替代 行级别输出
这是现在最多的我需要的功能,因为它可以提高机器的便携性。我可以简单地将它插入一个普通的蓝牙便携式扬声器。
MIDI输入
我正在努力使这个东西受MIDI控制,使其更加通用在现场情况。这个想法的主要挑战是这个鼓手所需的预期时间,midi协议不起作用。
电池操作
这是事情最让人头疼的是。我一直无法弄清楚电池是如何工作的。我需要一种可靠且可充电的方式来提供这个东西所需的12v功能,但到目前为止还没有任何工作。在我愚蠢的尝试中,我尝试了一对带有TP4056模块和电压升降模块的18650 。..。..失败了。有什么想法吗?
全部0条评论
快来发表一下你的评论吧 !