『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 4218|回复: 3

[已解决] 我想改一个ESP8266读取串口数据的插件,想请教C语言大神

[复制链接]

16

主题

135

帖子

1095

积分

金牌会员

Rank: 6Rank: 6

积分
1095
金钱
960
HASS币
0
发表于 2018-12-22 22:32:30 | 显示全部楼层 |阅读模式
本帖最后由 roc634 于 2019-2-12 15:51 编辑

===================最近更新================
问题解决了,之前一直没能解决主要是因为不懂怎么调试,看不到反馈的信息,现在知道了,能看到反馈信息,调试起来就容易。
用Atom上传固件后,用PlatformIO下的Serial Monitor可以查看串口反馈的信息。下面是原代码中的记录日志的代码,只需做适应的修改,就可以在出错时显示想要的信息:
    log = F("PMSx003 : invalid framelength - ");
    log += String(data[0], HEX);
        log += F(" - ");
        log += String(data[1], HEX);
        log += F(" - ");
        log += String(data[2], HEX);
        log += F(" - ");
        log += String(data[3], HEX);
        log += F(" - ");
        log += String(data[4], HEX);
        log += F(" - ");
        log += String(data[5], HEX);
    addLog(LOG_LEVEL_ERROR, log);
    values_received = true;


=============稍早前内容================
我在淘宝上买了一个噪声模块,它是直接通过串口输出数据的,我在HA上通过改攀藤的组件实现了读取数据,由于HA上是Python写的,所以比较好理解,当我想通过改ESPEasy固件中的插件_P053_PMSx003.ino来实现读取数据时,由于我对C语言一窍不通,所以花费了不少时间尝试,一直改不出来,不得不在这里请教懂C语言的大神。两者数据包长度和数据格式不同。
噪声模块的数据格式是这样:数据包有6个字节,分别为:起始符+命令+数据+检验值。例如:
BB AA 00 02 00 67
其中起始符为BBAA,2字节(0xBB, 0xAA);
命令为00时,表示此帧为返回模块软件版本号;
数据:02 00 ,表示软件版本号为2.0
校验值:67, 为算术和校验值。

BB AA 01 7F 02 E7
当命令为01时,表示此帧返回的是分贝值。
数据:7F 02,表示分贝值为63.9db。低字节在前,高字节在后,且每单位为0.1db。

我看了插件中的这段代码:
// Read 2 bytes from serial and make an uint16 of it. Additionally calculate
// checksum for PMSx003. Assumption is that there is data available, otherwise
// this function is blocking.
void SerialRead16(uint16_t* value, uint16_t* checksum)
{
  uint8_t data_high, data_low;

  // If swSerial is initialized, we are using soft serial
  if (swSerial != NULL)
  {
    data_high = swSerial->read();
    data_low = swSerial->read();
  }
  else
  {
    data_high = Serial.read();
    data_low = Serial.read();
  }

  *value = data_low;
  *value |= (data_high << 8);

  if (checksum != NULL)
  {
    *checksum += data_high;
    *checksum += data_low;
  }
}


