14주차_카카오톡으로 토양 센서 값 전송하기 - sookite22/SmartDivice_24 GitHub Wiki

목차

  • 토양 센서
  • OAuth
  • REST API
  • 실습
    • 토양 센서 값 읽기
    • 토양 센서 값을 카카오톡으로 전송
  • 후기

1. 토양 센서란?

스크린샷 2024-06-05 140003

토양에 있는 두 개의 탐침이 전류를 통과시키고 저항 변화가 측정된다. 토양 수분이 높으면 저항이 낮아지고 센서값은 크게 나타난다. 그 반대의 경우도 마찬가지이다.

토양 센서는 토양의 여러 가지 특성을 측정하는 데 사용된다. 가장 일반적으로 사용되는 토양 센서 중 하나는 토양 수분 센서 이다. 이는 토양의 수분 함량을 측정하여 농업, 원예, 환경 모니터링 등에 활용된다.

  • 본 실습에서는 저항 방식 의 토양 센서를 사용한다.

저항 방식 센서는 토양의 전기 저항을 측정한다. 물은 전기가 잘 통하는 성질을 가지므로 토양에 포함된 수분량이 많을수록 전기 저항이 낮아진다. 두 개의 전극(프로브)를 토양에 삽입하면, 센서의 두 전극 사이에 전류를 흐르게 하여 저항을 측정할 수 있다.

토양 내의 수분 함량이 높을수록 토양의 전기 전도성이 증가하고, 저항이 감소한다. 반대로 토양이 건조할수록 저항이 증가한다. 센서는 이러한 저항 변화를 측정한다.

간단하고 저렴한 방식이지만 토양의 염분 농도에 영향을 받기 쉽다는 단점도 존재한다.

  • 토양 습도 센서의 경우, 센서에 공급된 전압과 토양의 저항을 통해 전류를 계산할 수 있다. 반대로 전류를 측정하여 토양의 저항을 계산할 수도 있다.
  1. 전압 공급: 센서에 일정한 전압이 공급된다.
  2. 저항 측정: 프로브를 통해 토양의 저항을 측정한다. 이때, 토양의 수분 함량에 따라 저항 값이 달라진다.
  3. 전류 측정: 저항 값을 알고 있다면 공급된 전압을 통해 전류를 계산할 수 있다.
  4. 수분 함량 계산: 측정된 저항 값을 바탕으로 토양의 수분 함량을 계산한다. 저항 값이 낮을수록 수분 함량이 높다는 것을 의미한다.

1.1. 옴의 법칙

  • 앞선 주차들의 실습에 사용된 센서 설명에도 있듯이, 토양 습도 센서 또한 옴의 법칙이 적용된다.

옴의 법칙은 전압(V), 전류(I), 저항(R) 사이의 관계를 설명하는 물리 법칙이다.

V = I * R

  • 전압(Voltage) 은 전기 회로에서 전력 을 나타낸다. 단위 - 볼트(V)
  • 전류(Current) 는 전기 회로를 통해 흐르는 전자의 흐름 을 나타낸다. 단위 - 암페어(Ampere, A)
  • 저항(Resistance) 은 전기 회로에서 전류의 흐름을 제한하는 속성 을 나타낸다. 단위 - 옴(Ohm, Ω)

예를 들어, 센서에 5V 전압을 인가하고, 전류가 2mA (0.002A)로 측정되었다고 가정해보자. 옴의 법칙을 이용하여 저항을 계산하면 2500Ω가 나온다. 이 저항값은 토양의 습도 상태를 나타내며, 저항값이 특정 범위에 있으면 습도가 높거나 낮다고 판단할 수 있다.

2. OAuth(Open Authorization)이란?

사용자나 응용 프로그램이 타 서비스에 자신의 정보를 공유하는 데 사용할 수 있는 표준화된 인증 프로토콜 이다. 일반적으로 웹 애플리케이션에서 사용자 인증이나 데이터 공유를 위한 권한을 부여할 때 사용된다. 이를 통해 사용자는 자신의 정보나 리소스를 안전하게 공유할 수 있게 된다.

특히 개발자들이 다른 웹 서비스와 상호 작용할 수 있도록 해주고, 데이터를 보호하고 액세스 권한을 제한하는 기능을 제공한다.

