使用esp8266开发的工业现场控制器

前几年开发的一个工业现场使用的遥控器项目,使用了esp8266,一个单色oled显示屏,12864,4线IIC接口。4个按钮。

基本功能包含:

1:从EEPROM读取wifi配置,并链接到目标wifi。

2:如果没有配置wifi,或者wifi链接不上,自动启动AP,手机链接设备热点,浏览器打开配置页面,进入web配网模式。

3:提供从后台查询当前设备(炼钢炉)工作状态,并通过按钮选择炉次,进行控制。

4:长时间不使用,进入轻度睡眠模式。按两个按键唤醒。

5:oled屏幕显示菜单,并当前选中项高亮显示。按钮切换菜单,进行操作。

这个项目时间太久了,弄完以后,他们又不用了(比较尴尬)。因此给代码贡献出来,有需要的可以参考其中的部分功能。如wifi配置,oled屏幕等。

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>//建立web服务用
#include <ESP8266mDNS.h>
#include <EEPROM.h> //文件读写用
#include <ESP8266HTTPClient.h>
ESP8266WebServer server(80);
String ssid = "";//wifi名称
String password = "";//密码
const char* wifiname = "TYMESCTL"; //热点名称
const char* wifipwd = "12345678"; //热点名称
String host = ""; //需要访问的域名
bool err = false; //是否有错误
DynamicJsonDocument doc(1024);//json对象
JsonArray arr;//存储返回的数组对象
WiFiClient client;//wifi链接对象,为http对象提供访问支持
JsonObject curData;//用来显示当前option的对象
HTTPClient http;//http对象
void showAP();//显示wifi信息,提供客户链接
void startAP();//开始AP
void readConfig();//读取配置信息
String httpGet(String url);//声明get函数
void connectWifi();//链接到wifi
void getRNO();//获取列表
void scanBtn();//扫描按钮状态
void showError();//显示错误
void showOff();//啥也u显示
void showMenu();//显示炉次
void showOption(JsonObject d, char t); //显示记录界面
void showMsg(String msg);//显示消息
void startY(String rno);//记录充氧开始时间
void endY(String rno);//记录充氧结束时间
void startP(String rno);//记录浇灌开始时间
void endP(String rno);//记录浇灌结束时间
void handleHtml();//返回html页面
void handleSave();//存储配置post
void clearConfig();//清除所有配置
void light_sleep();//进入睡眠,省电
//iic驱动方式
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
char b1 = 16; //btn1pin
char b2 = 13; //btn2pin
char b3 = 15; //btn3pin
char b4 = 12; //btn4pin
char b5 = 14; //btn5pin
char b1state = 0;
char b2state = 0;
char b3state = 0;
char b4state = 0;
char b5state = 0;
char s1, s2, s3, s4, s5; //用量判断是不是上升沿的临时变量
char curselect = -1;//-1是没有选中
char curstep = 0; //当前操作步骤,0表示息屏,进入省电模式,1表示联网中,2表示选中炉号,3表示记录时间,10表示联网不成功,进入热点
char nulltime = 0;//空循环次数,每次1秒,10秒没有操作就息屏
String currno;//当前操作的rno;
bool issleep=false;
unsigned short delaytime = 1000; //默认延迟1秒
const String html = "<!DOCTYPE html><html>\
 <head>\
    <meta charset='utf-8'>\
    <title>TYMESCTL Web Config</title>\
  </head>\
  <body>\
    <h2>控制器配置</h2>\
    <form action='/save' method='post'>\
      <p> wifi名称<input name='ssid'></p>\
      <p> wifi密码<input name='pwd'></p>\
      <p> 接口地址<input name='host'></p>\
      <p> <input type='submit' value='提交'></p>\
    </form>\
  </body>\
</html>";
/***
 * 进入轻度睡眠模式
 */
