Uknow's Lab.
article thumbnail

 

 

스마트연구실과 온도, 습도

임베디드(IoT) 과제겸 프로젝트로 스마트 연구실을 해보려 하는데,

그 중 하나로 연구실의 온도와 습도를 폰에서 볼 수 있게 하면 좋겠다는 생각이 들어 개발을 시작하게 되었습니다.

 

제가 위 보드는 Wemos D1 mini 보드로, UNO 보드보다 작으면서 동시에 WiFi 까지 이미 내장되어 있기 때문에,

프로젝트 활용에 아주 좋을 것 같아 선택하게 되었습니다.

사실 UNO 보드보다 Wemos D1 mini 보드가 더 많이 굴러다녔기 때문도 있지만.

 

 

 

온습도 확인에  쓴 센서는 DHT11 모듈로, 사용방법은 UNO 보드와 크게 다르지 않습니다.

+에 5V, -에 GND, 값에 D1 핀을 연결하였습니다.

 

문제는 RestAPI 였습니다.

여러 예제 등에서 json을 담는 방법을 찾아봤는데

String json = "{\Temp\" + temp " + "}"를 보고 그만 혼절하고 말았습니다,,,

때문에 저는 Json을 보다 효율적으로 다룰 순 없을까 하는 마음에 구글링을 계속해봤습니다.

 

 

아두이노와 JSON

그러던 중, ArduinoJson을 찾게 되었습니다.

#include <ArduinoJson.h>

먼저, ArduinoJson을 사용하기 위해선 위 헤더 파일을 include 해줘야 합니다.

만약, 찾을 수 없다면 Tools -> Manage Libraray에서 ArduinoJson을 검색해 설치해주세요.

 

 

// JSON 오브젝트
StaticJsonDocument<200> json;

// 온습도 모듈에서 습도, 온도를 입력받음
float humi = dht.readHumidity();
float temp = dht.readTemperature();

// json에 데이터 저장
json["humidity"] = humi;
json["temperature"] = temp;

String parsedJsonToString;
// json을 String으로 변환
serializeJson(json, parsedJsonToString);

 

위와 같이 Json 객체를 생성하고, 해당 객체에 값을 넣어줄 수 있습니다.

사용할 때엔 결국 String으로 변환해야 하긴 하지만,

serializeJson을 통해 쉽게 String으로 파싱이 가능합니다.

 

저는 자바/코틀린을 주로 써온 탓에, POJO/DTO 객체를 만들어놓고 멤버변수에 접근해 값을 할당한 후,

DTO 자체를 전송하는 방법을 주로 쓰지만,

String에 다 때려넣는 것에 비하면 이것만으로도 감지덕지네요.

 

 

아두이노와 RestAPI - POST 요청

아두이노에서 RestAPI 통신을 하려면 기본적으로 인터넷 환경이 제공되어야 합니다.

저는 WiFi 내장 보드를 사용하고 있기 때문에 WiFi를 사용하였습니다.

Wemos D1 mini 보드를 사용하고 계신 분은 아래 포스팅을 참고해주세요.

(일반 아두이노 UNO 보드여도, 와이파이 모듈을 사용해 WiFi 환경 접속이 가능합니다.)

https://uknowblog.tistory.com/255

 

[아두이노] Wemos D1 mini로 WiFi(와이파이) 연결

Wemos D1 mini. UNO 보다 작으면서, 와이파이가 내장되어 있어서 저는 와이파이를 사용해야 하는 작업이 있을 때 주로 씁니다 이번엔 D1 mini로 와이파이 연결을 해봅시다. Wemos D1 mini 세팅에 관해선 아

uknowblog.tistory.com

 

 

#define dhtpin D1
#define dhttype DHT11

#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <stdio.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <DHT.h>

void loop() {
  Serial.println("Point1");
  if((WiFi.status() == WL_CONNECTED)) {
    WiFiClient wifiClient;
    HTTPClient httpClient;

    // URL은 요청을 보내고자 하는 URL 입력
    httpClient.begin(wifiClient,"http://www.example.com/api/v1/temp");
    // 필요한 헤더 추가
    httpClient.addHeader("Content-Type", "application/json");

    // JSON 오브젝트
    StaticJsonDocument<200> json;

    // 온습도 모듈에서 습도, 온도를 입력받음
    float humi = dht.readHumidity();
    float temp = dht.readTemperature();
    
    // json에 데이터 저장
    json["humidity"] = humi;
    json["temperature"] = temp;

    String parsedJsonToString;
    // json을 String으로 변환
    serializeJson(json, parsedJsonToString);

    // POST 요청 보내기
    int httpResponseCode = httpClient.POST(parsedJsonToString);
 
    // HTTP 응답이 왔다면
    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);

      String response = httpClient.getString(); // 응답 본문을 문자열로 읽어옴
      Serial.println(response); // 시리얼 모니터에 출력
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }

    httpClient.end();
    delay(1000);
  }
}

 

와이파이를 연결하는 부분은 생략하였습니다.

 

