아빠 비닐하우스 개폐기 더이상 손 안 댈 정도로 끝 - 블라인드로 같은 방식으로 하면된다.

반응형
추가사항
1. 실기간으로 앱에서 그림으로 개폐기 얼마다 열리고 닫혔는지 확인
2. 계절별 열림 정도 적용
3. 서버 리스타트후 개폐기 닫힘 누르면 작동 안하던 버그 고침(방식을 바꿔서)
4. 개폐기 오프라인 스위치
5. 코드 와이파이로 업로드
6. 좌측 개폐기도 수치 입력 받아서 한번에 원하는 만큼만 열릴 수 있도록함.

전체 코드


//하우스 개폐기

#include <WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <WiFiClient.h> //추가한 헤더파일
#include <WebServer.h>  //추가한 헤더파일
#include <ESPmDNS.h>    //추가한 헤더파일
#include <Update.h>     //추가한 헤더파일

#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

const char* host = "esp32"; //ota 때문에 추가
const char *ssid =  "KT_WLAN_A914";   
const char *password =  "000000214F"; 
WebServer server(80);  //ota 때문에 추가
//여기서 부터 OTA용 코드
const char* loginIndex =
 "<form name='loginForm'>"
    "<table width='20%' bgcolor='A09F9F' align='center'>"
        "<tr>"
            "<td colspan=2>"
                "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                "<br>"
            "</td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
             "<td>Username:</td>"
             "<td><input type='text' size=25 name='userid'><br></td>"
        "</tr>"
        "<br>"
        "<br>"
        "<tr>"
            "<td>Password:</td>"
            "<td><input type='Password' size=25 name='pwd'><br></td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
            "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
        "</tr>"
    "</table>"
"</form>"
"<script>"
    "function check(form)"
    "{"
    "if(form.userid.value=='admin' && form.pwd.value=='admin')"
    "{"
    "window.open('/serverIndex')"
    "}"
    "else"
    "{"
    " alert('Error Password or Username')/*displays error message*/"
    "}"
    "}"
"</script>";

/*
 * Server Index Page
 */

const char* serverIndex =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
   "<input type='file' name='update'>"
        "<input type='submit' value='Update'>"
    "</form>"
 "<div id='prg'>progress: 0%</div>"
 "<script>"
  "$('form').submit(function(e){"
  "e.preventDefault();"
  "var form = $('#upload_form')[0];"
  "var data = new FormData(form);"
  " $.ajax({"
  "url: '/update',"
  "type: 'POST',"
  "data: data,"
  "contentType: false,"
  "processData:false,"
  "xhr: function() {"
  "var xhr = new window.XMLHttpRequest();"
  "xhr.upload.addEventListener('progress', function(evt) {"
  "if (evt.lengthComputable) {"
  "var per = evt.loaded / evt.total;"
  "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  "}"
  "}, false);"
  "return xhr;"
  "},"
  "success:function(d, s) {"
  "console.log('success!')"
 "},"
 "error: function (a, b, c) {"
 "}"
 "});"
 "});"
 "</script>";
// 여기 까지

const char* ID = "걔폐기1 ";  // Name of our device, must be unique
const char* TOPIC = "house/right/motor/pos";
const char* TOPICLEFT = "house/right/motor/posleft";
const char* INTOPIC = "house/right/motor/set";
const char* TEMPTOPIC = "house/temperature";
const char* SEASONTOPIC = "house/season";
const char* mqttUser = "dragon";
const char* mqttPassword = "qazwsxedcrfvtgbyhnujm";
const char* broker = "59.22.159.5";

WiFiClient wclient;
PubSubClient client(wclient); 
char messages[50];
char messagesl[50];
char tempers[50];
int SW_state = 0;
int Motor_open1 = 12;
int Motor_close1 = 13;
int Motor_open2 = 26;
int Motor_close2 = 27;
unsigned long setTime = 0;
unsigned long preTime = 0;
float tem = 0;
float tem1 = 0;
char val[20];

unsigned long time1 = 0;  //시간 관리
unsigned long time2 = 0;  //시간 관리
unsigned long time3 = 0;  //시간 관리
unsigned long time4 = 0;  //시간 관리
unsigned long timeState = 0;
unsigned long msgLeftSet = 0;
unsigned long rollover = 0;  //우측 메세지 받는 시각 함수
unsigned long rolloverl = 0; //좌측 메세지 받는 시각 함수
String msg = "";       //mqtt메세지 문자열 변환 후 담는 변수
String msgFormer = ""; //여러번 누름 방지용 변수
String msgRight = "l"; //그냥 ""으로 설정하면 0으로 인식함.
String msgLeft = "l";  //그냥 ""으로 설정하면 0으로 인식함.
int maxTime = 100; 