void light_sleep() {
  //if(issleep)return;
  //issleep=true;
  Serial.print("into light sleep mode\n");
  wifi_set_opmode_current(NULL_MODE);//断开wifi
  delay(3000);//断开ap时间
  if(curstep==10){
    Serial.print("set ap off\n");
    WiFi.softAPdisconnect(true);  
    delay(3000);//断开ap时间
    
  }
  wifi_station_disconnect();
  delay(3000);//断开wifi时间
  
  /*
  bool stopped;
  do {
    stopped = wifi_station_get_connect_status() == DHCP_STOPPED;
    if (!stopped)
    {
      Serial.println("dhcp not stopped?");
      delay(500);
    }
  } while (!stopped);
  */
  Serial.println("---off...");
  wifi_set_opmode_current(NULL_MODE);//断开wifi
  delay(3000);//断开wifi时间
  wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);//轻度睡眠
  wifi_fpm_open(); // Enables force sleep
  gpio_pin_wakeup_enable(b2, GPIO_PIN_INTR_HILEVEL);
  wifi_fpm_do_sleep(0xFFFFFFF); // Sleep for longest possible time
  Serial.print("sleep success \n");
  nulltime=255;//ap模式下,必须到255才睡觉,所以直接给设置为255
  //delaytime=3000;
  
}

void setup(void) {


  EEPROM.begin(512);//开始山村读取模块,读取512字节
  //0-20,存储wifi名称,遇到0结束
  //20-40,存储wifi密码,遇到0结束
  //40-100存储api接口,遇到0结束

  u8g2.begin();
  // u8g2.enableUTF8Print();    // enable UTF8 support for the Arduino print() function
  u8g2.setFont(u8g2_font_unifont_t_chinese2);  // use chinese2 for all the glyphs of "你好世界"
  // u8g2.setFont(u8g2_font_gb24st_t_3);
  // u8g2.drawStr(0, 15, "connecting");

  //链接到wifi
  Serial.begin(115200);
  Serial.println();
  readConfig();


  if (ssid == "") startAP();
  else  connectWifi();


  ///io初始化
  pinMode(b1, INPUT);
  pinMode(b2, INPUT);
  pinMode(b3, INPUT);
  pinMode(b4, INPUT);
  pinMode(b5, INPUT);


}