OAuth 유형

  • OAuth 1.0a: 인증의 신뢰성이나 보안을 높이기 위해 사용되는 더 오래된 프로토콜이다. API 사용 시 인증에 서명이 필요하다.
  • OAuth 2.0: 더 간단하고 확장성이 높은 인증 프로토콜이다. 다양한 애플리케이션 및 사용 사례에 맞게 다양한 인증 방법을 제공한다.

주요 요소

  • Consumer: 액세스 권한을 요청하는 애플리케이션이나 서비스이다.
  • Resource Owner: 사용자나 시스템의 소유자이다. 액세스 권한을 제공할 수 있다.
  • Resource Server: 데이터나 리소스를 보관하고 제공하는 서버이다.
  • Authorization Server: 사용자의 권한을 승인하는 서버이다.

기본 단계

  1. 리소스 소유자 인증: 리소스 소유자가 인증을 받는다. 예를 들어, 웹 브라우저에서의 로그인이 있다.
  2. 권한 요청: 소비자가 리소스 서버와 통신하기 전에 권한을 요청한다.
  3. 인가 코드 발급: 리소스 서버는 리소스 소유자에게 권한을 부여하고 인가 코드를 발급한다.
  4. 액세스 토큰 발급: 인가 코드가 발급되면 소비자가 액세스 토큰을 요청할 수 있다. 이 토큰은 리소스 서버와 소비자 사이의 인증을 위한 것이다.
  5. 액세스 토큰 사용: 소비자는 해당 액세스 토큰을 사용하여 리소스 서버에 요청을 보낼 수 있다.

3. REST API(Representational State Transfer Application Programming Interface)란?

네트워크 상에서 자원을 처리하는 방식 을 정의한 아키텍처 스타일 이다.

HTTP 프로토콜을 기반으로 클라이언트와 서버 간 통신을 통해 자원(데이터)을 처리하고, 이를 통해 웹 서비스나 애플리케이션을 설계할 때 자주 사용된다.

REST 기본 원칙

  1. 자원(Resource): 자원은 네트워크 상에서 액세스할 수 있는 모든 것을 의미한다. 사용자, 파일, 이미지 등이 있다. 각 자원은 고유한 URI(Uniform Resource Identifier) 로 식별된다.

  2. 표현(Representation): 클라이언트가 자원에 접근할 때, 서버는 자원의 현재 상태를 클라이언트에 전달한다. 이는 일반적으로 JSON, XML, HTML 등으로 전송된다.

  3. HTTP 메서드: REST API는 HTTP 메서드를 사용하여 자원에 대한 작업을 정의한다.

스크린샷 2024-06-11 033801

  • GET: 자원 조회
  • POST: 새로운 자원 생성
  • PUT: 기존 자원 수정
  • DELETE: 자원 삭제
  • PATCH: 자원의 일부 수정
  1. 상태 없는(stateless) 각 요청은 클라이언트의 상태를 서버에 저장하지 않는다. 모든 요청은 독립적이며, 필요한 모든 정보는 요청에 포함되어야 한다. 예를 들어, 사용자가 로그인을 한 후 다른 작업을 할 때마다 로그인 정보를 포함해야 한다.

  2. 캐시 가능(Cachable): 응답은 명시적으로 캐시할 수 있어야 한다. 이를 통해 성능 향상이 가능하다.

  3. 계층화(Layered System) 클라이언트는 서버와 직접 통신하지 않을 수 있으며, 중간 서버를 통해 통신할 수도 있다. 중간 서버는 로드 밸런싱, 보안, 캐싱 등의 역할을 수행할 수 있다.

  4. 인터페이스 일관성(Uniform Interface) RESTful API는 일관된 인터페이스를 제공한다. 자원과 상호작용하는 방법이 일관되게 정의되어 있어야 한다.

장점

  • 확장성: 클라이언트와 서버가 독립적으로 동작할 수 있다.
  • 유연성: 다양한 데이터 형식을 지원하며, HTTP 표준을 사용하여 다양한 플랫폼에서 사용 가능하다.
  • 유지보수성: 일관된 인터페이스를 제공하므로 유지보수가 용이하다.

단점

  • 복잡성: 상태를 유지하지 않아 클라이언트가 요청마다 모든 정보를 제공해야 할 때가 많이 발생한다.
  • 보안: HTTP를 사용하므로 기본적으로 보안에 취약하며 HTTPS를 사용하여 보안을 강화해야 한다.

