Original post - 12/2022
I'm currently trying to transmit audio via nRF24 with an ESP32 development board using a driver library from nopnop2002. I'm using the i2s-adc lib functions to get data from an analog microphone and save it into a buffer after scaling it down to 8-bit so that the other esp32 can play it directly with its DAC converter.
At first, I had a large i2s_read_len and later I suspected that this could be an issue so I decided to reduce it to 12-bit (the ADC bit width of the esp32) for testing purposes. What is the ideal size for my audio packets?
I'm also not sure how to handle the sampling rate of i2s_read and the rate at which I attempt to send data to the nRF24 via SPI. As far as I know the internal ADC-I2S already has a queue implemented, should I make another one and use vTaskDelay(??? / portTICK_PERIOD_MS); to avoid sending data too fast?
Here's what I currently have ignoring the includes:
#define V_REF 1100
#define I2S_COMM_MODE 0 // ADC/DAC Mode
#define I2S_SAMPLE_RATE 44100
#define I2S_SAMPLE_BITS 16
#define I2S_BUF_DEBUG 0 // enable display buffer for debug
#define I2S_READ_LEN 16 * 1024 // I2S read buffer length
#define I2S_FORMAT (I2S_CHANNEL_FMT_ONLY_RIGHT)
#define I2S_CHANNEL_NUM 0 // I2S channel number
#define I2S_ADC_UNIT ADC_UNIT_1 // I2S built-in ADC unit
#define I2S_ADC_CHANNEL ADC1_CHANNEL_0 // I2S built-in ADC channel GPIO36
#define BIT_SAMPLE 16
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#define NUM_CHANNELS 1 // For mono recording only!
#define SAMPLE_SIZE (BIT_SAMPLE * 1024)
#define BYTE_RATE (I2S_SAMPLE_RATE * (BIT_SAMPLE / 8)) * NUM_CHANNELS
/**
* #brief I2S ADC mode init.
*/
void init_microphone(void)
{
int i2s_num = I2S_COMM_MODE;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_SAMPLE_BITS,
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.channel_format = I2S_FORMAT,
.intr_alloc_flags = 0,
.dma_buf_count = 6,
.dma_buf_len = 256,
.use_apll = 1,
};
// Call driver installation function and adc pad.
ESP_ERROR_CHECK(i2s_driver_install(i2s_num, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL));
}
/**
* #brief Scale data to 8bit for data from ADC.
* Data from ADC are 12bit width by default.
* #param d_buff: destination buffer
* #param s_buff: source buffer
* #param len: length of source buffer
*/
void i2s_adc_data_scale(uint8_t *d_buff, uint8_t *s_buff, uint32_t len)
{
uint32_t j = 0;
uint32_t dac_value = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
for (int i = 0; i < len; i += 2)
{
dac_value = ((((uint16_t)(s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0]))));
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#else
for (int i = 0; i < len; i += 4)
{
dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#endif
}
#if CONFIG_TRANSMITTER
void transmitter(void *pvParameters)
{
size_t bytes_read;
ESP_LOGI(pcTaskGetName(0), "Start");
int i2s_read_len = (12);
char *i2s_read_buff = (char *)calloc(i2s_read_len, sizeof(char));
uint8_t *i2s_write_buff = (uint8_t *)calloc(i2s_read_len, sizeof(char));
i2s_adc_enable(I2S_CHANNEL_NUM);
NRF24_t dev;
Nrf24_init(&dev);
uint8_t payload = sizeof(i2s_read_buff);
uint8_t channel = 90;
Nrf24_config(&dev, channel, payload);
// Set the receiver address using 5 characters
esp_err_t ret = Nrf24_setTADDR(&dev, (uint8_t *)"FGHIJ");
if (ret != ESP_OK)
{
ESP_LOGE(pcTaskGetName(0), "nrf24l01 not installed");
while (1)
{
vTaskDelay(1);
}
}
#if CONFIG_ADVANCED
AdvancedSettings(&dev);
#endif // CONFIG_ADVANCED
// Print settings
Nrf24_printDetails(&dev);
// Start ADC
while (1)
{
// Read data from I2S bus, in this case, from ADC. //
i2s_read(I2S_CHANNEL_NUM, (void *)i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
// process data and scale to 8bit for I2S DAC.
i2s_adc_data_scale(i2s_write_buff, (uint8_t *)i2s_read_buff, i2s_read_len);
// i2s_write_buff needs to be the buffer that is sent via nr24l01.
Nrf24_send(&dev, i2s_write_buff);
if (Nrf24_isSend(&dev, 1000))
{
ESP_LOGI(pcTaskGetName(0), "sending audio data ...");
}
else
{
ESP_LOGW(pcTaskGetName(0), "sending failed ...");
}
}
}
#endif // CONFIG_TRANSMITTER
void app_main(void)
{
// I2S ADC mode microphone init.
init_microphone();
#if CONFIG_TRANSMITTER
xTaskCreate(transmitter, "TRANSMITTER", 1024 * 3, NULL, 2, NULL);
#endif
// Stop I2S driver and destroy
// ESP_ERROR_CHECK(i2s_driver_uninstall(I2S_COMM_MODE));
}
Here is the code where Nrf24_send is defined:
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include "esp_log.h"
#include "mirf.h"
#define TAG "NRF24"
// SPI Stuff
#if CONFIG_SPI2_HOST
#define HOST_ID SPI2_HOST
#elif CONFIG_SPI3_HOST
#define HOST_ID SPI3_HOST
#endif
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"};
const char rf24_crclength[][10] = {"Disabled", "8 bits", "16 bits"};
const 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;
}
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_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[5];
Nrf24_readRegister(dev, RX_ADDR_P0, 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;
}
// 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);
if ( status & (1 << RX_DR) ) 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);
return ((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
}
// 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;
}
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 TX 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) ) );
}
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_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_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, "EN_AA\t", EN_AA, 1);
Nrf24_print_byte_register(dev, "EN_RXADDR", EN_RXADDR, 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, "CONFIG\t", CONFIG, 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)]);
#if 0
printf_P(PSTR("Model\t\t = "
PRIPSTR
"\r\n"),pgm_read_ptr(&rf24_model_e_str_P[isPVariant()]));
#endif
//printf("getCRCLength()=%d\n",Nrf24_getCRCLength(dev));
printf("CRC Length\t = %s\n", rf24_crclength[Nrf24_getCRCLength(dev)]);
//printf("getPALevel()=%d\n",Nrf24_getPALevel(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;
}
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);
}
uint8_t Nrf24_getRetransmitDelay(NRF24_t * dev)
{
uint8_t value;
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
return (value >> 4);
}
I'm getting the following output:
...
W (19498) NRF24: Maximum number of TX retries interrupt
W (19498) TRANSMITTER: sending failed ...
...
Update: Transmission succeeds but audio is not inteligible - 02/2023
I found out that I'm using Si24R1 (Chinese clone of nRF24L01) and I'm still using the i2s-adc esp-idf lib functions to get data from an analog microphone and I need to get this I2S stream through the SPI interface between the ESP32 and the nRF24L01 breakout board clone. Is this possible?
I've seen some people mention on the internet that Nordic Semiconductors has nRF chips that support I2S but I would rather stick to the hardware I have available.
Here's the link I've consulted.
I've also found this library that supports audio transmission using the nRF24L01/Si24R1 chip using the nrf24.h lib but it is writeen in c++ and it is not clear to me how I could integrate it into my project.
Let me know what you think the problem is or/and if you have good recommendations for learning material about this subject.
Thanks for the help in advance!
Do your hardware works with a simple sender/receiver from sample codes you can find in adruino ide? If you didn't checked, have a try so you can validate the hardware is ok.
If simple sender/receiver doesn't work, check your power supply lines. Sometimes not enough current from the voltage regulator.
Add some caps on the rf24 module power-supply pins to filter some noise can also helps depending on the quality of the power supply.
./X
I'm trying to interface vl53l0x sensor with my atSamE51A19 controller. I can send the data to the sensor. As it has established connection I send identification code and it responds. But further initialization and ranging is not working.
I'm using stm library implementation on my sam board. [stmArduinoLib][1]
The only changes I made is in the read write functions. It was using stm base functions but I'm using different functions.
/****************** Write and read functions from I2C *************************/
VL53L0X_Error VL53L0X::VL53L0X_WriteMulti(VL53L0X_DEV Dev, uint8_t index, uint8_t *pdata, uint32_t count)
{
int status;
status = VL53L0X_I2CWrite(index, pdata, (uint16_t) count);
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_ReadMulti(VL53L0X_DEV Dev, uint8_t index, uint8_t *pdata, uint32_t count)
{
int status;
if (count >= VL53L0X_MAX_I2C_XFER_SIZE)
{
status = VL53L0X_ERROR_INVALID_PARAMS;
}
status = VL53L0X_I2CRead(index, pdata, (uint16_t) count);
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrByte(VL53L0X_DEV Dev, uint8_t index, uint8_t data)
{
int status;
status = WriteRegister(index, data); //VL53L0X_I2CWrite(index, &data, 1);
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrWord(VL53L0X_DEV Dev, uint8_t index, uint16_t data)
{
int status;
int32_t status_int;
alignas(2) uint8_t buffer[2];
buffer[0] = data >> 8;
buffer[1] = data & 0x00FF;
status_int = VL53L0X_I2CWrite(index, (uint8_t*) buffer, 2);
// buffer[0] = MSB
// buffer[1]
// buffer[2]
// buffer[3] = LSB
if (status_int == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrDWord(VL53L0X_DEV Dev, uint8_t index, uint32_t data)
{
int status;
int32_t status_int;
alignas(2) uint8_t buffer[4];
buffer[0] = (data >> 24) & 0xFF; // MSB
buffer[1] = (data >> 16) & 0xFF;
buffer[2] = (data >> 8) & 0xFF;
buffer[3] = (data >> 0) & 0xFF; // LSB
status_int = VL53L0X_I2CWrite(index, buffer, 4);
if (status_int == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdByte(VL53L0X_DEV Dev, uint8_t index, uint8_t *data)
{
int status = VL53L0X_ERROR_NONE;
status = ReadRegister(index, *data); //
if (status)
{
status = VL53L0X_ERROR_NONE;
return status;
}
else
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdWord(VL53L0X_DEV Dev, uint8_t index, uint16_t *data)
{
int status = 0;
uint8_t buffer[2] =
{ 0, 0 };
status = VL53L0X_I2CRead(index, buffer, 2);
if (status == 0)
{
*data = (uint16_t) (buffer[0] << 8) | (buffer[1] & 0xFF); // | ? +
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdDWord(VL53L0X_DEV Dev, uint8_t index, uint32_t *data)
{
int status;
uint8_t buffer[4] =
{ 0, 0, 0, 0 };
status = VL53L0X_I2CRead(index, buffer, 4);
if (!status) // use OR
{
*data = ((uint32_t) buffer[0] << 24) | ((uint32_t) buffer[1] << 16) | ((uint32_t) buffer[2] << 8) | (uint32_t) buffer[3];
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_UpdateByte(VL53L0X_DEV Dev, uint8_t index, uint8_t AndData, uint8_t OrData)
{
int status;
uint8_t buffer = 0;
/* read data direct onto buffer */
status = VL53L0X_I2CRead(index, &buffer, 1);
if (!status)
{
buffer = (buffer & AndData) | OrData;
status = VL53L0X_I2CWrite(index, &buffer, (uint8_t) 1);
}
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_I2CWrite(uint8_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToWrite)
{
return WriteRegisters(RegisterAddr, pBuffer, NumByteToWrite);
}
VL53L0X_Error VL53L0X::VL53L0X_I2CRead(uint8_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToRead)
{
alignas(2) uint8_t vpBuffer[5];
/* dev_i2c->beginTransmission(((uint8_t) (((DeviceAddr) >> 1) & 0x7F)));
*/
if (ReadRegisters(RegisterAddr, vpBuffer, NumByteToRead))
{
pBuffer = vpBuffer;
return 0;
}
return 1;
}
In this functions I'm not sending device address everytime just sending the index and data. In constructor only I've set the address once. The only error I'm getting when I use Status = VL53L0X_PerformRefSpadManagement(Device, &refSpadCount, &isApertureSpads); This function. In initialization. Don't know how to solve this issue. Tried to change the mode to continous that didn't help. Note: the sensor is working fine with arduino.
[1]: https://github.com/stm32duino/VL53L0X
I found a new possible solution to this problem, as I spent 2 days to solve it, I feel that I need to share it with you guys. Here is the summary of solutions:
Powering problem 1: add a 50ms delay in the setup function (before the sensor init, obviously). This way, the Vin will have time enough to reach the level needed by the sensor, as explained in a previous message (ocrdu)
Powering problem 2: This sensor demands more than others, normally the 5V pin of your board will do it better than the 3v3. If there are many sensors connected at the same time, disconnect all of them to check whether they are using the power our vl53l0x needs.
Powering problem 3: If you feed it with an external source, unify grounds with the board, don't forget it
SCL - SDA weak signals: add a pullup (2k2 - 4k7 from each channel to vin), this way the signals will perform better, that could be the problem
Soldering problem: Make sure that each header pin is well soldered. I've found many cases of poor soldering. The tiny pads don't help. It is advisable to check continuity with a multimeter between the headers and the pads where they should be connected
XSHUT not pulled up: My problem, this was that AZDelivery board says that it has a pull up in XSHUT (which works like an enable). But it could either be false, or badly implemented. Xshut needs to be high, 3v3/5v, otherwise, the sensor won't work. Bridge the XSHUT to Vin or implement a pull up and the problem will be solved.
I am using adafruit's library 1.1.2, and it works, with pullups, without pullups, connected to 5V and also connected to 3V3, with the
50ms delay, and connecting XSHUT to high (3V3 / Vin)
My board is vl53l0x from AZDelivery (5€ amazon)
I have tested the sensor with arduino nano and with ESP32, both work fine
Hope you guys find this useful <3
I'm trying to modify the UDP payload I receive from the client to clone and redirect the packet in order to respond to it. Swapping the MAC and IP addresses is already done, as well as the cloning, but I don't know how to modify the UDP payload.
I want to send a payload with the following 3 bytes: 0x1a 0x31 0x0f back to the client, either by creating a new payload (better) and replacing it in the packet or by replacement. How can I do that?
Here is my code so far:
UPDATED BASED ON COMMENTS (Now only needs checksum recalculation):
int pingpong(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udpdata;
if ((void *)eth + sizeof(*eth) > data_end) {
return TC_ACT_UNSPEC;
}
ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end) {
return TC_ACT_UNSPEC;
}
udpdata = (void *)ip + sizeof(*ip);
if ((void *)udpdata + sizeof(*udpdata) > data_end) {
return TC_ACT_UNSPEC;
}
if (eth->h_proto != htons(ETH_P_IP)) {
return TC_ACT_UNSPEC;
}
if (ip->protocol != IPPROTO_UDP) {
return TC_ACT_UNSPEC;
}
unsigned int payload_size;
unsigned char *payload;
payload_size = ntohs(udpdata->len) - sizeof(*udpdata);
payload = (unsigned char *)udpdata + sizeof(*udpdata);
if ((void *)payload + payload_size > data_end) {
return TC_ACT_UNSPEC;
}
// 1. Swap the MACs
__u8 tmp_mac[ETH_ALEN];
memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
memcpy(eth->h_source, tmp_mac, ETH_ALEN);
// 2. Swap the IPs
if (eth->h_proto == htons(ETH_P_IP)) {
__u32 tmp_ip = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp_ip;
}
// 3. Swap the ports
udpdata->source = port;
udpdata->dest = srcport;
// 4. Change the payload to be 0x1a 0x31 0x0f
bpf_skb_adjust_room(skb, -1, BPF_ADJ_ROOM_NET, 0);
uint8_t byte1 = 0x1a;
uint8_t byte2 = 0x31;
uint8_t byte3 = 0x0f;
int ret = bpf_skb_store_bytes(skb, payload_offset, &byte1, sizeof(byte1), 0);
ret = bpf_skb_store_bytes(skb, payload_offset+1, &byte2, sizeof(byte2), 0);
ret = bpf_skb_store_bytes(skb, payload_offset+2, &byte3, sizeof(byte3), 0);
// Re-calculate the checksum
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[0], byte1, 0);
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[1], byte2, 0);
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[2], byte3, 0);
// Not working!
// Final: Redirect to be sent
bpf_clone_redirect(skb, skb->ifindex, 0);
}
If I just change the payload without removing the 1 byte using the adjust_room function, it is sent, but the last byte is 00. I want to remove that.
Any tips please? Thanks!
As suggested by #Qeole, you can use the following to change the size and content of your UDP payload:
// Remove 1 byte after IP header.
bpf_skb_adjust_room(ctx, -1, BPF_ADJ_ROOM_NET, 0);
... re-check packet pointers or verifier will complain ...
// Need to rewrite the UDP header as the extra space was added before it.
*new_udphdr = *old_udphdr;
// Write payload.
udp_payload[0] = 0x1a
udp_payload[1] = 0x31
udp_payload[2] = 0x0f
My brother and I have been trying to get this working for days now and we just can't figure out what we are doing wrong, we could really use some help please!
What we're trying to accomplish is reading in data from a light sensor on an RPI expansion board through our own program written in C, using the BCM2835 library.
This is the light sensor we are using: TSL2561
https://cdn-shop.adafruit.com/datasheets/TSL2561.pdf
We are using a raspberry pi B+ model with Raspbian installed on it (through noobs).
This is the C library we are using:
http://www.airspayce.com/mikem/bcm2835/
I activated I2c through the raspian configuration.
and I detected the light sensor through i2ctools and it shows up correctly with adress 0x29.
We get 0 values back with our reads, and we tested our commands with an osciloscope and it seems he does ACK the write commands. he just doesn't send anything back...
Can someone please help us ?
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint16_t clk_div = BCM2835_I2C_CLOCK_DIVIDER_148;
uint8_t slave_address = 0x29; //0101001 - this is the sensor address
uint64_t delay = 70000;
int main(int argc, char **argv)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
char buffer[10]={0};
char addr;
uint16_t data;
uint8_t data2;
int i =0;
char writeBuff[1] = {0x8C}; ////Address the Ch0 lower data register
char readBuff[10];
char writeBuff2[2] = {0x8D}; ////Address the Ch0 upper data register
char readBuff2[10];
char *wb_ptr,*r_ptr,*wb_ptr2,*r_ptr2;
wb_ptr = writeBuff;
wb_ptr2 = writeBuff2;
r_ptr = readBuff;
r_ptr2 = readBuff2;
printf("Running ... \n");
bcm2835_init():
bcm2835_i2c_begin();
bcm2835_i2c_setSlaveAddress(slave_address); //0x29
printf("Clock divider set to: %d\n", clk_div);
printf("Slave address set to: %d or %X\n",slave_address,slave_address);
//needed according to datasheet to read although unsure if it needs to be sent in two writes or in one ? 0x83 instead of 0x80 + 0x03 ?
bcm2835_i2c_write(0x80, 1); //command register
bcm2835_i2c_write(0x03, 1); //command itself
bcm2835_delayMicroseconds(delay);
//--------------------------
while (1)
{
printf("reading data from light sensor\n");
bcm2835_i2c_write(wb_ptr, 1); // 0x8C
bcm2835_delayMicroseconds(delay);
data = bcm2835_i2c_read(readBuff,1);
bcm2835_delayMicroseconds(delay);
printf("Read Result 1 = %d\n", data);
bcm2835_i2c_write(wb_ptr2, 1); //0x8D
bcm2835_delayMicroseconds(delay);
data2 = bcm2835_i2c_read(readBuff2,1);
bcm2835_delayMicroseconds(delay);
printf("Read Result 2 = %d\n", data);
bcm2835_delay(1000);
}
bcm2835_i2c_end();
bcm2835_close();
printf("... done\n");
return 0;
}
This is a Quick edit
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint16_t clk_div = BCM2835_I2C_CLOCK_DIVIDER_148;
uint8_t slave_address = 0x29; //0101001 - this is the sensor address
uint64_t delay = 70000;
int main(int argc, char **argv)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
/*
enum bcm2835I2CReasonCodes { BCM2835_I2C_REASON_OK = 0x00, BCM2835_I2C_REASON_ERROR_NACK = 0x01, BCM2835_I2C_REASON_ERROR_CLKT = 0x02, BCM2835_I2C_REASON_ERROR_DATA = 0x04 }
*/
char buffer[10]={0};
char addr;
uint16_t data;
uint8_t data2;
uint8_t error = 0xff;
int i =0;
char writeBuff[1] = {0x8C}; ////Address the Ch0 lower data register
char readBuff[10];
char writeBuff2[2] = {0x8D}; ////Address the Ch0 upper data register
char readBuff2[10];
char writeBuff3[2] = {0x80};
char writeBuff4[2] = {0x03};
char *wb_ptr,*r_ptr,*wb_ptr2,*r_ptr2,*r_ptr3,*r_ptr4;
wb_ptr = writeBuff;
wb_ptr2 = writeBuff2;
r_ptr = readBuff;
r_ptr2 = readBuff2;
r_ptr3 = writeBuff3;
r_ptr4 = writeBuff4;
printf("Running ... \n");
bcm2835_init();
bcm2835_i2c_begin();
bcm2835_i2c_setSlaveAddress(slave_address); //0x29
printf("Clock divider set to: %d\n", clk_div);
printf("Slave address set to: %d or %X\n",slave_address,slave_address);
//needed according to datasheet to read although unsure if it needs to be sent in two writes or in one ? 0x83 instead of 0x80 + 0x03 ?
bcm2835_i2c_write(r_ptr3, sizeof(r_ptr3)); //command register
bcm2835_i2c_write(r_ptr4, sizeof(r_ptr4)); //command itself
bcm2835_delayMicroseconds(delay);
//--------------------------
// Blink
while (1)
{
printf("reading data from light sensor\n");
error = bcm2835_i2c_write(wb_ptr, sizeof(wb_ptr)); // 0x8C
bcm2835_delayMicroseconds(delay);
data = bcm2835_i2c_read(readBuff,sizeof(readBuff));
bcm2835_delayMicroseconds(delay);
printf("readbuff1 = 0x%02X \n",readBuff);
printf("error result = 0x%02X\n", error);
printf("Read Result 1 = 0x%02X\n", data);
error = bcm2835_i2c_write(wb_ptr2, sizeof(wb_ptr2)); //0x8D
bcm2835_delayMicroseconds(delay);
data2 = bcm2835_i2c_read(readBuff2,sizeof(readBuff2));
bcm2835_delayMicroseconds(delay);
printf("readbuff2 = 0x%02X \n",readBuff2);
printf("error result = 0x%02X\n", error);
printf("Read Result 2 = 0x%02X\n", data2);
bcm2835_delay(1000);
}
bcm2835_i2c_end();
bcm2835_close();
printf("... done\n");
return 0;
}
I managed to fix it, I had several problems , one of which was that I was adressing the wrong adress to send commands, it has to be 0xa0 instead of 0x80.
#include <bcm2835.h>
#include <stdio.h>
int main(void)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
/*
enum bcm2835I2CReasonCodes { BCM2835_I2C_REASON_OK = 0x00, BCM2835_I2C_REASON_ERROR_NACK = 0x01, BCM2835_I2C_REASON_ERROR_CLKT = 0x02, BCM2835_I2C_REASON_ERROR_DATA = 0x04 }
*/
if (!bcm2835_init())
return 1;
char uitgelezenTempWaarde[1];
int totalTemp[2];
int error =0;
bcm2835_i2c_begin(); // start i2c
bcm2835_i2c_setSlaveAddress(0x29); // slave address
bcm2835_i2c_set_baudrate(1000); // default
//----------- turn channels on for measurement ------
uitgelezenTempWaarde[0] = 0xa0;
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
uitgelezenTempWaarde[0] = 0x03;
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
bcm2835_delay(500);
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
//----------- read data ------
uitgelezenTempWaarde[0] = 0xac; //DATA0LOW Ch 7:0 ADC channel 0 lower byte
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
totalTemp[1]= (int)uitgelezenTempWaarde[0];
uitgelezenTempWaarde[0] = 0xad; //DATA0HIGH Dh 7:0 ADC channel 0 upper byte
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
totalTemp[0] = (int)uitgelezenTempWaarde[0]; //
totalTemp[0] *= 256; //hex conversion for the highest byte so it is seen as a high number (16 bits)
printf("The light value is :%d\n",totalTemp[0]+totalTemp[1]);
bcm2835_i2c_end();
bcm2835_close();
return 0;
}
I'm trying to use SPI on Raspberry PI using spidev and his test code as skeleton for my own. I have a problem with table size. My table has different size before I pass it to transfer function i.e. my table has 3 elements, inside function it has 4. Here is my code:
// spi.cpp : Defines the entry point for the console application.
//
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static const char *device = "/dev/spidev0.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 1000000;
static uint16_t delay;
//this function gives problems with diffrent size of array
static void transfer(int fd, uint8_t tx[])
{
printf("transfer1");
printf(" rozmiar tab=%d ", ARRAY_SIZE(tx));
int ret;
uint8_t rx[ARRAY_SIZE(tx)] = { 0, };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = 3,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%d. %.2X ", ret,rx[ret]);
}
puts("");
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
fd = open(device, O_RDWR);
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
uint8_t tx1[] = {
0x0, 0x1b, 0xa5
};
//here I'm passing table to function
transfer(fd, tx1);
uint8_t tx2[] = {
0x0, 0x33, 0x30, 0x01, 0x02
};
printf(" %d. ", ARRAY_SIZE(tx2));
transfer(fd, tx2);
uint8_t tx3[] = {
0x0, 0x52, 0x90
};
transfer(fd, tx3);
uint8_t tx4[] = {
0x80, 0x60
};
printf(" %d. ", ARRAY_SIZE(tx4));
transfer(fd, tx4);
close(fd);
return ret;
}
An array sent as a parameter to a function is treated as a pointer. Your ARRAY_SIZE(a) is roughly expanded to (sizeof(uint8_t*) / sizeof(uint8_t)), which equals 4 on 32-bit platform.
You should explicitly pass array size as a 3rd parameter:
void transfer(int fd, const uint8_t *tx, size_t size) { ... }
Then, you can use your macro to properly calculate array size:
transfer(fd, tx1, ARRAY_SIZE(tx1));
If tx is not being modified inside transfer(), it is a matter of good taste to make it const.