RestAPI는 HttpClient, WiFiClient를 먼저 생성한 뒤,

httpClient.begin()의 인자로 wificlient와 요청을 보낼 URL 주소를 입력하면 됩니다

 

헤더는 아래와 같이 추가할 수 있습니다. (복수개의 헤더는 그냥 addHeader를 여러개 작성하는 방식으로 가능합니다.)

httpClient.addHeader("Content-Type", "application/json");

 

이후 json을 매개변수로 넘겨주어 httpClient의 POST 메서드를 사용하여 요청을 보냅니다.

int httpResponseCode = httpClient.POST(parsedJsonToString);
 

응답 코드가 반환되며, 저는 httpResponseCode에 저장하였습니다.

 

// HTTP 응답이 왔다면
if (httpResponseCode > 0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);

String response = httpClient.getString(); // 응답 본문을 문자열로 읽어옴
Serial.println(response); // 시리얼 모니터에 출력
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}

httpResponseCode가 0 이상이라면 서버와의 통신이 잘 이루어진 것입니다.

 

마지막으로 httpClient의 사용이 끝났다면, end() 메서드로 클라이언트를 종료합니다.

httpClient.end();
 

 

 

전체 소스코드

#define dhtpin D1
#define dhttype DHT11

#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <stdio.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <DHT.h>

// 와이파이명
const char* ssid = "와이파이 이름 입력";
const char* password = "와이파이 비밀번호 입력";
String humiTempUrl = "(here Your Url)";

DHT dht(dhtpin, dhttype);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(10);
  dht.begin();

  int n = WiFi.scanNetworks();
  Serial.println("Scan Done");
  if(n == 0) {
    Serial.println("no networks found");
  } else {
    Serial.print(n);
    Serial.println(" networks found");

    for(int i = 0; i<n; i++) {
     Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*");
      delay(10);
    }
  }



  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);


  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("connection Start");

}

void loop() {
  Serial.println("Point1");
  if((WiFi.status() == WL_CONNECTED)) {
    WiFiClient wifiClient;
    HTTPClient httpClient;

    // URL은 요청을 보내고자 하는 URL 입력
    httpClient.begin(wifiClient,humiTempUrl);
    // 필요한 헤더 추가
    httpClient.addHeader("Content-Type", "application/json");

    // JSON 오브젝트
    StaticJsonDocument<200> json;

    // 온습도 모듈에서 습도, 온도를 입력받음
    float humi = dht.readHumidity();
    float temp = dht.readTemperature();
    
    // json에 데이터 저장
    json["humidity"] = humi;
    json["temperature"] = temp;

    // 시리얼 모니터에 출력 (생략가능)
    Serial.print("humi : ");
    Serial.println(humi);
    Serial.print("temp : ");
    Serial.println(temp);

    String parsedJsonToString;
    // json을 String으로 변환
    serializeJson(json, parsedJsonToString);

    // POST 요청 보내기
    int httpResponseCode = httpClient.POST(parsedJsonToString);
 
    // HTTP 응답이 왔다면
    if (httpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);

      String response = httpClient.getString(); // 응답 본문을 문자열로 읽어옴
      Serial.println(response); // 시리얼 모니터에 출력
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }

    httpClient.end();
    delay(1000);
  }
}

 

서버 URL을 char*로 해줬더니 왠지 모르게 오류가 발생해서 Spring으로 고쳐줬습니다.

C/C++은 1~2학년 때만 조금 끄적이고, 제 주 언어가 아니다 보니 왜 그런진 잘 모르겠네요...

 

 

 

후기

좌충우돌이였던 Arduino에서 RESTful API 통신...

 

DHT11 모듈에서 온/습도를 읽어오는 것 부터 꽤 쉽지 않았습니다.

인터넷에 돌아다니던 코드 중 안되는 코드도 꽤 많았기에,,, 별 코드를 다 가져와 돌려본 후, 간신히 DHT11이 잘 작동되는 코드를 찾았는데,

잘되던 와이파이가 갑자기 연결이 안되질 않나... (보드를 아예 리셋시켜 해결)

Spring에 통짜로 JSON을 넣는 걸 보고 당황해서 더 나은 방법이 없나 싶어 헤매다가 ArduinoJson을 찾았고,

서버로 요청을, Bad Request가 떠서 정말 헤메이다가, char*를 Spring으로 바꿔 해결.

그랬더니, 이번엔 서버측 개발자가 null 값만 온다해서 ArduinoJson을 만지작거렸더니 갑자기 해결됬습니다 (...)

 

 

 

서버 프로그래머가 "이제 잘 옵니다"라는 말과 함께 보여준 로그를 보고, 정말 감격스러웠어요 ㅠㅠ...

 

 

 

 

안드로이드 - 서버 통신 코드는 이미 작성해놓았기 때문에,

아두이노 - 서버 통신을 구현하자마자 바로 모바일 앱에서 확인할 수 있었습니다.

profile

Uknow's Lab.

@유노 Uknow

인생은 Byte와 Double 사이 Char다. 아무말이나 해봤습니다.