반응형

아두이노 끼리 시리얼 통신을 이용하여 연결하는 방법은 간단하다. 각각 아두이노의 시리얼 통신 RX 포트와 TX 포트를 연결해주면 시리얼 통신 함수들을 통해 데이터의 전송 및 수신이 가능하다. 이는 비동기식 방식인 TTL 레벨 시리얼 통신의(1:1 통신) 특징을 바탕으로 한다. 간단하게 설명하면 두 아두이노 보드 간에 송/수신을 위한 보드 레이트(서로 약속한 시간 간격)가 서로 같도록 설정된 상태에서 송신 측 TX선이 수신 측 RX 포트에 연결되고 수신 측 RX선이 송신 측 TX포트에 서로 교차되게끔 연결만 되면 아두이노의 시리얼 통신 기본 프로그램(하드웨어 시리얼)에 따라 데이터의 송수신이 문제없이 이루어지게 된다.

아두이노 끼리 연결과 관련된 함수는 아래와 같다. 

Serial.begin(9600); // 시리얼 통신 프로그램 작동 시작, 각 보드 간 송/수신 속도(보드 레이트)를 9600으로 맞춰준다.

 

만약 소프트웨어 시리얼을 사용하여 연결하고 싶다면 소프트 웨어 시리얼 라이브러리를 설정해주고 소프트웨어 시리얼의 TX포트와 RX포트를 서로 교차 연결해 주면 된다.

#include <SoftwareSerial.h>          // 소프트웨어 시리얼 라이브러리 등록

#define TX 3                              // 소프트웨어 TX포트로 사용할 핀 번호 정의
#define RX 2                              // 소프트웨어 RX포트로 사용할 핀 번호 정의
SoftwareSerial nodeSerial(RX, TX);   // 소프트웨어 시리얼 객체명 선언(설정 보드의 RX 핀, 설정보드의 TX 핀) 

 

사용 가능한 연결방식

1. 하드웨어 시리얼 RX/TX - 하드웨어 시리얼 RX/TX : 아두이노 IDE 시리얼 모니터(하드웨어 시리얼 사용) 사용 불가

2. 소프트웨어 시리얼 RX/TX - 하드웨어 시리얼 RX/TX : 소프트웨어 시리얼 사용 보드만 시리얼 모니터 사용 가능

3. 소프트웨어 시리얼 RX/TX - 소프트웨어 시리얼 RX/TX : 두 보드 모두 시리얼 모니터를 이용할 수 있다.

 

아두이노 IDE에서 시리얼 모니터 두 개 사용하는 방법

아두이노 두 개가 컴퓨터의 USB에 연결되어 있다면 아두이노가 연결된 com포트도 두 개 생성되어 있을 것이다. 이때 각각 보드에 업로드하고자 하는 스케치를 아두이노 IDE 프로그램에서 파일 - 열기를 통해 스케치를 불러오지 않고, 이미 저장된 두 보드용 스케치 파일 폴더에 들어가서 ino파일을 더블 클릭하여 아두이노 IDE를 각각 구동시킨 후 업로드할 보드가 연결된 com포트 번호를 각각 지정해주면 두 개의 시리얼 모니터를 사용할 수 도있고 변경된 스케치를 각각 바로 업로드시킬 수도 있게 된다.

 

NodeMcu와 아두이노 연결

NodeMcu는 아두이노 IDE에서 아두이노와 같이 사용할 수 있고, 사용할 수 있는 라이브러리도 거의 비슷하다. 따라서 아두이노끼리 연결하는 것과 차이가 없게 된다.

필자는 NodeMcu와 아두이노와 연결을 시키고 연결 상태를 확인하기 위하여 아두이노에 센서 역할을 할당하고 NodeMcu에 컨트롤러 역할을 하도록 코딩을 하고 float 값을 송신/수신해 보겠다. 아두이노를 센서 역할로 할당한 이유는 단지, 아두이노에는 기본 LED가 장치되어 있어 그 LED를 데이터 수신 확인에 이용할 수 있기 때문이다.

 

 

