본문 바로가기

Firmware/stm32

NanoPB - Stm32

NanoPb를 STM32에 적용시켜보자 

 

NanoPb란?

 -> Protobuf를 Embedded System에 적용하기 위해 C기반 경량화로 나온 프로토콜을 NanoPb라고 한다.

 

 

 

NanoPb 순서

 

1. ~~~.proto 파일에 인코딩 하고 싶은 데이터,구조체등을 정의한다.

 

2. ~~~.proto 파일을 컴파일 한다. (ex: protoc --nanopb_out=/SaveDirectory ~~~proto)

 

3. 컴파일이 정상적으로 되면 Save Directory에 ~~~.pb.c, ~~~.pb.h 파일이 나온다.

 

4. ~~~.pb.c, ~~~.pb.h 파일 및 Common protobuf.c,h 를 stm32에 넣고 컴파일을 돌린다.

 

5. Test 코드 작성후 검증

 

순서에 들어가기 전에 NanoPb를 컴파일 할 수 있는 환경을 구축한다.

 

NanoPb 컴파일 다운로드

Nanopb - downloads (kapsi.fi)

 

Nanopb - downloads

 

jpa.kapsi.fi

NanoPb 컴파일 환경만들기

GitHub - nanopb/nanopb: Protocol Buffers with small code size

 

nanopb/nanopb

Protocol Buffers with small code size. Contribute to nanopb/nanopb development by creating an account on GitHub.

github.com

위 링크와 같이 우분투에서 Linux-x86 버전을 다운 받았고 pip install grpcio-tools, apt-get install protobuf-compiler

명령어로 설치

 

cd tests

scons

명령어를 하여 정상 컴파일 동작 확인함.

(설치 시 pip3로 설치하면 에러가 난다. 반드시 pip로 설치 할 것)

 

링크에 scons PLATFORM=STM32 명령어도 있는데 에러만 뜬다. (굳이 할 필요 도 없다)

 

 

1. ~~~.proto 파일에 인코딩 하고 싶은 데이터,구조체등을 정의한다.

 

alltypes.proto 작성

syntax = "proto2";
// package name placeholder

import "nanopb.proto";

enum NonZeroBasedEnum {
    Two = 2;
    Three = 3;
    Four = 4;
}

message SubMessage {
    required string substuff1 = 1 [default = "1"];
    required int32 substuff2 = 2 [default = 2];
    optional fixed32 substuff3 = 3 [default = 3];
}

message EmptyMessage {

}

enum HugeEnum {
    Negative = -2147483647; /* protoc doesn't accept -2147483648 here */
    Positive =  2147483647;
}

message Limits {
    required int32      int32_min  =  1 [default = 2147483647];
    required int32      int32_max  =  2 [default = -2147483647];
    required uint32     uint32_min =  3 [default = 4294967295];
    required uint32     uint32_max =  4 [default = 0];
    required int64      int64_min  =  5 [default = 9223372036854775807];
    required int64      int64_max  =  6 [default = -9223372036854775807];
    required uint64     uint64_min =  7 [default = 18446744073709551615];
    required uint64     uint64_max =  8 [default = 0];
    required HugeEnum   enum_min   =  9 [default = Positive];
    required HugeEnum   enum_max   = 10 [default = Negative];
    required int32      largetag   = 65535 [default = 0];
}

 

다운받은 디렉토리중 Tests 파일에서 Alltypes.proto에서 정의된 Limits로 정의 후 Test 진행

 

2. ~~~.proto 파일을 컴파일 한다. (ex: protoc --nanopb_out=/SaveDirectory ~~~proto)

 

Alltype 구조체는 string type, byte type 등 몇가지 type은 그냥 컴파일 돌리면 void형 포인터로 output이 나오기 때문에

따로 Size를 지정해 줘야 컴파일 에러를 피할 수 있다.

링크 참조

Nanopb: Basic concepts (kapsi.fi)

 

Nanopb: Basic concepts

The things outlined here are the underlying concepts of the nanopb design. All Protocol Buffers implementations use .proto files to describe the message format. The point of these files is to be a portable interface description language. Nanopb comes with

jpa.kapsi.fi