//读取配置
void readConfig() {
  //0-20,存储wifi名称,遇到0结束
  //20-40,存储wifi密码,遇到0结束
  //40-120存储api接口,遇到0结束
  //从EEPROM中逐个取出每一位的值,并链接
  char t;
  for (int i = 0; i < 20; i++) {
    t = char(EEPROM.read(i));
    //Serial.printf("i=%d   t=%d   \n ",i,t);
    if (t == '\0' || t == 255 || t == 0 ) break;
    ssid += t;

  }
  for (int i = 20; i < 40; i++) {
    t = char(EEPROM.read(i));
    if (t == '\0' || t == 255 || t == 0 ) break;
    password += t;
  }
  for (int i = 40; i < 120; i++) {
    t = char(EEPROM.read(i));
    if (t == '\0' || t == 255 || t == 0 ) break;
    host += t;

  }
  //Serial.print("SSID= "+ssid+"\n");
  //Serial.println("PWD= "+password);
  //Serial.println("HOST= "+host);

}
//链接到wifi
void connectWifi() {
  // u8g2.setFont(u8g2_font_unifont_t_chinese2);  // use chinese2 for all the glyphs of "你好世界"

  //Serial.print("connecting to ");
  //Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  int dly = 0;
  int p = 0;
  while (WiFi.status() != WL_CONNECTED) {
    //Serial.print(".");
    u8g2.firstPage();
    do {
      u8g2.drawStr(0, 20, ssid.c_str());
      for (int i = 0; i <= p; i += 2) {
        u8g2.drawStr(i * 2, 35, ".");
      }

    } while ( u8g2.nextPage() );
    //u8g2.nextPage();
    p++;
    if (p > 40) {
      p = 0;
      dly++;//延时加
      if (dly >= 3) {
        break;//停止链接wifi
      }
    }
    delay(50);
  }

  if (dly < 3) {
    showMsg("   net ok");
    //Serial.println("");
    //Serial.println("WiFi connected");
    //Serial.println("IP address: ");
    // Serial.println(WiFi.localIP());
  }
  else {
    startAP();

  }

  delay(1000);//延迟1秒

}
//开始热点配网
void startAP() {
  curstep = 10; //当前是配网模式
  WiFi.mode(WIFI_AP);//切换位热点模式
  WiFi.softAP(wifiname, wifipwd);
  showAP();
  server.on("/", handleHtml);
  server.on("/save", handleSave);
  server.begin();
  //Serial.println("start AP");
  //Serial.print("AP name:" );
  //Serial.println(wifiname);
  //Serial.println("IP address: ");
  //Serial.println(WiFi.softAPIP());

}
//显示ap信息
void showAP()
{
  nulltime = 0;
  u8g2.firstPage();
  do {
    u8g2.drawStr(20, 20, wifiname);
    u8g2.drawStr(20, 40, wifipwd);
  } while ( u8g2.nextPage() );

}
//扫描按钮输入状态
void scanBtn() {
  // b1state=b2state=b3state=b4state=b5state=0;
  s1 = digitalRead(b1);
  s2 = digitalRead(b2);
  s3 = digitalRead(b3);
  s4 = digitalRead(b4);
  s5 = digitalRead(b5);
 // Serial.printf("s5==%d \n", s5);
  if (s1 == 1 && b1state == 0)b1state = 1;
  else b1state = 0;

  if (s2 == 1 && b2state == 0)b2state = 1;
  else b2state = 0;

  if (s3 == 1 && b3state == 0)b3state = 1;
  else b3state = 0;

  if (s4 == 1 && b4state == 0)b4state = 1;
  else b4state = 0;

  if (s5 == 1 && b5state == 0)b5state = 1;
  else b5state = 0;

}


