examples : update Arduino and ESP32 examples

This commit is contained in:
Georgi Gerganov
2022-07-07 21:14:42 +03:00
parent 16ec2ec916
commit dd04cf608f
3 changed files with 199 additions and 76 deletions

View File

@@ -1,20 +1,52 @@
// arduino-rx
//
// Sample sketch for receiving data using "ggwave"
//
// Tested with:
// - Arduino Nano RP2040 Connect
//
// The Arduino Nano RP2040 Connect board has a built-in microphone which is used
// in this example to capture audio data.
//
// The sketch optionally supports displaying the received "ggwave" data on an OLED display.
// Use the DISPLAY_OUTPUT macro to enable or disable this functionality.
//
// If you don't have a display, you can simply observe the decoded data in the serial monitor.
//
// If you want to perform a quick test, you can use the free "Waver" application:
// - Web: https://waver.ggerganov.com
// - Android: https://play.google.com/store/apps/details?id=com.ggerganov.Waver
// - iOS: https://apps.apple.com/us/app/waver-data-over-sound/id1543607865
//
// Make sure to enable the "Fixed-length" option in "Waver"'s settings and set the number of
// bytes to be equal to "payloadLength" used in the sketch. Also, select a protocol that is
// listed as Rx in the current sketch.
//
// Demo: https://youtu.be/HiDpGvnxPLs
//
// Sketch: https://github.com/ggerganov/ggwave/tree/master/examples/arduino-rx
//
// Uncoment this line to enable SSD1306 display output
//#define DISPLAY_OUTPUT 1
#include <ggwave.h>
#include <PDM.h>
// Pin configuration
const int kPinButton0 = 5;
const int kPinSpeaker = 10;
// Audio capture configuration
using TSample = int16_t;
const size_t kSampleSize_bytes = sizeof(TSample);
// default number of output channels
const char channels = 1;
// default PCM output sampleRate
const int sampleRate = 6000;
const int samplesPerFrame = 128;
const char channels = 1;
const int sampleRate = 6000;
const int samplesPerFrame = 128;
// Audio capture ring-buffer
const int qpow = 9;
const int qmax = 1 << qpow;
@@ -22,17 +54,14 @@ volatile int qhead = 0;
volatile int qtail = 0;
volatile int qsize = 0;
// buffer to read samples into, each sample is 16-bits
TSample sampleBuffer[qmax];
// Error handling
volatile int err = 0;
// global GGwave instance
// Global GGwave instance
GGWave ggwave;
// uncoment this to enable SSD1306 display output
#define DISPLAY_OUTPUT 1
#ifdef DISPLAY_OUTPUT
#include <SPI.h>
@@ -54,7 +83,7 @@ Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#endif
// helper function to output the generated GGWave waveform via a buzzer
// Helper function to output the generated GGWave waveform via a buzzer
void send_text(GGWave & ggwave, uint8_t pin, const char * text, GGWave::TxProtocolId protocolId) {
Serial.print(F("Sending text: "));
Serial.println(text);
@@ -77,7 +106,7 @@ void send_text(GGWave & ggwave, uint8_t pin, const char * text, GGWave::TxProtoc
void setup() {
Serial.begin(57600);
while (!Serial);
//while (!Serial);
pinMode(kPinSpeaker, OUTPUT);
pinMode(kPinButton0, INPUT_PULLUP);
@@ -110,30 +139,37 @@ void setup() {
}
#endif
Serial.println(F("Trying to create ggwave instance"));
ggwave.setLogFile(nullptr);
// Initialize "ggwave"
{
Serial.println(F("Trying to initialize the ggwave instance"));
ggwave.setLogFile(nullptr);
auto p = GGWave::getDefaultParameters();
// Adjust the "ggwave" parameters to your needs.
// Make sure that the "payloadLength" parameter matches the one used on the transmitting side.
p.payloadLength = 16;
p.sampleRateInp = sampleRate;
p.sampleRateOut = sampleRate;
p.sampleRate = sampleRate;
p.samplesPerFrame = samplesPerFrame;
p.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16;
p.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16;
p.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_U8;
p.operatingMode = GGWAVE_OPERATING_MODE_RX | GGWAVE_OPERATING_MODE_TX | GGWAVE_OPERATING_MODE_USE_DSS | GGWAVE_OPERATING_MODE_TX_ONLY_TONES;
// Protocols to use for TX
// Remove the ones that you don't need to reduce memory usage
GGWave::Protocols::tx().disableAll();
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_NORMAL, true);
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_FAST, true);
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_FASTEST, true);
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_MT_NORMAL, true);
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_MT_FAST, true);
//GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_NORMAL, true);
//GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_FAST, true);
//GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_DT_FASTEST, true);
//GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_MT_NORMAL, true);
//GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_MT_FAST, true);
GGWave::Protocols::tx().toggle(GGWAVE_PROTOCOL_MT_FASTEST, true);
// Protocols to use for RX
// Remove the ones that you don't need to reduce memory usage
GGWave::Protocols::rx().disableAll();
GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_DT_NORMAL, true);
GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_DT_FAST, true);
@@ -142,15 +178,15 @@ void setup() {
GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_MT_FAST, true);
GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_MT_FASTEST, true);
// Initialize the ggwave instance and print the memory usage
ggwave.prepare(p);
Serial.println(ggwave.heapSize());
Serial.print(F("Instance initialized successfully! Memory used: "));
Serial.print(ggwave.heapSize());
Serial.println(F(" bytes"));
}
delay(1000);
Serial.println(F("Instance initialized"));
// Start capturing audio
{
// Configure the data receive callback
PDM.onReceive(onPDMdata);
@@ -159,10 +195,7 @@ void setup() {
// Defaults to 20 on the BLE Sense and -10 on the Portenta Vision Shields
//PDM.setGain(30);
// Initialize PDM with:
// - one channel (mono mode)
// - a 16 kHz sample rate for the Arduino Nano 33 BLE Sense
// - a 32 kHz or 64 kHz sample rate for the Arduino Portenta Vision Shields
// Initialize PDM:
if (!PDM.begin(channels, sampleRate)) {
Serial.println(F("Failed to start PDM!"));
while (1);
@@ -178,8 +211,10 @@ void loop() {
GGWave::TxRxData result;
char resultLast[17];
// Main loop ..
while (true) {
while (qsize >= samplesPerFrame) {
// We have enough captured samples - try to decode any "ggwave" data:
auto tStart = millis();
ggwave.decode(sampleBuffer + qhead, samplesPerFrame*kSampleSize_bytes);
@@ -191,15 +226,16 @@ void loop() {
auto tEnd = millis();
if (++niter % 10 == 0) {
// print the time it took the last decode() call to complete
// should be smaller than samplesPerFrame/sampleRate seconds
// for example: samplesPerFrame = 128, sampleRate = 6000 => not more than 20 ms
// Print the time it took the last decode() call to complete.
// The time should be smaller than samplesPerFrame/sampleRate seconds
// For example: samplesPerFrame = 128, sampleRate = 6000 => not more than 20 ms
Serial.println(tEnd - tStart);
if (tEnd - tStart > 1000*(float(samplesPerFrame)/sampleRate)) {
Serial.println(F("Warning: decode() took too long to execute!"));
}
}
// Check if we have successfully decoded any data:
nr = ggwave.rxTakeData(result);
if (nr > 0) {
Serial.println(tEnd - tStart);
@@ -225,12 +261,16 @@ void loop() {
}
}
// This should never happen.
// If it does - there is something wrong with the audio capturing callback.
// For example, the microcontroller is not able to process the captured data in real-time.
if (err > 0) {
Serial.println(F("ERRROR"));
Serial.println(err);
err = 0;
}
// If the button has been presse - transmit the last received data:
int but0 = digitalRead(kPinButton0);
if (but0 == LOW && but0Prev == HIGH) {
Serial.println(F("Button 0 pressed - transmitting .."));
@@ -268,7 +308,7 @@ void onPDMdata() {
const int nSamples = bytesAvailable/kSampleSize_bytes;
if (qsize + nSamples > qmax) {
// if you hit this error, try to increase qmax
// If you hit this error, try to increase qmax
err += 10;
qhead = 0;
@@ -282,7 +322,7 @@ void onPDMdata() {
qsize += nSamples;
if (qtail > qmax) {
// if you hit this error, qmax is probably not a multiple of the recorded samples
// If you hit this error, qmax is probably not a multiple of the recorded samples
err += 1;
}