// Connect to WiFi network
void setup_wifi() {
  Serial.print("\nConnecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password); 
  while (WiFi.status() != WL_CONNECTED) { 
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}
//숫자인지 문자인지 판단하는 함수
bool isNumber(const String& str)
{
    for (char const &c : str) {
        if (std::isdigit(c) == 0) return false;
    }
    return true;
}

// mqtt메세지 받는 함수
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Received messages: ");
  Serial.println(INTOPIC);
  msg = "";
  //메세지 문자열로 변환
  for(int i=0; i<length; i++){
    //Serial.println((char)payload[i]);
    msg += (char)payload[i];
  }
  //메세지 컨트롤
  Serial.println(msg);
  if (msg == msgFormer) {
  } else if (msg == "STOP") {
    msgRight = "STOP";
    msgFormer = msg;
  } else if (msg == "stop") {
    msgLeft = "stop";
    msgFormer = msg;
  } else if (msg == "summer"){
    maxTime = 950;
    EEPROM.write(2, 9);
    EEPROM.commit();
    client.publish(SEASONTOPIC, "1");
  } else if (msg == "winter") {
    maxTime = 400;
    EEPROM.write(2, 4);
    EEPROM.commit();
    client.publish(SEASONTOPIC, "0");
  } else if (msg == "reset") {
    EEPROM.write(0, 0);
    EEPROM.write(1, 0);
    EEPROM.write(2, 0);
    EEPROM.commit();
  } else if(msg.toInt()){
    if (msg.toInt() <= 110){
      msgRight = msg;
      rollover = millis();
    } else if(msg.toInt() >= 200) {
      msgLeft = msg;
      rolloverl = millis();
    }
    msgFormer = msg;
  }
}


// Reconnect to client
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(ID, mqttUser, mqttPassword)) {
      Serial.println("connected");
      Serial.print("Publishing to: ");
      Serial.println(TOPIC);
      Serial.println('\n');
      client.subscribe(INTOPIC);

    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println("\n try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200); 
  pinMode(4, INPUT);
  pinMode(Motor_open1, OUTPUT);
  pinMode(Motor_close1, OUTPUT);
  pinMode(Motor_open2, OUTPUT);
  pinMode(Motor_close2, OUTPUT);  
  delay(100);
  setup_wifi(); // Connect to network
  client.setServer(broker, 1883);
  client.setCallback(callback);
  sensors.begin();
  EEPROM.begin(3);
  maxTime = EEPROM.read(2) * 100;
  preTime = EEPROM.read(1) * maxTime;
  timeState = EEPROM.read(0) * maxTime;
	//OTA 복사임 
  if (!MDNS.begin(host)) { //http://esp32.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");
  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
  // 여기 까지
}

void loop() {
  server.handleClient(); //OTA
// 와이파이 끊어지면 다시연결하는게 없더라 그래서 몇번 이상있을 때 문제 있어서 추가함 리부팅하도록
  if (WiFi.status() != WL_CONNECTED) {
    ESP.restart();
  }
  // MQTT 재연결
  if (!client.connected()){
    reconnect();
  }
  client.loop();
  
  // 온도 컨트롤
  sensors.requestTemperatures();
  tem = sensors.getTempCByIndex(0);
  tem = floor(tem*10)/10;
  //Serial.print("temp=");
  //Serial.println(tem);
  if (tem < -55){ //-55 까지 온도 범위 인데 -127으로 나올 때가 가끔 있음
    
  } else if (tem != tem1) {
    tem1 = tem;
    dtostrf(tem, 5, 1, val);
    snprintf(tempers, 75, "%s", val);
    //Serial.print("tempers=");
    //Serial.println(tem);
    client.publish(TEMPTOPIC, tempers);
  }
  // 좌측 개폐기 컨트롤
  if (isNumber(msgLeft)) {
    msgLeftSet = ((msgLeft.toInt() - 200) * maxTime);
    if (timeState < msgLeftSet) {
      digitalWrite(Motor_open1, HIGH);
      time4 = millis();
      time3 = time4 - rolloverl;
      if (time3 >= 500){
        timeState += 500;
        snprintf(messagesl, 75, "%ld", timeState / maxTime + 200);
        client.publish(TOPICLEFT, messagesl);
        rolloverl = 0;
      }
      if (timeState >= msgLeftSet){ 
        digitalWrite(Motor_open1, LOW);
        msgLeft = "dont";
        timeState = msgLeftSet;
        EEPROM.write(0, timeState/maxTime);
        EEPROM.commit();
        snprintf(messagesl, 75, "%ld", timeState / maxTime + 200);
        client.publish(TOPICLEFT, messagesl);
      }
    } else if (timeState > msgLeftSet) {
      Serial.print(timeState);
      digitalWrite(Motor_close1, HIGH);
      time4 = millis();
      time3 = time4 - rolloverl;
      if (time3 >= 500) {
        timeState -= 500;
        if (timeState < 500) {
          timeState = 0;
        }
        snprintf(messagesl, 75, "%ld", timeState / maxTime + 200);
        client.publish(TOPICLEFT, messagesl);
        rolloverl = 0;
      }
      if (timeState <= msgLeftSet){ 
        digitalWrite(Motor_close1, LOW);
        msgLeft = "dont";
        timeState = msgLeftSet;
        EEPROM.write(0, timeState/maxTime);
        EEPROM.commit();
        snprintf(messagesl, 75, "%ld", timeState / maxTime + 200);
        client.publish(TOPICLEFT, messagesl);
      }
    }
  } else if (msgLeft == "stop"){
    Serial.println("좌 닫힘");
    digitalWrite(Motor_open1, LOW);
    digitalWrite(Motor_close1, LOW);
    snprintf(messagesl, 75, "%ld", timeState / maxTime + 200);
    client.publish(TOPICLEFT, messagesl);
    msgLeft = "dont";
    EEPROM.write(0, timeState/maxTime);
    EEPROM.commit();
  }
  // 우측 개폐기 컨트롤(메세지가 0~100일 경우)
  if (isNumber(msgRight)) {
    setTime = maxTime * (msgRight.toInt() - 10);
    time2 = millis();
    time1 = (time2 - rollover);
    if (setTime > preTime) {
      digitalWrite(Motor_open2, HIGH);
      if (time1 >= 500) {
        preTime += 500;
        snprintf(messages, 75, "%ld", (preTime / maxTime) + 10);
        client.publish(TOPIC, messages);
        rollover = 0;
      }
      if (preTime >= setTime){
        digitalWrite(Motor_open2, LOW);
        preTime = setTime;
        snprintf(messages, 75, "%ld", (preTime / maxTime) + 10);
        client.publish(TOPIC, messages);
        msgRight = "dont";
        EEPROM.write(1, preTime/maxTime);
        EEPROM.commit();
      }
      
    } else if (setTime < preTime) {
      digitalWrite(Motor_close2, HIGH);
      if (time1 >= 500) {
        preTime -= 500;
        if (preTime < 500) {
          preTime = 0;
        }
        snprintf(messages, 75, "%ld", (preTime / maxTime) + 10);
        client.publish(TOPIC, messages);
        rollover = 0;
      }
      if (setTime >= preTime){
        digitalWrite(Motor_close2, LOW);
        preTime = setTime;
        snprintf(messages, 75, "%ld", (preTime / maxTime) + 10);
        client.publish(TOPIC, messages);
        msgRight = "dont";
        EEPROM.write(1, preTime/maxTime);
        EEPROM.commit();
      }
    } else {
      msgRight = "dont"; 
    }
  } else if (msgRight == "STOP"){
    Serial.println("우 멈춤");
    digitalWrite(Motor_open2, LOW);
    digitalWrite(Motor_close2, LOW);
    snprintf(messages, 75, "%ld", (preTime / maxTime) + 10);
    client.publish(TOPIC, messages);
    msgRight = "dont";
    EEPROM.write(1, preTime/maxTime);
    EEPROM.commit();
  }
}
알고리즘
 - 열린 정도를 업데이트 루프를 돌면서 매번 mqtt로 퍼블리쉬하고 열린 정도를 더해주면서 명령받은 수치랑 같으면 멈추도록 설정.
 - 예제에서 web update 가져와서 겹치는 것 제거하고 고대로 복사해서 붙여넣기로 구현
 - 모터에 전기를 공급하는 시간을 컨트롤하여 개폐함으로 전기 공급 시간을 maxTime으로 설정하여 여름에는 최대 95초 동안 공급
겨울에는 최대 40초간 열리도록 명령을 받음 컨트롤하도록 함. 
- 메세지가 우측은 10 ~ 110으로 좌측은 200 ~ 300으로 범위를 설정하여 각각 조절함.

하도 빼고 꼽고해서 방가졌다 ㄷ
포트도 새로 납땜하고 이제 와이파이로 업로드해서 망가질 일은 없다 ㅋ
주렁주렁

Esp32 보드 고장 대비 완전 오프라인 스위치 설정!

(이것도 스위치 하나 불량인지 모르고 했다가 안되서 당황했는데 처근차근 알아보니 불량이더라 ㄷ ㄷ500원 짜리라 교환도 못 받고...)

반응형