-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
microphone support #17
Comments
Answering to @DonvdH in #14 (comment) I am involved in the developpement of Wasp-OS (micropython OS on pinetime from Pine64, based on the nRF52832) In this code they manage to get a reading of the heart sensor at 24Hz. I do not know if they can get it to be faster. They also succed in doing a bunch of post processing on the watch by applying filters and stuff. Is that relevant? How do you update from this info regarding the feasability of using the Additionnaly, when you say you fear that python might not be fast enough, do you take into account the different code emitters that can dramatically improve the speed? Thanks a lot! |
Great to hear that you are so actively involved with smartwatch development. I have also looked at the Pinetime and Wasp-OS by the way. While it also looks very nice, I have chosen the T-Watch in this case because of the wifi connectivity. Regarding the microphone: It is connected to the following pins: So from what I understand is that the ESP32 needs to generate a clock signal on IO00 and per clock cycle one bit can then be read on IO02. So you would need about 24000 clock cyles to obtain some usable audio. The code to obtain the PDM data would look something like this:
If I do a little benchmarking, it appears that reading 24000 bits of PDM data takes +/- 0.669 seconds:
So you appear to be right that the required speed is achievable using Python. I believe the next steps would be to get the timing right and to convert the data into a usable format (WAV) afterwards. |
Thanks a lot! Really! WIth a bit of help from GPT-4 I apparently achieved a bit faster still :
Test with
Average result was 0.437s. I also tried a bit with the viper emitter (apparently even faster) but I couldn't make it work. Regarding the timing, isn't it just a matter of adding some timers in the loop? The heart.py code from wasp-os seems to do that. Also, what is the unit of the value stored at each clock cycle in pdm_data? Is it directly an amplitude reading of the air pressure? As you can see I'm quite lost (I'm a med student and this is a side project!) |
Nice job, that's an impressive performance which is being achieved there by the micropython.native decorator. There are numerous strategies to add delay. Wikipedia explains how a PDM Mems Microphone works quite nicely: Once the delay has been added, a function is needed to convert the PDM data to PCM/WAV. But I couldn't find a ready to use library for this unfortunately, so this may prove to be the hardest part. |
I initially went with just adding a timer with With the following code I managed to get good enough accuracy:
The answer is around 1000ms +- 0.03ms Now regarding the PDM to PCM/WAV, would you mind explaining to me the job of the ADC? I thought I understood that it could be used to turn a PDM signal into PCM. Also here's some links I stumbled upon last time I tried to understand how to use audio on this watch:
This is way outside of my scope so any hand holding is greatly appreciated! |
I appreciate your persistence. :) Keep in mind that I'm not an audio specialist and my time is unfortunately rather limited. Also I don't currently have plans to use the microphone myself. The ADC (Analog to Digital Converter) is unrelated to what you're trying to achieve. The ADC makes it possible to read an analog signal (voltage) on a pin, but a PDM MEMS Microphone emits a digital signal (instead of a analog signal). If I had to make an educated guess, then you would need something like the code below. Several filters appear to be needed to actually convert PDM to PCM, but unfortunately I couldn't find a Python library for that.
|
I believe that, given the lack of available Micropython libraries for PDM conversion, the best solution would be to transfer the PDM data to a HTTP API and then convert it to PCM/WAV using this tool: For example FastAPI could be used on the server side. |
ESP32 actually contains an Inter-IC Sound (I2S) peripheral that can convert from PDM to PCM by using There is a repository from 2020 that adds the I2S PDM mode to MicroPython: https://github.com/lemariva/micropython-i2s-driver I'm not sure how much effort it would require to port it over to a recent MicroPython. @jeffmer Is https://github.com/jeffmer/micropython/tree/GPIO_WAKEUP the right branch for compiling your custom firmware.bin? |
I can't add anything regarding those hardware stuff unfortunately.
I would prefer to keep this as a last resort option. Here's my latest trial:
The function "play_audio" work fine with a regular wav file found on the internet. Is there any obvious mistake somewhere? |
You're using a decimation factor of 16: With a corresponding pcm buffer length: This results in a pcm_buffer size of 1500 which is then being played at a bitrate of 24000. So the sound will only last 1/16th of a second. I believe you need a decimation factor of 1 (no decimation). |
Regarding the play duration, you're still dividing the buffer length by 16 in pdm_to_pcm: And then just replace pcm_buffer with pdm_buffer in later lines. The 0's might indeed be caused by the clock speed being too low or the delay before reading the data pin. Try changing:
to this:
The first/old code moves the clock line up/down, then waits a while and only then reads the data pin. It's likely that the data pin has then moved to 0 after a while. |
Oh okay. I thought that the length had to be shorter because 16 1s and 0s are turned into a single value if the recording is 16bits, my bad.
Very smart thanks a lot. I'm testing a bunch right now and will report back. |
Welp here's where I'm at for the day. I still get noise at the end. I added a few prints to get an idea of the signal and how it's being processed.
Output:
My guess is that my filters are not functionnal. I tried a lot of variation of filter order, arguments, rewriting from scratch, etc but I'm giving up for now. Any pointers? What kind of values should I actually expect as output of each filter? Am I supposed to get frequencies at some point? Isn't the interpolation upsampling stupidly space-wasting? |
Best course of action would be to first use the PDM2PCM tool I mentioned to validate whether the PDM data is valid at all: Now you basically don't know whether the PDM is valid or whether the filters are failing. Also I wonder where you found the Micropython filters, the context may tell something about the suitability of the filters and how they should be used. |
I'll give it a go thank you.
You're not going to like it but it's a mix of intuition and GPT-4 prompting. Unfortunately it's getting worse and worse as they RLHF it but it's still faster than trying to compensate my lack of background. |
ChatGPT can be a great tool when it has seen a lot of examples. But when it hasn't, it can convincingly give incorrect results. So that's something to keep in mind at least. If I look at https://github.com/siorpaes/pdm_playground/blob/master/pdm2pcm/pdm2pcm.c then appears that it requires at least a decimation factor of 64. So it looks that for 16kbit PCM, 1024000 PDM samples would be needed. When I look at https://github.com/siorpaes/pdm_playground/blob/master/pdm2pcm/OpenPDMFilter.c then it appears that it is really reconstructing audio data from the bit stream:
Also I read the following here: So in contrast to what I previously believed, a higher PDM bitrate really appears to be required and the highest bitrate that can be achieved using Micropython is about 55khz as we previously learned. So then I have to agree with @devnoname120 that the best (or only) possible route would be to compile the I2S-PDM driver he mentioned into the firmware that @jeffmer has provided. |
Alright. Well thank you immensely @DonvdH for helping me along the way. The next step is then to wait for @jeffmer to answer this :
|
Hello @jeffmer I was wondering if you had time to answer the question raised by @devnoname120 here? That would save me plausibly liters of tears and sweat :) My enthusiasm about the chatgpt watch has not faded in the least |
Apologies, I missed the question from @devnoname120. The answer is yes, you have to build the GPIO_WAKEUP branch to make firmware.bin. |
Great. And any guidance you mught have or specific warnings or helpful tips before I dive into adding i2s microphone into it? The other repo linked is making me optimistic but I have no idea what i'm doing frankly. |
Try building the firmware without your mods first and check the firmware works as before:-) |
Hello again. So I did build the firmware from your repo using those instructions. It flashes fine then running "verify_flash" shows no error. But when trying I did flash back your firmware and ran install.sh successfuly so the issue seems to be with the build. What should I do :)? Are there special arguments to pass when building? EDIT: also I can't seem to get a prompt when using mpremote with this build. edit : I may have fixed it. Will post a new message if help is needed :) |
Hello again again. So I managed to get a working REPL from the watch using the firmware using those instructions. But when doing So I decided to stop digging until I get more information from you @jeffmer because I'm guessing lots more might be missing. Any idea what's happening? Also to make the REPL flashing work I had to flash using When building, I'm assuming the default board is the right one. But is there another board I should specify like ESP32_GENERIC_S3 for example? |
Hi, a better approach might be to clone the official Micropython repository and build it. I have only changed one file which you can find here https://github.com/jeffmer/micropython/blob/master/ports/esp32/machine_pin.c In summary, clone the official repository and replace the machine_pin.c file which has the gpio_wakeup stuff. You should have viper etc. |
Thanks for the quick reply. So I did try with the recent micropython and putting the file
So knowing elementary grade C I poked around with adding some lines from |
It looks like there have been changes to the Micropython repository that are incompatible. It will take me some time to sort out - hopefully in the next week or so ... Yes there have been some changes to machine_pin.c in the main repository. It seems to have some code related to GPIO_WAKEUP so my mods may not be necessary. In any case the file I suggested is out of date - sorry! I will have a look when I get a chance. |
Thank you so much! I can't wait to have that AI watch :) |
OK - my micropython repository now has a GPIO_WAKEUP branch with an updated machine_pin.c which I have tested on a V1 watch. A bit of a struggle as for some reason modmachine.c cancelled all wakeup sources - now fixed. To make it you need to clone my repository and choose the GPIO_WAKEUP branch. The make command is: make BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM Good luck! PS re AI: Espruino Banglejs2 watch has a working TensorFlow module. |
Thank you so much! I'll take a look monday |
Hi! So building works, flashing works, install.sh works. But when I get to the REPL and type "import loader" I get I'm using the GPIO branch of your repo. Any idea what's the issue? |
No idea, that did not happen with me. Maybe, rebuild your mpy-cross compiler and recompile the modules that have the viper decorator. |
OMG I got your build working :)! I think the issue was that I was not using the mpy-cross compiler when compiling the apps or something. Thank you immensely. |
Okay so I then tried to merge the commit from that lemariva repo who apparently got PDM support on esp32 to the recent micropython version. I managed to make a build and flash it but am not sure I did something wrong so I documented the whole thing in this PR. I added a few questions at the end and tried to be extra clear to make it as painless as possible to help the clown that I am 🤡 . Pinging @devnoname120 @jeffmer @DonvdH Thank you again everyone for getting me this far :) |
I knew it wasn't a good idea to ask for help 2 days before christmas :) Can anyone give me a few pointers? My end project will basically be like a super cheapt rabbit r1 or openwearables Thanks! Pinging @devnoname120 @jeffmer @DonvdH |
Sorry I missed this and I am away for the next month so cannot be of much help. To date, I have had very little success with ESP32 audio devices and Micropython:-) |
@thiswillbeyourgithub: Based on the errors you are getting, have you got I2S_NUM_MAX defined in driver/i2s.h? Like this: |
Thank you very much @DonvdH, indeed that simple line was missing and adding it fixed the first issue I was getting. The second hurdle is about this section from machine_i2s.c:
which returned errors like Noticing that the end of machine_i2c.c ended up with a similar looking bunch of lines I intuitively merged it to:
which succesfully builds but I don't see Thanks! |
There is now a pull request in mainline MicroPython for PDM support for the I2S. It should work on the T-Watch V3 |
Thank you so much @jonnor for the heads up. I haven't had the time to give it a try yet but I saw you owned a T-Watch V3 like me. Would you by any chance have any ready-to-use testing code for micropython to play sounds and record stuff? That would be very helpful to me. |
I have not yet had time to try out audio with MicroPython yet. But the MR uses the I2S peripheral, which has been around for a long time. There is a set of examples here that look very good, https://github.com/miketeachman/micropython-i2s-examples/tree/master |
I had to jump through a few hoops but I apparently successfuly merged the two commits (the one from the PR and the one from @jeffmer for GPIO wakeup). It ended up vastly simpler to fork the latest micropython, remove the latest commits since one about berkeley-db that didn't seem important but wouldn't build, then apply the PR patch, then manually apply the modification from jeffmer. You can keep track of that in my fork. I successfuly build the watch and I see this:
So I think that something went right becauseIIRC
Next steps I assume are testing the microphone using the link from @jonnor |
I went as far as I can afford in my random walk today and for the weeks to come. I updated my fork to mention everything to get started where I left off in the README : https://github.com/thiswillbeyourgithub/micropython
I'm especially eager to hear about @jonnor and @DonvdH when and if you get a chance of course. As I reminder I know next to nothing about C and am just a lost medical student wanting this project too bad :) so there's a real chance that a 15 minute look from you can save me weeks of hair pulling! Thanks! |
Bump |
As per @DonvdH request there
Thanks a lot!
The text was updated successfully, but these errors were encountered: