NRF24 Communication Issues Between STM32F407 and Arduino Nano

I want to establish communication between an Arduino Nano and an STM32F407VGT6 using NRF24 modules. The Arduino Nano will send data, and the STM32 will receive it. However, the STM32 behaves very strangely. Sometimes it receives data from the Arduino, sometimes it doesn’t.

I suspected there might be an issue with the modules, so I tested the same module between two Arduinos and it worked flawlessly. The STM32 receives data in the morning but not in the evening. Sometimes it receives data correctly only once, then the STATUS register constantly returns the value 0x0E.

I monitored the SPI communication with a logic analyzer and it seems to work fine. I also placed a 0.1 µF ceramic capacitor between the RF+ and RF- lines, but it didn’t help.

I also suspected a voltage issue with the STM32’s 3.3V rail, so I powered the NRF24 module from an external regulated 3.3V source instead, but that didn’t solve the problem either.

As far as I can tell, the RF24 is properly configured on the STM32 side — when I read back the configuration registers, it shows that the module is in listening (RX) mode as expected.

What could be the reason for this problem?

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

#define CE_PIN 9
#define CSN_PIN 10

RF24 radio(CE_PIN, CSN_PIN);

const byte address[5] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};

#define PACKET_SIZE 32
//byte txData[PACKET_SIZE];
uint8_t index = 0;


byte txData[19]={0xFF,0xFB, 0xE7, 0x69, 0xC0, 0x20 ,0x45,0x44, 0x40 ,0xE9, 0xB6, 0x44, 0x2E, 0x38, 0xFD ,0x44, 0x40,0xCC,0xBB};

void setup() {
  Serial.begin(9600); // UART (C# haberleşmesi için)
  radio.begin();
  radio.openWritingPipe(address);
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(76);
  radio.setPALevel(RF24_PA_LOW);
  radio.setAutoAck(false);
  radio.stopListening();   
}

void loop() {

radio.write(&txData, PACKET_SIZE);
delay(1);
    
  }


kodu buraya yazın veya yapıştırın

My STM32 main.c

kodu buraya yazın veya ya/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include  "queue.h"
#include "timers.h"
#include "rf24.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi2;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
xTaskHandle task1;
xTaskHandle task2;
uint8_t rx_buffer[NRF_PAYLOAD_SIZE];
uint8_t tx_buffer[NRF_PAYLOAD_SIZE];


//BaseType_t status;
uint8_t status2;
uint8_t cfg;
uint32_t counter=0;
double sayi,sayi2;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
//static void task1_f(void* parameters);
//static void task2_f(void* parameters);


/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */



  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI2_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
 
  NRF24_Init();
  HAL_Delay(100);
  NRF24_SetRXMode();
  HAL_Delay(100);
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

	  if (NRF24_Receive(rx_buffer)) {
		  

	          // Veri geldi
		  	  HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
	          memcpy(&sayi, &rx_buffer[1], 8);
	          memcpy(&sayi2, &rx_buffer[9], 8);
	          memcpy(buff, &rx_buffer[1], 8);
	          NRF24_FlushRX();
	          status2=1;



	  }
	  else {
	
	  }

  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9|GPIO_PIN_11, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

  /*Configure GPIO pins : PE9 PE11 */
  GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
/*
 *
 *
 *






/* USER CODE END 4 */

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM6 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM6) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
pıştırın

RF24.C Driver

#include "rf24.h"
#include "main.h"

extern SPI_HandleTypeDef hspi2;

uint8_t status;

uint8_t tx_address1[5] = { 0xBB, 0xBB, 0xBB, 0xBB, 0xBB };
uint8_t rx_address1[5] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};

void NRF24_CSN_Select(void) {
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_RESET);
}

void NRF24_CSN_Unselect(void) {
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
}

void NRF24_CE_Enable(void) {
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_SET);
}

void NRF24_CE_Disable(void) {
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_RESET);
}

void NRF24_WriteRegister(uint8_t reg, uint8_t data) {
    NRF24_CSN_Select();
    uint8_t buf[2] = { (0x20 | reg), data };
    HAL_SPI_Transmit(&hspi2, buf, 2, 100);
    NRF24_CSN_Unselect();
}

void NRF24_WriteRegisterMulti(uint8_t reg, uint8_t* data, uint8_t length) {
    NRF24_CSN_Select();
    uint8_t cmd = 0x20 | (reg & 0x1F);
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_Transmit(&hspi2, data, length, 100);
    NRF24_CSN_Unselect();
}

void NRF24_ReadPayload(uint8_t *data, uint8_t size) {
    NRF24_CSN_Select();
    uint8_t cmd = 0x61; // R_RX_PAYLOAD
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi2, data, size, 100);
    NRF24_CSN_Unselect();
}

void NRF24_WritePayload(uint8_t *data, uint8_t size) {
    NRF24_CSN_Select();
    uint8_t cmd = 0xA0; // W_TX_PAYLOAD
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_Transmit(&hspi2, data, size, 100);
    NRF24_CSN_Unselect();
}

void NRF24_FlushRX(void) {
    NRF24_CSN_Select();
    uint8_t cmd = 0xE2;
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    NRF24_CSN_Unselect();
}

