MES机联网2:采集网关

目录信息

 

硬件清单

  • ESP8266(Wifi模块)
  • 降压模块(24V转3V)
  • 按键(无锁,自复位)

采集参数

  • 时长:IO变化,上报时长
  • 产量:IO变化,上报计数
  • 节拍:时间差=合格/不合格按键时间-开始按键时间
  • 合格:IO变化,合格数+1
  • 不合格:IO变化,不合格数+1

接线示意图

支持功能

  • 自动联网(程序启动后,查找本地文件存储的wifi账号和密码,如果文件为空,或者wifi连不上则进入入ap模式,在网页界面上输入账户和密码,保存到本地文件并进行连接,连接成功后重启ESP;超时5分钟未连上wifi则重启ESP)
  • 自动同步时间、硬件喂狗、OTA固件升级(上电检测/远程升级)
  • IO电平监测
  • MQClient订阅和发布

开发工具

  • Arduino IDE

代码结构

#include <Ticker.h>
#include <ESP8266httpUpdate.h>
#include "GlobalVar.h"
#include "GpioInput.h"
#include "WebConfig.h"
#include "Common.h"
#include "KeyValueStore.h"
#include "MQClient.h"
#include "JsonGenerator.h"
#include <ArduinoJson.h>

// 三色灯
GPIOInput RED(RED_PIN, INPUT_PULLUP, 20, 5);// INPUT
GPIOInput YELLOW(YELLOW_PIN, INPUT_PULLUP, 20, 5);
GPIOInput GREEN(GREEN_PIN, INPUT_PULLUP, 20, 5);

// 计数
GPIOInput COUNT(COUNT_PIN, INPUT_PULLUP, 20, 5);

// 按键
GPIOInput START(START_PIN, INPUT_PULLUP, 20, 5);
GPIOInput PASS(PASS_PIN, INPUT_PULLUP, 20, 5);
GPIOInput NG(NG_PIN, INPUT_PULLUP, 20, 5);

MQClient mqttClient;
Ticker wdtTicker;

String WorkshopId_Value;
String LineId_Value;
String DeviceId_Value; 
bool First = true;
int MCUTYPE = 1000;

void setup() {
  ESP.wdtEnable(8000); // 8秒看门狗
  wdtTicker.attach_ms(5000, hardwareWatchdog);
  //
  Serial.begin(9600);  // 先初始化串口
  Serial.println("");
  Serial.printf("MCU RUN,Version:%s\n", Version.c_str());  // 再输出信息
  SPIFFS.begin();
  WebConfig::initWifi();

  if (KeyValueStore::exists(MAC)) {
    // 初始化时间
    Common::initTime();

    // 获取完整日期时间
    String fullDateTime = Common::getCurrentTime();
    Serial.println("当前时间: " + fullDateTime);  // 输出示例:2023-10-05T08:59:22Z
    
    String mac = KeyValueStore::get(MAC);
    SubTopic.concat(mac);  
    PubTopic.concat(mac);  

    WorkshopId_Value = KeyValueStore::get(WorkshopId);
    LineId_Value = KeyValueStore::get(LineId);
    DeviceId_Value = KeyValueStore::get(DeviceId);
    Serial.printf("MAC:%s,WorkshopId:%s,LineId:%s,DeviceId:%s\n", MAC.c_str(), WorkshopId_Value.c_str(), LineId_Value.c_str(), DeviceId_Value.c_str()); 

    // MQTT初始化
    mqttClient.init(KeyValueStore::get(MQServer),
                    KeyValueStore::getLong(MQPort),
                    KeyValueStore::get(MQUid),
                    KeyValueStore::get(MQPwd),
                    SubTopic,
                    PubTopic,
                    "clientid",
                    subCallBack);
  }
}