특별한 실행 명령어 없이 시리얼 통신 버퍼를 이용하여 코드를 실행시키도록 센서측 아두이노의 시리얼 통신 버퍼에 값이 있는 경우와 없는 경우에 따라 코드를 실행하도록 짜 주었다. 컨트롤러 NodeMcu에서는 어떠한 값이든 최소 1byte 값 만 보내주면 센서 측 코드를 작동시킬 수 있게 된다.

 

아래 코드를 컨트롤러용 NodeMcu에 업로드해준다.

controller_nodemcu_basic.ino
0.00MB

// controller nodemcu code

#include <SoftwareSerial.h> 
#define TX D6 // arduino softSerial RX -> NodeMcu D6(TX)
#define RX D7 // arduino softSerial TX -> NodeMcu D7(RX) 
SoftwareSerial arduSerial(RX, TX); // (RX, TX)

void setup() {
  Serial.begin(9600);         // 시리얼 모니터용 하드웨어 시리얼 시작
  arduSerial.begin(9600);     // 센서 제어용 소프트웨어 시리얼 시작
}

void loop() {
  if (arduSerial.available() > 0) {    // 시리얼 버퍼에 데이터가 있으면
    uint8_t temp = arduSerial.read();  // 1바이트씩 읽고 
    Serial.print(temp,HEX);            // HEX 값으로 시리얼 모니터에 표시
  }
  else {                               // 시리얼 버퍼의 모든 값을 읽어 테이터가 없으면
    Serial.println();                  // 줄바꿈
    arduSerial.write(0);               // 아두이노 센서에 코드 실행 명령 전송
    delay(1000);          // 응답 대기 시간, 시리얼 값이 들어올 때 까지 대기 (1초에 한번씩 전송)
  }
}

NodeMcu대신 아두이노를 사용한다면 아래 소프트웨어 시리얼 코드만 수정하면 된다. 

#define TX D6 -> #define TX 3
#define RX D7 -> #define RX 2

 

아래 코드를 센서용 아두이노에 업로드해준다.

sensor_arduino_basic.ino
0.00MB

// sensor arduino code

#include <SoftwareSerial.h> 
#define TX 3 // nodeMcu softSerial RX -> arduino 3
#define RX 2 // nodeMcu softSerial TX -> arduino 2 
SoftwareSerial nodeSerial(RX, TX); // (RX, TX)

#define ledPin 13  // 기본 LED, 시리얼 데이터 수신 표시

float val = 12.54;

void setup() {
  Serial.begin(9600);        // 시리얼 모니터용 하드웨어 시리얼 시작
  nodeSerial.begin(9600);    // 컨트롤러 명령 수신용 소프트웨어 시리얼 시작
  pinMode(ledPin, OUTPUT);   // 기본 LED 출력 설정
}

void loop() {
  if (nodeSerial.available() > 0) {      // 시리얼 버퍼에 데이터가 있으면
    uint8_t dump = nodeSerial.read();    // 1바이트 값 읽고 시리얼 버퍼를 비운다 - 이값은 버리는 값임
    digitalWrite(ledPin, HIGH);          // 코드 실행 명령 수신했으므로 기본 LED ON
    nodeSerial.print(val);               // 컨트롤러에 String 형식으로 float값 전송
  }
  else {
    Serial.println(val);                 // 시리얼 모니터에 전송값 표시
    delay(500);                          // LED ON 유지 시간
    digitalWrite(ledPin, LOW);           // LED OFF 
    delay(500);                          // LED OFF 유지 시간
  }
}

* if (nodeSerial.available() > 0) { } 함수 안에서 컨트롤러에 float 값을 전송하기 위한 소프트 웨어 시리얼과 전송한 값을 시리얼 모니터에 출력하기 위한 하드웨어 시리얼을 연이어 실행할 경우 예기치 않은 오류(float 값을 두 번 전송 등)가 불특정 하게 발생했다. 시리얼 모니터에 출력하는 코드는 소프트웨어 시리얼 작업이 끝난 뒤에 하도록 하자. 

 