//主循环
void loop(void) {

  scanBtn();
  // if(issleep)return;//如果是在睡眠,就等待

  //获取按钮状态
  Serial.printf("curtime= %d, delaytime= %d \n",nulltime,delaytime);
   Serial.printf("state = %d  %d  %d  %d  %d   \n",b1state,b2state,b3state,b4state,b5state);

  if (b1state == 1 && b2state == 1) {
    ESP.restart();//重启启动
    return;
  }
  else if (b1state == 1 && b3state == 1) {
    //Serial.print("will be clear config");
    clearConfig();
    ESP.reset();//重启
  }

  if (curstep == 10) {
    //配网模式
    server.handleClient();
    nulltime++;
    if (nulltime >= 30 ) {
      delaytime = 3000;
      //nulltime = 0; //重新计数
      showOff();//清空屏幕
    }
    if(nulltime>=60){
      light_sleep();//进入轻度睡眠
      return;
    }
    delay(delaytime);
    return;
  }

  //空闲判断
  if (b1state == 0 && b2state == 0 && b3state == 0 && b4state == 0 && b5state == 0 ) {
    nulltime++;
    if (!issleep && nulltime >= 10) {
      delaytime = 3000;
      curstep = 0; //息屏
     // nulltime = 0; //重新计数
      showOff();//清空屏幕
    }

    if(!issleep && nulltime>=20){
      light_sleep();//进入轻度睡眠
      return;
    }
    delay(delaytime);//延迟1秒
    return;//没有按键啥也不做
  }//空闲判断结束

  delaytime=1000;//延迟1秒
  nulltime = 0;
  issleep=false;

  //判断是不是掉线了
  if (WiFi.status() != WL_CONNECTED) {
    Serial.print("reconnecting");
    connectWifi();
  }
  
  //如果按下了1号键
  if (b1state == 1) {
    if (curstep == 2) {
      //正在选中炉次
      curselect += 1; //增加一个
      if (curselect >= arr.size())curselect = 0;
      showMenu();
    }
    else {
      curstep = 2; //切换到步骤2,显示菜单
      curselect = 0; //切换回第一个炉次
      getRNO();  //获取菜单
      if (!err) showMenu();
      else curstep = 1; //返回第一步
    }
  }
  else  {
    //其他按钮按下
    if (curstep == 2 || curstep == 3) {
      //其他按钮按下,只有目录和操作界面可以按
      curstep = 3; //转到界面3
      //String rno=arr[curselect]["rno"].as<String>();//获取rno
      // Serial.print("rno="+rno);
      if (b2state == 1) {
        startY(currno);
      }
      else if (b3state == 1) {
        endY(currno);
      }
      else if (b4state == 1) {
        startP(currno);
      }
      else if (b5state == 1) {
        endP(currno);
      }
      // delay(5000);
    }
  }//按键判断结束
  // getRNO();
  delay(delaytime);//延迟0.5秒
}
//充氧开始时间
void startY(String rno) {
  String rsp = httpGet("/starty?rno=" + rno);
  // Serial.print(rsp);
  if (rsp == "") {
    showError();
    delay(1000);
    return;
  }
  deserializeJson(doc, rsp);
  //Serial.print("parse json");
  JsonObject obj = doc.as<JsonObject>();
  // Serial.print("to jsonobject");
  showOption(obj, 1); //显示结果


}
void endY(String rno) {
  String rsp = httpGet("/endy?rno=" + rno);
  if (rsp == "") {
    showError();
    delay(1000);
    return;
  }
  deserializeJson(doc, rsp);
  JsonObject obj = doc.as<JsonObject>();
  showOption(obj, 2); //显示结果


}
//充氧开始时间
void startP(String rno) {
  String rsp = httpGet("/startp?rno=" + rno);
  if (rsp == "") {
    showError();
    delay(1000);
    return;
  }
  deserializeJson(doc, rsp);
  JsonObject obj = doc.as<JsonObject>();
  showOption(obj, 3); //显示结果


}
void endP(String rno) {
  String rsp = httpGet("/endp?rno=" + rno);
  if (rsp == "") {
    showError();
    delay(1000);
    return;
  }
  deserializeJson(doc, rsp);
  JsonObject obj = doc.as<JsonObject>();
  showOption(obj, 4); //显示结果


}



