arduino-audio-tools
Loading...
Searching...
No Matches
SPIAudioSlave.h
Go to the documentation of this file.
1#pragma once
2
3#include <SPI.h>
4
10#include "SPIAudioCommand.h"
11
12#if defined(ESP32)
13#include "driver/spi_slave.h"
14#endif
15
16namespace audio_tools {
17
32 size_t max_command_payload = 1024;
34 size_t max_mime_len = 20;
35#if defined(ESP32)
36 bool use_hw_slave = true;
37#if defined(SPI2_HOST)
39#elif defined(HSPI_HOST)
41#elif defined(SPI3_HOST)
43#elif defined(VSPI_HOST)
45#else
47#endif
48 int dma_chan = 1;
49 int pin_mosi = 23;
50 int pin_miso = 19;
51 int pin_sclk = 18;
52 int pin_cs = 5;
55 int queue_size = 4;
56#endif
57 void log() const {
58 LOGI(
59 "SPIAudioSlaveConfig: sample_rate=%d, channels=%d, bits_per_sample=%d, "
60 "max_command_payload=%d, max_response_payload=%d, max_mime_len=%d ",
61 (int)sample_rate, (int)channels, (int)bits_per_sample,
63#if defined(ESP32)
64 LOGI(
65 "ESP32 SPI Slave Config: use_hw_slave=%s, host=%d, dma_chan=%d, "
66 "pin_mosi=%d, pin_miso=%d, pin_sclk=%d, pin_cs=%d, queue_size=%d",
67 use_hw_slave ? "true" : "false", host, dma_chan, pin_mosi, pin_miso,
69#endif
70 }
71};
72
236 public:
244
247
250 config = cfg;
251 config.log();
252 setAudioInfo(cfg);
256
257 if (p_buffer == nullptr) return false;
258 p_buffer->reset();
262 mime = "";
263
265
266#if defined(ESP32)
267 if (config.use_hw_slave) {
269 memset(&bus_cfg, 0, sizeof(bus_cfg));
270 bus_cfg.mosi_io_num = config.pin_mosi;
271 bus_cfg.miso_io_num = config.pin_miso;
272 bus_cfg.sclk_io_num = config.pin_sclk;
273 bus_cfg.quadwp_io_num = -1;
274 bus_cfg.quadhd_io_num = -1;
275
277 memset(&slave_cfg, 0, sizeof(slave_cfg));
278 slave_cfg.mode = 0;
279 slave_cfg.spics_io_num = config.pin_cs;
280 slave_cfg.queue_size = config.queue_size;
281 slave_cfg.flags = 0;
282
285 if (rc != ESP_OK) {
286 active = false;
287 return false;
288 }
289 esp32_spi_active = true;
290 spi_tx_byte = 0;
291 }
292#endif
293
294 active = true;
295 return true;
296 }
297
299 void end() override {
300#if defined(ESP32)
301 if (esp32_spi_active) {
303 esp32_spi_active = false;
304 }
305#endif
306 active = false;
307 if (p_buffer != nullptr) p_buffer->reset();
309 }
310
312 bool isActive() const { return active; }
313
319
323 bool process() {
324#if defined(ESP32)
325 if (!esp32_spi_active) return false;
326
328 memset(&t, 0, sizeof(t));
329 t.length = 8;
330 t.tx_buffer = &spi_tx_byte;
331 t.rx_buffer = &spi_rx_byte;
332
334 if (rc != ESP_OK) return false;
335
337 // If the last Rx byte triggered a Rx→TxStatus transition, the returned
338 // value was 0 (Rx state return). Pre-advance through TxStatus now so that
339 // spi_tx_byte holds tx_status before the next spi_slave_transmit call,
340 // eliminating the 1-byte pipeline shift in the response stream.
341 if (state == TxStatus) {
343 }
344 return true;
345#else
346 return false;
347#endif
348 }
349
352 switch (state) {
353 case RxCmd:
354 current_cmd = in;
356 rx_pos = 0;
357 tx_pos = 0;
358 tx_len = 0;
359 tx_status = Ok;
360 state = RxLenLo;
361 return 0;
362
363 case RxLenLo:
365 state = RxLenHi;
366 return 0;
367
368 case RxLenHi:
369 expected_payload_len |= static_cast<uint16_t>(in) << 8;
370 if (expected_payload_len == 0) {
372 state = TxStatus;
373 } else {
374 rx_pos = 0;
376 }
377 return 0;
378
379 case RxPayload:
380 if (rx_pos < request_payload.size()) {
382 }
383 rx_pos++;
386 state = TxStatus;
387 }
388 return 0;
389
390 case TxStatus:
391 state = TxLenLo;
392 return tx_status;
393
394 case TxLenLo:
395 state = TxLenHi;
396 return static_cast<uint8_t>(tx_len & 0xFF);
397
398 case TxLenHi: {
399 uint8_t out = static_cast<uint8_t>((tx_len >> 8) & 0xFF);
400 if (tx_len == 0) {
401 state = RxCmd;
402 } else {
404 }
405 return out;
406 }
407
408 case TxPayload: {
410 tx_pos++;
411 if (tx_pos >= tx_len) {
412 state = RxCmd;
413 }
414 return out;
415 }
416 }
418 return 0;
419 }
420
424 uint16_t* response_frame_len = nullptr) {
425 if (request_frame == nullptr || request_frame_len < 3 ||
426 response_frame == nullptr || response_frame_max < 3) {
427 return false;
428 }
429
430 uint8_t cmd = request_frame[0];
431 uint16_t payload_len = static_cast<uint16_t>(request_frame[1]) |
432 (static_cast<uint16_t>(request_frame[2]) << 8);
433 if (static_cast<uint32_t>(payload_len) + 3 > request_frame_len) {
434 return false;
435 }
436
437 uint16_t out_len = 0;
438 uint8_t status = processCommand(
439 static_cast<SPIAudioCommand>(cmd), request_frame + 3, payload_len,
441
442 response_frame[0] = status;
443 response_frame[1] = static_cast<uint8_t>(out_len & 0xFF);
444 response_frame[2] = static_cast<uint8_t>((out_len >> 8) & 0xFF);
445
446 if (response_frame_len != nullptr) {
448 }
449 return true;
450 }
451
452 // Audio data access
454 int available() override {
455 return p_buffer == nullptr ? 0 : p_buffer->available();
456 }
457
459 int availableForWrite() override {
460 return p_buffer == nullptr ? 0 : p_buffer->availableForWrite();
461 }
462
464 size_t readBytes(uint8_t* data, size_t len) override {
465 if (!active || data == nullptr || len == 0) return 0;
466 if (p_buffer == nullptr) return 0;
467 int to_read = min(static_cast<int>(len), p_buffer->available());
468 if (to_read <= 0) return 0;
469 return p_buffer->readArray(data, to_read);
470 }
471
473 size_t write(const uint8_t* data, size_t len) override {
474 if (!active || data == nullptr || len == 0) return 0;
475 if (p_buffer == nullptr) return 0;
476 int to_write = min(static_cast<int>(len), p_buffer->availableForWrite());
477 if (to_write <= 0) return 0;
478 return p_buffer->writeArray(data, to_write);
479 }
480
482 void clearBuffer() {
483 if (p_buffer != nullptr) p_buffer->reset();
484 }
485
487 const char* mimeType() { return mime.c_str(); }
488
489 protected:
500
502 bool active = false;
507
508#if defined(ESP32)
509 bool esp32_spi_active = false;
512#endif
513
514 // protocol state
522
525 state = RxCmd;
526 current_cmd = 0;
528 rx_pos = 0;
529 tx_pos = 0;
530 tx_len = 0;
531 tx_status = Ok;
532 }
533
538 tx_len = 0;
539 tx_pos = 0;
540 return;
541 }
542
543 uint16_t out_len = 0;
547 static_cast<uint16_t>(response_payload.size()), out_len);
548 tx_len = out_len;
549 tx_pos = 0;
550 }
551
556 response_len = 0;
557
558 switch (cmd) {
560 if (payload_len == 0 || payload == nullptr) return InvalidPayload;
561 size_t max_len = config.max_mime_len > 0 ? config.max_mime_len - 1 : 0;
562 size_t n = min(static_cast<size_t>(payload_len), max_len);
563 if (n > 0 && payload[n - 1] == '\0') {
564 --n;
565 }
566 mime.copyFrom(reinterpret_cast<const char*>(payload), n, max_len);
567 return Ok;
568 }
569
571 if (payload_len != 6 || payload == nullptr) return InvalidPayload;
574 info.channels = payload[4];
577 return Ok;
578 }
579
581 if (payload_len == 0 || payload == nullptr) return Ok;
582 if (p_buffer == nullptr) return InternalError;
583 int written = p_buffer->writeArray(payload, payload_len);
584 return written == static_cast<int>(payload_len) ? Ok : BufferOverflow;
585 }
586
588 if (response == nullptr || response_max < 4) return InternalError;
589 if (p_buffer == nullptr) return InternalError;
591 writeU32LE(response, static_cast<uint32_t>(avail > 0 ? avail : 0));
592 response_len = 4;
593 return Ok;
594 }
595
597 if (p_buffer != nullptr) p_buffer->reset();
598 return Ok;
599 }
600
602 if (response == nullptr || response_max < 4) return InternalError;
603 if (p_buffer == nullptr) return InternalError;
604 int filled = p_buffer->available();
605 writeU32LE(response, static_cast<uint32_t>(filled > 0 ? filled : 0));
606 response_len = 4;
607 return Ok;
608 }
609
610 default:
611 return InvalidCommand;
612 }
613 }
614
616 static void writeU32LE(uint8_t* out, uint32_t value) {
617 out[0] = static_cast<uint8_t>(value & 0xFF);
618 out[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
619 out[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
620 out[3] = static_cast<uint8_t>((value >> 24) & 0xFF);
621 }
622
624 static uint32_t readU32LE(const uint8_t* in) {
625 return static_cast<uint32_t>(in[0]) | (static_cast<uint32_t>(in[1]) << 8) |
626 (static_cast<uint32_t>(in[2]) << 16) |
627 (static_cast<uint32_t>(in[3]) << 24);
628 }
629};
630
631} // namespace audio_tools
#define LOGI(...)
Definition AudioLoggerIDF.h:28
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:123
AudioInfo info
Definition BaseStream.h:174
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:131
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition BaseStream.h:154
Shared functionality of all buffers.
Definition Buffers.h:22
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:33
virtual void reset()=0
clears the buffer
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
SPI Audio Slave endpoint matching SPIAudioMaster protocol.
Definition SPIAudioSlave.h:235
bool active
Definition SPIAudioSlave.h:502
uint8_t tx_status
Definition SPIAudioSlave.h:521
uint16_t expected_payload_len
Definition SPIAudioSlave.h:517
void clearBuffer()
Clears local buffered audio data.
Definition SPIAudioSlave.h:482
BaseBuffer< uint8_t > * p_buffer
Definition SPIAudioSlave.h:503
uint8_t spi_tx_byte
Definition SPIAudioSlave.h:510
bool processFrame(const uint8_t *request_frame, uint16_t request_frame_len, uint8_t *response_frame, uint16_t response_frame_max, uint16_t *response_frame_len=nullptr)
Frame-based helper (same wire format as master, useful for tests)
Definition SPIAudioSlave.h:422
bool isActive() const
Returns true when the slave is active.
Definition SPIAudioSlave.h:312
static void writeU32LE(uint8_t *out, uint32_t value)
Writes a 32-bit little-endian integer to the target buffer.
Definition SPIAudioSlave.h:616
const char * mimeType()
Returns the currently configured mime type.
Definition SPIAudioSlave.h:487
SPIAudioSlaveConfig config
Definition SPIAudioSlave.h:501
uint8_t current_cmd
Definition SPIAudioSlave.h:516
size_t readBytes(uint8_t *data, size_t len) override
Reads buffered audio bytes.
Definition SPIAudioSlave.h:464
bool esp32_spi_active
Definition SPIAudioSlave.h:509
uint16_t rx_pos
Definition SPIAudioSlave.h:518
static uint32_t readU32LE(const uint8_t *in)
Reads a 32-bit little-endian integer from the source buffer.
Definition SPIAudioSlave.h:624
uint8_t spi_rx_byte
Definition SPIAudioSlave.h:511
void end() override
Stops the slave and releases all resources.
Definition SPIAudioSlave.h:299
int available() override
Returns readable bytes currently buffered.
Definition SPIAudioSlave.h:454
void resetProtocolState()
Resets internal request/response parser state.
Definition SPIAudioSlave.h:524
size_t write(const uint8_t *data, size_t len) override
Writes bytes directly into the local slave buffer.
Definition SPIAudioSlave.h:473
StatusCode
Definition SPIAudioSlave.h:237
@ Ok
Definition SPIAudioSlave.h:238
@ BufferOverflow
Definition SPIAudioSlave.h:241
@ InvalidPayload
Definition SPIAudioSlave.h:240
@ InvalidCommand
Definition SPIAudioSlave.h:239
@ InternalError
Definition SPIAudioSlave.h:242
int availableForWrite() override
Returns remaining writable bytes in the input buffer.
Definition SPIAudioSlave.h:459
uint8_t processCommand(SPIAudioCommand cmd, const uint8_t *payload, uint16_t payload_len, uint8_t *response, uint16_t response_max, uint16_t &response_len)
Executes one protocol command and writes response payload.
Definition SPIAudioSlave.h:553
Vector< uint8_t > request_payload
Definition SPIAudioSlave.h:504
Vector< uint8_t > response_payload
Definition SPIAudioSlave.h:505
uint16_t tx_len
Definition SPIAudioSlave.h:520
void prepareResponse()
Evaluates current request payload and prepares response payload.
Definition SPIAudioSlave.h:535
ProtocolState
Definition SPIAudioSlave.h:490
@ RxCmd
Definition SPIAudioSlave.h:491
@ RxLenHi
Definition SPIAudioSlave.h:493
@ TxPayload
Definition SPIAudioSlave.h:498
@ RxPayload
Definition SPIAudioSlave.h:494
@ TxLenHi
Definition SPIAudioSlave.h:497
@ RxLenLo
Definition SPIAudioSlave.h:492
@ TxStatus
Definition SPIAudioSlave.h:495
@ TxLenLo
Definition SPIAudioSlave.h:496
ProtocolState state
Definition SPIAudioSlave.h:515
bool begin(SPIAudioSlaveConfig cfg=SPIAudioSlaveConfig())
Initializes protocol state, buffers and optional HW slave support.
Definition SPIAudioSlave.h:249
void setAudioInfo(AudioInfo info) override
Updates current audio format and mirrors it into config.
Definition SPIAudioSlave.h:315
uint16_t tx_pos
Definition SPIAudioSlave.h:519
SPIAudioSlave(BaseBuffer< uint8_t > &buffer)
Constructor using an externally provided buffer implementation.
Definition SPIAudioSlave.h:246
uint8_t onTransfer(uint8_t in)
Byte-wise SPI handler. Call this for each received SPI byte.
Definition SPIAudioSlave.h:351
bool process()
Definition SPIAudioSlave.h:323
Str mime
Definition SPIAudioSlave.h:506
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition Str.h:24
void copyFrom(const char *source, int len, int maxlen=0)
assigns a memory buffer
Definition Str.h:96
void setCapacity(size_t newLen)
Definition Str.h:86
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
bool resize(int newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
SPIAudioCommand
SPI command ids used by SPIAudioMaster.
Definition SPIAudioCommand.h:19
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:512
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:105
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61
Configuration for SPIAudioSlave.
Definition SPIAudioSlave.h:31
int pin_mosi
Definition SPIAudioSlave.h:49
int pin_sclk
Definition SPIAudioSlave.h:51
int pin_miso
Definition SPIAudioSlave.h:50
int dma_chan
Definition SPIAudioSlave.h:48
int pin_cs
Definition SPIAudioSlave.h:52
void log() const
Definition SPIAudioSlave.h:57
bool use_hw_slave
Definition SPIAudioSlave.h:36
size_t max_command_payload
Definition SPIAudioSlave.h:32
int queue_size
Definition SPIAudioSlave.h:55
size_t max_response_payload
Definition SPIAudioSlave.h:33
spi_host_device_t host
Definition SPIAudioSlave.h:46
size_t max_mime_len
Definition SPIAudioSlave.h:34