두 코드를 업로드하고 시리얼 모니터 두 개를 살펴보면 아래와 같이 표시된다.

시리얼 모니터에 상기와 같이 1초마다 값이 표시된다면 정상적으로 연결되고 코드도 정상 작동하는 것이다. 

센서 측에서 12.54를 보냈고, 컨트롤러에서는 헥사 값으로 표시했을 때 "31 32 2E 35 34"를 받았다. 아스키코드표에서 해당 값을 찾아보면 31 = 1, 32 = 2, 2E = '.', 35 = 5, 43 = 4 임을 확인할 수 있다. 

 

 

우선 float 자료형에 대해 살펴보자.

float: 크기는 32 bits(4byte)이며, 음수를 포함한 범위로서 "-3.4028235E+38 ~ 3.4028235E+38(3.4028235 * 10^38)" 표현한다고 한다. 여기서 중요한 점은 float 자료형 크기가 4바이트라는 점이다. 상기 코드에서는 float 값을 스트링 값으로 전송하도록(nodeSerial.print(val); 함수 사용) 하였다. 또한, 센서에서는 4 바이트 값을 읽고 보냈으나 센서 측 시리얼 모니터에 표시된 것과 같이 컨트롤러에서는 점('.')을 포함한 모든 자릿수만큼 데이터인 5바이트 값을 수신한 것을 볼 수 있다.

이 얘기는 float의 자료형 크기는 4바이트 동일한 상태에서 4바이트가 표시하는 값이 점을 포함한 6자리이면 6바이트가 전송되고 8자리 이면 8바이트가 전송된다는 것이다. 만약 센서에서 생성되는 float값이 -10000.00 ~ 10000.00이라면 최대 자릿수는 음수일 경우 9자리(-10000.00)이고 최소 3자리(0.00)가 되게 된다. float값을 수신하는 컨트롤러에서는 센서가 스트링 형식으로 float 값을 보낸다면 그에 상응하는 최대 자릿수를 감안하여 코드를 짜주어야 오류 없이 float값을 표시할 수 있게 된다. 

 

float 값을 스트링 형식으로만 보낼 수 있는 게 아니라 4바이트를 1바이트씩 쪼개서 보낼 수도 있다. 이는 센서 측에서 어떤 형식을 사용해서 보내느냐에 따라 결정되게 된다. 보통 기성품 센서들은 이러한 정보에 대해 매뉴얼에 표시하거나 센서 값 수신 라이브러리 제공을 통해 사용자가 어떤 방식으로 수신 값을 처리해야 할지 고민을 하지 않도록 하고 있지만 모든 상황에 적용되지는 않게 되고 사용자가 코드를 직접 짜서 처리해야 할 경우도 발생하게 된다. 

 

보통 전력 측정기의 전압값(float)을 아두이노에서 수신하여 표시하는데 많이 사용하는 것 같다. 이것에 관해 잠깐 언급하자면 기성품 전력 측정기는 보통 모드버스 프로토콜을 지원하는 것 같으며, 모드버스 통신 같은 경우에는 아날로그 값을 처리하는 기본 레지스터 크기가 16비트(2바이트) 단위이다. 따라서 float 값인 전압 값을 전송하기 위해서는 4바이트 float 값을 2바이트씩 두 개로 쪼개어 보내는데 보통 LSB영역(16bit)이 먼저 들어오고 그다음으로 MSB영역(16bit)이 들어온다. 따라서 모드버스를 통해 들어온 float 값인 경우 두 레지스터 값을 스왑한 뒤(서로 바꾼 뒤) 하나로 합쳐줘야 정상적인 값을 표시하게 된다. 하지만 전송 시 반드시 LSB영역부터 보낸다는 보장은 없다. 프로그램상에 어떻게 코딩되어 있는지에 달려있는 것이다.

 

