Saturday

More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library


 More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library

Direct Playback of MP3s from SD card

Since designing and having some new circuit boards made for audio prototyping, I've begun testing more and more with advanced audio options, like decoding and playing MP3 files from SD Card. This is all thanks to a sponsorship from http://PCBWay.com

I've revised some previous experimental code and worked out some bugs, thanks to the simplicity of having a circuit board instead of a big ball of wires and breadboard.

The code shown below requires installation of my AutoAnalogAudio library and the MicroDecoder library found at https://github.com/TMRh20/microDecoder which can be installed from ZIP.

With my existing circuit board and the Feather 52840 Express or Sense boards, this code will work out of the box. It will also work with any 52840 based board. It is still in experimental stages but is working very well so far!


/*
* Requires https://github.com/TMRh20/microDecoder library
*/

#include <SD.h>
#include <AutoAnalogAudio.h>
#include "mp3.h"  // decoder
#include "pcm.h"

mp3 MP3;
pcm audio;
AutoAnalog aaAudio;


/************ USER CONFIGURATION ***************/
const char* audioFilename = "noceil.mp3";
uint8_t SD_CS_PIN = 2;  // Set this to your CS pin for the SD card/module

float volumeControl = 0.2;
#define AUDIO_BUFFER_SIZE 1152  // Should be a multiple of 64
/**********************************************/

void setup() {

  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.print("Init SD card...");
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("init failed!");
    return;
  }
  Serial.println("SD init ok");

  aaAudio.I2S_PIN_LRCK = 28;  // Use different LRCK pin for Feather Express 52840
  aaAudio.maxBufferSize = AUDIO_BUFFER_SIZE;
  aaAudio.begin(0, 2);

  playAudio(audioFilename);
}

void loop() {

  loadBuffer();

  // Control via Serial for testing
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '=') {
      volumeControl += 0.1;
    } else if (c == '-') {
      volumeControl -= 0.1;
      volumeControl = max(0.0, volumeControl);
    } else if (c == 'p') {
      playAudio("atliens.mp3");
    } else if (c == 'c') {
      playAudio("dopeBoys.mp3");
    }
    Serial.println(volumeControl);
  }
}


File myFile;

void playAudio(const char* audioFile) {

  MP3.end();
  if (myFile) {
    myFile.close();
  }
  aaAudio.disableDAC();

  //Open the designated file
  myFile = SD.open(audioFile);

  if (myFile) {

    MP3.begin(myFile);
    MP3.getMetadata();
    aaAudio.setSampleRate(MP3.Fs, 1);
    Serial.println(MP3.Fs);
    aaAudio.dacBitsPerSample = MP3.bitsPerSample;
    Serial.println(MP3.bitsPerSample);
  } else {
    Serial.println("Failed to open file");
  }
}

void loadBuffer() {

  uint32_t sampleCounter = 0;
  if (myFile.available() >= AUDIO_BUFFER_SIZE) {
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 64; i++) {
      audio = MP3.decode();
      memcpy(&aaAudio.dacBuffer16[sampleCounter], audio.interleaved, 128);  // 128 bytes
      sampleCounter += 64;                                                  // 64 samples per go
    }
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
      int16_t sample = aaAudio.dacBuffer16[i];
      sample *= volumeControl;
      aaAudio.dacBuffer16[i] = sample;
    }
    aaAudio.feedDAC(0, sampleCounter);
  } else {
    aaAudio.disableDAC();
  }
}

 


Wednesday

Designing & 3D Printing Some Cases for My New Circuit Boards for Audio Prototyping

Designing & 3D Printing Some Cases for My New Circuit Boards 

 The beginnings of a final design

In order to put the finishing touches on my custom designed circuit boards, I figured its about time to build some cases for them. The final addition was a set of 1.5", 4-Ohm speakers I just purchased to complete the project. As shown below, iteration v1 of a case was pretty rough, just figuring out and confirming the basic measurements and form of a design. This ended up being very bulky, which would be OK for use as a tabletop speaker or intercom etc.   

 Case V1.0
Case V1.0
 
In the case shown below, V2, I opted to make it longer instead of wider. I tend to like this layout a little more. There are still a few tweaks to make, like lowering the speaker down into the case, so the USB cable will plug into the MCU without being blocked. I may add round holes for the buttons, but I kind of like the square layout, I still need to wire up the speaker, but this is how it will look. 
 
Case V1.1
Case V1.1
Once I make the necessary changes to this form, I will put the files in the associated GitHub repository here

I'm still trying to decide on a cover, whether or not I really need one, and if so, how to make it snap in and out, so it is easy to remove. I wish I would have made the PCB a little smaller, which I could probably do pretty easily by rearranging the components. All in all though, I think its pretty good for a first attempt at building a real circuit board with functional components. Thanks again to PCBWay.com for sponsoring the PCBs!

 

