rak3172 모듈과 Nucleo 보드를 연결하여 수신 게이트웨이 구현.
1. 구성
■수신기
RAK3172_IO ==== Nucleo STM32F446RE ==== PC(UART)
■송신기
RAK3172_IO(USB) ==== PC(UART)
송신기에서 SEND Message전송 시 수신기에서 디버깅 메시지 출력
예1)
송신기
AT+PSEND=02011001011103

수신기

예2)
송신기
AT+PSEND=0201100201021003

수신기

예3)
송신기
AT+PSEND=020110030102031203

수신기

2. 테스트 코드
- 환경 STM32CubeIDE 1.8.0
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.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 ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int fd, char *ptr, int len)
{
HAL_UART_Transmit(&huart2, (unsigned char*)ptr, len, HAL_MAX_DELAY);
return len;
}
/*
uint8_t rx_byte;
uint8_t rx_buf[128];
uint16_t rx_idx = 0;
void AT_Send(char *cmd)
{
HAL_UART_Transmit(&huart1, (uint8_t *)cmd, strlen(cmd), HAL_MAX_DELAY);
}
void AT_Receive(void)
{
if (HAL_UART_Receive(&huart1, &rx_byte, 1, 10) == HAL_OK)
{
if (rx_idx < sizeof(rx_buf) - 1)
{
rx_buf[rx_idx++] = rx_byte;
rx_buf[rx_idx] = 0; // 문자?�� 종료
}
}
}*/
#define state_AT 0
#define state_AT_OK 1
#define state_VER 2
#define state_VER_OK 3
#define state_NWM 4
#define state_NWM_OK 5
#define state_P2P 6
#define state_P2P_OK 7
#define state_PRECV 8
#define state_PRECV_OK 9
#define state_PSEND 10
#define state_PSEND_OK 11
#define state_IDLE_PRE 12
#define state_IDLE 100
#define state_Respon_PSEND 13
#define state_Respon_PSEND_OK 14
#define rx_size 256
uint8_t rx_char;
uint8_t rx_line[rx_size];
uint8_t rx_idx = 0;
volatile uint8_t data_ready = 0;
volatile uint8_t done_state =0;
volatile uint8_t state =0;
volatile uint8_t at_ok = 0;
volatile uint8_t at_error = 0;
#define LINE_SIZE 256
#define DATA_SIZE 256
uint8_t line_buf[LINE_SIZE];
uint8_t line_idx = 0;
uint8_t data_buf[DATA_SIZE];
typedef struct {
int rssi;
int snr;
char payload[64];
} rxp2p_t;
rxp2p_t rxp2p;
#define rx_SIZE 256
uint8_t rx_buf[rx_SIZE];
void Process_Line(char *line)
{
strncpy((char *)data_buf, line, LINE_SIZE - 1);
data_buf[LINE_SIZE - 1] = '\0';
data_ready = 1;
done_state=1;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint8_t prev1_char = 0;
if (huart->Instance == USART1)
{
/* LF 기준 라인 종료 (CR 있으면 제거) */
if (rx_char == '\n')
{
if (line_idx > 0 && prev1_char == '\r'){
line_buf[line_idx - 1] = '\0';
}
else
line_buf[line_idx] = '\0';
Process_Line((char *)line_buf);
line_idx = 0;
}
else
{
line_buf[line_idx++] = rx_char;
if (line_idx >= LINE_SIZE - 1)
line_idx = 0;
}
prev1_char = rx_char;
HAL_UART_Receive_IT(&huart1, &rx_char, 1);
}
//if (huart->Instance != USART1) return;
/*
// '\n'은 무시
if (rx_char == '\n')
{
HAL_UART_Receive_IT(&huart1, &rx_char, 1);
return;
}
// 한 줄 끝
if (rx_char == '\r')
{
line_buf[line_idx] = 0;
// OK 라인이 아니면 데이터로 저장
if (!(line_idx == 2 &&
line_buf[0] == 'O' &&
line_buf[1] == 'K'))
{
memcpy(&data_buf[data_idx], line_buf, line_idx);
data_idx += line_idx;
data_buf[data_idx++] = '\n';
}
else
{
at_ok = 1; // OK 감지
line_ready = 1; // 명령 완료
}
line_idx = 0;
}
else
{
line_buf[line_idx++] = rx_char;
}
// 보호
if (line_idx >= LINE_SIZE - 1) line_idx = 0;
if (data_idx >= DATA_SIZE - 1) data_idx = 0;
HAL_UART_Receive_IT(&huart1, &rx_char, 1);
*/
}
void rak_send_cmd(char *cmd)
{
HAL_UART_Transmit(&huart1, (uint8_t *)cmd, strlen(cmd), 100);
HAL_UART_Transmit(&huart1, (uint8_t *)"\r\n", 2, 100);
}
uint8_t buff_VER[30];
//uint8_t buff_SYSV[10];
//uint8_t buff_NWM[5];
//uint8_t buff_P2P[20];
//uint8_t buff_PRECV[10];
void rak_send(void){
//if(done_state) return;
switch (state){
case state_AT:
{
done_state=0;
rak_send_cmd("AT");
state = state_AT_OK;
break;
}
case state_AT_OK:
{
if(done_state == 1 && at_ok ==0 &&data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at_ok\r\n");
done_state =0;
state=state_VER;
}
break;
}
case state_VER:
{
at_ok=0;
done_state=0;
rak_send_cmd("AT+VER=?");
state = state_VER_OK;
break;
}
case state_VER_OK:
{
if(done_state ==1){
if(data_buf[0] != 'O' && data_buf[1] !='K'){
uint8_t len = strlen((char *)data_buf);
if (len >= sizeof(buff_VER))
len = sizeof(buff_VER) - 1;
memcpy(buff_VER, data_buf, len);
buff_VER[len] = 0;
printf("buff_ver = [%s]\r\n",buff_VER);
}
if(at_ok ==0 && data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at ver ok\r\n");
}
done_state =0;
state = state_NWM;
}
break;
}
case state_NWM:
{
at_ok=0;
done_state=0;
rak_send_cmd("AT+NWM=0");
state = state_NWM_OK;
break;
}
case state_NWM_OK:
{
if(done_state == 1){
if(at_ok ==0 && data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at nwm ok\r\n");
}
else {
printf("at nwm error : %s\r\n",data_buf);
}
done_state =0;
state = state_P2P;
}
break;
}
case state_P2P:
{
at_ok=0;
done_state=0;
rak_send_cmd("AT+P2P=920000000:7:0:0:20:14");
state=state_P2P_OK;
break;
}
case state_P2P_OK:
{
if(done_state == 1){
if(at_ok ==0 && data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at p2p ok\r\n");
}
else {
printf("at p2p error : %s\r\n",data_buf);
}
done_state =0;
state = state_PRECV;
}
break;
}
case state_PRECV:
{
at_ok=0;
done_state=0;
rak_send_cmd("AT+PRECV=65533");
state =state_PRECV_OK;
break;
}
case state_PRECV_OK:
{
if(done_state == 1){
if(at_ok ==0 && data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at precv ok\r\n");
}
else {
printf("at precv error : %s\r\n",data_buf);
}
done_state =0;
state = state_IDLE_PRE;
}
break;
}
case state_PSEND:
{
at_ok=0;
done_state=0;
rak_send_cmd("AT+PSEND=1234567890");
state=state_PSEND_OK;
break;
}
case state_PSEND_OK:
{
if(done_state == 1){
if(at_ok ==0 && data_buf[0] == 'O' && data_buf[1] == 'K'){
at_ok=1;
printf("at psend ok\r\n");
}
done_state =0;
state=state_IDLE_PRE;
}
}
case state_IDLE_PRE:
{
at_ok=0;
done_state=0;
state=state_IDLE;
break;
}
case state_Respon_PSEND:
{
break;
}
case state_Respon_PSEND_OK:
{
break;
}
default:
{
//idle
if(done_state == 1){
done_state =0;
}
break;
}
}
}
#define STX 0x02
#define ETX 0x03
#define MAX_DATA_LEN 32
typedef struct
{
uint8_t number;
uint8_t cmd;
uint8_t len;
uint8_t data[MAX_DATA_LEN];
uint8_t crc;
} p2p_frame_t;
static uint8_t hex2byte(char h, char l)
{
uint8_t hi = (h >= 'A') ? (h - 'A' + 10) : (h - '0');
uint8_t lo = (l >= 'A') ? (l - 'A' + 10) : (l - '0');
return (hi << 4) | lo;
}
static int hexstr_to_bytes(const char *hex, uint8_t *out, int maxlen)
{
int len = 0;
while (*hex && *(hex + 1) && len < maxlen)
{
out[len++] = hex2byte(hex[0], hex[1]);
hex += 2;
}
return len;
}
static uint8_t calc_crc(uint8_t *buf, uint8_t len)
{
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++)
crc ^= buf[i];
return crc;
}
static int parse_p2p_frame(uint8_t *buf, int buf_len, p2p_frame_t *out)
{
if (buf_len < 7) return -1;
if (buf[0] != STX) return -2;
if (buf[buf_len - 1] != ETX) return -3;
out->number = buf[1];
out->cmd = buf[2];
out->len = buf[3];
if (out->len > MAX_DATA_LEN) return -4;
if (buf_len != (out->len + 6)) return -5;
memcpy(out->data, &buf[4], out->len);
out->crc = buf[4 + out->len];
uint8_t crc = calc_crc(&buf[1], 3 + out->len);
if (crc != out->crc) return -6;
return 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_USART2_UART_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("hello\r\n");
/*
HAL_StatusTypeDef RcvStat;
uint8_t cmd_AT[10]="AT\r";
uint8_t rx_buff[10];
HAL_UART_Transmit(&huart1, cmd_AT, strlen((char*)cmd_AT), HAL_MAX_DELAY);
*/
//AT_Send("AT\r\n");
HAL_UART_Receive_IT(&huart1, &rx_char, 1);
//HAL_UART_Transmit(&huart1, (uint8_t *)"AT\r\n", 4, 100);
//rak_send_cmd("AT+VER=?");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
rak_send();
if (data_ready)
{
//printf("DATA = [%s]\r\n", data_buf);
data_ready = 0;
if(state == state_IDLE){
char *p = (char *)data_buf;
if (strncmp(p, "+EVT:RXP2P:", 11) == 0)
{
char *rssi_p = strchr(p + 11, ':');
if (!rssi_p) break;
char *snr_p = strchr(rssi_p + 1, ':');
if (!snr_p) break;
rxp2p.rssi = atoi(p + 11);
rxp2p.snr = atoi(rssi_p + 1);
strncpy(rxp2p.payload, snr_p + 1, sizeof(rxp2p.payload) - 1);
rxp2p.payload[sizeof(rxp2p.payload) - 1] = '\0';
uint8_t raw[64];
int raw_len = hexstr_to_bytes(rxp2p.payload, raw, sizeof(raw));
p2p_frame_t frame;
if (parse_p2p_frame(raw, raw_len, &frame) == 0)
{
printf("P2P RX OK RSSI=%d SNR=%d NUM=%d CMD=0x%02X LEN=%d\r\n",
rxp2p.rssi,
rxp2p.snr,
frame.number,
frame.cmd,
frame.len);
switch (frame.cmd) //command parser
{
case 0x01:
printf("CMD 0x01 DATA[0]=0x%02X\r\n", frame.data[0]);
break;
case 0x10:
printf("CMD 0x10\r\n");
switch (frame.len)
{
case 0:
printf("LED STATUS REQ\r\n");
break;
case 1:
printf("LED SET: %d\r\n", frame.data[0]);
break;
case 2:
printf("LED SET: %d BR=%d\r\n",
frame.data[0],
frame.data[1]);
break;
case 3:
printf("LED SET: %d BR=%d TIME=%d\r\n",
frame.data[0],
frame.data[1],
frame.data[2]);
break;
default:
printf("CMD 0x10 invalid LEN=%d\r\n", frame.len);
break;
}
break;
default:
printf("Unknown CMD\r\n");
break;
}
}
else
{
printf("P2P Frame Error\r\n");
}
}
}
}
/*
AT_Receive();
if (strstr((char *)rx_buf, "OK"))
{
printf("AT OK\r\n");
rx_idx = 0;
memset(rx_buf, 0, sizeof(rx_buf));
}
*/
//rak_send_once();
/*
if (line_ready)
{
line_ready = 0;
// printf("RX: %s\r\n", rx_line);
printf("debug : data_buff : %s\r\n",data_buf);
printf("debug : line_buff : %s\r\n",line_buf);
line_idx = 0;
done_state = 0;
}*/
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* 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_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 180;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != 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 USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_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)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @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 */
'Project > Lora_RAK3172' 카테고리의 다른 글
| RAK3172 AT Command (0) | 2026.01.15 |
|---|---|
| Lora_RAK3172 Module_IO_board (0) | 2026.01.15 |








