만약, 모드버스 프로토콜을 사용하는 전력 측정기에서 보낸 float 전압값을 모드버스 프로토콜 없이 아두이노에서 수신하게 된다면 모드버스 프로토콜 설정용 데이이터와 썩여진 float 전압값의 데이터위치를 알아야 할 것이고 또한 1바이트씩 수신된 2바이트(16bits) 단위 값의 처리를 모드버스 프로토콜을 사용하면 자동으로 처리하지만, 모드버스 프로토콜을 사용하지 않는다면 스왑을 시켜야 할지 말아야 할지 확인을 해야 하며, 위에서 언급한 4바이트를 만들기 위해 다시 스왑을 해줘야 할 수 도 있게 된다. 불가능 한건 아니겠지만, 어렵게 된다. 

 

float val를 1바이트씩 쪼갤 경우 인덱스에 따라 val[0], val[1], val[2], val[3] 이라고할 때,

수신 측 4바이트 배열 float val에 들어온 값이 {val[3], val[2], val[1], val[0] } 또는 {val[2], val[3], val[0], val[1] } 일지는 전송 측 코드에 달려있게 된다.  다만, {val[3], val[2], val[1], val[0] }일 가능성이 크다.

 

** 1바이트 스왑 방법

송신측에서 시리얼 통신을 통해 2byte number 넘버의 값(16비트 MCU에서 16비트 자료형 : int 또는 uint16_t)을 전송한다고 했을 때 시리얼 통신은 1byte씩 데이터를 수신하게 되는데, 16bit중 LSB 영역(8bit)이 먼저 들어오고 그 다음으로 MSB 영역(8bit)이 들어오게 된다. 

     0000 0000     0000 0000     - 2Byte(16bit) 
  |   MSB 영역  |   LSB  영역  | 

이런 경우에는 아래처럼 1바이트 스왑을 시켜 코딩해 주어야 한다.

uint8_t val[2]; // 8비트 데이터 수신 배열, 2바이트(16비트)  

if (Serial.available() > 0) { 
  val[0] = Serial.read(); // LSB 영역 8비트 수신 및 저장 
  val[1] = Serial.read(); // MSB 영역 8비트 수신 및 저장 
  uint16_t temp = val[1];  // MSB 영역 값을 16비트 임시 변수에 저장 
  temp = temp << 8 | val[0];  // 임시 변수(MSB)를 왼쪽으로 8비트만큼 시프트하고 LSB 영역 값과 비트연산 -> 바이트 스왑 
}

이글에서는 모드버스를 이용한 float값에 대한 처리는 다루지는 않겠다. 

 

모드버스를 언급한 이유는 앞으로 다룰 float 값의 4바이트를 1바이트씩 쪼개서 MSB영역부터 순차적으로 float값을 컨트롤러에 보내는 코드를 사용할 것이기 때문이고, 이 코드의 의미를 좀 더 이해하기 쉽도록 하기 위함이다. 

 

다시, 스트링 형식으로 들어오는 float값의 처리에 대해 살펴보자. 

아두이노 시리얼 class에는 스트링 형식으로 들어오는 float 값을 처리하는 Serial.parseFloat() 함수가 있다. 

 

https://www.arduino.cc/reference/ko/language/functions/communication/serial/parsefloat/

Serial.parseFloat() returns the first valid floating point number from the Serial buffer. Characters that are not digits (or the minus sign) are skipped. parseFloat() is terminated by the first character that is not a floating point number. The function terminates if it times out (see Serial.setTimeout()).

 

시리얼 버퍼 내 유효한 float 스트링이 있으면 float자료형으로 값을 출력하고, float 스트링에 해당되지 않는 문자(예: " " - 스페이스 등)에 의해 구분(종료)되며, 함수의 기능은 Serial.setTimeout() 함수의 시간에 따라 종료된다.  Serial.setTimeout() 함수의 디폴트 값은 1000 밀리 초이다. 

 

