英文:
ESP8266 analogRead() microphone Input into playable audio
问题
我的目标是使用连接到esp8266(12E)模拟引脚的电容麦克风录制音频,然后能够在另一台设备上播放此音频。我的电路如下:
为了检查麦克风的输出,我将电路连接到示波器并得到了以下结果:
在上面的GIF中,您可以看到我与麦克风交谈时声音产生的波形。
以下是我在esp8266上的代码:
void loop() {
sensorValue = analogRead(sensorPin);
Serial.print(sensorValue);
Serial.print(" ");
}
我想在"Audacity"软件上播放音频以了解结果。因此,我复制了串行监视器中的数字,并将其粘贴到将数据映射到(-1,1)间隔的Python代码中:
def mapPoint(value, currentMin, currentMax, targetMin, targetMax):
currentInterval = currentMax - currentMin
targetInterval = targetMax - targetMin
valueScaled = float(value - currentMin) / float(currentInterval)
return round(targetMin + (valueScaled * targetInterval), 5)
class mapper():
def __init__(self, raws):
self.raws = raws.split(" ")
self.raws = [float(i) for i in self.raws]
def mapAll(self):
self.mappeds = [mapPoint(i, min(self.raws), max(self.raws), -1, 1) for i in self.raws]
self.strmappeds = str(self.mappeds).replace(",", "").replace("]", "").replace("[", "")
return self.strmappeds
这个代码接受数字的字符串,将它们映射到目标间隔(-1,+1),并返回一个以空格分隔的数据字符串,可导入到Audacity软件中(使用“工具>样本数据导入”,然后选择包含数据的文本文件)。从近5秒的声音导入数据的结果如下:
这大约是半秒钟的音频,当我播放时听到了无法理解的噪音。我还尝试了较低的频率,但那里也只有噪音。
问题的疑点原因可能是:
-
Esp8266没有足够快地读取模拟引脚以返回有意义的数据(这可能不是问题,因为它的时钟速度约为100MHz)。
-
软件获取数据并输出数据的方式不是最优化的方式(在循环中使用Serial.print等)。
-
麦克风电路的输出太嘈杂(这可能是原因,但从示波器测试中观察到,我的声音应该在输出音频中产生差异,但从Audacity中听不到)。
-
我对数据进行映射和准备的方式不正确。
- 是否有其他我可以尝试的方法?
- 是否有类似的项目?(令我惊讶的是,我找不到任何以透明方式完成的项目!)
- 这样做的正确方法是什么?(因为这可能是录制、传输和分析音频的非常有用和经济的方法。)
英文:
My goal is to record audio using an electret microphone hooked into the analog pin of an esp8266 (12E) and then be able to play this audio on another device. My circuit is:
In order to check the output of the microphone I connected the circuit to the oscilloscope and got this:
In the "gif" above you can see the waves made by my voice when talking to microphone.
here is my code on esp8266:
void loop() {
sensorValue = analogRead(sensorPin);
Serial.print(sensorValue);
Serial.print(" ");
}
I would like to play the audio on the "Audacity" software in order to have an understanding of the result. Therefore, I copied the numbers from the serial monitor and paste it into the python code that maps the data to (-1,1) interval:
def mapPoint(value, currentMin, currentMax, targetMin, targetMax):
currentInterval = currentMax - currentMin
targetInterval = targetMax - targetMin
valueScaled = float(value - currentMin) / float(currentInterval)
return round(targetMin + (valueScaled * targetInterval),5)
class mapper():
def __init__(self,raws):
self.raws=raws.split(" ")
self.raws=[float(i) for i in self.raws]
def mapAll(self):
self.mappeds=[mapPoint(i,min(self.raws),max(self.raws),-1,1) for i in self.raws ]
self.strmappeds=str(self.mappeds).replace(",","").replace("]","").replace("[","")
return self.strmappeds
Which takes the string of numbers, map them on the target interval (-1 ,+1) and return a space (" ") separated string of data ready to import into Audacity software. (Tools>Sample Data Import
and then select the text file including the data). The result of importing data from almost 5 seconds voice:
which is about half a second and when I play I hear unintelligible noise. I also tried lower frequencies but there was only noise there, too.
The suspected causes for the problem are:
1- Esp8266 has not the capability to read the analog pin fast enough to return meaningful data (which is probably not the case since it's clock speed is around 100MHz).
2- The way software is gathering the data and outputs it is not the most optimized way (In the loop, Serial.print, etc.)
3- The microphone circuit output is too noisy. (which might be, but as observed from the oscilloscope test, my voice has to make a difference in the output audio. Which was not audible from the audacity)
4- The way I mapped and prepared the data for the Audacity.
-
Is there something else I could try?
-
Are there similar projects out there? (which to my surprise I couldn't find anything which was done transparently!)
-
What can be the right way to do this? (since it can be a very useful and economic method for recording, transmitting and analyzing audio.)
答案1
得分: 2
以下是已翻译的内容:
-
你没有在A0上设置偏置电压。ADC只能测量在地面和VCC之间的电压。当从电路中移除麦克风时,A0处的电压应该接近VCC/2。通常,可以通过在VCC和GND之间添加两个电阻制成的电压分压器来实现这一点,并直接连接到A0。位于电容器和A0之间。
-
此外,你的电路看起来很奇怪... 47uF电容是否直接连接到3.3V?如果是这样的话,你应该将它连接到麦克风的引脚2上。这还表明,当前你的ADC只记录噪声(没有偏置电压将会这样做)。
-
你没有对输入进行节奏控制,这意味着你没有恒定的采样率。这是一个非常重要的问题。我建议你设置一个在ADC限制和串行端口限制内的现实目标。串行端口的传输速率通常等于波特率 / 8。对于9600波特,大约只有1200字节/秒,这意味着一旦转换为文本,你的最大传输速率降至约400样本/秒。这个问题需要在开始之前解决,因为可达到的总体采样率的最大值是ADC的采样率和串行端口的传输率的最大值。
-
从你的需求和这个项目的实际情况来看,获取样本的方式取决于很多因素,包括音频带宽、分辨率和应用程序的音频质量要求,以及你可以投入其中的工作量。像你现在所做的从循环中读取可能可以在足够快的串行端口下工作,但质量将始终很差。
-
通常的做法是使用定时器中断开始ADC测量,并使用ADC中断获取结果并将其存储在一个小的FIFO中,同时主循环从这个ADC fifo传输到串行端口,以及分配给芯片的其他任务。这不能直接使用Arduino库来完成,因为你需要直接控制ADC来做到这一点。
-
下面是一份要做的事项清单:
- 从Expressif获取完整的ESP8266数据表。查找ADC的实际规格,主要是:你的振荡器可用的采样率和分辨率,以及它的电气约束,至少是输入电压范围和输入阻抗。
- 一旦知道这些数字,为成功的项目需要输入数字来设定一些目标。你的应用是什么?你想记录音频还是只是检测不明噪音?需要让事情正常工作的最低要求是什么?
- 在Arduino文档中查找如何设置定时器中断和ADC中断。
- 在数据表中查找需要访问以配置和运行ADC的寄存器。
- 修复ADC输入上的电压偏置问题。在这之前,什么都不能工作,你也不想损坏处理器。
- 确保输入的交流电压('摆动'电压)足够大,可以给你想要的结果。通常需要放大麦克风信号(使用运放或晶体管)以匹配阻抗。
- 然后你可以开始编写代码。
这对于这样一个小任务来说可能听起来非常复杂,但这就是嵌入式程序员的平均日常工作。
[编辑] 如果你只是用一个串联电阻替换47uF的直流阻塞电容,你的电路将工作得更好。其阻值应在2.2k到7.6k范围内,以保持电路阻抗在ADC所需的约10k欧姆左右。这将确保A0的输入电压在ADC的操作限制内(NodeMCU板上为GND-3.3V,裸芯片为0-1V)。
然而,对于你的应用来说,信号可能仍然太弱。你的示波器上的信号幅度是多少?一旦由ADC转换,该范围覆盖了多少位的分辨率?例如,对于0.1V峰峰值信号(SIG = 0.1),ADC范围为0-3.3V(RNG = 3.3),分辨率为10位(RES = 1024),你将有
二进制范围 = RES *(SIG / RNG)
= 1024 *(0.1 / 3.3)
= 1024 * 0.03
= 31.03
一个范围为31,这意味着大约Log2(31)(约= 5)个有用的分辨率位,这对你的应用是否足够?
另外注意:ADC将给出带有直流偏移的正值,你可能需要在回放之前使用直流阻塞滤波器来过滤数字输出。链接
英文:
There are many issues with your project:
You do not set a bias voltage on A0. The ADC can only measure voltages between Ground and VCC. When removing the microphone from the circuit, the voltage at A0 should be close to VCC/2. This is usually achieved by adding a voltage divider between VCC and GND made of 2 resistors, and connected directly to A0. Between the cap and A0.
Also, your circuit looks weird... Is the 47uF cap connected directly to the 3.3V ? If that's the case, you should connect it to pin 2 of the microphone instead. This would also indicate that right now your ADC is only recording noise (no bias voltage will do that).
You do not pace you input, meaning that you do not have a constant sampling rate. That is a very important issue. I suggest you set yourself a realistic target that is well within the limits of the ADC, and the limits of your serial port. The transfer rate in bytes/sec of a serial port is usually equal to baud-rate / 8. For 9600 bauds, that's only about 1200 bytes/sec, which means that once converted to text, you max transfer rate drops to about 400 samples per second. This issue needs to be addressed and the max calculated before you begin, as the max attainable overall sample rate is the maximum of the sample rate from the ADC and the transfer rate of the serial port.
The way to grab samples depends a lot on your needs and what you are trying to do with this project, your audio bandwidth, resolution and audio quality requirements for the application and the amount of work you can put into it. Reading from a loop as you are doing now may work with a fast enough serial port, but the quality will always be poor.
The way that is usually done is with a timer interrupt starting the ADC measurement and an ADC interrupt grabbing the result and storing it in a small FIFO, while the main loop transfers from this ADC fifo to the serial port, along the other tasks assigned to the chip. This cannot be done directly with the Arduino libraries, as you need to control the ADC directly to do that.
Here a short checklist of things to do:
- Get the full ESP8266 datasheet from Expressif. Look up the actual specs of the ADC, mainly: the sample rates and resolutions available with your oscillator, and also its electrical constraints, at least its input voltage range and input impedance.
- Once you know these numbers, set yourself some target, the math needed for successful project need input numbers. What is your application? Do you want to record audio or just detect a nondescript noise? What are the minimum requirements needed for things to work?
- Look up in the Arduino documentartion how to set up a timer interrupt and an ADC interrupt.
- Look up in the datasheet which registers you'll need to access to configure and run the ADC.
- Fix the voltage bias issue on the ADC input. Nothing can work before that's done, and you do not want to destroy your processor.
- Make sure the input AC voltage (the 'swing' voltage) is large enough to give you the results you want. It is not unusual to have to amplify a mic signal (with an opamp or a transistor), just for impedance matching.
- Then you can start writing code.
This may sound awfully complex for such a small task, but that's what the average day of an embedded programmer looks like.
[EDIT] Your circuit would work a lot better if you simply replaced the 47uF DC blocking capacitor by a series resistor. Its value should be in the 2.2k to 7.6k range, to keep the circuit impedance within the 10k Ohms or so needed for the ADC. This would insure that the input voltage to A0 is within the operating limits of the ADC (GND-3.3V on the NodeMCU board, 0-1V with bare chip).
The signal may still be too weak for your application, though. What is the amplitude of the signal on your scope? How many bits of resolution does that range cover once converted by the ADC? Example, for a .1V peak to peak signal (SIG = 0.1), an ADC range of 0-3.3V (RNG = 3.3) and 10 bits of resolution (RES = 1024), you'll have
binary-range = RES * (SIG / RNG)
= 1024 * (0.1 / 3.3)
= 1024 * .03
= 31.03
A range of 31, which means around Log2(31) (~= 5) useful bits of resolution, is that enough for your application ?
As an aside note: The ADC will give you positive values, with a DC offset, You will probably need to filter the digital output with a DC blocking filter before playback. https://manual.audacityteam.org/man/dc_offset.html
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论