week__14 (REST API_카카오톡 활용) - JINEEH/SmartDevice_JinHee GitHub Wiki

ESP 토양 습도 센서를 활용한 실습

1. 학습 목표

1-1. 카카오톡으로 토양센서값 전송

2. REST API

2-1. 의미: HTTP를 사용하여 컴퓨터의 기능을 실행시키는 명령
2-2. 구성: 클라이언트, HTTP 메서드, URL
           - 클라이언트: 웹 브라우저, 모바일 앱
           - HTTP 메서드: GET, POST, DELEtE, PUT
           - URL: 클라이언트가 요청하는 자원
           - 서버: 클라이언트의 요청 처리 및 응답 반환, 데이터 생성/업데이트/삭제
           - JSON: 클라이언트와 서버 간에 주고받는 데이터 형식, 가볍고 사람이 읽기 쉬움

14주차 REST API

3. OAuth 2.0

3-1. 의미: 인터넷에서 안전하게 권한을 위임하기 위한 표준 프로토콜
3-2. 구성: 리소스 소유자(사용자), 클라이언트(사용하려는 앱), 리소스 서버(보호된 데이터를 호스팅하는 서버), 권한 서버(사용자를 인증하고 토큰을 발급하는 서버)
3-3. 과정: 사용자 인증 요청 -> 사용자 인증 -> 토큰 요청 -> 토큰 발급
           - 사용자 인증 요청: 앱이 권한 서버에 사용자를 인증해달라고 요청함
           - 사용자 인증: 사용자가 로그인하고 권한 부여, 권한 서버는 인가 코드를 앱에 전송함
           - 토큰 요청: 인가 코드를 사용하여 권한 서버에 액세스 토큰을 요청함
           - 토큰 발급: 권한 서버는 액세스 토큰을 발급, 앱은 이를 사용하여 리소스 서버에 접근이 가능함
3-4. 예시: 실습에서 사용되는 프로그램(Firebase, Arduino Cloud, 공공데이터포털 등)에 구글 계정을 통해 회원가입 및 로그인하여 접근 가능

14주차 OAuth 2 0

4. 옴의 법칙

4-1. 의미: 전류, 전압, 저항 사이의 관계를 나타내며 전류의 세기(I)는 전압(V)에 비례하고 저항(R)에 반비례함
           - 전압은 전류와 저항의 곱으로 계산
           - 옴의 법칙을 사용하여 전압분배법칙을 적용하면 복잡한 회로의 전압을 쉽게 계산 가능함
4-2. 전압분배법칙: 직렬 연결된 저항으로 구성된 회로에서 특정 전압에서 걸리는 전압을 공식으로 나타냄
                  - 해당 저항에서의 전압 강하를 계산하는 법칙
4-3. 예시: 직렬 연결된 저항이 2개가 있는 회로가 있다면 전압분배법칙을 적용하여 전체 전압에 따라 각각의 저항에 걸리는 전압을 계산 -> 전체 전압이 10V이고 첫번째 저항이 4옴, 두번째 저항이 6옴이라면 각각의 전압은 4V, 6V

14주차 옴의 법칙

5. 토양센서 실습 1

5-1. Arduino IDE 시리얼 모니터에 토양 센서 값 출력하기
5-2. 실습 준비물: ESP32, ESP32 확장 실드, 토양 습도 센서, 점퍼 케이블
5-3. 센서 값 해석: 0 ~1500 건조한 토양 / 1500~2000 정상 / 2000이상 물 속
  • 실행 코드
const int soilSensorPin = 34;
 void setup(){
 Serial.begin(115200);
 }
 void loop(){
 Serial.print("토양습도센서값:");
 Serial.println(analogRead(soilSensorPin));
 delay(1000);
 }
  • 실습 결과
  • 1분 단위로 센서 값이 출력되는 것을 확인 가능

14주차(실습1, 1분 단위)

6. 토양센서 실습 2

6-1. 실습 1에서 시리얼 모니터로 확인한 센서 값을 카카오톡으로 전송받기
  • 카카오톡 개발자 사이트 가입 및 애플리케이션 추가

14주차

14주차-1

  • REST API 키 복사

14주차-2

  • Web 플랫폼 등록 및 로그인 활성화

14주차-3

14주차-4

14주차-5

  • Access 토큰 발급
  • Redirect URI로 인가 코드 전달받고 code = 이후의 값 복사

14주차-6

  • 명령 프롬프트에 아래 명령어 입력 후 access_token, refresh_token 발급받기
  • client id에 REST API 키 입력, 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= " --data-urlencode "redirect_uri=https://www.example.com/oauth" -d "code= "

14주차-7

  • 명령어 입력 후 발급받은 access token, refresh token 실행 코드에 입력
  • 토큰 만료 시간

14주차(토근 만료시간)

  • 실행 코드
#include <WiFi.h>
#include <HTTPClient.h>

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

const String rest_api_key = " "; 
String access_token = " ";
String 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;
}

  • 실습 결과
  • 1시간 단위로 토양 센서 값이 카카오톡으로 전송되는 것을 확인
⚠️ **GitHub.com Fallback** ⚠️