"float 스트링에 해당되지 않는 문자(예: " " - 스페이스 등)에 의해 구분(종료)되며"를 좀 더 자세히 살펴보면 함수 자체의 기능이 종료되는 게 아니고 float형식 스트링을 float타입 변숫값으로 변환하는 코드의 종료를 의미한다. 만약 스트링 "12.54  26.71"의 값이 들어올 경우 우선 스페이스에 의해 종료되는 12.54까지를 float 타입 변수로 변환한 다음 다시 26.71의 값을 float 타입 변수로 변환한다는 얘기이다. 두 값의 변환이 완료되었다 할지라도 Serial.setTimeout() 함수의 디폴트 값(1000 밀리 초)을 변경하지 않았다면 1000 밀리 초 동안 자체 루프 하게 된다. 즉 1초의 딜레이가 발생하는 것이다. 

 

 

Serial.setTimeout() 함수의 값도 설정하여 딜레이 발생을 최소화시키고 연속된 값도 받아 시리얼 모니터에 출력해 보자.

// 컨트롤러 코드 수정
// if (arduSerial.available() > 0) 조건문 함수 수정 전
uint8_t temp = arduSerial.read();
Serial.print(temp,HEX);

// if (arduSerial.available() > 0) 조건문 함수 수정 후
arduSerial.setTimeout(1);               // 시리얼 스트림을 읽는 시간을 1 밀리초로 변경
float value = arduSerial.parseFloat();  // 스페이스에의해 종료되기 전 float값 변환
float value2 = arduSerial.parseFloat(); // 다음 float 값 변환
arduSerial.setTimeout(1000);            // 시리얼 스트림 읽는 시간을 초기화 
Serial.print(value); Serial.print(", "); Serial.print(value2); // 시리얼 모니터 출력

// 센서 코드 수정
// 두번째 float 변수 추가
float val2 = 26.71;

// if (arduSerial.available() > 0) 조건문 함수 수정 전
uint8_t dump = nodeSerial.read();
digitalWrite(ledPin, HIGH);
nodeSerial.print(val);

// if (arduSerial.available() > 0) 조건문 함수 수정 후
uint8_t dump = nodeSerial.read();  
digitalWrite(ledPin, HIGH);
nodeSerial.print(val);            // 컨트롤러에 첫번째 float 값 전송
nodeSerial.print(" ");            // 변환코드 종료 문자
nodeSerial.print(val2);           // 컨트롤러에 두번째 float 값 전송

아래 코드를 각각 코드에 업로드하고 시리얼 모니터에 1초 간격으로 값이 표시되는지 확인해 보자.

컨트롤러 코드

controller_nodemcu_basic_string.ino
0.00MB

센서 코드

sensor_arduino_basic_string.ino
0.00MB

Serial.parseFloat() 함수를 사용하면 스트링 형식으로 수신되는 float값을 한 개 또는 복수개이든지 상관없이 편하게 변환할 수 있다. 전송 측에서 몇 개의 값을 전송하는지에 따라 적용하면 된다. 하지만 Serial.setTimeout() 함수의 세팅은 최솟값이 1 밀리 초이다. 1 밀리 초는 우리에게 찰나의 순간이지만 MCU 세계에서는 그렇지 않다. 물론 코드에 따라 다르다는 얘기이다. 상기의 코드처럼 delay(1000)을 사용하여 딜레이 1000 밀리 초를 만드는 상황에서는 1 밀리 초는 고려할 시간도 아니게 된다. 하지만 코드에 따라서는 고려를 해야 할 경우도 있으므로 이제 배열을 이용하여 시리얼 버퍼 내의 특정 범위 바이트 값만 읽어오고 그 시간만큼만 딜레이 되도록 해보자. 아마 몇 마이크로초 소요될 것이다.

controller_nodemcu_basic_array.ino
0.00MB

// controller nodemcu code

