공기정화 식물-인공지능-IOT

Moisturesensor_esp32_AI_TalkBack

coding art 2021. 4. 23. 20:04
728x90

그린와처에서 실행해야 할 코드 구조를 살펴보자. esp32 클라이언트에서 기본적으로 필요한 라이브러리는 Thingspeak.h ThingSpeak 서버에 출력하기 위한 WriteAPI 값이다.

아울러 원격에서 esp32 클라이언트 보드를 조종하기 위해서는 TalkBack 기능을 설치해야 한다. 이는 원격에 해당하는 ThingSpeak 서버 해당 채널의 Private View 화면에서 Apps 의 메뉴 중 TalkBack이 있음을 알 수 있다. 누르고 들어가자.

 

TalkBack 창을 살펴보면 이미 코딩하여 사용하던 파일이 있을 수 있다. 초록색 New TalkBack 버튼을 누르고 들어가면 오른 쪽 편집 창을 볼 수 있다. Name 은 새로운 파일명으로 편집 가능하다. 따라서 “PUMPONOFF” 로 파일명을 부여하고 빨간색 Save TalkBack 버튼을 누르자.

 

TalkBack 파일에서 보면 TalkBack 아이디와 API Key 가 있음을 알 수 있다. 이 파라메터 값은 아두이노 esp32 보드의 실행 코드에 정보로 제공되어야 한다.

 

다음의 코드를 살펴보면 ThingSpeak 서버에서 가져온 2개의 파라메터 값들이 있음을 알 수 있다. 한편 전역 변수 Thresholdmin Thresholdsd AI 코드에서 필요로 하는 이미 사전에 학습된 값이다.

 

이 단계에서 esp32 보드에 업로딩할 headersetup loop WiFi 코드 영역을 제외한 코드의 중요구조를 살펴보기로 한다.

Loop 코드에는 TalkBack 코드와 ThingSpeak에 데이터를 보내는 루틴으로 구성되며 필요하다면 측정된 데이터들에 대해 AI 루틴을 추가할 수도 있다.

 

 

TalkBack 코드는 ThingSpeak 서버에서 설정한 TalkBack 아이디와 Key 값을 각각 문자열 변수 tbURI postMessage httpPost 루틴에 입력하여 문자열 변수 newCommand를 인수로 받아 낸다. 이 인수는 newCommand.indexOf(“△□○”) 명령으로 parsing 하여 확인이 되면 해당하는 명령 즉 LED ON 또는 LED OFF 또는 ESP.restart() 와 같은 명령을 실행시키도록 한다. 특히 ESP.restart() 명령 사용 시 주의할 점은 esp32 보드가 분리된 센서 보드와 인터페이스로 구성되었을 경우 esp32 restart 할 것인지 아니면 센서보드도 함께 restart() 시킬 것인지를 고려해야 한다. 함께 restart() 해야 할 경우에는 esp32 보드에서 먼저 센서보드를 restart() 시킨 후에 esp32 자신도 restart() 시키도록 한다.

매번 Loop를 돌면서 TalkBack 명령이 있는지 여부를 체크하여 실행 후 본연의 센서 데이터를 ThingSpeak 서버에 전송한다.

 

한편 최근 연속적으로 측정된 데이터에 노이즈 포함 여부를 가리도록 하자. 3점 이동 평균 방식을 적용하기 위해 header 영역에 2개의 초기 값을 설정한다. setup()을 실행하고 Loop 문에서 analogRead() 명령을 사용하여 3번째 데이터를 읽어 들여 3점 이동 평균을 계산한다. 이 평균 값을 사용하여 3개의 데이터에 대한 표준편차 계산 후 3점 이동 평균 값과 함께 노이즈가 포함되었는지 여부를 판단하도록 한다.

 

 

#include "ThingSpeak.h"

#include <WiFi.h>

 

char ssid[] = "droidan1234"; // your network SSID (name)

char pass[] = "dddddddddd"; // your network password

 

WiFiClient client;

 

unsigned long myChannelNumber = 1213935;

const char * myWriteAPIKey = "5F01CRTZXJ5X35JR";

 

unsigned long myTalkBackID = 42457;

const char * myTalkBackKey = "8JGK95U5ADNXYI3G";

 

int sensor = 33;

int lw = 2100;

int hg = 4095;

// 10분 이상 샘플링 타임추천- 모터 재급수 방지

unsigned long sampling_time = 300000;//샘플링 타임 간격 5

int Motorpin = 22;//GPIO

int Thresholdmin = 95;// Moisture 적정값으로 설정

int Thresholdsd = 3.0;// 허용표준편차

int watering_time = 240000;//급수시간 240-4

float soil0, soil1, soil2;

 

