14주차 카카오톡을 활용해 토양 센서 값 읽기 - hyunwoo14/SmartDevice GitHub Wiki

목차

  • 토양 센서 값 읽기
    • 회로
    • 코드
    • 실행 결과
  • 카카오톡 앱 설정하기
    • 정보 등록
    • Access 토큰 발급 받기
  • 토양 센서 값 카카오톡으로 전송하기
    • 코드
    • 실행 결과
  • 후기

토양 센서 값 읽기

회로

코드

const int soilSensorPin = 34;
 void setup(){
 Serial.begin(115200);
 }
 void loop(){
 Serial.print("토양습도센서값:");
 Serial.println(analogRead(soilSensorPin));
 delay(1000);
 }

토양 수분이 높으면 저항이 낮아지고 센서 값이 크게 나타나고 토양 수분이 낮으면 저항이 커져서 센서 값이 작게 나타난다. 01500 정도는 건조한 토양 15002000 정도는 정상적인 토양 2000 이상은 매우 습한 토양이거나 물 속이다.

실행 결과

시리얼 모니터를 통해 센서 값이 출력 된다.

카카오톡 앱 설정하기

정보 등록

먼저 카카오톡 개발자 사이트에 들어가서 가입을 해준다.(https://developers.kakao.com/)

내 애플리케이션에 가서 애플리케이션 추가하기를 누른다.

"앱 키" 메뉴로 들어가서 REST API키를 복사해둔다.

"플랫폼" 메뉴로 들어가서 Web 플랫폼 등록을 한다.

사이트 도메인도 등록 해준다.

"카카오톡 로그인" 메뉴로 들어가서 활성화 설정을 ON으로 해준다.

Access 토큰 발급 받기

Redirect URL에 "https://www.example.com/oauth"를 저장한다.

카카오톡 메시지 전송 항목의 설정에 들어가서 선택 동의를 해준다.

다음으로 주소창에 (https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=REST API값&redirect_uri=리다이렉션URL) 이라고 입력한다.

동의를 해준 뒤 나오는 브라우져 주소창의 code= 이후의 값이 인가 코드(Authorization code)이므로 복사해둔다.

다음으로 명령 프롬프트 창을 열어 아래와 같이 명령어를 입력하면

curl -v -X POST "https://kauth.kakao.com/oauth/token" =H "Content-Type: application/x-www-form-urlencoded" -d"grant_type=authorization_code" -d "client_id=REST_API값 --data-urlencode "redirect_uri=REDIRECT_URI값 -d "code=AUTHORIZE_CODE값

Access토큰과 Refresh토큰을 받을 수 있다.

토양 센서 값 카카오톡으로 전송하기

코드

위에서 구한 "REST API KEY"와 "Access token", "Refresh token"을 각각 밑의 코드에 입력한다.

#include <WiFi.h>
#include <HTTPClient.h>

const char *ssid = "Your_SSID";          // 사용하는 WiFi 네트워크 이름 (SSID)
const char *password = "Your_Password";  // 사용하는 WiFi 네트워크 비밀번호

const String rest_api_key = "REST API KEY"; 
String access_token = "Access token";
String refresh_token = "Refresh token";

#define MsgSendInterval 3600 // 60 * 60 초, 즉 한시간 간격으로 전송
long timeout = 3600;  //시간을 초로 나타냄
int sensorValue = 0;
int sensorPin = 34; // 토양 습도 센서 핀

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.print("\nConnected to WiFi : ");
  Serial.println(WiFi.localIP());
}
void loop() {
  if (timeout++ > MsgSendInterval)  // 1시간(60 * 60)에 1번씩 전송
  {
    if (isAccessTokenExpired() == true) { //access token 만료 여부 확인
      if (update_access_token() == false) { // access token 재발급
        Serial.println("Access token update failed");
      }
    }
    sensorValue = analogRead(sensorPin);//토양 센서값 읽기
    send_message();
    timeout = 0;
  }

  delay(1000);
}