Friday

Initial Testing, Notes & Progress on new PCB for Wireless Audio Prototyping

 Initial Testing, Notes & Progress on new PCB for Wireless Audio Prototyping

 Testing out the new circuit boards and getting the built-in microphone working

 If you weren't following along I just received some new custom built circuit boards from PCBWay.com and have been testing things out extensively. Everything works! I even got the custom-added I2S microphone working with the nRF52840.

The boards support functionality like voice transmission over the built-in radio, and can function as a radio, intercom or simple music player etc. 



                                            A full test of the new circuit boards

Getting the Microphone working with the AutoAnalogAudio library: 

The nRF52840 Feather Express/Sense does have an I2S interface, but it only supports up to a 24-bit window, whereas the SPH0645LM4H expects 32 SCK pulses per audio frame (18 bits of data + 14 bits of padding) and a 64 x BCK signal, where the nRF52840 typically does a 48 x BCK signal.

After a bit of searching, I found that the appropriate signal could be created by the PWM peripheral of the 52840, and the I2S interface would just need to be configured with special settings to grab the 18-bits of data properly.

The main drawback with using this mode is the I2S interface can only operate in either Input or Output mode, they can't be run at the same time unless both I2S Input & Output devices use the same settings. I had to make some modifications to the AutoAnalogAudio library, released in v1.54.0, in order to allow switching between I2S modes.

Currently, this mode is only supported for input devices. 

                                                                The Feather Express 52840 

Configuration for the Microphone: 

To use the new special settings, users need to set the aaAudio.manualI2S variable to True before calling the aaAudio.begin(2,0); function.

With the Feather Express, the LRCK or WS pin needs to be configured from the default of 29, to something like 28, which is A3 on the Feather Express 52840

Results: 

 I've put together a full Radio Transceiver example for this PCB, found at https://github.com/TMRh20/FeatherAudio/tree/main/Examples/I2S_TransmitterAndReceiver

In order to use the example, upload to two Feather 52840 Express or Sense boards, and plug them into the custom PCB, or manually attach external I2S microphone & amplifier.  

The example features 3 buttons, the PTT button is used to transmit, then there are two for Volume Up/Down. If users press and hold Volume Down, then Press Volume Up, the device will go into a continuous transmission mode, not requiring the PTT button to be pressed.

The device also toggles the onboard red LED when data is received.

 Audio is relatively high quality at 16-bits and 32kHz, so it is pretty clear and useful for many different applications.

 

Tuesday

New Circuit Board for Audio Prototyping, Sponsored by PCBWay.com


 New Circuit Board for Audio Prototyping, Sponsored by PCBWay.com

 Design and manufacturing of a custom circuit board

 With recent developments to my AutoAnalogAudio library for Arduino adding support for nRF52840 based devices like the Feather 52840 or XIAO 52840 lines, I was getting a little sick and tired of having a big tangle of wires in order to run an SD card, I2S microphone, I2S amplifier, speaker etc, in order to test things out and develop this library.

Thanks to a sponsorship from PCBWay.com, I was able to develop a custom circuit board for prototyping and development, that can later be used as a walkie-talkie or intercom, with range similar to bluetooth.

The first part of this was to actually create the custom circuit board, and I chose the KiCad application. This was a big learning curve, as I had only ever designed an extremely simple circuit board before, and that was quite a challenge for me. This would take things to the next level.

So first, in KiCad, one needs to create a schematic for the circuit. This part was not too difficult, although it still presented some challenges, and took up a fair bit of time.   

 


 Above is the original circuit design and all the KiCad files etc, are available on https://github.com/TMRh20/FeatherAudio

The people at PCBWay were very helpful in figuring this all out, and since this is what I consider to be my first real attempt at a proper circuit board, I made a number of mistakes. One of the hardest parts seemed to be matching the KiCad component footprints up with the proper part numbers. I assume there must be an easier way to do this, but I ended up searching and searching, and still made some somewhat unrecoverable errors.

Beyond the correctable errors that the folks at PCBWay helped me fix, one of the main mistakes was ordering the wrong part for the J5 connector. This was in addition to a bunch of other parts that I had incorrect, but were caught prior to production. In the end, I had the boards shipped without the J5 connector, but the PCBWay people shipped some connectors that I was able to modify to work, and soldered them on myself.

I was also missing a position file initially, because I didn't know at first, but you need to send three main files, the gerber files in a ZIP format, the Bill of Materials or BOM, and a Position file, all of which can be generated from within KiCad. Putting this all together was one of the hard parts, again I struggled quite a bit with finding the correct part numbers etc.

 