//显示炉次
void showMenu()
{
  int size = arr.size();//获取长度
  //Serial.printf("allcount %d ,cur select %d\n",size,curselect);
  u8g2.firstPage();
  do {
    for (int i = 0; i < size; i++) {
      String rno = arr[i]["rno"].as<String>();
      String startydt = arr[i]["starty"].as<String>();
      String endydt = arr[i]["endy"].as<String>();
      String startpdt = arr[i]["startp"].as<String>();
      String endpdt = arr[i]["endp"].as<String>();


      if (curselect == i) {
        currno = rno; //记录当前rno;
        u8g2.setFontMode(1);
        u8g2.setDrawColor(1);
        u8g2.drawBox(0, i * 15, 128, 16);
        u8g2.setDrawColor(2);
      }
      else {
        u8g2.setFontMode(0);
        u8g2.setDrawColor(1);
      }

      // Serial.print(startydt+" "+endydt+" "+startpdt+"  "+endpdt+" \n");
      u8g2.drawStr(0, i * 15 + 13, rno.c_str()); //显示到屏幕
      if (startydt == "null") {
        // u8g2.drawStr(68, i * 15 + 13, " "); //显示到屏幕
      }
      else {
        u8g2.drawStr(68, i * 15 + 13, "*"); //显示到屏幕
      }

      if (endydt == "null") {
        // u8g2.drawStr(83, i * 15 + 13, " "); //显示到屏幕
      }
      else {
        u8g2.drawStr(83, i * 15 + 13, "*"); //显示到屏幕
      }

      if (startpdt == "null") {
        // u8g2.drawStr(98, i * 15 + 13, " "); //显示到屏幕
      }
      else {
        u8g2.drawStr(98, i * 15 + 13, "*"); //显示到屏幕
      }

      if (endpdt == "null") {
        //u8g2.drawStr(113, i * 15 + 13, " "); //显示到屏幕
      }
      else {
        u8g2.drawStr(113, i * 15 + 13, "*"); //显示到屏幕
      }

    }

  }
  while (  u8g2.nextPage());

}
//显示操作界面
void showOption(JsonObject  d, char t) {
  //Serial.print("show optin \n");
  String rno = d["data"]["rno"].as<String>();
  String startydt = d["data"]["starty"].as<String>();
  String endydt = d["data"]["endy"].as<String>();
  String startpdt = d["data"]["startp"].as<String>();
  String endpdt = d["data"]["endp"].as<String>();
  String tm = d["message"].as<String>();
  String tm1 = "";
  if (tm.length() > 10) {
    tm1 = tm.substring(10);
    tm = tm.substring(0, 10);
  }

  Serial.print("option rno=" + rno + "tm=" + tm + " tm1=" + tm1 + "\n");

  u8g2.firstPage();
  do {
    u8g2.drawStr(0,  13, rno.c_str()); //显示到屏幕
    if (startydt == "null") {
      //u8g2.drawStr(70,  13, " "); //显示到屏幕
    }
    else {
      if (t == 1) u8g2.drawStr(62,  13, "[*]"); //显示到屏幕选择状态
      else  u8g2.drawStr(68,  13, "*"); //显示到屏幕
    }

    if (endydt == "null") {
      //u8g2.drawStr(85,  13, " "); //显示到屏幕
    }
    else {
      if (t == 2) u8g2.drawStr(77,  13, "[*]"); //显示到屏幕选择状态
      else  u8g2.drawStr(83,  13, "*"); //显示到屏幕
    }

    if (startpdt == "null") {
      //u8g2.drawStr(100,  13, " "); //显示到屏幕
    }
    else {
      if (t == 3) u8g2.drawStr(92,  13, "[*]"); //显示到屏幕选择状态
      else u8g2.drawStr(98,  13, "*"); //显示到屏幕
    }

    if (endpdt == "null") {
      //u8g2.drawStr(115,  13, " "); //显示到屏幕
    }
    else {
      if (t == 4) u8g2.drawStr(107,  13, "[*]"); //显示到屏幕选择状态
      else u8g2.drawStr(113,  13, "*"); //显示到屏幕
    }
    u8g2.drawStr(25,  28, tm.c_str()); //时间显示到屏幕
    u8g2.drawStr(28,  43, tm1.c_str()); //时间显示到屏幕
  } while (u8g2.nextPage());
}

//啥也不显示
void showOff()
{


  u8g2.firstPage();
  do {

  } while ( u8g2.nextPage() );
}
//显示错误
void showError()
{
  u8g2.firstPage();
  do {
    u8g2.drawStr(0, 30, "   error");

  } while ( u8g2.nextPage() );

}
//显示消息
void showMsg(String msg)
{
  u8g2.firstPage();
  do {
    u8g2.drawStr(0, 30, msg.c_str());
  } while ( u8g2.nextPage() );

}
//获取炉次
void getRNO()
{

  showMsg(" geting data...");
  String payload = httpGet("/t");
  if (payload == "") {
    showError();
    return;
  }


  deserializeJson(doc, payload);
  JsonObject obj = doc.as<JsonObject>();
  arr  = obj["data"].as<JsonArray>();//转换为数组

  // delay(2000);
}


