반응형

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

+ Recent posts