Above are pictures of the PCB itself, and the assembled PCB, missing the J5 connector which I messed up the part number on. Everything else was correct, thanks to a lot of help from PCBWay.com. As stated, I was able to solder on some slightly modified J5 connectors they sent along with the boards.

Upon hooking it up, inserting an SD card and attempting to play some music, the damn thing worked! I was pretty surprised everything actually seemed to be connected correctly, and functioned flawlessly! Upon trying the microphone however, I discovered that I might have selected an I2S microphone that the attached micro-controller (Feather Express 52840 or Feather Sense 52840) cannot quite handle. According to some searches, I may be able to modify my AutoAnalogAudio library to handle it, but as it stands, the built in mic does not function.

 I am still able to do some testing and work with the Feather Sense 52840, because it has a built in PDM microphone, but I was hoping to use the custom-added I2S microphone. Everything seems to be connected properly circuit wise, its just the 52840 MCU that needs what seems to be a special configuration to work with this microphone.

 


 Above is my test setup with all the components attached. The small battery runs the micro-controller, SD Card and microphone, while the large batteries handle the I2S amplifier. I'm still kind of amazed all the circuitry seems to be correct, there is just a compatibility issue with the microphone!


 

 That's all for now, I've posted all the related files and examples to my GitHub repo at https://github.com/TMRh20/FeatherAudio, and will do some follow up posts detailing how to use the AutoAnalogAudio library along with this circuit board. To be continued... 

 

Sunday

Direct TCP/IP connectivity between Arduinos using a nRF24 or nRF52 radio link w/RF24Ethernet

 Setting up direct TCP/IP connectivity between Arduinos using a nRF24 or nRF52 radio link w/RF24Ethernet

 Utilizing the new functionality of the RF24Ethernet library

 With some recent experimentation and prototyping involving the lwIP IP Stack, I was able to modify the RF24Ethenet library to function standalone, without the need for a Linux/Raspberry Pi device running RF24Gateway. This allows users to directly connect multiple Arduino devices using the RF24Ethernet library, utilizing TCP or UDP protocols to communicate between devices.

The RF24Ethernet library API is based on the official Arduino Ethernet API, so users utilize the same coding style to communicate over nRF24 or nRF52 radio links.  


Setting things up:

1. The first thing to do is verify you have working radios. With nRF52 devices, they are built-in so, there is not much to worry about, but with nRF24 radios, users need to keep in mind power supply and wiring issues, so testing using the official gettingStarted sketch included with the RF24 library is recommended before attempting this.

 2. For now, users need to install the RF24Network library from ZIP file. This will be all be configured automatically once the current updates are deployed to the main branch of RF24Ethernet. This document will be updated at that time.

3. Install the RF24Ethernet library from ZIP from https://github.com/nRF24/RF24Ethernet/tree/lwIP  Note: Once deployed, the updated library will be available from the Arduino Library Manager. Users may need to uninstall/reinstall to get the latest updates at that time.

 4. Install the Arduino lwIP library using the Arduino Library Manager as required for non-ESP32 & non-ESP8266 devices which already include lwIP.

5. Run the included examples from the Headless directory in the RF24Ethernet examples on two devices. I've tested so far on Arduino Due, ESP8266, ESP32 and nRF52 based devices. The RPi Pico still utilizes the uIP stack due to technical issues using lwIP with the Arduino MBED based core.

 

What to expect:

The main server example sets up a RF24Mesh 'Master Node' which handles addressing and address look-ups for all other nodes. Nodes not in range of the Master Node will attempt to connect automatically via routing traffic through other connected nodes. 

The client examples simply connect to the Master Node and request an HTML based web-page. 

 In a real life deployment this activity may be reversed. With the Master Node running a modified Client example, and sensor or other nodes running modified Server examples, the Master Node could then connect to each device in turn and retrieve data as required.

To designate a master node, simply call the following before calling mesh.begin()

mesh.setNodeID(0);

then from the main loop()

 mesh.DHCP();

 

 Things to Note: 

 RF24Ethernet makes use of two separate IP Stacks, the older, unmaintained uIP Stack works on smaller devices like Uno, Nano, Mega, etc, while the newer, lwIP stack is used automatically for devices >50MHz CPU speed, including the Arduino Due, ESP32, ESP8266, and nRF52 based devices using the nrf_to_nrf radio library.

To ensure you are using the lwIP stack, users can #define USE_LWIP 1 & #define RF24ETHERNET_USE_UDP 1 in the RF24Ethernet.h file or prior to compilation. 

 

More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library

 More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library Direct Playback of MP3s from SD card Since designi...