Init
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -y && apt-get install udev -y
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Directory metadata
|
||||
.directory
|
||||
|
||||
# Temporary files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
*.bak
|
||||
*.tmp
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Build artifacts and directories
|
||||
**/build/
|
||||
build/
|
||||
*.o
|
||||
*.a
|
||||
*.out
|
||||
*.exe # For any host-side utilities compiled on Windows
|
||||
|
||||
# ESP-IDF specific build outputs
|
||||
*.bin
|
||||
*.elf
|
||||
*.map
|
||||
flasher_args.json # Generated in build directory
|
||||
sdkconfig.old
|
||||
sdkconfig
|
||||
|
||||
# ESP-IDF dependencies
|
||||
# For older versions or manual component management
|
||||
/components/.idf/
|
||||
**/components/.idf/
|
||||
# For modern ESP-IDF component manager
|
||||
managed_components/
|
||||
# If ESP-IDF tools are installed/referenced locally to the project
|
||||
.espressif/
|
||||
|
||||
# CMake generated files
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CTestTestfile.cmake
|
||||
|
||||
# Python environment files
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
dist/
|
||||
|
||||
# Virtual environment folders
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
|
||||
# Language Servers
|
||||
.clangd/
|
||||
.ccls-cache/
|
||||
compile_commands.json
|
||||
|
||||
# Windows specific
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# User-specific configuration files
|
||||
*.user
|
||||
*.workspace # General workspace files, can be from various tools
|
||||
*.suo # Visual Studio Solution User Options
|
||||
*.sln.docstates # Visual Studio
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ESP-IDF",
|
||||
"compilerPath": "${default}",
|
||||
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdbtarget",
|
||||
"request": "attach",
|
||||
"name": "Eclipse CDT GDB Adapter"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"C_Cpp.intelliSenseEngine": "default",
|
||||
"idf.currentSetup": "/home/bruno/.espressif/v6.0/esp-idf",
|
||||
"idf.flashType": "UART",
|
||||
"idf.port": "/dev/ttyUSB0"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(rfkamera)
|
||||
@@ -0,0 +1,7 @@
|
||||
set(component_srcs "mirf.c")
|
||||
|
||||
idf_component_register(
|
||||
SRCS "${component_srcs}"
|
||||
PRIV_REQUIRES driver esp_driver_spi esp_driver_gpio
|
||||
INCLUDE_DIRS "."
|
||||
)
|
||||
@@ -0,0 +1,130 @@
|
||||
menu "nRF24L01 Configuration"
|
||||
|
||||
config GPIO_RANGE_MAX
|
||||
int
|
||||
default 33 if IDF_TARGET_ESP32
|
||||
default 46 if IDF_TARGET_ESP32S2
|
||||
default 48 if IDF_TARGET_ESP32S3
|
||||
default 18 if IDF_TARGET_ESP32C2
|
||||
default 19 if IDF_TARGET_ESP32C3
|
||||
default 30 if IDF_TARGET_ESP32C6
|
||||
|
||||
config RADIO_CHANNEL
|
||||
int "Channel number"
|
||||
range 0 127
|
||||
default 90
|
||||
help
|
||||
Channel number.
|
||||
|
||||
config MISO_GPIO
|
||||
int "MISO GPIO number"
|
||||
range 0 GPIO_RANGE_MAX
|
||||
default 19 if IDF_TARGET_ESP32
|
||||
default 37 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 4 # C3 and others
|
||||
help
|
||||
GPIO number (IOxx) to SPI MISO.
|
||||
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to MISO.
|
||||
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
|
||||
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
|
||||
|
||||
config SCLK_GPIO
|
||||
int "SCLK GPIO number"
|
||||
range 0 GPIO_RANGE_MAX
|
||||
default 18 if IDF_TARGET_ESP32
|
||||
default 36 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 3 # C3 and others
|
||||
help
|
||||
GPIO number (IOxx) to SPI SCLK.
|
||||
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to SCLK.
|
||||
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
|
||||
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
|
||||
|
||||
config MOSI_GPIO
|
||||
int "MOSI GPIO number"
|
||||
range 0 GPIO_RANGE_MAX
|
||||
default 23 if IDF_TARGET_ESP32
|
||||
default 35 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 2 # C3 and others
|
||||
help
|
||||
GPIO number (IOxx) to SPI MOSI.
|
||||
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to MOSI.
|
||||
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
|
||||
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
|
||||
|
||||
config CE_GPIO
|
||||
int "CE GPIO number"
|
||||
range 0 GPIO_RANGE_MAX
|
||||
default 16 if IDF_TARGET_ESP32
|
||||
default 34 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 1 # C3 and others
|
||||
help
|
||||
GPIO number (IOxx) to CE.
|
||||
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to CE.
|
||||
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
|
||||
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
|
||||
|
||||
config CSN_GPIO
|
||||
int "CSN GPIO number"
|
||||
range 0 GPIO_RANGE_MAX
|
||||
default 17 if IDF_TARGET_ESP32
|
||||
default 33 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 0 # C3 and others
|
||||
help
|
||||
GPIO number (IOxx) to CSN.
|
||||
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to CSN.
|
||||
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
|
||||
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
|
||||
|
||||
choice SPI_HOST
|
||||
prompt "SPI peripheral that controls this bus"
|
||||
default SPI2_HOST
|
||||
help
|
||||
Select SPI peripheral that controls this bus.
|
||||
config SPI2_HOST
|
||||
bool "SPI2_HOST"
|
||||
help
|
||||
Use SPI2_HOST. This is also called HSPI_HOST.
|
||||
config SPI3_HOST
|
||||
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
bool "SPI3_HOST"
|
||||
help
|
||||
USE SPI3_HOST. This is also called VSPI_HOST
|
||||
endchoice
|
||||
|
||||
config ADVANCED
|
||||
bool "Enable Advanced Setting"
|
||||
default false
|
||||
help
|
||||
Enable Advanced Setting.
|
||||
|
||||
choice RF_RATIO
|
||||
depends on ADVANCED
|
||||
prompt "RF Data Ratio"
|
||||
default RF_RATIO_2M
|
||||
help
|
||||
Select RF Data Ratio.
|
||||
config RF_RATIO_2M
|
||||
bool "2Mbps"
|
||||
help
|
||||
RF Data Ratio is 2Mbps.
|
||||
config RF_RATIO_1M
|
||||
bool "1Mbps"
|
||||
help
|
||||
RF Data Ratio is 1Mbps.
|
||||
config RF_RATIO_250K
|
||||
bool "250Kbps"
|
||||
help
|
||||
RF Data Ratio is 250Kbps.
|
||||
endchoice
|
||||
|
||||
config RETRANSMIT_DELAY
|
||||
depends on ADVANCED
|
||||
int "Auto Retransmit Delay"
|
||||
range 0 15
|
||||
default 0
|
||||
help
|
||||
Set Auto Retransmit Delay.
|
||||
Delay = value * 250us.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,677 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_master.h>
|
||||
|
||||
#include "mirf.h"
|
||||
|
||||
#define TAG "NRF24"
|
||||
|
||||
#define HOST_ID SPI2_HOST
|
||||
|
||||
// static const int SPI_Frequency = 4000000; // Stable even with a long jumper
|
||||
// cable static const int SPI_Frequency = 6000000; static const int
|
||||
// SPI_Frequency = 8000000; // Requires a short jumper cable
|
||||
static const int SPI_Frequency =
|
||||
10000000; // Unstable even with a short jumper cable
|
||||
|
||||
// const char rf24_datarates[][8] = {"1Mbps", "2Mbps", "250Kbps"};
|
||||
char rf24_datarates[][8] = {"1Mbps", "2Mbps", "250Kbps"};
|
||||
const char rf24_crclength[][10] = {"Disabled", "8 bits", "16 bits"};
|
||||
// const char rf24_pa_dbm[][8] = {"PA_MIN", "PA_LOW", "PA_HIGH", "PA_MAX"};
|
||||
char rf24_pa_dbm[][8] = {"PA_MIN", "PA_LOW", "PA_HIGH", "PA_MAX"};
|
||||
|
||||
void Nrf24_init(NRF24_t *dev) {
|
||||
esp_err_t ret;
|
||||
|
||||
ESP_LOGI(TAG, "CONFIG_MISO_GPIO=%d", CONFIG_MISO_GPIO);
|
||||
ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d", CONFIG_MOSI_GPIO);
|
||||
ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d", CONFIG_SCLK_GPIO);
|
||||
ESP_LOGI(TAG, "CONFIG_CE_GPIO=%d", CONFIG_CE_GPIO);
|
||||
ESP_LOGI(TAG, "CONFIG_CSN_GPIO=%d", CONFIG_CSN_GPIO);
|
||||
|
||||
// gpio_pad_select_gpio(CONFIG_CE_GPIO);
|
||||
gpio_reset_pin(CONFIG_CE_GPIO);
|
||||
gpio_set_direction(CONFIG_CE_GPIO, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(CONFIG_CE_GPIO, 0);
|
||||
|
||||
// gpio_pad_select_gpio(CONFIG_CSN_GPIO);
|
||||
gpio_reset_pin(CONFIG_CSN_GPIO);
|
||||
gpio_set_direction(CONFIG_CSN_GPIO, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(CONFIG_CSN_GPIO, 1);
|
||||
|
||||
spi_bus_config_t spi_bus_config = {.sclk_io_num = CONFIG_SCLK_GPIO,
|
||||
.mosi_io_num = CONFIG_MOSI_GPIO,
|
||||
.miso_io_num = CONFIG_MISO_GPIO,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1};
|
||||
|
||||
ret = spi_bus_initialize(HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO);
|
||||
ESP_LOGI(TAG, "spi_bus_initialize=%d", ret);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset(&devcfg, 0, sizeof(spi_device_interface_config_t));
|
||||
devcfg.clock_speed_hz = SPI_Frequency;
|
||||
// It does not work with hardware CS control.
|
||||
// devcfg.spics_io_num = csn_pin;
|
||||
// It does work with software CS control.
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = 7;
|
||||
devcfg.mode = 0;
|
||||
devcfg.flags = SPI_DEVICE_NO_DUMMY;
|
||||
|
||||
spi_device_handle_t handle;
|
||||
ret = spi_bus_add_device(HOST_ID, &devcfg, &handle);
|
||||
ESP_LOGI(TAG, "spi_bus_add_device=%d", ret);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
dev->cePin = CONFIG_CE_GPIO;
|
||||
dev->csnPin = CONFIG_CSN_GPIO;
|
||||
dev->channel = 1;
|
||||
dev->payload = 16;
|
||||
dev->_SPIHandle = handle;
|
||||
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
uint8_t bTmp[2] = {ACTIVATE, 0x73};
|
||||
spi_write_byte(dev, bTmp, 2); // Write cmd to flush tx fifo
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
|
||||
Nrf24_enableAckPayloadFeature(dev);
|
||||
}
|
||||
|
||||
void Nrf24_deinit(NRF24_t *dev) {
|
||||
memset(dev, 0, sizeof(NRF24_t));
|
||||
spi_bus_free(HOST_ID);
|
||||
}
|
||||
|
||||
bool spi_write_byte(NRF24_t *dev, uint8_t *Dataout, size_t DataLength) {
|
||||
spi_transaction_t SPITransaction;
|
||||
|
||||
if (DataLength > 0) {
|
||||
memset(&SPITransaction, 0, sizeof(spi_transaction_t));
|
||||
SPITransaction.length = DataLength * 8;
|
||||
SPITransaction.tx_buffer = Dataout;
|
||||
SPITransaction.rx_buffer = NULL;
|
||||
spi_device_transmit(dev->_SPIHandle, &SPITransaction);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool spi_read_byte(NRF24_t *dev, uint8_t *Datain, uint8_t *Dataout,
|
||||
size_t DataLength) {
|
||||
spi_transaction_t SPITransaction;
|
||||
|
||||
if (DataLength > 0) {
|
||||
memset(&SPITransaction, 0, sizeof(spi_transaction_t));
|
||||
SPITransaction.length = DataLength * 8;
|
||||
SPITransaction.tx_buffer = Dataout;
|
||||
SPITransaction.rx_buffer = Datain;
|
||||
spi_device_transmit(dev->_SPIHandle, &SPITransaction);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t spi_transfer(NRF24_t *dev, uint8_t address) {
|
||||
uint8_t datain[1];
|
||||
uint8_t dataout[1];
|
||||
dataout[0] = address;
|
||||
// spi_write_byte(dev, dataout, 1 );
|
||||
spi_read_byte(dev, datain, dataout, 1);
|
||||
return datain[0];
|
||||
}
|
||||
|
||||
void spi_csnHi(NRF24_t *dev) { gpio_set_level(dev->csnPin, 1); }
|
||||
|
||||
void spi_csnLow(NRF24_t *dev) { gpio_set_level(dev->csnPin, 0); }
|
||||
|
||||
// Sets the important registers in the MiRF module and powers the module
|
||||
// in receiving mode
|
||||
// NB: channel and payload must be set now.
|
||||
void Nrf24_config(NRF24_t *dev, uint8_t channel, uint8_t payload) {
|
||||
dev->channel = channel;
|
||||
dev->payload = payload;
|
||||
Nrf24_configRegister(dev, RF_CH, dev->channel); // Set RF channel
|
||||
Nrf24_configRegister(dev, RX_PW_P0,
|
||||
dev->payload); // Set length of incoming payload
|
||||
Nrf24_configRegister(dev, RX_PW_P1, dev->payload);
|
||||
Nrf24_configRegister(dev, SETUP_AW, 0x01);
|
||||
Nrf24_powerUpRx(dev); // Start receiver
|
||||
Nrf24_flushRx(dev);
|
||||
}
|
||||
|
||||
// Sets the receiving device address
|
||||
// void Nrf24_setRADDR(NRF24_t * dev, uint8_t * adr)
|
||||
esp_err_t Nrf24_setRADDR(NRF24_t *dev, uint8_t *adr) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
Nrf24_writeRegister(dev, RX_ADDR_P1, adr, mirf_ADDR_LEN);
|
||||
uint8_t buffer[5];
|
||||
Nrf24_readRegister(dev, RX_ADDR_P1, buffer, sizeof(buffer));
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]);
|
||||
if (adr[i] != buffer[i])
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Sets the transmitting device address
|
||||
// void Nrf24_setTADDR(NRF24_t * dev, uint8_t * adr)
|
||||
esp_err_t Nrf24_setTADDR(NRF24_t *dev, uint8_t *adr) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
Nrf24_writeRegister(dev, RX_ADDR_P0, adr,
|
||||
mirf_ADDR_LEN); // RX_ADDR_P0 must be set to the sending
|
||||
// addr for auto ack to work.
|
||||
Nrf24_writeRegister(dev, TX_ADDR, adr, mirf_ADDR_LEN);
|
||||
uint8_t buffer[3];
|
||||
Nrf24_readRegister(dev, RX_ADDR_P0, buffer, sizeof(buffer));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]);
|
||||
if (adr[i] != buffer[i])
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Add the receiving device address
|
||||
void Nrf24_addRADDR(NRF24_t *dev, uint8_t pipe, uint8_t adr) {
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, EN_RXADDR, &value, 1);
|
||||
|
||||
if (pipe == 2) {
|
||||
Nrf24_configRegister(dev, RX_PW_P2, dev->payload);
|
||||
Nrf24_configRegister(dev, RX_ADDR_P2, adr);
|
||||
value = value | 0x04;
|
||||
Nrf24_configRegister(dev, EN_RXADDR, value);
|
||||
} else if (pipe == 3) {
|
||||
Nrf24_configRegister(dev, RX_PW_P3, dev->payload);
|
||||
Nrf24_configRegister(dev, RX_ADDR_P3, adr);
|
||||
value = value | 0x08;
|
||||
Nrf24_configRegister(dev, EN_RXADDR, value);
|
||||
} else if (pipe == 4) {
|
||||
Nrf24_configRegister(dev, RX_PW_P4, dev->payload);
|
||||
Nrf24_configRegister(dev, RX_ADDR_P4, adr);
|
||||
value = value | 0x10;
|
||||
Nrf24_configRegister(dev, EN_RXADDR, value);
|
||||
} else if (pipe == 5) {
|
||||
Nrf24_configRegister(dev, RX_PW_P5, dev->payload);
|
||||
Nrf24_configRegister(dev, RX_ADDR_P5, adr);
|
||||
value = value | 0x20;
|
||||
Nrf24_configRegister(dev, EN_RXADDR, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if data is available for reading
|
||||
extern bool Nrf24_dataReady(NRF24_t *dev) {
|
||||
// See note in getData() function - just checking RX_DR isn't good enough
|
||||
uint8_t status = Nrf24_getStatus(dev);
|
||||
// printf("Nrf24_dataReady status=0x%x\n", status);
|
||||
if (status & (1 << RX_DR)) {
|
||||
// Save status
|
||||
dev->status = status;
|
||||
return 1;
|
||||
}
|
||||
// We can short circuit on RX_DR, but if it's not set, we still need
|
||||
// to check the FIFO for any pending packets
|
||||
// return !Nrf24_rxFifoEmpty(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get pipe number for reading
|
||||
uint8_t Nrf24_getDataPipe(NRF24_t *dev) {
|
||||
// uint8_t status = Nrf24_getStatus(dev);
|
||||
// printf("dev->status=0x%x\n",dev->status);
|
||||
return ((dev->status & 0x0E) >> 1);
|
||||
}
|
||||
|
||||
extern bool Nrf24_rxFifoEmpty(NRF24_t *dev) {
|
||||
uint8_t fifoStatus;
|
||||
Nrf24_readRegister(dev, FIFO_STATUS, &fifoStatus, sizeof(fifoStatus));
|
||||
return (fifoStatus & (1 << RX_EMPTY));
|
||||
}
|
||||
|
||||
// Reads payload bytes into data array
|
||||
extern void Nrf24_getData(NRF24_t *dev, uint8_t *data) {
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, R_RX_PAYLOAD); // Send cmd to read rx payload
|
||||
spi_read_byte(dev, data, data, dev->payload); // Read payload
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
// NVI: per product spec, p 67, note c:
|
||||
// "The RX_DR IRQ is asserted by a new packet arrival event. The procedure
|
||||
// for handling this interrupt should be: 1) read payload through SPI,
|
||||
// 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more
|
||||
// payloads available in RX FIFO, 4) if there are more data in RX FIFO,
|
||||
// repeat from step 1)."
|
||||
// So if we're going to clear RX_DR here, we need to check the RX FIFO
|
||||
// in the dataReady() function
|
||||
Nrf24_configRegister(dev, STATUS, (1 << RX_DR)); // Reset status register
|
||||
}
|
||||
|
||||
// Clocks only one byte into the given MiRF register
|
||||
void Nrf24_configRegister(NRF24_t *dev, uint8_t reg, uint8_t value) {
|
||||
spi_csnLow(dev);
|
||||
spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg));
|
||||
spi_transfer(dev, value);
|
||||
spi_csnHi(dev);
|
||||
}
|
||||
|
||||
// Reads an array of bytes from the given start position in the MiRF registers
|
||||
void Nrf24_readRegister(NRF24_t *dev, uint8_t reg, uint8_t *value,
|
||||
uint8_t len) {
|
||||
spi_csnLow(dev);
|
||||
spi_transfer(dev, R_REGISTER | (REGISTER_MASK & reg));
|
||||
spi_read_byte(dev, value, value, len);
|
||||
spi_csnHi(dev);
|
||||
}
|
||||
|
||||
// Writes an array of bytes into inte the MiRF registers
|
||||
void Nrf24_writeRegister(NRF24_t *dev, uint8_t reg, uint8_t *value,
|
||||
uint8_t len) {
|
||||
spi_csnLow(dev);
|
||||
spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg));
|
||||
spi_write_byte(dev, value, len);
|
||||
spi_csnHi(dev);
|
||||
}
|
||||
|
||||
// Sends a data package to the default address. Be sure to send the correct
|
||||
// amount of bytes as configured as payload on the receiver.
|
||||
void Nrf24_send(NRF24_t *dev, uint8_t *value) {
|
||||
uint8_t status;
|
||||
status = Nrf24_getStatus(dev);
|
||||
while (dev->PTX) // Wait until last paket is send
|
||||
{
|
||||
status = Nrf24_getStatus(dev);
|
||||
if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) {
|
||||
dev->PTX = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Nrf24_ceLow(dev);
|
||||
Nrf24_powerUpTx(dev); // Set to transmitter mode , Power up
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, FLUSH_TX); // Write cmd to flush tx fifo
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, W_TX_PAYLOAD); // Write cmd to write payload
|
||||
spi_write_byte(dev, value, dev->payload); // Write payload
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
Nrf24_ceHi(dev); // Start transmission
|
||||
}
|
||||
|
||||
void Nrf24_send_in_ack(NRF24_t *dev, uint8_t *value) {
|
||||
uint8_t status;
|
||||
status = Nrf24_getStatus(dev);
|
||||
while (dev->PTX) // Wait until last paket is send
|
||||
{
|
||||
status = Nrf24_getStatus(dev);
|
||||
if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) {
|
||||
dev->PTX = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, W_ACK_PAYLOAD); // Write cmd to write payload
|
||||
spi_write_byte(dev, value, dev->payload); // Write payload
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
}
|
||||
|
||||
// Sends payload without expecting an ACK from the receiver effectively turning
|
||||
// off retransmission of failed payloads. See Nrf24l01 "PTX Operation" flowchart
|
||||
// in the datasheet. NOTE: Make sure to call Nrf24_enableNoAckFeature() before
|
||||
// calling this function. Is useful when achieving maximum throughput without
|
||||
// caring much about losses.
|
||||
void Nrf24_sendNoAck(NRF24_t *dev, uint8_t *value) {
|
||||
uint8_t status;
|
||||
status = Nrf24_getStatus(dev);
|
||||
while (dev->PTX) // Wait until last paket is sent
|
||||
{
|
||||
status = Nrf24_getStatus(dev);
|
||||
if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) {
|
||||
dev->PTX = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Nrf24_ceLow(dev);
|
||||
Nrf24_powerUpTx(dev); // Set to transmitter mode , Power up
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, FLUSH_TX); // Write cmd to flush tx fifo
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
spi_csnLow(dev); // Pull down chip select
|
||||
spi_transfer(dev, W_TX_PAYLOAD_NO_ACK); // Write cmd to write payload
|
||||
spi_write_byte(dev, value, dev->payload); // Write payload
|
||||
spi_csnHi(dev); // Pull up chip select
|
||||
Nrf24_ceHi(dev); // Start transmission
|
||||
}
|
||||
|
||||
// Test if chip is still sending.
|
||||
// When sending has finished return chip to listening.
|
||||
bool Nrf24_isSending(NRF24_t *dev) {
|
||||
uint8_t status;
|
||||
if (dev->PTX) {
|
||||
status = Nrf24_getStatus(dev);
|
||||
if ((status &
|
||||
((1 << TX_DS) | (1 << MAX_RT)))) { // if sending successful (TX_DS) or
|
||||
// max retries exceded (MAX_RT).
|
||||
Nrf24_powerUpRx(dev);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if Sending has finished or retry is over.
|
||||
// When sending has finished return trur.
|
||||
// When reach maximum number of TX retries return false.
|
||||
bool Nrf24_isSend(NRF24_t *dev, int timeout) {
|
||||
uint8_t status;
|
||||
TickType_t startTick = xTaskGetTickCount();
|
||||
if (dev->PTX) {
|
||||
while (1) {
|
||||
status = Nrf24_getStatus(dev);
|
||||
/*
|
||||
if sending successful (TX_DS) or max retries exceded (MAX_RT).
|
||||
*/
|
||||
|
||||
if (status & (1 << TX_DS)) { // Data Sent TX FIFO interrup
|
||||
Nrf24_powerUpRx(dev);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status & (1 << MAX_RT)) { // Maximum number of TX retries interrupt
|
||||
// ESP_LOGW(TAG, "Maximum number of TX retries interrupt");
|
||||
Nrf24_powerUpRx(dev);
|
||||
return false;
|
||||
}
|
||||
|
||||
// I believe either TX_DS or MAX_RT will always be notified.
|
||||
// Therefore, it is unusual for neither to be notified for a period of
|
||||
// time. I don't know exactly how to respond.
|
||||
TickType_t diffTick = xTaskGetTickCount() - startTick;
|
||||
if ((diffTick * portTICK_PERIOD_MS) > timeout) {
|
||||
ESP_LOGE(TAG, "Status register timeout. status=0x%x", status);
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enables the W_TX_PAYLOAD command
|
||||
// NOTE: Make sure to call this before using Nrf24_sendNoAck().
|
||||
// Can be called anytime after the call to Nrf24_init() and preferably only
|
||||
// once.
|
||||
void Nrf24_enableNoAckFeature(NRF24_t *dev) {
|
||||
uint8_t value;
|
||||
|
||||
Nrf24_readRegister(dev, FEATURE, &value, 1);
|
||||
value = value | 1;
|
||||
Nrf24_configRegister(dev, FEATURE, value);
|
||||
}
|
||||
|
||||
void Nrf24_enableAckPayloadFeature(NRF24_t *dev) {
|
||||
uint8_t value;
|
||||
|
||||
Nrf24_readRegister(dev, FEATURE, &value, 1);
|
||||
value = value | 2;
|
||||
Nrf24_configRegister(dev, FEATURE, value);
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getStatus(NRF24_t *dev) {
|
||||
uint8_t rv;
|
||||
Nrf24_readRegister(dev, STATUS, &rv, 1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void Nrf24_powerUpRx(NRF24_t *dev) {
|
||||
dev->PTX = 0;
|
||||
Nrf24_ceLow(dev);
|
||||
Nrf24_configRegister(
|
||||
dev, CONFIG,
|
||||
mirf_CONFIG | ((1 << PWR_UP) | (1 << PRIM_RX))); // set device as RX mode
|
||||
Nrf24_ceHi(dev);
|
||||
Nrf24_configRegister(
|
||||
dev, STATUS,
|
||||
(1 << TX_DS) |
|
||||
(1 << MAX_RT)); // Clear seeded interrupt and max tx number interrupt
|
||||
}
|
||||
|
||||
void Nrf24_flushRx(NRF24_t *dev) {
|
||||
spi_csnLow(dev);
|
||||
spi_transfer(dev, FLUSH_RX);
|
||||
spi_csnHi(dev);
|
||||
}
|
||||
|
||||
void Nrf24_powerUpTx(NRF24_t *dev) {
|
||||
dev->PTX = 1;
|
||||
Nrf24_configRegister(
|
||||
dev, CONFIG,
|
||||
mirf_CONFIG | ((1 << PWR_UP) | (0 << PRIM_RX))); // set device as TX mode
|
||||
Nrf24_configRegister(
|
||||
dev, STATUS,
|
||||
(1 << TX_DS) |
|
||||
(1 << MAX_RT)); // Clear seeded interrupt and max tx number interrupt
|
||||
}
|
||||
|
||||
void Nrf24_ceHi(NRF24_t *dev) { gpio_set_level(dev->cePin, 1); }
|
||||
|
||||
void Nrf24_ceLow(NRF24_t *dev) { gpio_set_level(dev->cePin, 0); }
|
||||
|
||||
void Nrf24_powerDown(NRF24_t *dev) {
|
||||
Nrf24_ceLow(dev);
|
||||
Nrf24_configRegister(dev, CONFIG, mirf_CONFIG);
|
||||
}
|
||||
|
||||
// Set tx power : 0=-18dBm,1=-12dBm,2=-6dBm,3=0dBm
|
||||
void Nrf24_SetOutputRF_PWR(NRF24_t *dev, uint8_t val) {
|
||||
if (val > 3)
|
||||
return;
|
||||
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, RF_SETUP, &value, 1);
|
||||
value = value & 0xF9;
|
||||
value = value | (val << RF_PWR);
|
||||
// Nrf24_configRegister(dev, RF_SETUP, (val<< RF_PWR) );
|
||||
Nrf24_configRegister(dev, RF_SETUP, value);
|
||||
}
|
||||
|
||||
// Select between the high speed data rates:0=1Mbps, 1=2Mbps, 2=250Kbps
|
||||
void Nrf24_SetSpeedDataRates(NRF24_t *dev, uint8_t val) {
|
||||
if (val > 2)
|
||||
return;
|
||||
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, RF_SETUP, &value, 1);
|
||||
if (val == 2) {
|
||||
value = value | 0x20;
|
||||
value = value & 0xF7;
|
||||
// Nrf24_configRegister(dev, RF_SETUP, (1 << RF_DR_LOW) );
|
||||
Nrf24_configRegister(dev, RF_SETUP, value);
|
||||
} else {
|
||||
value = value & 0xD7;
|
||||
value = value | (val << RF_DR_HIGH);
|
||||
// Nrf24_configRegister(dev, RF_SETUP, (val << RF_DR_HIGH) );
|
||||
Nrf24_configRegister(dev, RF_SETUP, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Set Auto Retransmit Delay 0=250us, 1=500us, ... 15=4000us
|
||||
void Nrf24_setRetransmitDelay(NRF24_t *dev, uint8_t val) {
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
|
||||
value = value & 0x0F;
|
||||
value = value | (val << ARD);
|
||||
Nrf24_configRegister(dev, SETUP_RETR, value);
|
||||
}
|
||||
|
||||
void Nrf24_setRetransmitCount(NRF24_t *dev, uint8_t val) {
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
|
||||
value = value & 0xF0;
|
||||
value = value | val;
|
||||
Nrf24_configRegister(dev, SETUP_RETR, value);
|
||||
}
|
||||
|
||||
void Nrf24_printDetails(NRF24_t *dev) {
|
||||
|
||||
printf("================ SPI Configuration ================\n");
|
||||
printf("CSN Pin \t = GPIO%d\n", dev->csnPin);
|
||||
printf("CE Pin \t = GPIO%d\n", dev->cePin);
|
||||
printf("Clock Speed\t = %d\n", SPI_Frequency);
|
||||
printf("================ NRF Configuration ================\n");
|
||||
|
||||
Nrf24_print_status(Nrf24_getStatus(dev));
|
||||
|
||||
Nrf24_print_byte_register(dev, "CONFIG\t", CONFIG, 1);
|
||||
Nrf24_print_byte_register(dev, "EN_AA\t", EN_AA, 1);
|
||||
Nrf24_print_byte_register(dev, "EN_RXADDR", EN_RXADDR, 1);
|
||||
Nrf24_print_byte_register(dev, "SETUP_AW", SETUP_AW, 1);
|
||||
Nrf24_print_byte_register(dev, "SETUP_RETR", SETUP_RETR, 1);
|
||||
Nrf24_print_byte_register(dev, "RF_CH\t", RF_CH, 1);
|
||||
Nrf24_print_byte_register(dev, "RF_SETUP", RF_SETUP, 1);
|
||||
Nrf24_print_byte_register(dev, "STATUS", STATUS, 1);
|
||||
Nrf24_print_byte_register(dev, "OBSERVE_TX", OBSERVE_TX, 1);
|
||||
Nrf24_print_byte_register(dev, "CD", CD, 1);
|
||||
|
||||
Nrf24_print_address_register(dev, "RX_ADDR_P0-1", RX_ADDR_P0, 2);
|
||||
Nrf24_print_byte_register(dev, "RX_ADDR_P2-5", RX_ADDR_P2, 4);
|
||||
Nrf24_print_address_register(dev, "TX_ADDR\t", TX_ADDR, 1);
|
||||
|
||||
Nrf24_print_byte_register(dev, "RX_PW_P0-6", RX_PW_P0, 6);
|
||||
Nrf24_print_byte_register(dev, "FIFO_STATUS", FIFO_STATUS, 1);
|
||||
Nrf24_print_byte_register(dev, "DYNPD/FEATURE", DYNPD, 2);
|
||||
// printf("getDataRate()=%d\n",Nrf24_getDataRate(dev));
|
||||
printf("Data Rate\t = %s\n", rf24_datarates[Nrf24_getDataRate(dev)]);
|
||||
|
||||
printf("CRC Length\t = %s\n", rf24_crclength[Nrf24_getCRCLength(dev)]);
|
||||
printf("PA Power\t = %s\n", rf24_pa_dbm[Nrf24_getPALevel(dev)]);
|
||||
uint8_t retransmit = Nrf24_getRetransmitDelay(dev);
|
||||
int16_t delay = (retransmit + 1) * 250;
|
||||
printf("Retransmit\t = %d us\n", delay);
|
||||
}
|
||||
|
||||
#define _BV(x) (1 << (x))
|
||||
|
||||
void Nrf24_print_status(uint8_t status) {
|
||||
printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x "
|
||||
"TX_FULL=%x\r\n",
|
||||
status, (status & _BV(RX_DR)) ? 1 : 0, (status & _BV(TX_DS)) ? 1 : 0,
|
||||
(status & _BV(MAX_RT)) ? 1 : 0, ((status >> RX_P_NO) & 0x07),
|
||||
(status & _BV(TX_FULL)) ? 1 : 0);
|
||||
}
|
||||
|
||||
void Nrf24_print_address_register(NRF24_t *dev, const char *name, uint8_t reg,
|
||||
uint8_t qty) {
|
||||
printf("%s\t =", name);
|
||||
while (qty--) {
|
||||
// uint8_t buffer[addr_width];
|
||||
uint8_t buffer[5];
|
||||
Nrf24_readRegister(dev, reg++, buffer, sizeof(buffer));
|
||||
|
||||
printf(" 0x");
|
||||
#if 0
|
||||
uint8_t* bufptr = buffer + sizeof buffer;
|
||||
while (--bufptr >= buffer) {
|
||||
printf("%02x", *bufptr);
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < 5; i++) {
|
||||
printf("%02x", buffer[i]);
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
void Nrf24_print_byte_register(NRF24_t *dev, const char *name, uint8_t reg,
|
||||
uint8_t qty) {
|
||||
printf("%s\t =", name);
|
||||
while (qty--) {
|
||||
uint8_t buffer[1];
|
||||
Nrf24_readRegister(dev, reg++, buffer, 1);
|
||||
printf(" 0x%02x", buffer[0]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getDataRate(NRF24_t *dev) {
|
||||
rf24_datarate_e result;
|
||||
uint8_t dr;
|
||||
Nrf24_readRegister(dev, RF_SETUP, &dr, sizeof(dr));
|
||||
// printf("RF_SETUP=%x\n",dr);
|
||||
dr = dr & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));
|
||||
|
||||
// switch uses RAM (evil!)
|
||||
// Order matters in our case below
|
||||
if (dr == _BV(RF_DR_LOW)) {
|
||||
// '10' = 250KBPS
|
||||
result = RF24_250KBPS;
|
||||
} else if (dr == _BV(RF_DR_HIGH)) {
|
||||
// '01' = 2MBPS
|
||||
result = RF24_2MBPS;
|
||||
} else {
|
||||
// '00' = 1MBPS
|
||||
result = RF24_1MBPS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *Nrf24_getDataRateString(NRF24_t *dev) {
|
||||
return rf24_datarates[Nrf24_getDataRate(dev)];
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getCRCLength(NRF24_t *dev) {
|
||||
rf24_crclength_e result = RF24_CRC_DISABLED;
|
||||
|
||||
uint8_t config;
|
||||
Nrf24_readRegister(dev, CONFIG, &config, sizeof(config));
|
||||
// printf("CONFIG=%x\n",config);
|
||||
config = config & (_BV(CRCO) | _BV(EN_CRC));
|
||||
uint8_t AA;
|
||||
Nrf24_readRegister(dev, EN_AA, &AA, sizeof(AA));
|
||||
|
||||
if (config & _BV(EN_CRC) || AA) {
|
||||
if (config & _BV(CRCO)) {
|
||||
result = RF24_CRC_16;
|
||||
} else {
|
||||
result = RF24_CRC_8;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getPALevel(NRF24_t *dev) {
|
||||
uint8_t level;
|
||||
Nrf24_readRegister(dev, RF_SETUP, &level, sizeof(level));
|
||||
// printf("RF_SETUP=%x\n",level);
|
||||
level = (level & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1;
|
||||
return (level);
|
||||
}
|
||||
|
||||
char *Nrf24_getPALevelString(NRF24_t *dev) {
|
||||
return rf24_pa_dbm[Nrf24_getPALevel(dev)];
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getRetransmitDelay(NRF24_t *dev) {
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
|
||||
return (value >> 4);
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getRetransmitCount(NRF24_t *dev) {
|
||||
uint8_t value;
|
||||
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
|
||||
return (value & 0x0F);
|
||||
}
|
||||
|
||||
uint8_t Nrf24_getChannle(NRF24_t *dev) { return dev->channel; }
|
||||
|
||||
uint8_t Nrf24_getPayload(NRF24_t *dev) { return dev->payload; }
|
||||
@@ -0,0 +1,227 @@
|
||||
#ifndef MAIN_MIRF_H_
|
||||
#define MAIN_MIRF_H_
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t PTX; // In sending mode.
|
||||
uint8_t cePin; // CE Pin controls RX / TX, default 8.
|
||||
uint8_t csnPin; // CSN Pin Chip Select Not, default 7.
|
||||
uint8_t channel; // Channel 0 - 127 or 0 - 84 in the US.
|
||||
uint8_t payload; // Payload width in bytes default 16 max 32.
|
||||
spi_device_handle_t _SPIHandle;
|
||||
uint8_t status; // Receive status
|
||||
} NRF24_t;
|
||||
|
||||
/* Memory Map */
|
||||
#define CONFIG 0x00
|
||||
#define EN_AA 0x01
|
||||
#define EN_RXADDR 0x02
|
||||
#define SETUP_AW 0x03
|
||||
#define SETUP_RETR 0x04
|
||||
#define RF_CH 0x05
|
||||
#define RF_SETUP 0x06
|
||||
#define STATUS 0x07
|
||||
#define OBSERVE_TX 0x08
|
||||
#define CD 0x09
|
||||
#define RX_ADDR_P0 0x0A
|
||||
#define RX_ADDR_P1 0x0B
|
||||
#define RX_ADDR_P2 0x0C
|
||||
#define RX_ADDR_P3 0x0D
|
||||
#define RX_ADDR_P4 0x0E
|
||||
#define RX_ADDR_P5 0x0F
|
||||
#define TX_ADDR 0x10
|
||||
#define RX_PW_P0 0x11
|
||||
#define RX_PW_P1 0x12
|
||||
#define RX_PW_P2 0x13
|
||||
#define RX_PW_P3 0x14
|
||||
#define RX_PW_P4 0x15
|
||||
#define RX_PW_P5 0x16
|
||||
#define FIFO_STATUS 0x17
|
||||
#define DYNPD 0x1C
|
||||
#define FEATURE 0x1D
|
||||
|
||||
/* Bit Mnemonics */
|
||||
#define MASK_RX_DR 6
|
||||
#define MASK_TX_DS 5
|
||||
#define MASK_MAX_RT 4
|
||||
#define EN_CRC 3
|
||||
#define CRCO 2
|
||||
#define PWR_UP 1
|
||||
#define PRIM_RX 0
|
||||
#define ENAA_P5 5
|
||||
#define ENAA_P4 4
|
||||
#define ENAA_P3 3
|
||||
#define ENAA_P2 2
|
||||
#define ENAA_P1 1
|
||||
#define ENAA_P0 0
|
||||
#define ERX_P5 5
|
||||
#define ERX_P4 4
|
||||
#define ERX_P3 3
|
||||
#define ERX_P2 2
|
||||
#define ERX_P1 1
|
||||
#define ERX_P0 0
|
||||
#define AW 0
|
||||
#define ARD 4
|
||||
#define ARC 0
|
||||
#define RF_DR_LOW 5
|
||||
#define PLL_LOCK 4
|
||||
#define RF_DR_HIGH 3
|
||||
#define RF_PWR 1
|
||||
#define LNA_HCURR 0
|
||||
#define RX_DR 6
|
||||
#define TX_DS 5
|
||||
#define MAX_RT 4
|
||||
#define RX_P_NO 1
|
||||
#define TX_FULL 0
|
||||
#define PLOS_CNT 4
|
||||
#define ARC_CNT 0
|
||||
#define TX_REUSE 6
|
||||
#define FIFO_FULL 5
|
||||
#define TX_EMPTY 4
|
||||
#define RX_FULL 1
|
||||
#define RX_EMPTY 0
|
||||
|
||||
/* Instruction Mnemonics */
|
||||
#define R_REGISTER 0x00
|
||||
#define W_REGISTER 0x20
|
||||
#define REGISTER_MASK 0x1F
|
||||
#define R_RX_PAYLOAD 0x61
|
||||
#define W_TX_PAYLOAD 0xA0
|
||||
#define FLUSH_TX 0xE1
|
||||
#define FLUSH_RX 0xE2
|
||||
#define REUSE_TX_PL 0xE3
|
||||
#define ACTIVATE 0x50
|
||||
#define R_RX_PL_WID 0x60
|
||||
#define W_ACK_PAYLOAD 0xA8
|
||||
#define NOP 0xFF
|
||||
|
||||
/* Non-P omissions */
|
||||
#define LNA_HCURR 0
|
||||
|
||||
/* P model memory Map */
|
||||
#define RPD 0x09
|
||||
#define W_TX_PAYLOAD_NO_ACK 0xB0
|
||||
|
||||
/* P model bit Mnemonics */
|
||||
#define RF_DR_LOW 5
|
||||
#define RF_DR_HIGH 3
|
||||
#define RF_PWR_LOW 1
|
||||
#define RF_PWR_HIGH 2
|
||||
|
||||
/* Device addrees length:3~5 bytes */
|
||||
#define mirf_ADDR_LEN 5
|
||||
|
||||
/*
|
||||
enable interrupt caused by RX_DR.
|
||||
enable interrupt caused by TX_DS.
|
||||
enable interrupt caused by MAX_RT.
|
||||
enable CRC and CRC data len=1
|
||||
mirf_CONFIG = 00001000B
|
||||
*/
|
||||
// #define mirf_CONFIG ((1<<EN_CRC) | (0<<CRCO) )
|
||||
|
||||
/*
|
||||
enable interrupt caused by RX_DR.
|
||||
disable interrupt caused by TX_DS.
|
||||
enable interrupt caused by MAX_RT.
|
||||
enable CRC and CRC data len=2
|
||||
mirf_CONFIG == 00101000B
|
||||
*/
|
||||
#define mirf_CONFIG ((1 << MASK_TX_DS) | (1 << EN_CRC) | (1 << CRCO))
|
||||
|
||||
/**
|
||||
* Power Amplifier level.
|
||||
*
|
||||
* For use with setPALevel()
|
||||
*/
|
||||
typedef enum {
|
||||
RF24_PA_MIN = 0,
|
||||
RF24_PA_LOW,
|
||||
RF24_PA_HIGH,
|
||||
RF24_PA_MAX,
|
||||
RF24_PA_ERROR
|
||||
} rf24_pa_dbm_e;
|
||||
|
||||
/**
|
||||
* Data rate. How fast data moves through the air.
|
||||
*
|
||||
* For use with setDataRate()
|
||||
*/
|
||||
typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e;
|
||||
|
||||
/**
|
||||
* CRC Length. How big (if any) of a CRC is included.
|
||||
*
|
||||
* For use with setCRCLength()
|
||||
*/
|
||||
typedef enum {
|
||||
RF24_CRC_DISABLED = 0,
|
||||
RF24_CRC_8,
|
||||
RF24_CRC_16
|
||||
} rf24_crclength_e;
|
||||
|
||||
void Nrf24_init(NRF24_t *dev);
|
||||
void Nrf24_deinit(NRF24_t *dev);
|
||||
bool spi_write_byte(NRF24_t *dev, uint8_t *Dataout, size_t DataLength);
|
||||
bool spi_read_byte(NRF24_t *dev, uint8_t *Datain, uint8_t *Dataout,
|
||||
size_t DataLength);
|
||||
uint8_t spi_transfer(NRF24_t *dev, uint8_t address);
|
||||
void spi_csnLow(NRF24_t *dev);
|
||||
void spi_csnHi(NRF24_t *dev);
|
||||
void Nrf24_config(NRF24_t *dev, uint8_t channel, uint8_t payload);
|
||||
void Nrf24_send(NRF24_t *dev, uint8_t *value);
|
||||
void Nrf24_send_in_ack(NRF24_t *dev, uint8_t *value);
|
||||
void Nrf24_enableNoAckFeature(NRF24_t *dev);
|
||||
void Nrf24_enableAckPayloadFeature(NRF24_t *dev);
|
||||
void Nrf24_sendNoAck(NRF24_t *dev, uint8_t *value);
|
||||
esp_err_t Nrf24_setRADDR(NRF24_t *dev, uint8_t *adr);
|
||||
esp_err_t Nrf24_setTADDR(NRF24_t *dev, uint8_t *adr);
|
||||
void Nrf24_addRADDR(NRF24_t *dev, uint8_t pipe, uint8_t adr);
|
||||
bool Nrf24_dataReady(NRF24_t *dev);
|
||||
uint8_t Nrf24_getDataPipe(NRF24_t *dev);
|
||||
bool Nrf24_isSending(NRF24_t *dev);
|
||||
bool Nrf24_isSend(NRF24_t *dev, int timeout);
|
||||
bool Nrf24_rxFifoEmpty(NRF24_t *dev);
|
||||
bool Nrf24_txFifoEmpty(NRF24_t *dev);
|
||||
void Nrf24_getData(NRF24_t *dev, uint8_t *data);
|
||||
uint8_t Nrf24_getStatus(NRF24_t *dev);
|
||||
void Nrf24_configRegister(NRF24_t *dev, uint8_t reg, uint8_t value);
|
||||
void Nrf24_readRegister(NRF24_t *dev, uint8_t reg, uint8_t *value, uint8_t len);
|
||||
void Nrf24_writeRegister(NRF24_t *dev, uint8_t reg, uint8_t *value,
|
||||
uint8_t len);
|
||||
void Nrf24_powerUpRx(NRF24_t *dev);
|
||||
void Nrf24_powerUpTx(NRF24_t *dev);
|
||||
void Nrf24_powerDown(NRF24_t *dev);
|
||||
void Nrf24_SetOutputRF_PWR(NRF24_t *dev, uint8_t val);
|
||||
void Nrf24_SetSpeedDataRates(NRF24_t *dev, uint8_t val);
|
||||
void Nrf24_setRetransmitDelay(NRF24_t *dev, uint8_t val);
|
||||
void Nrf24_setRetransmitCount(NRF24_t *dev, uint8_t val);
|
||||
void Nrf24_ceHi(NRF24_t *dev);
|
||||
void Nrf24_ceLow(NRF24_t *dev);
|
||||
void Nrf24_flushRx(NRF24_t *dev);
|
||||
void Nrf24_printDetails(NRF24_t *dev);
|
||||
void Nrf24_print_status(uint8_t status);
|
||||
void Nrf24_print_address_register(NRF24_t *dev, const char *name, uint8_t reg,
|
||||
uint8_t qty);
|
||||
void Nrf24_print_byte_register(NRF24_t *dev, const char *name, uint8_t reg,
|
||||
uint8_t qty);
|
||||
uint8_t Nrf24_getDataRate(NRF24_t *dev);
|
||||
char *Nrf24_getDataRateString(NRF24_t *dev);
|
||||
uint8_t Nrf24_getCRCLength(NRF24_t *dev);
|
||||
uint8_t Nrf24_getPALevel(NRF24_t *dev);
|
||||
char *Nrf24_getPALevelString(NRF24_t *dev);
|
||||
uint8_t Nrf24_getRetransmitDelay(NRF24_t *dev);
|
||||
uint8_t Nrf24_getRetransmitCount(NRF24_t *dev);
|
||||
uint8_t Nrf24_getChannle(NRF24_t *dev);
|
||||
uint8_t Nrf24_getPayload(NRF24_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAIN_MIRF_H_ */
|
||||
@@ -0,0 +1,35 @@
|
||||
dependencies:
|
||||
espressif/esp32-camera:
|
||||
component_hash: 2b75f6f0c987fb0e3687ccbd464cd00c163ea8cb05d2470cfb05adf6535bc488
|
||||
dependencies:
|
||||
- name: espressif/esp_jpeg
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: ^1.3.1
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 2.1.6
|
||||
espressif/esp_jpeg:
|
||||
component_hash: defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.0'
|
||||
source:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.3.1
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 6.0.0
|
||||
direct_dependencies:
|
||||
- espressif/esp32-camera
|
||||
- idf
|
||||
manifest_hash: bb2e04f03fc22fe03978f48d3f4f74ead5a698f8aba43ac46cd3353f373a000c
|
||||
target: esp32
|
||||
version: 3.0.0
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
"../components/mirf/mirf.c"
|
||||
"../components/mirf/mirf.h"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,17 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=4.1.0'
|
||||
# # Put list of dependencies here
|
||||
# # For components maintained by Espressif:
|
||||
# component: "~1.0.0"
|
||||
# # For 3rd party components:
|
||||
# username/component: ">=1.0.0,<2.0.0"
|
||||
# username2/component2:
|
||||
# version: "~1.0.0"
|
||||
# # For transient dependencies `public` flag can be set.
|
||||
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||
# # All dependencies of `main` are public by default.
|
||||
# public: true
|
||||
espressif/esp32-camera: '*'
|
||||
+504
@@ -0,0 +1,504 @@
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "sensor.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "CAM"
|
||||
#include "esp_camera.h"
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 0
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y2_GPIO_NUM 5
|
||||
#define Y3_GPIO_NUM 18
|
||||
#define Y4_GPIO_NUM 19
|
||||
#define Y5_GPIO_NUM 21
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y9_GPIO_NUM 35
|
||||
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "../components/mirf/mirf.h"
|
||||
#include <driver/uart.h>
|
||||
|
||||
#define FEC_INTERVAL 3
|
||||
|
||||
#define BUF_SIZE 20000
|
||||
|
||||
typedef struct {
|
||||
int x, y, width, height;
|
||||
} Rectangle;
|
||||
|
||||
#define MAX_CHUNK_DATA 28
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t header[4];
|
||||
uint8_t data[28];
|
||||
} RadioStruct;
|
||||
|
||||
typedef enum {
|
||||
CMD_SET_CHANNEL = 1,
|
||||
CMD_SET_RESOLUTION = 2,
|
||||
CMD_RESTART_FRAME = 3
|
||||
} RadioCommandType;
|
||||
|
||||
uint8_t abort_sending = 0;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t cmd; // CMD_SET_CHANNEL or CMD_SET_RESOLUTION
|
||||
uint16_t param1; // new channel or width
|
||||
uint16_t param2; // height (only used for resolution)
|
||||
uint16_t param3;
|
||||
} RadioCommand;
|
||||
|
||||
framesize_t width_height_to_framesize(uint16_t w, uint16_t h) {
|
||||
if (w <= 96 && h <= 96) return FRAMESIZE_96X96;
|
||||
if (w <= 128 && h <= 128) return FRAMESIZE_128X128;
|
||||
if (w <= 160 && h <= 120) return FRAMESIZE_QQVGA;
|
||||
if (w <= 176 && h <= 144) return FRAMESIZE_QCIF;
|
||||
if (w <= 240 && h <= 176) return FRAMESIZE_HQVGA;
|
||||
if (w <= 240 && h <= 240) return FRAMESIZE_240X240;
|
||||
if (w <= 320 && h <= 240) return FRAMESIZE_QVGA;
|
||||
if (w <= 320 && h <= 320) return FRAMESIZE_320X320;
|
||||
if (w <= 400 && h <= 296) return FRAMESIZE_CIF;
|
||||
if (w <= 480 && h <= 320) return FRAMESIZE_HVGA;
|
||||
if (w <= 640 && h <= 480) return FRAMESIZE_VGA;
|
||||
if (w <= 800 && h <= 600) return FRAMESIZE_SVGA;
|
||||
if (w <= 1024 && h <= 768) return FRAMESIZE_XGA;
|
||||
if (w <= 1280 && h <= 720) return FRAMESIZE_HD;
|
||||
if (w <= 1280 && h <= 1024) return FRAMESIZE_SXGA;
|
||||
if (w <= 1600 && h <= 1200) return FRAMESIZE_UXGA;
|
||||
|
||||
return FRAMESIZE_QVGA; // fallback if unknown
|
||||
}
|
||||
NRF24_t dev;
|
||||
|
||||
static void packHeader4_15bit(const uint8_t flags, const uint16_t chunkIndex,
|
||||
const uint16_t totalChunks, uint8_t header[4]) {
|
||||
uint32_t bits = ((flags & 0x3) << 30) | // 2 bits flags
|
||||
((chunkIndex & 0x7FFF) << 15) | // 15 bits chunkIndex
|
||||
(totalChunks & 0x7FFF); // 15 bits totalChunks
|
||||
header[0] = (bits >> 24) & 0xFF;
|
||||
header[1] = (bits >> 16) & 0xFF;
|
||||
header[2] = (bits >> 8) & 0xFF;
|
||||
header[3] = bits & 0xFF;
|
||||
}
|
||||
|
||||
static void unpackHeader4_15bit(const uint8_t header[4], uint8_t *flags,
|
||||
uint16_t *chunkIndex, uint16_t *totalChunks) {
|
||||
uint32_t bits =
|
||||
(header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
|
||||
*flags = (bits >> 30) & 0x3;
|
||||
*chunkIndex = (bits >> 15) & 0x7FFF;
|
||||
*totalChunks = bits & 0x7FFF;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Code start
|
||||
//
|
||||
int vsync_count = 0;
|
||||
int sync_fail_count = 0;
|
||||
int reset_fail_count = 0;
|
||||
int64_t vsyncDetectedUsec;
|
||||
|
||||
static camera_config_t camera_config = {
|
||||
.pin_pwdn = PWDN_GPIO_NUM,
|
||||
.pin_reset = RESET_GPIO_NUM,
|
||||
.pin_xclk = XCLK_GPIO_NUM,
|
||||
|
||||
.pin_sccb_sda = SIOD_GPIO_NUM,
|
||||
.pin_sccb_scl = SIOC_GPIO_NUM,
|
||||
|
||||
.pin_d7 = Y9_GPIO_NUM,
|
||||
.pin_d6 = Y8_GPIO_NUM,
|
||||
.pin_d5 = Y7_GPIO_NUM,
|
||||
.pin_d4 = Y6_GPIO_NUM,
|
||||
.pin_d3 = Y5_GPIO_NUM,
|
||||
.pin_d2 = Y4_GPIO_NUM,
|
||||
.pin_d1 = Y3_GPIO_NUM,
|
||||
.pin_d0 = Y2_GPIO_NUM,
|
||||
.pin_vsync = VSYNC_GPIO_NUM,
|
||||
.pin_href = HREF_GPIO_NUM,
|
||||
.pin_pclk = PCLK_GPIO_NUM,
|
||||
|
||||
.xclk_freq_hz = 10000000, // EXPERIMENTAL: Set to 16MHz on ESP32-S2 or
|
||||
// ESP32-S3 to enable EDMA mode
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size =
|
||||
FRAMESIZE_UXGA, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA
|
||||
// when not JPEG. The performance of the ESP32-S series
|
||||
// has improved a lot, but JPEG mode always gives better
|
||||
// frame rates.+
|
||||
//.frame_size = FRAMESIZE_128X128, // QQVGA-UXGA, For ESP32, do not use
|
||||
// sizes above QVGA when not JPEG. The performance of the ESP32-S series has
|
||||
// improved a lot, but JPEG mode always gives better frame rates.
|
||||
|
||||
.jpeg_quality = 63, // 0-63, for OV series camera sensors, lower number
|
||||
// means higher quality
|
||||
.fb_count = 2, // When jpeg mode is used, if fb_count more than one, the
|
||||
// driver will work in continuous mode.
|
||||
//.fb_location = CAMERA_FB_IN_PSRAM,
|
||||
.fb_location = CAMERA_FB_IN_PSRAM,
|
||||
.grab_mode = CAMERA_GRAB_LATEST // CAMERA_GRAB_LATEST. Sets when buffers
|
||||
// should be filled
|
||||
};
|
||||
|
||||
void handle_radio_commands() {
|
||||
uint8_t buf[32];
|
||||
if (Nrf24_dataReady(&dev)) {
|
||||
Nrf24_getData(&dev, buf);
|
||||
RadioCommand *cmd = (RadioCommand*)buf;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case CMD_SET_CHANNEL:
|
||||
ESP_LOGI(TAG, "Changing RF channel to %d", cmd->param1);
|
||||
Nrf24_config(&dev, cmd->param1, 32); // reconfigure channel
|
||||
break;
|
||||
case CMD_SET_RESOLUTION:
|
||||
ESP_LOGI(TAG, "Changing resolution to %dx%d", cmd->param1, cmd->param2);
|
||||
camera_config.frame_size = width_height_to_framesize(cmd->param1, cmd->param2);
|
||||
uint8_t quality = cmd->param3;
|
||||
if (quality > 63) {
|
||||
quality = 63;
|
||||
}
|
||||
camera_config.jpeg_quality = quality;
|
||||
esp_camera_deinit();
|
||||
esp_camera_init(&camera_config);
|
||||
break;
|
||||
case CMD_RESTART_FRAME:
|
||||
abort_sending = 1;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown command %d", cmd->cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// frames = array of RadioStruct data frames (not FEC frames)
|
||||
// fecFrame = RadioStruct to write FEC frame into
|
||||
// numFrames = number of frames to XOR
|
||||
void createFECFrame(RadioStruct *frames, uint16_t numFrames,
|
||||
RadioStruct *fecFrame) {
|
||||
|
||||
uint8_t flags;
|
||||
uint16_t chunkIndex;
|
||||
uint16_t totalChunks;
|
||||
unpackHeader4_15bit(frames[0].header, &flags, &chunkIndex, &totalChunks);
|
||||
|
||||
flags = 0x3; // mark as FEC
|
||||
// uint16_t totalChunks = numFrames; // optional
|
||||
packHeader4_15bit(flags, chunkIndex, totalChunks, fecFrame->header);
|
||||
memset(fecFrame->data, 0, MAX_CHUNK_DATA);
|
||||
for (uint16_t i = 0; i < numFrames; i++) {
|
||||
for (uint8_t j = 0; j < MAX_CHUNK_DATA; j++) {
|
||||
fecFrame->data[j] ^= frames[i].data[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dataPtr = pointer to JPEG data
|
||||
// chunkIndex = index of this chunk
|
||||
// chunkCount = total chunks
|
||||
// flags = 0, START_OF_FRAME, END_OF_FRAME, or FEC
|
||||
// outFrame = pointer to RadioStruct to fill
|
||||
void createFrame(const uint8_t *dataPtr, size_t jpegLen, uint16_t chunkIndex,
|
||||
uint16_t totalChunks, uint8_t flags, RadioStruct *outFrame) {
|
||||
packHeader4_15bit(flags, chunkIndex, totalChunks, outFrame->header);
|
||||
size_t remaining = jpegLen - chunkIndex * MAX_CHUNK_DATA;
|
||||
size_t copyLen = remaining > MAX_CHUNK_DATA ? MAX_CHUNK_DATA : remaining;
|
||||
|
||||
memcpy(outFrame->data, dataPtr + chunkIndex * MAX_CHUNK_DATA, copyLen);
|
||||
if (copyLen < MAX_CHUNK_DATA) {
|
||||
memset(outFrame->data + copyLen, 0, MAX_CHUNK_DATA - copyLen); // pad last
|
||||
}
|
||||
|
||||
/*
|
||||
ESP_LOGI(TAG, "Flags: 0x%02x, chunk index: %04d, chunk count: %04d", flags,
|
||||
chunkIndex, totalChunks);
|
||||
uint8_t *frmPTR = (uint8_t *)outFrame;
|
||||
ESP_LOGI(TAG, "Bytes: 0x%02x 0x%02x 0x%02x", frmPTR[0], frmPTR[1], frmPTR[2]);
|
||||
*/
|
||||
}
|
||||
|
||||
void sendPacket(const RadioStruct * packet) {
|
||||
uint8_t packetData[32];
|
||||
memcpy(packetData, packet->header, 4);
|
||||
memcpy(&(packetData[4]), packet->data, 28);
|
||||
Nrf24_send(&dev, (uint8_t *)&packetData);
|
||||
if (Nrf24_isSend(&dev, 1000)) {
|
||||
// ESP_LOGI(pcTaskGetName(NULL), "Send success.");
|
||||
} else {
|
||||
// ESP_LOGW(pcTaskGetName(NULL), "Send fail.");
|
||||
}
|
||||
handle_radio_commands();
|
||||
}
|
||||
|
||||
void send_jpeg_nrf(const uint8_t *jpegData, size_t jpegLen) {
|
||||
// calculate total chunks
|
||||
uint16_t totalChunks = (jpegLen + MAX_CHUNK_DATA - 1) / MAX_CHUNK_DATA;
|
||||
|
||||
RadioStruct frameBuffer[FEC_INTERVAL]; // buffer to store frames for FEC
|
||||
uint16_t fecCount = 0;
|
||||
|
||||
for (uint16_t chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
||||
if (abort_sending == 1) {
|
||||
abort_sending = 0;
|
||||
break;
|
||||
}
|
||||
uint8_t flags = 0;
|
||||
if (chunkIndex == 0) {
|
||||
flags |= 0x1;
|
||||
ESP_LOGI(TAG, "Flag start");
|
||||
}
|
||||
if (chunkIndex == totalChunks - 1) {
|
||||
flags |= 0x2;
|
||||
ESP_LOGI(TAG, "Flag end");
|
||||
}
|
||||
|
||||
RadioStruct packet;
|
||||
createFrame(jpegData, jpegLen, chunkIndex, totalChunks, flags, &packet);
|
||||
frameBuffer[fecCount++] = packet;
|
||||
|
||||
sendPacket(&packet);
|
||||
if (fecCount == FEC_INTERVAL) {
|
||||
RadioStruct fecPacket;
|
||||
createFECFrame(frameBuffer, FEC_INTERVAL, &fecPacket);
|
||||
sendPacket(&fecPacket);
|
||||
fecCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// send FEC for leftover frames
|
||||
if (fecCount > 0) {
|
||||
RadioStruct fecPacket;
|
||||
createFECFrame(frameBuffer, fecCount, &fecPacket);
|
||||
sendPacket(&fecPacket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t camera_init() {
|
||||
// initialize the camera
|
||||
esp_err_t err = esp_camera_init(&camera_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera Init Failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t len;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
pixformat_t format;
|
||||
struct timeval timestamp;
|
||||
} PreData;
|
||||
|
||||
uint8_t *create_frame(const uint8_t *data, size_t *outSize, PreData pre) {
|
||||
|
||||
|
||||
|
||||
const size_t totalSize = pre.len + 23;
|
||||
ESP_LOGI(TAG, "DataWithFrame: %d\n", totalSize);
|
||||
uint8_t *dataOut = malloc(totalSize);
|
||||
if (!dataOut) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for frame");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytePos = 0;
|
||||
|
||||
int64_t time_us =
|
||||
(int64_t)pre.timestamp.tv_sec * 1000000L + (int64_t)pre.timestamp.tv_usec;
|
||||
|
||||
dataOut[bytePos++] = 'B';
|
||||
dataOut[bytePos++] = 'R';
|
||||
dataOut[bytePos++] = 'N';
|
||||
dataOut[bytePos++] = 'F';
|
||||
dataOut[bytePos++] = 'R';
|
||||
dataOut[bytePos++] = 'M';
|
||||
dataOut[bytePos++] = (pre.len >> 24) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.len >> 16) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.len >> 8) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.len) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.width >> 8) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.width) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.height >> 8) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.height) & 0xFF;
|
||||
dataOut[bytePos++] = (pre.format) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 56) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 48) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 40) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 32) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 24) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 16) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us >> 8) & 0xFF;
|
||||
dataOut[bytePos++] = (time_us) & 0xFF;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Len: %ld, width:%d, height:%d, format:%d, timestamp: %lld",
|
||||
pre.len, pre.width, pre.height, pre.format, time_us);
|
||||
// Copy image data after PreData
|
||||
memcpy(dataOut + bytePos, data, pre.len);
|
||||
|
||||
*outSize = totalSize;
|
||||
return dataOut;
|
||||
}
|
||||
|
||||
esp_err_t camera_capture() {
|
||||
gpio_set_level(GPIO_NUM_4, 1);
|
||||
ESP_LOGI(TAG, "Pregrab");
|
||||
camera_fb_t *fb = esp_camera_fb_get();
|
||||
ESP_LOGI(TAG, "Postgrab");
|
||||
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAG, "Camera Capture Failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
PreData pre;
|
||||
pre.len = fb->len;
|
||||
pre.width = fb->width;
|
||||
pre.height = fb->height;
|
||||
pre.format = fb->format;
|
||||
pre.timestamp = fb->timestamp;
|
||||
|
||||
size_t totalSize;
|
||||
uint8_t *dataOut = create_frame(fb->buf, &totalSize, pre);
|
||||
if (!dataOut) {
|
||||
esp_camera_fb_return(fb);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
gpio_set_level(GPIO_NUM_4, 0);
|
||||
send_jpeg_nrf(dataOut, totalSize);
|
||||
|
||||
free(dataOut);
|
||||
|
||||
/*
|
||||
uint8_t testData[1000];
|
||||
memcpy(testData, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ut felis vitae magna laoreet interdum dictum auctor risus. Quisque quis enim sagittis, venenatis dui sed, fermentum tortor. Fusce euismod vulputate felis at commodo. Ut ac fermentum metus. Proin in nunc ac libero accumsan vehicula. Praesent posuere nulla sit amet odio blandit, in pharetra odio dapibus. Nulla id ultrices erat. Cras porta sed nunc vitae ultricies. Duis luctus ipsum quis mauris maximus, in venenatis dui venenatis. Integer nec tellus turpis. Nulla erat sapien, tempus at velit sit amet, maximus euismod ipsum. Vivamus ac orci mi. Sed elementum tortor ac nisi lobortis, in sodales est lacinia.\n\nVestibulum a eros ut metus imperdiet finibus. Vivamus vel lacus blandit, accumsan massa ut, elementum lorem. Maecenas ac neque nibh. Integer et nisi pellentesque, iaculis augue vitae, lobortis urna. Nullam venenatis viverra odio, sed molestie orci sagittis in. Curabitur iaculis sem finibus arcu viverra, quis imperdiet libe", 1000);
|
||||
|
||||
PreData pre;
|
||||
pre.len = sizeof(testData);
|
||||
pre.width = fb->width;
|
||||
pre.height = fb->height;
|
||||
pre.format = fb->format;
|
||||
pre.timestamp = fb->timestamp;
|
||||
|
||||
size_t totalSize;
|
||||
uint8_t *dataOut = create_frame(testData, &totalSize, pre);
|
||||
if (!dataOut) {
|
||||
esp_camera_fb_return(fb);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
send_jpeg_nrf(dataOut, totalSize);
|
||||
|
||||
free(dataOut);
|
||||
*/
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void init_nrf() {
|
||||
Nrf24_init(&dev);
|
||||
uint8_t payload = 32;
|
||||
uint8_t channel = CONFIG_RADIO_CHANNEL;
|
||||
Nrf24_config(&dev, channel, payload);
|
||||
|
||||
// Set destination address using 5 characters
|
||||
//esp_err_t ret = Nrf24_setTADDR(&dev, (uint8_t *)"CAMRX");
|
||||
esp_err_t ret = Nrf24_setTADDR(&dev, (uint8_t *)"CAM");
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(pcTaskGetName(NULL), "nrf24l01 not installed");
|
||||
while (1) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
ret = Nrf24_setRADDR(&dev, (uint8_t *)"CAM");
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(pcTaskGetName(NULL), "nrf24l01 not installed");
|
||||
while (1) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGW(pcTaskGetName(NULL), "Set RF Data Ratio to 250kBps");
|
||||
Nrf24_SetSpeedDataRates(&dev, 2);
|
||||
|
||||
|
||||
/*
|
||||
ESP_LOGW(pcTaskGetName(NULL), "Set RF Data Ratio to 2MBps");
|
||||
Nrf24_SetSpeedDataRates(&dev, 1);
|
||||
*/
|
||||
|
||||
|
||||
ESP_LOGW(pcTaskGetName(NULL), "Set RF max retransmits to 4 tries");
|
||||
Nrf24_setRetransmitCount(&dev, 15);
|
||||
|
||||
ESP_LOGW(pcTaskGetName(NULL), "CONFIG_RETRANSMIT_DELAY=2mS");
|
||||
Nrf24_setRetransmitDelay(&dev, 8);
|
||||
|
||||
// Print settings
|
||||
Nrf24_printDetails(&dev);
|
||||
}
|
||||
|
||||
|
||||
void main_task(void *arg) {
|
||||
|
||||
init_nrf();
|
||||
gpio_set_direction(4, GPIO_MODE_DEF_OUTPUT);
|
||||
ESP_ERROR_CHECK(camera_init());
|
||||
while (1) {
|
||||
|
||||
int64_t start = esp_timer_get_time();
|
||||
camera_capture();
|
||||
handle_radio_commands();
|
||||
|
||||
int64_t end = esp_timer_get_time();
|
||||
|
||||
int64_t duration = end - start;
|
||||
|
||||
printf("%lf FPS, %lf SPF\n", 1 / (duration / 1000000.0),
|
||||
duration / 1000000.0);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void) {
|
||||
|
||||
const uart_port_t uart_num = UART_NUM_0;
|
||||
uart_config_t uart_config = {.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE};
|
||||
uart_param_config(uart_num, &uart_config);
|
||||
uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0);
|
||||
|
||||
nvs_flash_init();
|
||||
|
||||
xTaskCreate(main_task, "main_task", 8192, NULL, 5, NULL); // 8 KB stack
|
||||
}
|
||||
Reference in New Issue
Block a user