void setup() {

Serial.begin(115200); //Initialize serial

WiFi.mode(WIFI_STA);

ThingSpeak.begin(client); // Initialize ThingSpeak

pinMode(Motorpin, OUTPUT); // Set up LED

 

soil0 = 30;// 첫번 변수 초기화

soil1 = 40;// 둘째 변수 초기화

}

 

void loop() {

int err,xsoil;

float soil;

 

// Connect or reconnect to WiFi

if(WiFi.status() != WL_CONNECTED){

Serial.print("Attempting to connect to SSID: ");

while(WiFi.status() != WL_CONNECTED){

WiFi.begin(ssid,pass);

Serial.print(".");

delay(5000);

}

Serial.println("\nConnected.");

}

 

// Create the TalkBack URI

String tbURI = String("/talkbacks/") + String(myTalkBackID) + String("/commands/execute");

// Create the message body for the POST out of the values

String postMessage = String("api_key=") + String(myTalkBackKey);

// Make a string for any commands that might be in the queue

String newCommand = String();

// Make the POST to ThingSpeak

int x = httpPOST(tbURI, postMessage, newCommand);

client.stop();

 

// Check the result

if(x == 200) { //200 http communication ok.

Serial.println("checking queue...");

// Check for a command returned from TalkBack

if(newCommand.length() != 0){

Serial.println(newCommand.length());

Serial.print(" Latest command from queue: ");

Serial.println(newCommand);

 

if(newCommand.indexOf("TURN_ON") != -1){

digitalWrite(Motorpin, HIGH);

}

if(newCommand.indexOf("TURN_OFF") != -1){

digitalWrite(Motorpin, LOW);

}

if(newCommand.indexOf("RESTART") != -1){

ESP.restart();

}

//급수시간 짧게 10초로 조절-모터 런 확인용

if(newCommand.indexOf("SHORT") != -1){

int short_time = 10000;

digitalWrite(Motorpin, HIGH);

delay(short_time);

digitalWrite(Motorpin, LOW);

}

}

else{

Serial.println(" Nothing new.");

}

}

else{

Serial.println("Problem checking queue. HTTP error code " + String(x));

}

 

// Moisture measurment

xsoil = analogRead(sensor);

if( xsoil < lw ) xsoil = lw;

if( xsoil > hg ) xsoil = hg;

soil = 100.0*(xsoil-lw)/(hg - lw);

Serial.print(xsoil,0);

Serial.println();

 

soil2 = soil;

float a = (soil0 + soil1 + soil2)/3.0; // 3점 데이타 이동평균 계산

float sd = sqrt( ( (soil0-a)*(soil0-a) + (soil1-a)*(soil1-a) + (soil2-a)*(soil2-a) )/3);//표준편차 계산

 

 

Serial.print("3point average=: ");

Serial.println(a);

Serial.print("Standard Deviation=:");

Serial.println(sd);

 

ThingSpeak.setField(1, soil);

ThingSpeak.setField(2, a);

ThingSpeak.setField(3, sd);

int z = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

 

soil0 = soil1; // 하나씩 밀기

soil1 = soil2; // 하나씩 밀기

soil2 = 0; // 다시 읽어야 할 값 리세트

 

//client.stop(); //꼭 필요한지 체크 필요

// 나증에 soil 대신 3 이동평균 값 사용 동시에 표준편차가 0.47(1.0) 보다 적으면 급수

 

if ( ( a > Thresholdmin) && ( sd < Thresholdsd ) ) {

digitalWrite(Motorpin,HIGH);// PWM 급수모터 구동 루틴으로 대체

delay(watering_time);

digitalWrite(Motorpin,LOW);

 

}

 

delay(sampling_time);

}//end of Loop

 

 

 

 

 

// General function to POST to ThingSpeak

 

int httpPOST(String uri, String postMessage, String &response){

 

bool connectSuccess = false;

connectSuccess = client.connect("api.thingspeak.com",80);

 

if(!connectSuccess){

return -301;

 

}

 

postMessage += "&headers=false";

 

String Headers = String("POST ") + uri + String(" HTTP/1.1\r\n") +

String("Host: api.thingspeak.com\r\n") +

String("Content-Type: application/x-www-form-urlencoded\r\n") +

String("Connection: close\r\n") +

String("Content-Length: ") + String(postMessage.length()) +

String("\r\n\r\n");

 

client.print(Headers);

client.print(postMessage);

 

long startWaitForResponseAt = millis();

while(client.available() == 0 && millis() - startWaitForResponseAt < 5000){

delay(100);

 

}

 

if(client.available() == 0){

return -304; // Didn't get server response in time

 

}

 

if(!client.find(const_cast<char *>("HTTP/1.1"))){

return -303; // Couldn't parse response (didn't find HTTP/1.1)

 

}

 

int status = client.parseInt();

 

if(status != 200){

return status;

}

 

if(!client.find(const_cast<char *>("\n\r\n"))){

 

return -303;

}

 

String tempString = String(client.readString());

response = tempString;

return status;

}