강좌 전체보기

.

홈 오토메이션 서비스 기획대로 센서장치를 추가해 보겠습니다.

ESP32 모듈을 센서장치로 사용하고, 온도센서(DHT-11)를 달아서 측정값을 서버에 보내도록 하겠습니다. 서버에서 커맨드 (0- off, 1-on)를 받으면 측정값 전송을 멈추도록 만들겠습니다.

ESP32 를 이용한 센서장치 제작

우선 ESP32 보드에 온습도 센서 DHT-11 을 연결하고 동작을 확인해야 합니다. ESP32 보드에서 DHT-11 (또는 DHT-22) 온습도 센서 테스트 하는 방법은 아래에 자세한 설명이 있습니다.

연결은 아래와 같이 하면 됩니다.

만약 사용하는 온습도 센서가 interface board 위에 붙어있다면 3개의 핀만 아래와 같이 연결하면 됩니다.

  • ESP32 –> DHT11
  • 5V –> VCC (+)
  • GND –> GND
  • D16 –> Signal

DHT-11 센서를 ESP32 에서 사용하기 위해서는 라이브러리를 설치해줘야 합니다. 아래 두 라이브러리를 다운로드 받아 설치해주면 됩니다. (같은 이름의 라이브러리가 이미 존재하는 경우 기존 라이브러리를 백업 후 삭제하세요.)

스케치를 아래에서 다운로드 받아주세요.

소스코드에서 수정해줘야 할 부분이 많습니다. 상단의 전역 변수들을 바꿔줘야 합니다.

// Uncomment one of the lines below for whatever DHT sensor type you're using!
#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT21   // DHT 21 (AM2301)
//#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

// DHT Sensor
const int DHTPin = 16;

// Initialize DHT sensor.
DHT dht(DHTPin, DHTTYPE);

// Network settings
const char* ssid = "your_ssid";
const char* password =  "your_pass";

// Server info
#define SERVER_POST_URL "http://your_server:port_num/data"
#define SERVER_CHANNEL 3
#define SERVER_AUTHCODE "authcode"
#define HTTP_POST_INTERVAL 10000L

// MQTT info
const char* mqttServer = "your_server"; // do not use "http://"
const int mqttPort = 1883;
const char* mqttUser = "";
const char* mqttPassword = "";
const char* topic_sub = "3/status";   // 3 is channel number.

DHT-11 센서를 사용하기 위해 아래 #define 구문을 사용했습니다. 만약 정확도 향상을 위해 DHT-22 센서를 사용한다면 DHT22 define 구문을 활성화해주면 됩니다.

#define DHTTYPE DHT11

공유기 연결을 위해 ssid, password 값을 설정해주세요.

const char* ssid = "your_ssid";
const char* password =  "your_pass";

아직 서버 구축을 하진 않았지만, 서버에 HTTP request 를 보내기 위해 필요한 정보를 미리 설정해줍니다. 추후에 서버 설치를 마치고, 채널을 생성하고 나면 이 부분을 다시 수정해서 사용하면 됩니다. 인증 코드와 채널 넘버는 채널을 생성하면 서버에서 알려줍니다.

#define SERVER_POST_URL "http://your_server:port_num/data"
#define SERVER_CHANNEL 3
#define SERVER_AUTHCODE "authcode"

MQTT 관련된 설정을 변경해줍니다. MQTT 서버 주소와 포트, 토픽을 입력해주면 됩니다. 이것도 추후 채널을 생성하고 수정해주면 됩니다.

const char* mqttServer = "your_server"; // do not use "http://"
const int mqttPort = 1883;
const char* mqttUser = "";
const char* mqttPassword = "";
const char* topic_sub = "3/status";   // 3 is channel number.

이제 코드를 ESP32 에 업로드 해주세요. 일단은 공유기에 연결되어 동작하는 상태까지만 확인하면 됩니다. 서버 설정을 마치고 채널을 생성한 다음, 이 포스트로 돌아와서 채널 넘버/인증 코드를 넣고 업로드하면 정상 동작합니다.

코드 분석

이미 HTTP POST 전송 방법과 MQTT 사용 방법을 실습했었습니다.

따라서 여기서는 주요 코드만 살펴보겠습니다.

void connectMqtt() {
  ......
  mqttClient.subscribe(topic_sub);
  mqttClient.publish(topic_sub, "ESP32 logged in");
}

void setup() {
  ......
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(mqttCallback);

  connectMqtt();
}