//http请求
String httpGet(String url) {
  err = false;
  if (http.begin(client, host + url)) { // HTTP
    //  Serial.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();

    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      // Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        err = false;
        return payload;
      }
    } else {
      err = true;
      //Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
      return "";
    }

    http.end();
  } else {
    err = true;
    // Serial.printf("[HTTP} Unable to connect\n");
    return "";
  }

}
//显示html页面
void handleHtml()
{
  server.send(200, "text/html", html);
}
//清除所有配置
void clearConfig() {

  for (int i = 0; i < 120; i++) {
    EEPROM.write(i, 255);//标记为默认
  }
  EEPROM.commit();//提交,不提交无效
}
//保存配置
void handleSave()
{
  for (uint8_t i = 0; i < server.args(); i++) {
    String arg = server.argName(i);
    String v = server.arg(i);
    if (arg == "ssid")ssid = v;
    else if (arg == "pwd")password = v;
    else if (arg == "host")host = v;
    // message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  // Serial.print("SSID= "+ssid+"\n");
  //Serial.println("PWD= "+password);
  //Serial.println("HOST= "+host);
  clearConfig();//先清除一下
  for (int i = 0; i < ssid.length(); i++) {
    EEPROM.write(i, ssid[i]);
  }
  for (int i = 0; i < password.length(); i++) {
    EEPROM.write(20 + i, password[i]);
  }
  for (int i = 0; i < host.length(); i++) {
    EEPROM.write(40 + i, host[i]);
  }

  EEPROM.commit();


  server.send(200, "text/plain", "config success");
}

### 回答1: ESP8266四路继电器是一种集成了ESP8266 Wi-Fi模块和四个独立继电器的设备。ESP8266是一款低功耗、高性能的Wi-Fi模块,可以用于接入互联网,并通过无线网络进行通信。四路继电器可以控制四个不同的电路,可以通过Wi-Fi远程控制继电器的开关状态。 ESP8266四路继电器的使用非常方便,只需将其连接到电源和Wi-Fi网络,然后通过手机、电脑或其他设备的APP或浏览器就可以远程控制继电器的开关状态。用户可以通过手机或电脑APP实时监测和控制继电器的状态,如打开或关闭灯光、电器或其他设备。 除了远程控制外,ESP8266四路继电器也支持定时控制功能。用户可以设置定时开关,根据需要自动打开或关闭继电器,实现自动化控制。例如,可以设置定时在早晨打开灯光、定时关闭电视等。 此外,ESP8266四路继电器还支持多点控制功能。这意味着用户可以多个设备同时控制继电器的开关状态。这样,多个用户可以同时远程或定时控制继电器,实现多人协作。 综上所述,ESP8266四路继电器是一个功能强大、易于使用的设备,能够通过Wi-Fi远程控制多个继电器,实现智能家居或其他领域的自动化控制。无论是家庭、办公室还是工业环境,ESP8266四路继电器都提供了便捷、高效的解决方案。 ### 回答2: ESP8266四路继电器是一种基于ESP8266芯片的开发板,具有四个独立的继电器输出通道。它可以通过无线网络与其他设备进行通讯,并控制继电器的开关状态。 ESP8266四路继电器模块可以广泛应用于智能家居、物联网等领域。通过连接到无线网络,我们可以使用手机或电脑远程操控继电器的开关状态,实现远程控制和自动化。例如,我们可以通过手机APP控制灯光的开关,调节家居设备的温度,或者定时控制水泵等。 使用ESP8266四路继电器模块时,我们可以通过编程来实现控制逻辑。在编程方面,我们可以使用Arduino IDE或者类似的开发软件,通过简单的代码编写来实现控制继电器。 除了四个继电器输出通道外,ESP8266四路继电器模块还具备多个引脚,这些引脚可以用于连接其他传感器或执行其他功能。这使得它具备了更强的扩展性和灵活性。 总结起来,ESP8266四路继电器是一种功能强大的开发板,可以用于控制多个继电器的开关状态。它的无线通讯功能和编程灵活性使得我们可以实现远程控制或自动化的功能。在智能家居和物联网项目中,ESP8266四路继电器是一个重要的组成部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值