Version 3.3.1, August 2019
Chirp is a library enabling Arduino-based devices to send and receive data using sound. You'll need:
- A compatible Arduino board
- A digital I2S MEMS microphone (if your board does not contain a microphone)
- A digital I2S amplifier and compatible speaker
For receiving data, you will need a digital MEMS microphone. Some boards (for example, the Nano 33 Sense and Microsoft MXChip) already include a MEMS mic so you are good to go. For others, you will need an external mic such as the SPH0645 or ICS-43434.
For sending data, we recommend using a digital I2S audio output such as the UDA1334A or MAX98357A, connected to a compatible speaker.
You can quickly test that your device is receiving chirps by playing some random test signals from the Developer Hub.
To test whether your device is sending chirps OK, we recommend setting up the Python command-line tools to receive data from the Arduino.
The following Arduino-compatible boards are able to both send and receive chirps:
- Arduino Nano 33 Sense
- Microsoft MXChip
- ESP32
The following Arduino-compatible boards are only able to send chirps, as they are not able to do on-chip DSP:
- Arduino MKRZero
- Arduino Vidor 4000
- Genuino Zero
- MKR Fox 1200
- MKR1000 WiFi
Chirp is written for the Arduino IDE versions 1.8.6 and above.
Install ChirpSDK as a library using "Manage Libraries". For instructions, see
http://arduino.cc/en/Guide/Libraries
Once installed, you can access the example programs from the menu:
File > Examples > ChirpSDK > Example
and you can include the headers to use Chirp in your own code by adding:
#include "chirp_connect.h"
To set up the Chirp SDK, initialise and configure with your app key, secret and config from the Developer Hub.
Note You must select the 16khz-mono-embedded
protocol from the dropdown menu, when
selecting your chirp configuration.
chirp = new_chirp_connect(APP_KEY, APP_SECRET);
if (chirp == NULL) {
Serial.println("Chirp initialisation failed.");
return;
}
Then set any required callbacks and start the SDK running.
chirp_connect_error_code_t err = chirp_connect_set_config(chirp, APP_CONFIG);
if (err != CHIRP_CONNECT_OK)
return;
chirp_connect_callback_set_t callbacks = {0};
callbacks.on_received = onReceivedCallback;
err = chirp_connect_set_callbacks(chirp, callbacks);
if (err != CHIRP_CONNECT_OK)
return;
err = chirp_connect_set_callback_ptr(chirp, chirp);
if (err != CHIRP_CONNECT_OK)
return;
err = chirp_connect_set_input_sample_rate(chirp, input_sample_rate);
if (err != CHIRP_CONNECT_OK)
return;
err = chirp_connect_set_output_sample_rate(chirp, output_sample_rate);
if (err != CHIRP_CONNECT_OK)
return;
err = chirp_connect_start(chirp);
if (err != CHIRP_CONNECT_OK)
return;
The received data is passed back to the onReceivedCallback
function. If the payload pointer is null then there has been an error decoding the data.
void
onReceivedCallback(void *chirp, uint8_t *payload, size_t length, uint8_t channel) {
if (payload) {
char *hexString = chirp_connect_as_string(chirp, payload, length);
Serial.printf("Received data = %s\n", hexString);
chirp_connect_free(hexString);
} else {
Serial.println("Decode failed.");
}
}
A complete list of callbacks is shown below.
void onStateChangedCallback(void *ptr, chirp_connect_state_t old_state, chirp_connect_state_t new_state) {
// Put here what you want to do when the SDK's state is changing.
}
void onSendingCallback(void *ptr, uint8_t *bytes, size_t length, uint8_t channel) {
// Put here what you want to do when the SDK starts to send some data.
}
void onSentCallback(void *ptr, uint8_t *bytes, size_t length, uint8_t channel) {
// Put here what you want to do when the SDK has sent some data.
}
void onReceivingCallback(void *ptr, uint8_t *bytes, size_t length, uint8_t channel) {
// Put here what you want to do when the SDK starts receiving some data.
}
void onReceivedCallback(void *ptr, uint8_t *bytes, size_t length, uint8_t channel) {
// Put here what you want to do when the SDK has received some data.
}
// If you don't set all the callbacks, make sure the unused callbacks are set to NULL.
chirp_connect_callback_set_t callbacks_set = {
.on_state_changed = on_state_changed_callback,
.on_sending = on_sending_callback,
.on_sent = on_sent_callback,
.on_receiving = on_receiving_callback,
.on_received = on_received_callback
};
err = chirp_connect_set_callbacks(chirp, callbacks_set);
if (err != CHIRP_CONNECT_OK)
{
const char *error_string = chirp_connect_error_code_to_string(err);
printf("%s\n", error_string);
}
A Chirp payload is simply an array of bytes. You can send a random data payload to the speakers like so.
size_t payload_length = chirp_connect_get_max_payload_length(chirp);
uint8_t *payload = chirp_connect_random_payload(chirp, &payload_length);
err = chirp_connect_send(chirp, payload, payload_length);
if (err != CHIRP_CONNECT_OK) {
const char *error_string = chirp_connect_error_code_to_string(err);
printf("%s\n", error_string);
}
Or you can easily send an ASCII string
char *identifier = "hello";
err = chirp_connect_send(chirp, (uint8_t *)identifier, strlen(identifier));
if (err != CHIRP_CONNECT_OK) {
const char *error_string = chirp_connect_error_code_to_string(err);
printf("%s\n", error_string);
}
To process audio data from the microphone, and fill the output buffer with audio data, call the following functions with data periodically.
err = chirp_connect_process_input(chirp, input_buffer, input_buffer_length);
err = chirp_connect_process_output(chirp, output_buffer, output_buffer_length);
All content copyright © Asio Ltd, 2013-2019. All rights reserved.