4. 실습

4.1. 토양 센서 값 읽기

회로 연결

스크린샷 2024-06-05 102933

코드 작성

const int soilSensorPin = 34;

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

결과 확인

스크린샷 2024-06-05 140129

  • 시리얼 모니터를 통해 결과값을 확인할 수 있다.

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

센서값 실시간 확인을 위하여 별도의 앱을 설치하지 않아도 기존에 사용하던 카카오톡을 활용할 수 있다. 이를 위한 카카오톡 앱 설정을 해보자.

4.2.1. 카카오톡 개발자 사이트 가입

카카오톡 개발자 사이트 https://developers.kakao.com/ 에 로그인한다.

스크린샷 2024-06-05 104355


4.2.2. 앱 등록 및 정보 설정

  1. 내 애플리케이션을 추가하고 REST API키 복사해 둔다.

스크린샷 2024-06-05 140611(1)

  1. "플랫폼" 탭에서 Web 플랫폼 등록을 한다. 사이트 도메인은 "http://localhost"로 저장하면 된다.

스크린샷 2024-06-05 141103 스크린샷 2024-06-05 141340

  1. "카카오톡 로그인" 탭에서 카카오톡 로그인을 활성화하고, Redirect URL에 “https://www.example.com/oauth”를 저장한다. 스크린샷 2024-06-05 141639

  2. 카카오톡 로그인 탭 중 "동의항목"을 선택하고, 카카오톡 메시지 전송” 항목을 다음과 같이 설정한다.

스크린샷 2024-06-05 141846 스크린샷 2024-06-05 142037


4.2.3. Redirect URI로 인가 코드(Authorization code) 전달 받기

  • 토큰 은 사용자의 카카오 로그인 인증 및 인가 정보를 담은 권한 증명으로, 카카오 API 호출에 사용된다.
  • 카카오 로그인은 OAuth 2.0 표준 규격에 따라 액세스 토큰(Access token), 리프레시 토큰(Refresh token) 두 종류의 토큰을 발급한다.

토큰 종류

  • 액세스 토큰: 사용자 인증, 카카오 API 호출 권한을 부여한다.

  • 리프레시 토큰: 액세스 토큰 재발급에 사용된다. 유효한 리프레시 토큰이 있다면 사용자가 매번 카카오 계정 정보를 입력하거나 카카오톡으로 로그인하는 인증 절차를 거치지 않아도 액세스 토큰을 재발급 받을 수 있다.

  • ID 토큰: 카카오 로그인 사용자의 인증 정보를 제공한다.

  • 이러한 토큰들은 만료 시간이 존재하므로 주의하자.

브라우저 주소창에 다음과 같이 입력한다.

https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}

  • {CLIENT_ID}는 카카오 개발자 사이트에서 발급 받은 앱의 REST API 키, {REDIRECT_URI}는 설정한 Redirect URI를 의미한다.

브라우저에서 위 URL을 입력하고 엔터를 누르면, 카카오 로그인 화면으로 리디렉션된다. 사용자 로그인 및 권한 동의를 완료하면, 설정한 Redirect URI로 리디렉션되고 URL에 code 파라미터로 인가 코드 가 포함된다. code= 이후의 값을 복사해 둔다.

  • 인가 코드는 한 번 사용하면 만료되므로 필요 시 재발급을 받도록 하자.

스크린샷 2024-06-05 150501 스크린샷 2024-06-05 150426(1)


4.2.4. Access 토큰 발급 받기

최종적으로 Access 토큰을 발급 받기 위하여 HTTP POST 요청을 카카오 토큰 엔드포인트에 보내야 한다.

방법 1: curl 명령

명령 프롬포트에 아래와 같은 명령어를 입력한다.

curl -v -X POST "https://kauth.kakao.com/oauth/token"
-H "Content-Type: application/x-wwwform-urlencoded"
-d "grant_type=authorization_code"
-d "client_id=${CLIENT_ID}"
--dataurlencode "redirect_uri=${REDIRECT_URI}"
-d "code=${AUTHORIZE_CODE}"

다음과 같은 명령문으로도 가능하다.

curl -X POST "https://kauth.kakao.com/oauth/token"
-d "grant_type=authorization_code"
-d "client_id={CLIENT_ID}"
-d "redirect_uri={REDIRECT_URI}"
-d "code={AUTHORIZE_CODE}"

