Skip to content
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

DRAFT: Use AudioTools #89

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1a6ae57
embedded: Apply Formatting
wsciaroni Oct 20, 2024
3034609
Move main run modes into functions
wsciaroni Oct 20, 2024
cbbffe3
Move command handlers into functions
wsciaroni Oct 20, 2024
c61f657
Remove TODO
wsciaroni Oct 23, 2024
3f526a1
Fix Compile
wsciaroni Oct 24, 2024
6d26010
Add stubbed object oriented design
wsciaroni Oct 20, 2024
3f65e6d
Migrate to using an enumclass for the command values
wsciaroni Oct 23, 2024
0cf8607
Get Refactor to a compiling state. Known issues.
wsciaroni Oct 24, 2024
f91f590
Remove Serial.available on reading commands
wsciaroni Oct 26, 2024
c46c5df
Add Documentation
wsciaroni Oct 27, 2024
93972a6
Add Doxyfile
wsciaroni Oct 27, 2024
4c71b52
Add Doxygen notes
wsciaroni Oct 27, 2024
566efa3
Add Msg Data UML
wsciaroni Oct 28, 2024
5139fc0
Update Serial communication for redesign from main
wsciaroni Oct 28, 2024
900f7e6
Revert the RX Audio method
wsciaroni Oct 28, 2024
aa5b1bd
WIP: Create TX Audio Task
wsciaroni Oct 29, 2024
514bdb5
WIP: Currently in TX Loop
wsciaroni Oct 30, 2024
15b43a1
Smooth audio
wsciaroni Nov 3, 2024
e8326b5
Add initialAudioTools Implementation
wsciaroni Nov 8, 2024
8402fcd
Initial RX Configuration
wsciaroni Nov 8, 2024
543af08
Update Documentation
wsciaroni Nov 8, 2024
dff40fb
Merge remote-tracking branch 'upstream/main' into SimplifyRefactor
wsciaroni Nov 8, 2024
5e9db42
Tune the samples per second as received by the computer
wsciaroni Nov 8, 2024
231bcf8
Remove unused/bad Analog configuration
wsciaroni Nov 8, 2024
f5bf729
Update CI to build via ESP32:3.0.5
wsciaroni Nov 8, 2024
2022fd2
Rename minBufferSize to MIN_BUFFER_SIZE
wsciaroni Nov 8, 2024
6c4e381
Remove unused COMMAND_DELIMETER
wsciaroni Nov 8, 2024
17b9001
Prevent null exception at end of filling the audio buffer
wsciaroni Nov 8, 2024
a10a7bd
Change String encoding to StandardCharsets.UTF_8
wsciaroni Nov 8, 2024
4971cd0
Merge remote-tracking branch 'upstream/main' into SimplifyRefactor
wsciaroni Nov 17, 2024
ec3ea35
Add script to write serial samples to the board for testing.
wsciaroni Nov 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/arduino.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ jobs:
fqbn: esp32:esp32:esp32:EventsCore=0
platforms: |
- name: "esp32:esp32"
version: "2.0.17"
version: "3.0.5"
sketch-paths: |
- microcontroller-src/kv4p_ht_esp32_wroom_32
libraries: |
- name: EspSoftwareSerial
version: 8.1.0
- source-url: https://github.com/fatpat/arduino-dra818/archive/refs/tags/v1.0.1.zip
- source-url: https://github.com/pschatzmann/arduino-audio-tools/archive/5214dec10ebaab57d6eb485cb96b4180da476b54.zip
verbose: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ android-src/KV4PHT/app/debug/*
android-src/KV4PHT/app/release/*
android-src/KV4PHT/usbSerialForAndroid/build/*
build/
.idea/
.vscode/
backups/
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,21 @@ public byte getByte() {
}
}

private enum ESP32MsgType {
DATA((byte) 0xF0),
CMD((byte) 0x0F);

private byte commandByte;
ESP32MsgType(byte commandByte) {
this.commandByte = commandByte;
}

public byte getByte() {
return commandByte;
}
}


public static final byte SILENT_BYTE = -128;

// Callbacks to the Activity that started us
Expand All @@ -134,7 +149,7 @@ public byte getByte() {
public static final int AUDIO_SAMPLE_RATE = 44100;
public static final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
public static final int audioFormat = AudioFormat.ENCODING_PCM_8BIT;
public static final int minBufferSize = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, channelConfig, audioFormat) * 2;
public static final int MIN_BUFFER_SIZE = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, channelConfig, audioFormat) * 8;
private UsbManager usbManager;
private UsbDevice esp32Device;
private static UsbSerialPort serialPort;
Expand All @@ -144,16 +159,13 @@ public byte getByte() {

// For receiving audio from ESP32 / radio
private AudioTrack audioTrack;
private static final int PRE_BUFFER_SIZE = 1000;
private static final int PRE_BUFFER_SIZE = 4096;
private byte[] rxBytesPrebuffer = new byte[PRE_BUFFER_SIZE];
private int rxPrebufferIdx = 0;
private boolean prebufferComplete = false;
private static final float SEC_BETWEEN_SCANS = 0.5f; // how long to wait during silence to scan to next frequency in scan mode
private LiveData<List<ChannelMemory>> channelMemoriesLiveData = null;

// Delimiter must match ESP32 code
private static final byte[] COMMAND_DELIMITER = new byte[] {(byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00};

// AFSK modem
private Afsk1200Modulator afskModulator = null;
private PacketDemodulator afskDemodulator = null;
Expand Down Expand Up @@ -532,7 +544,7 @@ private void initAudioTrack() {
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setTransferMode(AudioTrack.MODE_STREAM)
.setBufferSizeInBytes(minBufferSize)
.setBufferSizeInBytes(MIN_BUFFER_SIZE)
.build();

restartAudioPrebuffer();
Expand Down Expand Up @@ -824,7 +836,19 @@ public void nextScan() {

public void sendAudioToESP32(byte[] audioBuffer, boolean dataMode) {
if (audioBuffer.length <= TX_AUDIO_CHUNK_SIZE) {
sendBytesToESP32(audioBuffer);
int numberOfBytesToSend = audioBuffer.length;
// TODO: Make this support packets of size greater that 2^8
// byte[] commandInfo = { ESP32MsgType.DATA.getByte(), 0, numberOfBytesToSend};
// byte[] combined = new byte[commandInfo.length + numberOfBytesToSend];
// System.arraycopy(commandInfo, 0, combined, 0, commandInfo.length);
// System.arraycopy(audioBuffer, 0, combined, commandInfo.length, numberOfBytesToSend);
// sendBytesToESP32(combined);
// numberOfBytesToSend = 10;
byte[] commandInfo = { ESP32MsgType.DATA.getByte(), (byte)(numberOfBytesToSend >> 8), (byte)numberOfBytesToSend};
sendBytesToESP32(commandInfo);
// byte[] fakeAudioData = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 };
// sendBytesToESP32(fakeAudioData);
sendBytesToESP32(Arrays.copyOfRange(audioBuffer, 0, numberOfBytesToSend));
} else {
// If the audio is fairly long, we need to send it to ESP32 at the same rate
// as audio sampling. Otherwise, we'll overwhelm its DAC buffer and some audio will
Expand All @@ -840,8 +864,23 @@ public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_BACKGROUND +
android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
sendBytesToESP32(Arrays.copyOfRange(audioBuffer, chunkStart,
Math.min(audioBuffer.length, chunkStart + TX_AUDIO_CHUNK_SIZE)));
int numberOfBytesToSend = Math.min(audioBuffer.length - chunkStart, TX_AUDIO_CHUNK_SIZE);
if (numberOfBytesToSend <= 0) {
// Something has occurred
} else {
// TODO: Make this support packets of size greater that 2^8
// byte[] commandInfo = { ESP32MsgType.DATA.getByte(), (byte)(numberOfBytesToSend >> 8), (byte)numberOfBytesToSend};
// byte[] combined = new byte[commandInfo.length + numberOfBytesToSend];
// System.arraycopy(commandInfo, 0, combined, 0, commandInfo.length);
// System.arraycopy(audioBuffer, chunkStart, combined, commandInfo.length, numberOfBytesToSend);
// sendBytesToESP32(combined);
// numberOfBytesToSend = 10;
byte[] commandInfo = { ESP32MsgType.DATA.getByte(), (byte)(numberOfBytesToSend >> 8), (byte)numberOfBytesToSend};
sendBytesToESP32(commandInfo);
// byte[] fakeAudioData = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 };
// sendBytesToESP32(fakeAudioData);
sendBytesToESP32(Arrays.copyOfRange(audioBuffer, chunkStart, chunkStart + numberOfBytesToSend));
}
}
}, (int) nextSendDelay);

Expand All @@ -864,17 +903,13 @@ public void run() {
}

public void sendCommandToESP32(ESP32Command command) {
byte[] commandArray = { COMMAND_DELIMITER[0], COMMAND_DELIMITER[1],
COMMAND_DELIMITER[2], COMMAND_DELIMITER[3], COMMAND_DELIMITER[4], COMMAND_DELIMITER[5],
COMMAND_DELIMITER[6], COMMAND_DELIMITER[7], command.getByte() };
byte[] commandArray = { ESP32MsgType.CMD.getByte(), command.getByte() };
sendBytesToESP32(commandArray);
Log.d("DEBUG", "Sent command: " + command);
}

public void sendCommandToESP32(ESP32Command command, String paramsStr) {
byte[] commandArray = { COMMAND_DELIMITER[0], COMMAND_DELIMITER[1],
COMMAND_DELIMITER[2], COMMAND_DELIMITER[3], COMMAND_DELIMITER[4], COMMAND_DELIMITER[5],
COMMAND_DELIMITER[6], COMMAND_DELIMITER[7], command.getByte() };
byte[] commandArray = { ESP32MsgType.CMD.getByte(), command.getByte() };
byte[] combined = new byte[commandArray.length + paramsStr.length()];
ByteBuffer buffer = ByteBuffer.wrap(combined);
buffer.put(commandArray);
Expand Down Expand Up @@ -944,28 +979,27 @@ public static UsbSerialPort getUsbSerialPort() {

private void handleESP32Data(byte[] data) {
// Log.d("DEBUG", "Got bytes from ESP32: " + Arrays.toString(data));
/* try {
String dataStr = new String(data, "UTF-8");
if (dataStr.length() < 100 && dataStr.length() > 0)
Log.d("DEBUG", "Str data from ESP32: " + dataStr);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} */
// try {
// String logDataStr = new String(data, StandardCharsets.UTF_8);
// if (logDataStr.length() < 100 && logDataStr.length() > 0)
// Log.d("DEBUG", "Str data from ESP32: " + logDataStr);
// } catch (UnsupportedEncodingException e) {
// throw new RuntimeException(e);
// }
// Log.d("DEBUG", "Num bytes from ESP32: " + data.length);