void loop() {
  WebConfig::checkWifi();
  mqttClient.loop();  // 保持MQTT连接

  // OTA发送
  if (First && WiFi.status() == WL_CONNECTED && mqttClient.isConnected()) {
    First = false; 
    String ota = JsonGenerator::generateOTA(DeviceId_Value, Version, MCUTYPE);
    mqttClient.publish(ota.c_str());
  }

  // 检查IO信号
  // 三色灯
  int red = RED.read();
  int yellow = YELLOW.read();
  int green = GREEN.read();
  if (red == HIGH || yellow == HIGH || green == HIGH) {  // LOW HIGH
      String startstate = KeyValueStore::get("LightChangeState"); if (startstate.equals("")) startstate = "000";
      String starttime = KeyValueStore::get("LightChangeTs"); if (starttime.equals("")) starttime = Common::getCurrentTime();
      unsigned long t1 = KeyValueStore::getLong("LightChangeTl");

      String currenttime = Common::getCurrentTime(); 
      unsigned long t2 = Common::getCurrentTimestamp();

      String currentstate = String(digitalRead(RED_PIN)) + String(digitalRead(YELLOW_PIN)) + String(digitalRead(GREEN_PIN));
      Serial.printf("Light Change,oldstate:%s,starttime:%s,currenttime:%s,currentstate:%s\n", startstate.c_str(), starttime.c_str(), currenttime.c_str(), currentstate.c_str());
      
      // 状态改变,进行上报
      if (!startstate.equals(currentstate)) {
        unsigned long periodsec = t2 - t1;

        // 保存
        KeyValueStore::set("LightChangeState", currentstate);
        KeyValueStore::set("LightChangeTs", currenttime);
        KeyValueStore::setLong("LightChangeTl", t2);

        // 上报数据
        String colorJson = JsonGenerator::generateColorChange(
            WorkshopId_Value,
            LineId_Value,
            DeviceId_Value,
            currentstate,
            startstate,
            starttime, // 开始时间
            currenttime, // 结束时间
            periodsec // 持续时间秒数
        );
        mqttClient.publish(colorJson.c_str()); 
      } 
  } 

  // 计数
  int count = COUNT.read();
  if (count == HIGH) {  // LOW HIGH
      unsigned long lightcount = KeyValueStore::getLong("LightCount");
      lightcount = lightcount + 1;
      Serial.println("Light Count: " + String(lightcount));

      // 保存时间戳
      KeyValueStore::setLong("LightCount", lightcount);
      
      // 上报数据
      String countJson = JsonGenerator::generateCount(
          WorkshopId_Value,
          LineId_Value,
          DeviceId_Value,
          lightcount
      );
      mqttClient.publish(countJson.c_str());
  } 

  // 按键
  int start = START.read();
  int pass = PASS.read();
  int ng = NG.read();
 
  // 开始
  if (start == HIGH) {  // LOW HIGH
      unsigned long t1 = Common::getCurrentTimestamp();
      Serial.println("Light Start: " + String(t1));
      KeyValueStore::setLong("LightStartTs", t1); // 保存时间戳
  } 
  
  // 结束
  if (pass == HIGH || ng == HIGH) {  // LOW HIGH 
      // 产量
      unsigned long lightCount = KeyValueStore::getLong("LightCount");
      unsigned long passCount = KeyValueStore::getLong("LightPass");
      unsigned long ngCount = KeyValueStore::getLong("LightNg");

      // 节拍
      unsigned long t1 = KeyValueStore::getLong("LightStartTs");
      unsigned long t2 = Common::getCurrentTimestamp();
      unsigned long pitchTime = t2 - t1; 
 
      // 合格+1
      if (pass == HIGH) {
        passCount = passCount + 1;
        KeyValueStore::setLong("LightPass", passCount);
      }

      // 不合格+1
      if (ng == HIGH) {
        ngCount = ngCount + 1;
        KeyValueStore::setLong("LightNg", ngCount);
      }

      Serial.printf("Light End,EndTime:%d,StartTime:%d,lightCount:%d,pitchtime:%d,passCount:%d,ngCount:%d\n",t2, t1, lightCount, pitchTime, passCount, ngCount);

      // 上报数据
      String completeJson = JsonGenerator::generateComplete(
          WorkshopId_Value,
          LineId_Value,
          DeviceId_Value,
          lightCount, 
          pitchTime,
          passCount,
          ngCount
      );
      mqttClient.publish(completeJson.c_str());
  } 
}
 
/**
 * 硬件喂狗 
 */
void hardwareWatchdog() {
  ESP.wdtFeed();
}
 
/**
 * MQ回调函数 
 */
void subCallBack(char* topic, byte* payload, unsigned int length) {
  // 转换payload为字符串
  payload[length] = '\0';
  String message = String((char*)payload);
  
  Serial.print("Received [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);

  // 解析JSON数据
  DynamicJsonDocument doc(256);
  DeserializationError error = deserializeJson(doc, message);
  
  if (error) {
    Serial.print("JSON解析失败: ");
    Serial.println(error.c_str());
    return;
  }

  // 处理指令类型
  int command = doc["c"];
  switch(command) {
    case 1:  // 固件升级指令
      if (doc["m"]["errcode"] == 0) {
        String url = doc["m"]["url"].as<String>();
        if (!url.equals("")) {
          Serial.println("开始固件升级: " + url);
          updateBin(url);
        } 
      }
      break;
    case 2:
      // 重启
      Serial.println("远程升级,即将重启");
      delay(1000);
      ESP.restart();
      break;
    case 6:  // 重置计数器指令
      Serial.println("收到重置指令");
      KeyValueStore::setLong("LightCount", 0);
      KeyValueStore::setLong("LightPass", 0);
      KeyValueStore::setLong("LightNg", 0);
      break;
      
    default:
      Serial.println("未知指令类型");
      break;
  }
}

/**
 * 固件升级函数 
 */
void updateBin(String upUrl){
  // 增加升级前校验
  ESPhttpUpdate.rebootOnUpdate(false); // 禁用自动重启
  
  // 增加升级失败重试机制
  for(int i=0; i<3; i++){
    WiFiClient UpdateClient;
    t_httpUpdate_return ret = ESPhttpUpdate.update(UpdateClient, upUrl);
    
    switch(ret) {
      case HTTP_UPDATE_FAILED:
        Serial.printf("升级失败原因: %s\n", ESPhttpUpdate.getLastErrorString().c_str());
        delay(5000);
        break;
      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("无可用更新");
        return;
      case HTTP_UPDATE_OK:
        Serial.println("升级成功,即将重启");
        delay(1000);
        ESP.restart();
        return;
    }
  }
}

 

posted @ 2025-03-08 08:54  CHHC  阅读(31)  评论(0)    收藏  举报