#include <SoftwareSerial.h> 
#define TX D6 // arduino softSerial RX -> NodeMcu D6(TX)
#define RX D7 // arduino softSerial TX -> NodeMcu D7(RX) 
SoftwareSerial arduSerial(RX, TX); // (RX, TX)

char float1[10] = {0,}; // atof()함수를 위해 char 자료형 배열을 10바이트 선언

void setup() {
  Serial.begin(9600);
  arduSerial.begin(9600);
}

void loop() {
  if (arduSerial.available() > 0) {
    for (int i = 0; i < 10; i++) { // 시리얼 버퍼내 10바이트의 데이터를 float1 배열에 저장
      uint8_t temp = arduSerial.read();
      float1[i] = temp;
    }
  }
  else {
    float val = atof(float1);  // 문자 배열을 실수로 변환하는 함수, 배열 자료형은 char
    Serial.print(val);
    Serial.println();
    arduSerial.write(0);
    delay(1000);  // 응답 대기 시간
  }
}

상기 코드에서 스트링 값을 받을 수 있는 10 바이트 배열을 선언해주고 for 루프를 이용하여 시리얼 버퍼 내 데이터를 10바이트까지 읽어서 배열에 저장한다. 값이 없는 시리얼 버퍼 값은 헥사 값 "FF"가 저장된다. 또한 문자 배열을 실수로 변화해주는 함수 atof(val);를 사용하기 위해 자료형은 char로 선언해주었다.

 

sensor_arduino_basic_array.ino
0.00MB

// sensor arduino code

#include <SoftwareSerial.h> 
#define TX 3 // nodeMcu softSerial RX -> arduino 3
#define RX 2 // nodeMcu softSerial TX -> arduino 2 
SoftwareSerial nodeSerial(RX, TX); // (RX, TX)

#define ledPin 13

float val = 12.54;

void setup() {
  Serial.begin(9600);
  nodeSerial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (nodeSerial.available() > 0) {
    uint8_t dump = nodeSerial.read();
    digitalWrite(ledPin, HIGH);
    nodeSerial.print(val);
    val += random(1000);            // 자리값에 변동을 주기위해 랜덤값을 더해준다. 
    if (val > 11000) val = 12.54;   // 자리값 초기화
  }
  else {
    Serial.print(val); 
    Serial.println();
    delay(500);                // LED 표시 시간
    digitalWrite(ledPin, LOW);
    delay(500);
  }
}

상기 코드를 업로드하고 점을 포함한 설정한 자릿수까지 숫자 표현이 잘되는지 확인해보자.  

송신 측에서 스트링 형식으로 float 값을 전송할 때 값을 수신하고 처리하는 방법에 대해 살펴보았다. 이제 float 4바이트 값을 1바이트씩 바이트 단위로 전송하고 수신한 바이트 값을 float 값으로 처리하도록 코딩하는 방법을 살펴보자.

controller_nodemcu_basic_array_byte.ino
0.00MB

// controller nodemcu code

#include <SoftwareSerial.h> 
#define TX D6 // arduino softSerial RX -> NodeMcu D6(TX)
#define RX D7 // arduino softSerial TX -> NodeMcu D7(RX) 
SoftwareSerial arduSerial(RX, TX); // (RX, TX)

uint8_t float1[4]; // 4바이트 배열 선언

void setup() {
  Serial.begin(9600);
  arduSerial.begin(9600);
}

void loop() {
  if (arduSerial.available() > 0) {
    for (int i = 0; i < 4; i++) {
      uint8_t temp = arduSerial.read();
      float1[i] = temp;
    }
  }
  else {
    float val = *(float*)float1; // float타입 포인터를 선언하고 역참조 한 값-메모리 주소의 값 4바이트를 저장
    Serial.print(val);
    Serial.println();
    arduSerial.write(0);
    delay(1000);  // 응답 대기 시간
  }
}

sensor_arduino_basic_byte.ino
0.00MB