그냥 protoc 명령어는 옵션에 nanopb_out 옵션이 존재하지 않는다. 다운받은 protoc를 활용하여 컴파일을 해야한다.

 

$ sudo ~/Desktop/nanopb/nanopb-0.4.5-linux-x86/generator-bin/protoc --nanopb_out=. alltypes.proto

 

3. 컴파일이 정상적으로 되면 Save Directory에 ~~~.pb.c, ~~~.pb.h 파일이 나온다.

 

어찌되었든, 위에 작성한 proto파일을 컴파일 돌리고 문제 없으면 아래와 같이 파일이 output으로 나온다.

 

 

4. ~~~.pb.c, ~~~.pb.h 파일 및 Common protobuf.c,h 를 stm32에 넣고 컴파일을 돌린다.

처음 디렉토리에 있는 pb_common.c,h, pb_decode.c,h, pb_encodec.c,h, pb.c, alltypes.pb.c,h, 를 넣고 컴파일 한다.

 

 

5. Test 코드 작성후 검증

 

Target을 STM32F407Discovery에 넣고 인코딩, 디코딩 함수를 만든 다음 디버깅 모드를 통해 확인하였다.

 

Test Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
 
#include "alltypes.pb.h"
#include "pb_decode.h"
#include "pb_encode.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 ---------------------------------------------------------*/
 
/* USER CODE BEGIN PV */
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void MX_USB_HOST_Process(void);
 
/* USER CODE BEGIN PFP */
Limits Encode_Limit_Proto;
Limits Decode_Limit_Proto;
/* 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_I2C1_Init();
    MX_I2S3_Init();
    MX_SPI1_Init();
    MX_USB_HOST_Init();
    /* USER CODE BEGIN 2 */
 
    Encode_Limit_Proto.int32_min = 25;
    Encode_Limit_Proto.int32_max = 99;
    Encode_Limit_Proto.uint32_min = 77;
    Encode_Limit_Proto.uint32_max = 255;
    Encode_Limit_Proto.int64_min = 1111;
    Encode_Limit_Proto.int64_max = 9999;
    Encode_Limit_Proto.uint64_min = 101;
    Encode_Limit_Proto.uint64_max = 808;
    Encode_Limit_Proto.enum_min = HugeEnum_Negative;
    Encode_Limit_Proto.enum_max = HugeEnum_Positive;
    Encode_Limit_Proto.largetag = 777;
 
    /* if you don't initialize to zero, there is problem with decoding */
    uint8_t encode_buffer[Limits_size] = { 0,};
 
    pb_ostream_t encode_stream = pb_ostream_from_buffer(encode_buffer, sizeof(encode_buffer));
 
    /* Encode_Limit_Proto -> Encoding -> encode_buffer */
    if(pb_encode(&encode_stream,Limits_fields,&Encode_Limit_Proto))
    {
        printf("Encode Success \r\n");
    }
    else
    {
        fprintf(stderr, "Encoding Failed: %s\n",PB_GET_ERROR(&encode_stream));
    }
 
    pb_istream_t decode_stream = pb_istream_from_buffer(encode_buffer, sizeof(encode_buffer));
 
    /* encode_buffer -> Decoding -> Decode_Limit_Proto */
    if(!(pb_decode(&decode_stream, Limits_fields, &Decode_Limit_Proto)))
    {
        printf("Decode Success \r\n");
    }
    else
    {
        fprintf(stderr, "Decoding Failed: %s\n",PB_GET_ERROR(&decode_stream));
    }
 
    /* USER CODE END 2 */
 
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        MX_USB_HOST_Process();
 
        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}
 
cs

Encode_Limit_Proto 선언 및 데이터를 임의로 지정한 후 인코딩한 데이터를 encode_Buffer에 넣었다.

 

그 다음 encode_buffer를 디코딩 함수에 넣으면 디코딩 3번째 Arg에 디코딩한 데이터가 저장 된다(Deode_Limit_Proto).

 

 /* Encode_Limit_Proto -> Encoding -> encode_buffer */ 

 /* encode_buffer -> Decoding -> Decode_Limit_Proto */ 

 

Test 결과 화면