// str문자열에서 start_string와 end_string사이의 문자열을 추출하는 함수
String extract_string(String str, String start_string, String end_string) {
  int index1 = str.indexOf(start_string) + start_string.length();
  int index2 = str.indexOf(end_string, index1);
  String value = str.substring(index1, index2);
  return value;
}

bool isAccessTokenExpired() {
  HTTPClient http;
  bool returnVal = true;
/*
curl -v -X GET "https://kapi.kakao.com/v1/user/access_token_info" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
*/
  if (!http.begin("https://kapi.kakao.com/v1/user/access_token_info")) {
    Serial.println("\nfailed to begin http\n");
  }
  http.addHeader("Authorization", "Bearer " + access_token);

  int httpCode = http.GET();

  // httpCode will be negative on error
  if (httpCode > 0) {
    // file found at server
    if (httpCode == HTTP_CODE_OK) {
      String payload = http.getString();
      Serial.println(payload);
      String expireT = extract_string(payload, "\"expires_in\":", ",");
      Serial.println(expireT.toInt());
      if (expireT.toInt() > 0) {
        returnVal = false;
      } else {
        returnVal = true;
      }
    }
  } else {
    Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();
  return returnVal;
}

void send_message() {
  HTTPClient http;
  String url = "https://kapi.kakao.com/v2/api/talk/memo/default/send";
  if (!http.begin(url)) {
    Serial.println("\nfailed to begin http\n");
  }
  http.addHeader("Authorization", "Bearer " + access_token);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");

  int http_code;
  /*
   template_object={
        "object_type": "text",
        "text": "텍스트 영역입니다. 최대 200자 표시 가능합니다.",
        "link": {
            "web_url": "https://developers.kakao.com",
            "mobile_web_url": "https://developers.kakao.com"
        },
        "button_title": "바로 확인"
    }
    */
  String data = String("template_object={") + 
                String("\"object_type\": \"text\",") + 
                String("\"text\": \"") + String("토양 센서 값 :") + 
                String(sensorValue) +  //토양 센서 값
                String("\",\"link\": {}}"); //link가 없으면 오류메세지 받음
  Serial.println(data);
  http_code = http.POST(data);
  Serial.print("HTTP Response code: ");
  Serial.println(http_code);

  String response;
  if (http_code > 0) {
    response = http.getString();
    Serial.println(response);
  }

  http.end();
}

/*
curl -v -X POST "https://kauth.kakao.com/oauth/token" \
 -H "Content-Type: application/x-www-form-urlencoded" \
 -d "grant_type=refresh_token" \
 -d "client_id=${REST_API_KEY}" \
 -d "refresh_token=${USER_REFRESH_TOKEN}"
*/
bool update_access_token() {
  HTTPClient http;
  bool retVal = false;

  String url = "https://kauth.kakao.com/oauth/token";
  String new_refresh_token = "";
  if (!http.begin(url)) {
    Serial.println("\nfailed to begin http\n");
  }
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  int http_code;
  String data = "grant_type=refresh_token&client_id=" + rest_api_key + "&refresh_token=" + refresh_token;
  Serial.println(data);
  http_code = http.POST(data);
  Serial.print("HTTP Response code: ");
  Serial.println(http_code);

  String response;
  if (http_code > 0) {
    response = http.getString();
    Serial.println(response);
    access_token = extract_string(response, "{\"access_token\":\"", "\"");
    new_refresh_token = extract_string(response, "\"refresh_token\":\"", "\"");
    //만료 1개월전부터 갱신되므로 data가 없을 수도 있음
    if (new_refresh_token != "") {
      refresh_token = new_refresh_token;
    }
    retVal = true;
  } else {
    retVal = false;
  }
  http.end();
  return retVal;
}

실행 결과

카카오톡을 통해 토양의 센서 값을 읽을 수 있다.

후기

이번 시간에는 카카오톡을 활용하여 토양의 습도를 측정하는 실습을 해 보았는데 카카오톡과 연계해서 개발하는 것이 생각보다 어렵지 않아서 다양한 곳에서 활용할 수 있을 것 같아서 얻어가는 것이 많았던 것 같다.

⚠️ **GitHub.com Fallback** ⚠️