방법 2: 파이썬 코드 실행

다음과 같은 파이썬 코드를 실행한다.

import requests
import json
url = 'https://kauth.kakao.com/oauth/token'
client_id = 'REST_API_KEY'
redirect_uri = 'https://www.example.com/oauth'
code = 'AUTHORIZATION_CODE'
data = {
'grant_type':'authorization_code',
'client_id':client_id,
'redirect_uri':redirect_uri,
'code': code,
}
response = requests.post(url, data=data)
tokens = response.json()
print(tokens)
  • {AUTHORIZE_CODE}는 위에서 발급 받은 인가 코드이다.
  • 위 요청이 성공하면 JSON 형태로 Access 토큰과 Refresh 토큰이 반환된다.

라이브러리 설치

먼저 'ArduinoJson' 라이브러리를 설치한다.

스크린샷 2024-06-07 092021

코드 작성 1

#include <ArduinoJson.h>
void setup() {
  // Initialize serial port
  Serial.begin(115200);
  while (!Serial) continue;
  delay(3000);
  // JSON 문서 할당
  //
  // 괄호 안의 200은 메모리 풀의 용량(바이트)입니다.
  // JSON 문서와 일치하도록 이 값을 변경하는 것을 잊지 마십시오.
  // https://arduinojson.org/v6/assistant 를 사용하여 용량을 계산합니다.
  StaticJsonDocument<96> doc;
  // StaticJsonDocument<N>은 스택에 메모리를 할당합니다.
  // 힙에 할당하는 DynamicJsonDocument로 대체할 수 있습니다.
  // DynamicJsonDocument doc(200);
  // JSON 입력 문자열.
  char json[] = R"rawliteral({
    "token_type":"bearer",
    "access_token":"c281d73b097",
    "expires_in":43199,
    "refresh_token":"0a0c90af08f",
    "refresh_token_expires_in":5184000,
    "scope":"account_email profile"
  })rawliteral";
  Serial.println(json);
  // Deserialize the JSON document
  DeserializationError error = deserializeJson(doc, json);
  // Test if parsing succeeds.
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }
  // Fetch values.
  const char* access_token = doc["access_token"];
  const char* refresh_token = doc["refresh_token"];
  long expires_in = doc["expires_in"];
  // Print values.
  Serial.print("Access token : ");
  Serial.println(access_token);
  Serial.print("Refresh token : ");
  Serial.println(refresh_token);
  Serial.print("Expire time : ");
  Serial.println(expires_in, 6);
}
void loop() {
  // not used in this example
}

코드 작성 2

#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;
}

최종 통합 코드

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

// WiFi 설정
const char *ssid = "AndroidHotspotSY";          // 사용하는 WiFi 네트워크 이름 (SSID)
const char *password = "s2178hr@";  // 사용하는 WiFi 네트워크 비밀번호

const String rest_api_key = ""; // REST API 값을 여가에 입력
String access_token = "";  // access_token 값을 여기에 입력
String refresh_token = ""; // refresh_token 값을 여기에 입력

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

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

  // WiFi 연결
  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;

  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) {
    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;
  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();
}

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\":\"", "\"");
    if (new_refresh_token != "") {
      refresh_token = new_refresh_token;
    }
    retVal = true;
  } else {
    retVal = false;
  }
  http.end();
  return retVal;
}

결과 확인

KakaoTalk_20240607_103518700

  • 토양 센서 값이 일정한 시간 간격으로 카카오톡 메세지로 전송되는 것을 확인할 수 있다. 쉬운 확인을 위하여 10초 간격으로 알림이 오게끔 변경하였다.

5. 후기

토양 센서 모듈로 값을 읽어올 뿐만 아니라, 평소 많은 사람들이 실제로 사용하는 카카오톡 앱을 이용하여 값을 확인할 수 있다는 것이 매우 흥미롭다. 현재 카카오 맵 API를 이용하는 웹을 개발 중인데, 이때보다 본 실습에서 OAuth를 통해 토큰을 발급 받는 것에 다소 어려움이 있었다. 그러나 이런 경험을 통해서 성공적으로 실습을 마무리할 수 있었고, 현재 개발 중인 웹에도 값을 카카오톡으로 읽어올 수 있도록 하는 방법을 적용해 볼 예정이다. 매우 도움이 되는 실습이었고 재미있었다.

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