貌似是一次读两个字节的内容,然后直接以高字节在前,低字节在后进行位或运算,但是我的噪声模块的数据是在第4和第5字节,而且是低字节在前,高字节在后,不能通过这样的方式计算。我希望它每次只读取一个字节,然后保存到变量中,这样后面我就可以计算第4和第5字节的数值。我试过改成下面的样子,但是没有用,不知是哪里出错了,请高手指导一下,谢谢了。
void SerialRead16(uint16_t* value, uint16_t* checksum)
{
  uint8_t data_raw;

  // If swSerial is initialized, we are using soft serial
  if (swSerial != NULL)
  {
    data_raw = swSerial->read();
  }
  else
  {
    data_raw = Serial.read();
  }

  *value = data_raw;

  if (checksum != NULL)
  {
    *checksum += data_raw;
  }


最后附上原插件的完整代码,以及攀藤和噪声模块的数据格式说明。我自己摸索很久了,实在不能解决才求助的,然后能得到帮助。谢谢了!
#ifdef USES_P053
//#######################################################################################################
//#################################### Plugin 053: Plantower PMSx003 ####################################
//#######################################################################################################
//
// [url=http://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf?sfvrsn=2]http://www.aqmd.gov/docs/default ... l_v2-3.pdf?sfvrsn=2[/url]
//
// The PMSx003 are particle sensors. Particles are measured by blowing air through the enclosure and,
// together with a laser, count the amount of particles. These sensors have an integrated microcontroller
// that counts particles and transmits measurement data over the serial connection.


#include <ESPeasySoftwareSerial.h>

#define PLUGIN_053
#define PLUGIN_ID_053 53
#define PLUGIN_NAME_053 "Dust - PMSx003"
#define PLUGIN_VALUENAME1_053 "pm1.0"
#define PLUGIN_VALUENAME2_053 "pm2.5"
#define PLUGIN_VALUENAME3_053 "pm10"
#define PMSx003_SIG1 0X42
#define PMSx003_SIG2 0X4d
#define PMSx003_SIZE 32

ESPeasySoftwareSerial *swSerial = NULL;
boolean Plugin_053_init = false;
boolean values_received = false;

// Read 2 bytes from serial and make an uint16 of it. Additionally calculate
// checksum for PMSx003. Assumption is that there is data available, otherwise
// this function is blocking.
void SerialRead16(uint16_t* value, uint16_t* checksum)
{
  uint8_t data_high, data_low;

  // If swSerial is initialized, we are using soft serial
  if (swSerial != NULL)
  {
    data_high = swSerial->read();
    data_low = swSerial->read();
  }
  else
  {
    data_high = Serial.read();
    data_low = Serial.read();
  }

  *value = data_low;
  *value |= (data_high << 8);

  if (checksum != NULL)
  {
    *checksum += data_high;
    *checksum += data_low;
  }

#if 0
  // Low-level logging to see data from sensor
  String log = F("PMSx003 : byte high=0x");
  log += String(data_high,HEX);
  log += F(" byte low=0x");
  log += String(data_low,HEX);
  log += F(" result=0x");
  log += String(*value,HEX);
  addLog(LOG_LEVEL_INFO, log);
#endif
}

void SerialFlush() {
  if (swSerial != NULL) {
    swSerial->flush();
  } else {
    Serial.flush();
  }
}

boolean PacketAvailable(void)
{
  if (swSerial != NULL) // Software serial
  {
    // When there is enough data in the buffer, search through the buffer to
    // find header (buffer may be out of sync)
    if (!swSerial->available()) return false;
    while ((swSerial->peek() != PMSx003_SIG1) && swSerial->available()) {
      swSerial->read(); // Read until the buffer starts with the first byte of a message, or buffer empty.
    }
    if (swSerial->available() < PMSx003_SIZE) return false; // Not enough yet for a complete packet
  }
  else // Hardware serial
  {
    // When there is enough data in the buffer, search through the buffer to
    // find header (buffer may be out of sync)
    if (!Serial.available()) return false;
    while ((Serial.peek() != PMSx003_SIG1) && Serial.available()) {
      Serial.read(); // Read until the buffer starts with the first byte of a message, or buffer empty.
    }
    if (Serial.available() < PMSx003_SIZE) return false; // Not enough yet for a complete packet
  }
  return true;
}

boolean Plugin_053_process_data(struct EventStruct *event) {
  String log;
  uint16_t checksum = 0, checksum2 = 0;
  uint16_t framelength = 0;
  uint16 packet_header = 0;
  SerialRead16(&packet_header, &checksum); // read PMSx003_SIG1 + PMSx003_SIG2
  if (packet_header != ((PMSx003_SIG1 << 8) | PMSx003_SIG2)) {
    // Not the start of the packet, stop reading.
    return false;
  }

  SerialRead16(&framelength, &checksum);
  if (framelength != (PMSx003_SIZE - 4))
  {
    log = F("PMSx003 : invalid framelength - ");
    log += framelength;
    addLog(LOG_LEVEL_ERROR, log);
    return false;
  }

  uint16_t data[13]; // byte data_low, data_high;
  for (int i = 0; i < 13; i++)
    SerialRead16(&data[i], &checksum);

  log = F("PMSx003 : pm1.0=");
  log += data[0];
  log += F(", pm2.5=");
  log += data[1];
  log += F(", pm10=");
  log += data[2];
  log += F(", pm1.0a=");
  log += data[3];
  log += F(", pm2.5a=");
  log += data[4];
  log += F(", pm10a=");
  log += data[5];
  addLog(LOG_LEVEL_DEBUG, log);

  log = F("PMSx003 : count/0.1L : 0.3um=");
  log += data[6];
  log += F(", 0.5um=");
  log += data[7];
  log += F(", 1.0um=");
  log += data[8];
  log += F(", 2.5um=");
  log += data[9];
  log += F(", 5.0um=");
  log += data[10];
  log += F(", 10um=");
  log += data[11];
  addLog(LOG_LEVEL_DEBUG_MORE, log);

  // Compare checksums
  SerialRead16(&checksum2, NULL);
  SerialFlush(); // Make sure no data is lost due to full buffer.
  if (checksum == checksum2)
  {
    // Data is checked and good, fill in output
    UserVar[event->BaseVarIndex]     = data[3];
    UserVar[event->BaseVarIndex + 1] = data[4];
    UserVar[event->BaseVarIndex + 2] = data[5];
    values_received = true;
    return true;
  }
  return false;
}

boolean Plugin_053(byte function, struct EventStruct *event, String& string)
{
  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
      {
        Device[++deviceCount].Number = PLUGIN_ID_053;
        Device[deviceCount].Type = DEVICE_TYPE_TRIPLE;
        Device[deviceCount].VType = SENSOR_TYPE_TRIPLE;
        Device[deviceCount].Ports = 0;
        Device[deviceCount].PullUpOption = false;
        Device[deviceCount].InverseLogicOption = false;
        Device[deviceCount].FormulaOption = false;
        Device[deviceCount].ValueCount = 3;
        Device[deviceCount].SendDataOption = true;
        Device[deviceCount].TimerOption = true;
        Device[deviceCount].GlobalSyncOption = true;
        success = true;
        break;
      }

    case PLUGIN_GET_DEVICENAME:
      {
        string = F(PLUGIN_NAME_053);
        success = true;
        break;
      }

    case PLUGIN_GET_DEVICEVALUENAMES:
      {
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_053));
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_053));
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_053));
        success = true;
        break;
      }

      case PLUGIN_GET_DEVICEGPIONAMES:
        {
          event->String1 = F("GPIO ← TX");
          event->String2 = F("GPIO → RX");
          event->String3 = F("GPIO → Reset");
          break;
        }

    case PLUGIN_INIT:
      {
        int rxPin = Settings.TaskDevicePin1[event->TaskIndex];
        int txPin = Settings.TaskDevicePin2[event->TaskIndex];
        int resetPin = Settings.TaskDevicePin3[event->TaskIndex];

        String log = F("PMSx003 : config ");
        log += rxPin;
        log += txPin;
        log += resetPin;
        addLog(LOG_LEVEL_DEBUG, log);

        if (swSerial != NULL) {
          // Regardless the set pins, the software serial must be deleted.
          delete swSerial;
          swSerial = NULL;
        }

        // Hardware serial is RX on 3 and TX on 1
        if (rxPin == 3 && txPin == 1)
        {
          log = F("PMSx003 : using hardware serial");
          addLog(LOG_LEVEL_INFO, log);
          Serial.begin(9600);
          Serial.flush();
        }
        else
        {
          log = F("PMSx003: using software serial");
          addLog(LOG_LEVEL_INFO, log);
          swSerial = new ESPeasySoftwareSerial(rxPin, txPin, false, 96); // 96 Bytes buffer, enough for up to 3 packets.
          swSerial->begin(9600);
          swSerial->flush();
        }

        if (resetPin >= 0) // Reset if pin is configured
        {
          // Toggle 'reset' to assure we start reading header
          log = F("PMSx003: resetting module");
          addLog(LOG_LEVEL_INFO, log);
          pinMode(resetPin, OUTPUT);
          digitalWrite(resetPin, LOW);
          delay(250);
          digitalWrite(resetPin, HIGH);
          pinMode(resetPin, INPUT_PULLUP);
        }

        Plugin_053_init = true;
        success = true;
        break;
      }

    case PLUGIN_EXIT:
      {
          if (swSerial)
          {
            delete swSerial;
            swSerial=NULL;
          }
          break;
      }

    // The update rate from the module is 200ms .. multiple seconds. Practise
    // shows that we need to read the buffer many times per seconds to stay in
    // sync.
    case PLUGIN_TEN_PER_SECOND:
      {
        if (Plugin_053_init)
        {
          // Check if a complete packet is available in the UART FIFO.
          if (PacketAvailable())
          {
            addLog(LOG_LEVEL_DEBUG_MORE, F("PMSx003 : Packet available"));
            success = Plugin_053_process_data(event);
          }
        }
        break;
      }
    case PLUGIN_READ:
      {
        // When new data is available, return true
        success = values_received;
        values_received = false;
      }
  }
  return success;
}
#endif // USES_P053