if (mode == MODE_STARTUP) {
try {
String dataStr = new String(data, "UTF-8");
versionStrBuffer += dataStr;
if (versionStrBuffer.contains(VERSION_PREFIX)) {
int startIdx = versionStrBuffer.indexOf(VERSION_PREFIX) + VERSION_PREFIX.length();
String verStr = "";
try {
verStr = versionStrBuffer.substring(startIdx, startIdx + VERSION_LENGTH);
} catch (IndexOutOfBoundsException iobe) {
return; // Version string not yet fully received.
}
int verInt = Integer.parseInt(verStr);
String dataStr = new String(data, StandardCharsets.UTF_8);
versionStrBuffer += dataStr;
if (versionStrBuffer.contains(VERSION_PREFIX)) {
int startIdx = versionStrBuffer.indexOf(VERSION_PREFIX) + VERSION_PREFIX.length();
String verStr = "";
try {
verStr = versionStrBuffer.substring(startIdx, startIdx + VERSION_LENGTH);
} catch (IndexOutOfBoundsException iobe) {
return; // Version string not yet fully received.
}
int verInt = Integer.parseInt(verStr);
if (verInt < FirmwareUtils.PACKAGED_FIRMWARE_VER) {
Log.d("DEBUG", "Error: ESP32 app firmware " + verInt + " is older than latest firmware " + FirmwareUtils.PACKAGED_FIRMWARE_VER);
if (callbacks != null) {
Expand All @@ -977,10 +1011,7 @@ private void handleESP32Data(byte[] data) {
versionStrBuffer = ""; // Reset the version string buffer for next USB reconnect.
initAfterESP32Connected();
}
return;
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
return;
}
}

Expand Down Expand Up @@ -1015,10 +1046,15 @@ private void handleESP32Data(byte[] data) {
synchronized (audioTrack) {
audioTrack.write(rxBytesPrebuffer, 0, PRE_BUFFER_SIZE);
}

synchronized (audioTrack) {
// write the remaining audio bytes from data[] so we don't drop them
audioTrack.write(data, i+1, data.length - i);
}
}

rxPrebufferIdx = 0;
break; // Might drop a few audio bytes from data[], should be very minimal
break;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions microcontroller-src/kv4p_ht_esp32_wroom_32/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
doxygen/
14 changes: 14 additions & 0 deletions microcontroller-src/kv4p_ht_esp32_wroom_32/CommandValueEnum.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <cstdint>

// Commands defined here must match the Android app
enum class CommandValue : uint8_t
{
COMMAND_PTT_DOWN = 1, // start transmitting audio that Android app will send
COMMAND_PTT_UP = 2, // stop transmitting audio, go into RX mode
COMMAND_TUNE_TO = 3, // change the frequency
COMMAND_FILTERS = 4, // toggle filters on/off
COMMAND_STOP = 5, // stop everything, just wait for next command
COMMAND_GET_FIRMWARE_VER = 6 // report FIRMWARE_VER in the format '00000001' for 1 (etc.)
};
Empty file.
45 changes: 45 additions & 0 deletions microcontroller-src/kv4p_ht_esp32_wroom_32/Constants.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

// Audio sampling rate, must match what Android app expects (and sends).
#define AUDIO_SAMPLE_RATE 44100

// Offset to make up for fact that sampling is slightly slower than requested, and we don't want underruns.
// But if this is set too high, then we get audio skips instead of underruns. So there's a sweet spot.
#define SAMPLING_RATE_OFFSET 218

// Buffer for outgoing audio bytes to send to radio module
#define TX_TEMP_AUDIO_BUFFER_SIZE 4096 // Holds data we already got off of USB serial from Android app
#define TX_CACHED_AUDIO_BUFFER_SIZE 1024 // MUST be smaller than DMA buffer size specified in i2sTxConfig, because we dump this cache into DMA buffer when full.

#define TX_AUDIO_CHUNK_SIZE 512

// Max data to cache from USB (1024 is ESP32 max)
#define USB_BUFFER_SIZE 1024

// ms to wait before issuing PTT UP after a tx (to allow final audio to go out)
#define MS_WAIT_BEFORE_PTT_UP 40

// Connections to radio module
#define RXD2_PIN 16
#define TXD2_PIN 17
#define DAC_PIN 25 // This constant not used, just here for reference. GPIO 25 is implied by use of I2S_DAC_CHANNEL_RIGHT_EN.
#define ADC_PIN 34 // If this is changed, you may need to manually edit adc1_config_channel_atten() below too.
#define PTT_PIN 18
#define PD_PIN 19
#define SQ_PIN 32

// Built in LED
#define LED_PIN 2

// Tx runaway detection stuff
#define RUNAWAY_TX_SEC 200

// I2S audio sampling stuff
#define I2S_READ_LEN 1024
#define I2S_WRITE_LEN 1024
#define I2S_ADC_UNIT ADC_UNIT_1
#define I2S_ADC_CHANNEL ADC1_CHANNEL_6

// Squelch parameters (for graceful fade to silence)
#define FADE_SAMPLES 256 // Must be a power of two
#define ATTENUATION_MAX 256
Loading
Loading