void NRF24_Init(void) {
    NRF24_CE_Disable();
    NRF24_CSN_Select();
    HAL_Delay(5);

    NRF24_WriteRegister(CONFIG, 0x0B); // RX mode, CRC enabled
    NRF24_WriteRegister(EN_AA, 0x00);  // Auto ACK kapalı
    NRF24_WriteRegister(EN_RXADDR, 0x01); // Pipe 0 aktif
    NRF24_WriteRegister(SETUP_AW, 0x03); // 5 byte adres
    NRF24_WriteRegister(SETUP_RETR, 0x00); // Retry yok
    NRF24_WriteRegister(RF_CH, 76); // Kanal 76
    NRF24_WriteRegister(RF_SETUP, 0x06); // 1 Mbps, 0 dBm
    NRF24_WriteRegister(RX_PW_P0, NRF_PAYLOAD_SIZE);
    NRF24_WriteRegisterMulti(TX_ADDR, tx_address1, 5);
    NRF24_WriteRegisterMulti(RX_ADDR_P0, rx_address1, 5);
    NRF24_FlushRX();
    NRF24_CE_Disable();
    NRF24_CSN_Unselect();

}

void NRF24_SetRXMode(void) {
    NRF24_CE_Disable();
    NRF24_WriteRegister(CONFIG, 0x0B); // RX mode + CRC + PWR_UP
    HAL_Delay(2);
    NRF24_CE_Enable();  // RX için CE HIGH olmalı
}


void NRF24_SetTXMode(void) {

    NRF24_WriteRegister(CONFIG, 0x0A); // TX mode
    HAL_Delay(2);

}

void NRF24_Transmit(uint8_t *data, uint8_t size) {
    NRF24_SetTXMode();
    NRF24_WritePayload(data, size);
    NRF24_CE_Enable();
    HAL_Delay(1);
    NRF24_CE_Disable();
    NRF24_SetRXMode();
}

uint8_t NRF24_ReadRegister(uint8_t reg) {
    uint8_t value;
    NRF24_CSN_Select();
    uint8_t cmd = reg & 0x1F;
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi2, &value, 1, 100);
    NRF24_CSN_Unselect();
    return value;
}


void NRF24_ReadRegisteraddr(uint8_t reg, uint8_t* deger) {

    NRF24_CSN_Select();
    uint8_t dummy[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    uint8_t cmd = reg & 0x1F;
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_TransmitReceive(&hspi2, dummy, deger, 5, 100);
    NRF24_CSN_Unselect();

}


uint8_t NRF24_GetStatus(void) {
    uint8_t status;
    uint8_t cmd = 0xFF;

    NRF24_CSN_Select();
    HAL_SPI_TransmitReceive(&hspi2, &cmd, &status, 1, 100);

    NRF24_CSN_Unselect();

    return status;
}

uint8_t NRF24_Receive(uint8_t *data) {
    status = NRF24_GetStatus();


    if (status & 0x40) { // RX_DR bit kontrolü

        NRF24_ReadPayload(data, NRF_PAYLOAD_SIZE);
        NRF24_WriteRegister(STATUS, 0x40); // RX_DR bayrağını temizle

        return 1; // Veri geldi

    }
    return 0; // Veri yok

}
void nrfsendCmd (uint8_t cmd)
{
    // CS pinini seç (LOW yap)
    NRF24_CSN_Select();

    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);

    // CS pinini bırak (HIGH yap)
    NRF24_CSN_Unselect();
}

void NRF24_ResetModule(void) {
			NRF24_CE_Disable();
             HAL_Delay(10);
             NRF24_WriteRegister(STATUS, 0x70); // tüm IRQ bayraklarını temizle
             nrfsendCmd(FLUSH_TX);
             nrfsendCmd(FLUSH_RX);
             NRF24_WriteRegister(CONFIG, 0x0B); // RX mode, CRC enabled, PWR_UP
             NRF24_CE_Enable();
             HAL_Delay(10);
         }

rf24.h

/*
 * rf24.h
 *
 *  Created on: Jul 5, 2025
 *      Author: Emirhan Akbulut
 */
#include "stm32f4xx_hal.h"
#include "stdio.h"
#include "stdint.h"

#ifndef INC_RF24_H_
#define INC_RF24_H_

#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 DYNPDc						0x1C
#define FEATUREc					0x1D

#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2

//MAKROLAR
#define NRF_CONFIG_PRX    			0x0B   // RX MOD AKTİF
#define NRF_CONFIG_PTX    			0x0A   // TX MOD AKTİF
#define NRF_CHANNEL       			76
#define NRF_ADDR_WIDTH    			0x03  // 5 byte adres
#define NRF_EN_RXADDR     			0x01
#define NRF_EN_AA         			0x01
#define NRF_SETUP_RETR    			0x03
#define NRF_RF_SETUP      			0x06
#define NRF_PAYLOAD_SIZE			32


void NRF24_FlushRX(void);
uint8_t NRF24_GetStatus(void);
void NRF24_Init(void);
void NRF24_SetRXMode(void);
void NRF24_SetTXMode(void);
void NRF24_Transmit(uint8_t *data, uint8_t size);
uint8_t NRF24_Receive(uint8_t *data);
uint8_t NRF24_ReadRegister(uint8_t reg);
void NRF24_WriteRegister(uint8_t reg, uint8_t data);
void NRF24_ReadRegisteraddr(uint8_t reg, uint8_t* deger);
void NRF24_ResetModule(void);

#endif /* INC_RF24_H_ */

sounds like it could be a power supply problem
have a look at Power Stability Issues with RF24 Radio Modules by @gilshultz

If it works generally, but not sometimes, I would suggest simple radio interference etc. especially since you don't seem to be using the ESB functionality, auto-ack etc. and it looks like you have tried a separate power supply.

Have you tried just using a different channel? There is a scanner example included with the library that lets you see how busy the available channels are.

You can also try raising the PA Level on the Arduino with radio.setPALevel(RF24_PA_MAX,1);, and I would make sure to lower it on the STM32 device.

Also, enabling the Auto-Ack functionality will let the radios retry failed payloads up to 15 times, so it might make a difference.

If none of this helps, I would first re-visit power supply issues and then look closer at your code.