攀藤的数据格式:
P5.jpg
P6.jpg
噪声模块的数据格式:
N1.jpg
回复

使用道具 举报

4

主题

531

帖子

4029

积分

论坛元老

Rank: 8Rank: 8

积分
4029
金钱
3488
HASS币
120
发表于 2018-12-22 23:05:59 | 显示全部楼层
前段时间买了个tds检测模块,协议跟你这个噪声模块差不多,你看看代码的checkTDS部分
hexToDec是把数据转换为数值
https://github.com/killadm/ESP-WaterMonitor/blob/master/ESP-WaterMonitor.ino

模块协议
https://github.com/killadm/ESP-WaterMonitor/blob/master/TDS%E6%A3%80%E6%B5%8B%E4%B8%93%E7%94%A8%E8%8A%AF%E7%89%87BA01-V1.0.pdf


回复

使用道具 举报

123

主题

4665

帖子

1万

积分

管理员

囧死

Rank: 9Rank: 9Rank: 9

积分
16452
金钱
11702
HASS币
45
发表于 2018-12-22 23:37:40 | 显示全部楼层
killadm 发表于 2018-12-22 23:05
前段时间买了个tds检测模块,协议跟你这个噪声模块差不多,你看看代码的checkTDS部分
hexToDec是把数据转换 ...

牛比的K大!
回复

使用道具 举报

6

主题

743

帖子

6008

积分

论坛元老

Rank: 8Rank: 8

积分
6008
金钱
5260
HASS币
20
发表于 2018-12-23 10:37:47 | 显示全部楼层
TASMOTA直接支持PMS5003,楼主可以试一下。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-12-26 11:10 , Processed in 0.495169 second(s), 28 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表