setup() 초기화 함수가 실행되면서 WiFi 연결을 하고 MQTT 서버 설정과 연결을 합니다. 이때 [채널 넘버/status] 토픽을 구독하고, 메시지를 받으면 mqttCallBack 함수가 호출되도록 설정했습니다. mqttCallback() 함수는 아래와 같습니다.

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  ......
  for (int i = 0; i < length; i++) {
    if(payload[i] == 0x30) {          // Char '0'
      sensorPower = false;
      ......
    }
    else if(payload[i] == 0x31) {     // Char '1'
      sensorPower = true;
      ......
    } else {
      Serial.print("0x");
      Serial.print(payload[i], HEX);
      Serial.print(", ");
    }
  }
  ......
}

[채널 넘버/status] 토픽을 구독한 이유는 여기로 들어오는 명령어를 받기 위함입니다. 여기로 ASCII 문자 0(0x30) 또는 1(0x31) 값이 들어옵니다. 해당 값에 따라 sensor 값 전송을 on/off 하도록 처리했습니다.

loop() 함수안에서 이루어지는 메인 작업을 살펴보겠습니다.

void loop() {
  if(WiFi.status() == WL_CONNECTED) {   //Check WiFi connection status
    mqttClient.loop();

    unsigned long currentTime = millis();
    
    if(sensorPower && currentTime > prevPostTime + HTTP_POST_INTERVAL) {
      // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
      float h = dht.readHumidity();
      // Read temperature as Celsius (the default)
      float t = dht.readTemperature();
      // Read temperature as Fahrenheit (isFahrenheit = true)
      float f = dht.readTemperature(true);
      
      // Check if any reads failed and exit early (to try again).
      if(isnan(h) || isnan(t) || isnan(f)) {
        Serial.println("Failed to read from DHT sensor!");
      } else {
        Serial.println("HTTP start --------------------------------");
        HTTPClient http;
     
        http.begin(SERVER_POST_URL);  //Specify destination for HTTP request
        http.addHeader("Content-Type", "application/json");             //Specify content-type header
  
        // Make JSON string
        std::stringstream ss;
        ss << "{\"channel\":" << SERVER_CHANNEL << ",\"auth_code\":\"" << SERVER_AUTHCODE << "\",\"data\":" << t << "}";
  
        // send HTTP POST request
        Serial.println(ss.str().c_str());
        int httpResponseCode = http.POST(ss.str().c_str());   //Send the actual POST request
  
        if(httpResponseCode > 0){
          String response = http.getString();                       //Get the response to the request
          Serial.println(httpResponseCode);   //Print return code
          Serial.println(response);           //Print request answer
        } else {
          Serial.print("Error on sending POST: ");
          Serial.println(httpResponseCode);
        }
        http.end();  //Free resources
        Serial.println("HTTP end --------------------------------");
      }
      prevPostTime = currentTime;
    }  // End of if(sensorPower)
 
  } else {
    Serial.println("Error in WiFi connection... Try to reconnect WiFi and MQTT.");   
    connectWiFi();
    connectMqtt();
  }
}

상단에 mqttClient.loop() 함수를 호출해주는 부분이 있습니다. 이 함수는 반드시 loop() 반복 함수 안에서 계속 호출해줘야 합니다. 그렇지 않으면 MQTT 로 들어오는 메시지를 받지 못하는 문제가 발생합니다.

loop() 함수는 HTTP_POST_INTERVAL 간격으로 센서값을 읽고 서버로 전송을 시도합니다. 이 때 전송할 JSON string 을 직접 만드는 루틴은 아래와 같습니다.

        // Make JSON string
        std::stringstream ss;
        ss << "{\"channel\":" << SERVER_CHANNEL << ",\"auth_code\":\"" << SERVER_AUTHCODE << "\",\"data\":" << t << "}";
  
        // send HTTP POST request
        Serial.println(ss.str().c_str());
        int httpResponseCode = http.POST(ss.str().c_str());   //Send the actual POST request

직접 string을 만드는 방식을 사용했는데, JSON 라이브러리가 제공하는 방법을 사용해도 됩니다. 우리가 사용하는 JSON 데이터가 간단하기 때문에 그냥 string을 조합해서 만든겁니다. JSON string 에는 서버 채널 번호, 인증 코드, 온도 데이터 값이 들어있습니다.

그리고 HTTP POST request 를 서버로 보냅니다. 이 request 가 성공하면 서버에 데이터가 저장됩니다.

참고자료

주의!!! [사물 인터넷 네트워크와 서비스 구축 강좌] 시리즈 관련 문서들은 무단으로 내용의 일부 또는 전체를 게시하여서는 안됩니다. 계속 내용이 업데이트 되는 문서이며, 문서에 인용된 자료의 경우 원작자의 라이센스 문제가 있을 수 있습니다.

강좌 전체보기

.