Skip to content

Commit e16ee98

Browse files
committed
Added example
1 parent 577ed9c commit e16ee98

File tree

1 file changed

+387
-0
lines changed

1 file changed

+387
-0
lines changed
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
#include <DebugLog.h>
2+
#include <RadioLib.h>
3+
#include <Wire.h>
4+
#include <driver/i2s.h>
5+
#include <codec2.h>
6+
#include <CircularBuffer.h>
7+
#include <arduino-timer.h>
8+
9+
//#define ENABLE_LIGHT_SLEEP true
10+
11+
// serial1
12+
#define SERIAL_BAUD_RATE 115200 // USB serial baud rate
13+
14+
// ptt button
15+
#define PTTBTN_PIN 39
16+
volatile bool btn_pressed_ = false;
17+
18+
// lora module
19+
20+
// lora module pinouts
21+
#define LORA_RADIO_PIN_SS SS
22+
#define LORA_RADIO_PIN_RST 27
23+
#define LORA_RADIO_PIN_A 12
24+
#define LORA_RADIO_PIN_B 14
25+
#define LORA_RADIO_PIN_RXEN 32
26+
#define LORA_RADIO_PIN_TXEN 33
27+
28+
// lora modulation parameters
29+
#define LORA_RADIO_FREQ 433.775
30+
#define LORA_RADIO_BW 125.0
31+
#define LORA_RADIO_SF 9
32+
#define LORA_RADIO_CR 7
33+
#define LORA_RADIO_PWR 2
34+
#define LORA_RADIO_CRC 1
35+
#define LORA_RADIO_EXPL true
36+
#define LORA_RADIO_SYNC 0x34
37+
38+
// lora support config
39+
#define LORA_RADIO_BUF_LEN 256 // packet buffer size
40+
#define LORA_RADIO_QUEUE_LEN 512 // queue length
41+
42+
#define LORA_RADIO_TASK_RX_BIT 0x01 // lora task rx bit command
43+
#define LORA_RADIO_TASK_TX_BIT 0x02 // lora task tx bit command
44+
45+
// lora task packet queues
46+
CircularBuffer<uint8_t, LORA_RADIO_QUEUE_LEN> lora_radio_rx_queue_;
47+
CircularBuffer<uint8_t, LORA_RADIO_QUEUE_LEN> lora_radio_rx_queue_index_;
48+
CircularBuffer<uint8_t, LORA_RADIO_QUEUE_LEN> lora_radio_tx_queue_;
49+
CircularBuffer<uint8_t, LORA_RADIO_QUEUE_LEN> lora_radio_tx_queue_index_;
50+
51+
// packet buffers
52+
byte lora_radio_rx_buf_[LORA_RADIO_BUF_LEN]; // tx packet buffer
53+
byte lora_radio_tx_buf_[LORA_RADIO_BUF_LEN]; // rx packet buffer
54+
55+
TaskHandle_t lora_task_; // lora rx/tx task
56+
volatile bool lora_enable_isr_ = true; // true to enable rx isr, disabled on tx
57+
SX1268 lora_radio_ = new Module(LORA_RADIO_PIN_SS, LORA_RADIO_PIN_A, LORA_RADIO_PIN_RST, LORA_RADIO_PIN_B);
58+
59+
// audio speaker pinouts
60+
#define AUDIO_SPEAKER_BCLK 26
61+
#define AUDIO_SPEAKER_LRC 13
62+
#define AUDIO_SPEAKER_DIN 25
63+
64+
// audio mic pinouts
65+
#define AUDIO_MIC_SD 2
66+
#define AUDIO_MIC_WS 15
67+
#define AUDIO_MIC_SCK 4
68+
69+
// audio support
70+
#define AUDIO_CODEC2_MODE CODEC2_MODE_450 // codec2 mode
71+
#define AUDIO_SAMPLE_RATE 8000 // audio sample rate
72+
#define AUDIO_MAX_PACKET_SIZE 48 // maximum packet size, multiple audio frames are inside
73+
#define AUDIO_TASK_PLAY_BIT 0x01 // task bit flag to start playback
74+
#define AUDIO_TASK_RECORD_BIT 0x02 // task bit flag to start recording
75+
76+
// audio task
77+
TaskHandle_t audio_task_; /// audio playback/record task
78+
79+
// codec2
80+
struct CODEC2* c2_; // codec2 instance
81+
int c2_samples_per_frame_; // how many raw samples in one frame
82+
int c2_bytes_per_frame_; // how many bytes in encoded frame
83+
int16_t *c2_samples_; // buffer for raw samples
84+
uint8_t *c2_bits_; // buffer for encoded frame
85+
86+
// deep sleep
87+
#define LIGHT_SLEEP_DELAY_MS 5000 // how long to wait before engering light sleep
88+
#define LIGHT_SLEEP_BITMASK (uint64_t)(1 << LORA_RADIO_PIN_A) | (1 << LORA_RADIO_PIN_B) // bit mask for ext1 high pin wake up
89+
90+
Timer<1> light_sleep_timer_; // light sleep timer
91+
uintptr_t light_sleep_timer_task_; // light sleep timer task
92+
93+
void setup() {
94+
// setup logging
95+
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
96+
//LOG_SET_LEVEL(DebugLogLevel::LVL_DEBUG);
97+
LOG_SET_OPTION(false, false, true); // disable file, line, enable func
98+
99+
// initialize serial
100+
Serial.begin(SERIAL_BAUD_RATE);
101+
while (!Serial);
102+
LOG_INFO("Board setup started");
103+
104+
// setup lora radio
105+
int lora_radio_state = lora_radio_.begin(LORA_RADIO_FREQ, LORA_RADIO_BW, LORA_RADIO_SF, LORA_RADIO_CR, LORA_RADIO_SYNC, LORA_RADIO_PWR);
106+
if (lora_radio_state == RADIOLIB_ERR_NONE) {
107+
LOG_INFO("Lora radio initialized");
108+
lora_radio_.setCRC(LORA_RADIO_CRC);
109+
lora_radio_.setRfSwitchPins(LORA_RADIO_PIN_RXEN, LORA_RADIO_PIN_TXEN);
110+
lora_radio_.clearDio1Action();
111+
lora_radio_.explicitHeader();
112+
lora_radio_.setDio1Action(onLoraDataAvailableIsr);
113+
} else {
114+
LOG_ERROR("Lora radio start failed:", lora_radio_state);
115+
}
116+
117+
// setup ptt button
118+
pinMode(PTTBTN_PIN, INPUT);
119+
120+
// setup speaker
121+
i2s_config_t i2s_speaker_config = {
122+
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
123+
.sample_rate = AUDIO_SAMPLE_RATE,
124+
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
125+
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
126+
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
127+
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
128+
.dma_buf_count = 8,
129+
.dma_buf_len = 1024,
130+
.use_apll=0,
131+
.tx_desc_auto_clear= true,
132+
.fixed_mclk=-1
133+
};
134+
i2s_pin_config_t i2s_speaker_pin_config = {
135+
.bck_io_num = AUDIO_SPEAKER_BCLK,
136+
.ws_io_num = AUDIO_SPEAKER_LRC,
137+
.data_out_num = AUDIO_SPEAKER_DIN,
138+
.data_in_num = I2S_PIN_NO_CHANGE
139+
};
140+
if (i2s_driver_install(I2S_NUM_0, &i2s_speaker_config, 0, NULL) != ESP_OK) {
141+
LOG_ERROR("Failed to install i2s speaker driver");
142+
}
143+
if (i2s_set_pin(I2S_NUM_0, &i2s_speaker_pin_config) != ESP_OK) {
144+
LOG_ERROR("Failed to set i2s speaker pins");
145+
}
146+
147+
// setup microphone
148+
i2s_config_t i2s_mic_config = {
149+
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
150+
.sample_rate = AUDIO_SAMPLE_RATE,
151+
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
152+
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
153+
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
154+
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
155+
.dma_buf_count = 8,
156+
.dma_buf_len = 1024,
157+
.use_apll=0,
158+
.tx_desc_auto_clear= true,
159+
.fixed_mclk=-1
160+
};
161+
i2s_pin_config_t i2s_mic_pin_config = {
162+
.bck_io_num = AUDIO_MIC_SCK,
163+
.ws_io_num = AUDIO_MIC_WS,
164+
.data_out_num = I2S_PIN_NO_CHANGE,
165+
.data_in_num = AUDIO_MIC_SD
166+
};
167+
if (i2s_driver_install(I2S_NUM_1, &i2s_mic_config, 0, NULL) != ESP_OK) {
168+
LOG_ERROR("Failed to install i2s mic driver");
169+
}
170+
if (i2s_set_pin(I2S_NUM_1, &i2s_mic_pin_config) != ESP_OK) {
171+
LOG_ERROR("Failed to set i2s mic pins");
172+
}
173+
174+
// start audio playback/recordign task
175+
xTaskCreate(&audio_task, "audio_task", 32000, NULL, 5, &audio_task_);
176+
177+
// start lora receive/transmit task task
178+
xTaskCreate(&lora_task, "lora_task", 8000, NULL, 5, &lora_task_);
179+
int state = lora_radio_.startReceive();
180+
if (state != RADIOLIB_ERR_NONE) {
181+
LOG_ERROR("Receive start error:", state);
182+
}
183+
LOG_INFO("Board setup completed");
184+
185+
#ifdef ENABLE_LIGHT_SLEEP
186+
LOG_INFO("Light sleep is enabled");
187+
#endif
188+
light_sleep_reset();
189+
}
190+
191+
// ISR is called when new data is available from radio
192+
ICACHE_RAM_ATTR void onLoraDataAvailableIsr() {
193+
if (!lora_enable_isr_) return;
194+
BaseType_t xHigherPriorityTaskWoken;
195+
uint32_t lora_rx_bit = LORA_RADIO_TASK_RX_BIT;
196+
// notify radio receive task on new data arrival
197+
xTaskNotifyFromISR(lora_task_, lora_rx_bit, eSetBits, &xHigherPriorityTaskWoken);
198+
}
199+
200+
// start counting timer for light sleep
201+
void light_sleep_reset() {
202+
#ifdef ENABLE_LIGHT_SLEEP
203+
LOG_DEBUG("Reset light sleep");
204+
if (light_sleep_timer_task_ != NULL) light_sleep_timer_.cancel(light_sleep_timer_task_);
205+
light_sleep_timer_task_ = light_sleep_timer_.in(LIGHT_SLEEP_DELAY_MS, light_sleep);
206+
#endif
207+
}
208+
209+
// called by timer to enter light sleep
210+
bool light_sleep(void *param) {
211+
#ifdef ENABLE_LIGHT_SLEEP
212+
LOG_INFO("Entering light sleep");
213+
esp_sleep_enable_ext0_wakeup(GPIO_NUM_39, 0);
214+
esp_sleep_enable_ext1_wakeup(LIGHT_SLEEP_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH);
215+
delay(100);
216+
esp_light_sleep_start();
217+
LOG_INFO("Exiting light sleep");
218+
#endif
219+
return false;
220+
}
221+
222+
// lora trasmit receive task
223+
void lora_task(void *param) {
224+
LOG_INFO("Lora task started");
225+
226+
// wait for ISR notification, read data and send for audio processing
227+
while (true) {
228+
uint32_t lora_status_bits = 0;
229+
xTaskNotifyWaitIndexed(0, 0x00, ULONG_MAX, &lora_status_bits, portMAX_DELAY);
230+
231+
LOG_DEBUG("Lora task bits", lora_status_bits);
232+
233+
// lora rx
234+
if (lora_status_bits & LORA_RADIO_TASK_RX_BIT) {
235+
int packet_size = lora_radio_.getPacketLength();
236+
if (packet_size > 0) {
237+
int state = lora_radio_.readData(lora_radio_rx_buf_, packet_size);
238+
if (state == RADIOLIB_ERR_NONE) {
239+
// process packet
240+
LOG_DEBUG("Received packet, size", packet_size);
241+
if (packet_size % c2_bytes_per_frame_ == 0) {
242+
for (int i = 0; i < packet_size; i++) {
243+
lora_radio_rx_queue_.push(lora_radio_rx_buf_[i]);
244+
}
245+
lora_radio_rx_queue_index_.push(packet_size);
246+
uint32_t audio_play_bit = AUDIO_TASK_PLAY_BIT;
247+
xTaskNotify(audio_task_, audio_play_bit, eSetBits);
248+
} else {
249+
LOG_ERROR("Audio packet of wrong size, expected mod", c2_bytes_per_frame_);
250+
}
251+
} else {
252+
LOG_ERROR("Read data error: ", state);
253+
}
254+
// probably not needed, still in receive
255+
state = lora_radio_.startReceive();
256+
if (state != RADIOLIB_ERR_NONE) {
257+
LOG_ERROR("Start receive error: ", state);
258+
}
259+
light_sleep_reset();
260+
} // packet size > 0
261+
} // lora rx
262+
// lora tx data
263+
else if (lora_status_bits & LORA_RADIO_TASK_TX_BIT) {
264+
lora_enable_isr_ = false;
265+
// take packet by packet
266+
while (lora_radio_tx_queue_index_.size() > 0) {
267+
// take packet size and read it
268+
int tx_bytes_cnt = lora_radio_tx_queue_index_.shift();
269+
for (int i = 0; i < tx_bytes_cnt; i++) {
270+
lora_radio_tx_buf_[i] = lora_radio_tx_queue_.shift();
271+
}
272+
// transmit packet
273+
int lora_radio_state = lora_radio_.transmit(lora_radio_tx_buf_, tx_bytes_cnt);
274+
if (lora_radio_state != RADIOLIB_ERR_NONE) {
275+
LOG_ERROR("Lora radio transmit failed:", lora_radio_state);
276+
}
277+
LOG_DEBUG("Transmitted packet", tx_bytes_cnt);
278+
vTaskDelay(1);
279+
} // packet transmit loop
280+
281+
// switch to receive after all transmitted
282+
int lora_radio_state = lora_radio_.startReceive();
283+
if (lora_radio_state != RADIOLIB_ERR_NONE) {
284+
LOG_ERROR("Start receive error: ", lora_radio_state);
285+
}
286+
lora_enable_isr_ = true;
287+
light_sleep_reset();
288+
} // lora tx
289+
} // task loop
290+
}
291+
292+
// audio record/playback encode/decode task
293+
void audio_task(void *param) {
294+
LOG_INFO("Audio task started");
295+
296+
// construct codec2
297+
c2_ = codec2_create(AUDIO_CODEC2_MODE);
298+
if (c2_ == NULL) {
299+
LOG_ERROR("Failed to create codec2");
300+
return;
301+
} else {
302+
c2_samples_per_frame_ = codec2_samples_per_frame(c2_);
303+
c2_bytes_per_frame_ = codec2_bytes_per_frame(c2_);
304+
c2_samples_ = (int16_t*)malloc(sizeof(int16_t) * c2_samples_per_frame_);
305+
c2_bits_ = (uint8_t*)malloc(sizeof(uint8_t) * c2_bytes_per_frame_);
306+
LOG_INFO("C2 initialized", c2_samples_per_frame_, c2_bytes_per_frame_);
307+
}
308+
309+
// wait for data notification, decode frames and playback
310+
size_t bytes_read, bytes_written;
311+
while(true) {
312+
uint32_t audio_bits = 0;
313+
xTaskNotifyWaitIndexed(0, 0x00, ULONG_MAX, &audio_bits, portMAX_DELAY);
314+
315+
LOG_DEBUG("Audio task bits", audio_bits);
316+
317+
// audio rx-decode-playback
318+
if (audio_bits & AUDIO_TASK_PLAY_BIT) {
319+
LOG_DEBUG("Playing audio");
320+
// while rx frames are available and button is not pressed
321+
while (!btn_pressed_ && lora_radio_rx_queue_index_.size() > 0) {
322+
int packet_size = lora_radio_rx_queue_index_.shift();
323+
LOG_DEBUG("Playing packet", packet_size);
324+
// split by frame, decode and play
325+
for (int i = 0; i < packet_size; i++) {
326+
c2_bits_[i % c2_bytes_per_frame_] = lora_radio_rx_queue_.shift();
327+
if (i % c2_bytes_per_frame_ == c2_bytes_per_frame_ - 1) {
328+
codec2_decode(c2_, c2_samples_, c2_bits_);
329+
i2s_write(I2S_NUM_0, c2_samples_, sizeof(uint16_t) * c2_samples_per_frame_, &bytes_written, portMAX_DELAY);
330+
vTaskDelay(1);
331+
}
332+
}
333+
} // while rx data available
334+
} // audio decode playback
335+
// audio record-encode-tx
336+
else if (audio_bits & AUDIO_TASK_RECORD_BIT) {
337+
LOG_DEBUG("Recording audio");
338+
int packet_size = 0;
339+
// record while button is pressed
340+
while (btn_pressed_) {
341+
// send packet if enough audio encoded frames are accumulated
342+
if (packet_size + c2_bytes_per_frame_ > AUDIO_MAX_PACKET_SIZE) {
343+
LOG_DEBUG("Recorded packet", packet_size);
344+
lora_radio_tx_queue_index_.push(packet_size);
345+
uint32_t lora_tx_bits = LORA_RADIO_TASK_TX_BIT;
346+
xTaskNotify(lora_task_, lora_tx_bits, eSetBits);
347+
packet_size = 0;
348+
}
349+
// read and encode one sample
350+
size_t bytes_read;
351+
i2s_read(I2S_NUM_1, c2_samples_, sizeof(uint16_t) * c2_samples_per_frame_, &bytes_read, portMAX_DELAY);
352+
codec2_encode(c2_, c2_bits_, c2_samples_);
353+
for (int i = 0; i < c2_bytes_per_frame_; i++) {
354+
lora_radio_tx_queue_.push(c2_bits_[i]);
355+
}
356+
packet_size += c2_bytes_per_frame_;
357+
vTaskDelay(1);
358+
} // btn_pressed_
359+
// send remaining tail audio encoded samples
360+
if (packet_size > 0) {
361+
LOG_DEBUG("Recorded packet", packet_size);
362+
lora_radio_tx_queue_index_.push(packet_size);
363+
uint32_t lora_tx_bits = LORA_RADIO_TASK_TX_BIT;
364+
xTaskNotify(lora_task_, lora_tx_bits, eSetBits);
365+
packet_size = 0;
366+
}
367+
vTaskDelay(1);
368+
} // task bit
369+
}
370+
}
371+
372+
void loop() {
373+
// button
374+
if (digitalRead(PTTBTN_PIN) == LOW && !btn_pressed_) {
375+
LOG_DEBUG("PTT pushed, start TX");
376+
btn_pressed_ = true;
377+
// notify to start recording
378+
uint32_t audio_bits = AUDIO_TASK_RECORD_BIT;
379+
xTaskNotify(audio_task_, audio_bits, eSetBits);
380+
} else if (digitalRead(PTTBTN_PIN) == HIGH && btn_pressed_) {
381+
LOG_DEBUG("PTT released");
382+
btn_pressed_ = false;
383+
}
384+
light_sleep_timer_.tick();
385+
delay(50);
386+
}
387+

0 commit comments

Comments
 (0)