Record sound with ADC DMA: Compiler crashes

Following the example here in the forum example ADC DMA for Argon

I intend to record audio using DMA and transmit the result over the 3G network using TCP.
Here is the source code one more time:

#include "Particle.h"

#include "ADCDMAGen3_RK.h"


SerialLogHandler logHandler;

const size_t SAMPLES_IN_BUFFER = 512;

static nrf_saadc_value_t buffer0[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t buffer1[SAMPLES_IN_BUFFER];

static nrf_saadc_value_t *bufferReady = 0;

// If you don't hit the setup button to stop recording, this is how long to go before turning it
// off automatically. The limit really is only the disk space available to receive the file.
const unsigned long MAX_RECORDING_LENGTH_MS = 30000;

// This is the IP Address and port that the audioServer.js node server is running on.
IPAddress serverAddr = IPAddress(xx,xx,xx,xx);
int serverPort = 7334;

TCPClient client;
unsigned long recordingStart;
ADCDMAGen3 adc;

State state = STATE_WAITING;

// Forward declarations
void buttonHandler(system_event_t event, int data);

void setup() {

	Particle.keepAlive(120);  //send a ping every 2 min
	Particle.syncTime(); /*sync to current time to the particle cloud*/

	// Register handler to handle clicking on the SETUP button
	System.on(button_click, buttonHandler);

	// Blue D7 LED indicates recording is on
	pinMode(D7, OUTPUT);

	// Optional, just for testing so I can see the logs below
	// waitFor(Serial.isConnected, 10000);

	adc.withBufferCallback([](nrf_saadc_value_t *buf, size_t size) {
		// This gets executed after each sample buffer has been read.
		// Note: This is executed in interrupt context, so beware of what you do here!

		// We just remember the buffer and handle it from loop
		bufferReady = buf;

	ret_code_t err = adc
		.withDoubleBuffer(SAMPLES_IN_BUFFER, buffer0, buffer1)
		.init();"adc.init %lu", err);

	// Start sampling!


void loop() {

	switch(state) {
		// Waiting for the user to press the SETUP button. The setup button handler
		// will bump the state into STATE_CONNECT

		// Ready to connect to the server via TCP
		if (client.connect(serverAddr, serverPort)) {
			// Connected"starting");

			recordingStart = millis();
			digitalWrite(D7, HIGH);

			state = STATE_RUNNING;
		else {"failed to connect to server");
			state = STATE_WAITING;

		if (bufferReady) {
			int16_t *src = (int16_t *)bufferReady;
			uint8_t *dst = (uint8_t *)bufferReady;
			bufferReady = 0;

			// The buffer contains 16-bit samples even when sampling at 8 bits!
			// Get rid of the unwanted bytes here
			for(size_t ii = 0; ii < SAMPLES_IN_BUFFER; ii++) {
				dst[ii] = (uint8_t) src[ii];

			// Note: When outputting 16-bit samples you'd write 2 * SAMPLES_IN_BUFFER bytes
			// since each sample is 2 bytes.
			client.write(dst, SAMPLES_IN_BUFFER);

		if (millis() - recordingStart >= MAX_RECORDING_LENGTH_MS) {
			state = STATE_FINISH;

		digitalWrite(D7, LOW);
		state = STATE_WAITING;

// button handler for the SETUP button, used to toggle recording on and off
void buttonHandler(system_event_t event, int data) {
	switch(state) {
		state = STATE_CONNECT;

		state = STATE_FINISH;

When I compile the code in WEB-IDE, I get the error

/firmware/third_party/nrf5_sdk/nrf5_sdk/modules/nrfx/drivers/nrfx_ppi.c:248:0: undefined reference to "app_util_critical_region_enter"


/usr/local/gcc-arm-embedded/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: /firmware/third_party/nrf5_sdk/nrf5_sdk/modules/nrfx/drivers/nrfx_ppi.c:255:0: undefined reference to "app_util_critical_region_exit"


/firmware/third_party/nrf5_sdk/nrf5_sdk/modules/nrfx/drivers/nrfx_ppi.c:280:0: undefined reference to "app_util_critical_region_enter"


/usr/local/gcc-arm-embedded/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: /firmware/third_party/nrf5_sdk/nrf5_sdk/modules/nrfx/drivers/nrfx_ppi.c:282:0: undefined reference to "app_util_critical_region_exit"

please what I am doing wrong

Let me answer my own question. Hope it is useful for others in the forum.

To run programs with the “ADCDMAGen3_RK” library, they have to be compiled locally. Here are the steps, how I proceeded.

  1. Download Particle Workbench from here particle workbench
  2. Open particle workbench, setup a new project
    Particle: Create New Project, with the folder of the main program. For the sake of convience, I have added the ADCDMA library to the same folder.
  3. Download the latest version of the nRF5 SDK library (17.0.2 at the time of writing) from here: nRF5-SDK
  4. In the workbench: File → Add folder to the workspace (where the nRF5 library is located)
  5. Install the extension for the local compiler (target version 2.1.0 at the time of writing), configure Project for Device as described in the instructions fo workbench.
  6. Make sure that particle CLI is installed (particle cli)
  7. Run → Run without Debugging

The compiled result works perfectly. My cellular operator TIM supports 8kHz sampling rate.

Thanks - I have run into the same problem and am about to follow your very useful guide.

I had moved from the Workbench to WebIDE because of the slow compile times on a fast PC. It seems that I will have to move back to Workbench.

A note to item 4 in the instructions.
For the nRF5 library, you only need to download the item “” from the nordicsemi-website. The unzipped file has a sub-directory “modules\nrfx”. This nrfx-subfolder containing all necessary drivers has to be added to the workspace in particle workbench.

I updated the library to add compatibility with Device OS 2.0.0 without having to add additional nRF52 SDK components to your build.

0.0.2 (2021-07-02)

  • Added compatibility with Device OS 2.0.0 and later.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.