201 lines
6.6 KiB
C
201 lines
6.6 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "driver/spi_slave.h"
|
|
#include "esp_log.h"
|
|
#include "esp_err.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_now.h"
|
|
#include "esp_netif.h"
|
|
#include "nvs_flash.h"
|
|
#include "esp_timer.h"
|
|
|
|
static const char *TAG = "SPI_SLAVE";
|
|
|
|
// SPI Slave configuration for ESP32-C3
|
|
#define SPI_SLAVE_HOST SPI2_HOST
|
|
#define SPI_SLAVE_MOSI 10
|
|
#define SPI_SLAVE_MISO 9
|
|
#define SPI_SLAVE_SCLK 8
|
|
#define SPI_SLAVE_CS 7
|
|
#define SPI_SLAVE_DMA_CHAN SPI_DMA_CH_AUTO
|
|
|
|
// Buffer size for SPI communication
|
|
#define BUFFER_SIZE 256
|
|
|
|
// ESP-NOW configuration
|
|
#define ESPNOW_MAXDELAY 512
|
|
#define ESPNOW_QUEUE_SIZE 6
|
|
|
|
// Max ESP-NOW payload length; keep within safe limit
|
|
#define ESPNOW_MAX_PAYLOAD 200
|
|
|
|
// SPI slave receive buffer
|
|
static uint8_t recvbuf[BUFFER_SIZE];
|
|
static uint8_t sendbuf[BUFFER_SIZE];
|
|
|
|
// ESP-NOW broadcast MAC address (broadcast to all devices)
|
|
static uint8_t broadcast_mac[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
|
|
// SPI slave configuration structure
|
|
static spi_slave_transaction_t slave_trans;
|
|
|
|
// ESP-NOW callback functions
|
|
static void espnow_send_cb(const esp_now_send_info_t *send_info, esp_now_send_status_t status)
|
|
{
|
|
ESP_LOGI(TAG, "ESP-NOW send status: %s", status == ESP_NOW_SEND_SUCCESS ? "SUCCESS" : "FAIL");
|
|
}
|
|
|
|
static void espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len)
|
|
{
|
|
ESP_LOGI(TAG, "ESP-NOW received %d bytes from %02x:%02x:%02x:%02x:%02x:%02x",
|
|
len, recv_info->src_addr[0], recv_info->src_addr[1], recv_info->src_addr[2],
|
|
recv_info->src_addr[3], recv_info->src_addr[4], recv_info->src_addr[5]);
|
|
}
|
|
|
|
// Initialize ESP-NOW
|
|
static esp_err_t espnow_init(void)
|
|
{
|
|
// Initialize NVS
|
|
esp_err_t ret = nvs_flash_init();
|
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
ret = nvs_flash_init();
|
|
}
|
|
ESP_ERROR_CHECK(ret);
|
|
|
|
// Initialize network interface
|
|
ESP_ERROR_CHECK(esp_netif_init());
|
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
|
|
// Initialize WiFi
|
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
ESP_ERROR_CHECK(esp_wifi_start());
|
|
// Keep defaults (no explicit channel pinning) and no power-save changes
|
|
|
|
// Initialize ESP-NOW
|
|
ESP_ERROR_CHECK(esp_now_init());
|
|
ESP_ERROR_CHECK(esp_now_register_send_cb(espnow_send_cb));
|
|
ESP_ERROR_CHECK(esp_now_register_recv_cb(espnow_recv_cb));
|
|
|
|
// Add peer (broadcast)
|
|
esp_now_peer_info_t peer;
|
|
memset(&peer, 0, sizeof(esp_now_peer_info_t));
|
|
memcpy(peer.peer_addr, broadcast_mac, ESP_NOW_ETH_ALEN);
|
|
// Use 0 so ESP-NOW uses the current primary channel (default behavior)
|
|
peer.channel = 0;
|
|
peer.ifidx = WIFI_IF_STA;
|
|
peer.encrypt = false;
|
|
ESP_ERROR_CHECK(esp_now_add_peer(&peer));
|
|
|
|
ESP_LOGI(TAG, "ESP-NOW initialized successfully");
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Broadcast raw SPI bytes via ESP-NOW
|
|
static void broadcast_spi_data(uint8_t *data, uint8_t length)
|
|
{
|
|
uint8_t send_len = length > ESPNOW_MAX_PAYLOAD ? ESPNOW_MAX_PAYLOAD : length;
|
|
|
|
// Send raw payload directly; receiver can json.loads if payload is JSON
|
|
esp_err_t result = esp_now_send(broadcast_mac, data, send_len);
|
|
if (result != ESP_OK) {
|
|
ESP_LOGE(TAG, "ESP-NOW send failed: %s", esp_err_to_name(result));
|
|
} else {
|
|
ESP_LOGI(TAG, "Broadcasting %d bytes via ESP-NOW", send_len);
|
|
}
|
|
}
|
|
|
|
void app_main(void)
|
|
{
|
|
ESP_LOGI(TAG, "Starting SPI Slave with ESP-NOW example");
|
|
|
|
// Initialize ESP-NOW first
|
|
esp_err_t ret = espnow_init();
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "ESP-NOW initialization failed: %s", esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
|
|
// Configure SPI slave bus
|
|
spi_bus_config_t buscfg = {
|
|
.mosi_io_num = SPI_SLAVE_MOSI,
|
|
.miso_io_num = SPI_SLAVE_MISO,
|
|
.sclk_io_num = SPI_SLAVE_SCLK,
|
|
.quadwp_io_num = -1,
|
|
.quadhd_io_num = -1,
|
|
.max_transfer_sz = BUFFER_SIZE,
|
|
};
|
|
|
|
// Configure SPI slave interface
|
|
spi_slave_interface_config_t slvcfg = {
|
|
.mode = 0, // SPI mode 0 (CPOL=0, CPHA=0)
|
|
.spics_io_num = SPI_SLAVE_CS,
|
|
.queue_size = 3,
|
|
.flags = 0,
|
|
};
|
|
|
|
// Initialize SPI slave bus
|
|
ret = spi_slave_initialize(SPI_SLAVE_HOST, &buscfg, &slvcfg, SPI_SLAVE_DMA_CHAN);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to initialize SPI slave: %s", esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "SPI Slave initialized successfully");
|
|
ESP_LOGI(TAG, "MOSI: GPIO%d, MISO: GPIO%d, SCLK: GPIO%d, CS: GPIO%d",
|
|
SPI_SLAVE_MOSI, SPI_SLAVE_MISO, SPI_SLAVE_SCLK, SPI_SLAVE_CS);
|
|
|
|
// Prepare send buffer with some test data
|
|
for (int i = 0; i < BUFFER_SIZE; i++) {
|
|
sendbuf[i] = i;
|
|
}
|
|
|
|
// Main loop
|
|
while (1) {
|
|
// Clear receive buffer
|
|
memset(recvbuf, 0, BUFFER_SIZE);
|
|
|
|
// Prepare transaction
|
|
slave_trans.length = BUFFER_SIZE * 8; // Length in bits
|
|
slave_trans.trans_len = 0;
|
|
slave_trans.tx_buffer = sendbuf;
|
|
slave_trans.rx_buffer = recvbuf;
|
|
|
|
// Wait for SPI master to initiate transaction
|
|
ret = spi_slave_transmit(SPI_SLAVE_HOST, &slave_trans, portMAX_DELAY);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "SPI slave transmit failed: %s", esp_err_to_name(ret));
|
|
continue;
|
|
}
|
|
|
|
// Print received data
|
|
uint8_t received_bytes = slave_trans.trans_len / 8;
|
|
ESP_LOGI(TAG, "Received %d bytes:", received_bytes);
|
|
for (int i = 0; i < received_bytes && i < 32; i++) {
|
|
printf("0x%02x ", recvbuf[i]);
|
|
if ((i + 1) % 16 == 0) printf("\n");
|
|
}
|
|
if (received_bytes % 16 != 0) printf("\n");
|
|
|
|
// Log received payload as UTF-8 string (for JSON visibility), then broadcast as-is
|
|
if (received_bytes > 0) {
|
|
{
|
|
uint8_t to_copy = received_bytes > ESPNOW_MAX_PAYLOAD ? ESPNOW_MAX_PAYLOAD : received_bytes;
|
|
char json_buf[ESPNOW_MAX_PAYLOAD + 1];
|
|
memcpy(json_buf, recvbuf, to_copy);
|
|
json_buf[to_copy] = '\0';
|
|
ESP_LOGI(TAG, "SPI RX (len=%d): %s", received_bytes, json_buf);
|
|
}
|
|
broadcast_spi_data(recvbuf, received_bytes);
|
|
}
|
|
|
|
// Small delay before next transaction
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
}
|