// sensor arduino code

#include <SoftwareSerial.h> 
#define TX 3 // nodeMcu softSerial RX -> arduino 3
#define RX 2 // nodeMcu softSerial TX -> arduino 2 
SoftwareSerial nodeSerial(RX, TX); // (RX, TX)

#define ledPin 13

float val = 12.54;
byte *temp; // 1바이트 포인터 선언(메모리 주소)

void setup() {
  Serial.begin(9600);
  nodeSerial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (nodeSerial.available() > 0) {
    uint8_t dump = nodeSerial.read();
    digitalWrite(ledPin, HIGH);
    temp = (byte *)&val;           // val 메모리 주소(&val)안의 값을 자료형 바이트로 선언 후 
    for (uint8_t i = 0; i< sizeof(val); i++) {   // 포인터 변수 temp에 저장, 포인터 주소는 배열처럼 사용가능 
      nodeSerial.write(temp[i]);                 // 메모리 주소값안의 인덱스에 해당하는 값을 읽어온다.
    }
    val += random(1000);          
    if (val > 11000) val = 12.54;  
  }
  else {
    Serial.print(val); 
    Serial.println();
    delay(500);                // LED 표시 시간
    digitalWrite(ledPin, LOW);
    delay(500);
  }
}

배열이 아닌 자료형 단독 값을 배열처럼 사용하기 위해 포인터를 이용하여 처리를 해준 코드이다. 필자가 주석을 달았지만 모두 이해하고 적용한 것은 아니다. 자세한 설명은 c언어 포인터를 검색해서 확인해 보자.

 

단순하게 정리하면 4바이트 변수의 값을 메모리 주소를 통해 접근한 뒤 0, 1, 2, 3 호수에 있는 값들을 읽어서 바이트 자료형으로 선언하고 자료형 타입 포인터 변수(메모리 주소)에 저장시켜준다.

바이트 형식으로 변환되고 저장된 값을 읽기 위해 포인터 변수(메모리 주소)에 접근한 뒤 0, 1, 2, 3 호수의 값들을 차례로 전송해 준다. 

 

byte *temp; // 1바이트 포인터 선언(메모리 주소)

temp = (byte *)&val;           // val 메모리 주소(&val) 안의 값을 자료형 바이트로 선언 후  
for (uint8_t i = 0; i< sizeof(val); i++) {   // 포인터 변수 temp에 저장, 포인터 주소는 배열처럼 사용 가능  
  nodeSerial.write(temp[i]);                 // 메모리 주 소값 안의 인덱스에 해당하는 값을 읽어온다. 
}

 

수신 측에서는 4바이트 배열에 저장된 값을 float 타입으로 합쳐서 읽기 위해 4바이트 배열의 주소를 float 타입으로 선언하고 그 주소에 접근한 뒤 바이트 단위로 보는 게 아니라 역참조(느낌상 멀리서 바라본(전체적인) 값 정도 되겠다)를 통해 읽은 뒤 float 변수 val에 넣어준다. 

 

uint8_t float1[4]; // 4바이트 배열 선언

float val = *(float*)float1; // float타입 포인터를 선언하고 역참조 한 값-메모리 주소의 값 4바이트를 저장

* 자료형 uint8_t와 byte를 혼용해서 사용했으나 둘은 같다.

 

1바이트 단위로 쪼개서 송신하게 되면 표시되는 숫자의 자릿수와 상관없이 4바이트만 수신하고 그 처리를 하면 된다. float 값의 송신 및 수신하는 방법은 코드에 따라 달라질 수 있으며 상기의 방법 외에도 다른 방법들이 있을 거라 생각한다. 이 글을 통해 float 값의 처리에 대한 접근 방법에 대해 언급하고자 작성하였다.  

 

관련 글

[arduino] - 아두이노 - 비트 시프트 연산자

[arduino] - 아두이노 - 시리얼통신 주요함수와 예제, String class

 

 

 

+ Recent posts