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

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

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

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분 단위로 센서 값이 출력되는 것을 확인 가능

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






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

- 명령 프롬프트에 아래 명령어 입력 후 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= "

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

#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시간 단위로 토양 센서 값이 카카오톡으로 전송되는 것을 확인