Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PM1 value of 16973 #34

Open
SimonOrdo opened this issue Mar 11, 2019 · 0 comments
Open

PM1 value of 16973 #34

SimonOrdo opened this issue Mar 11, 2019 · 0 comments

Comments

@SimonOrdo
Copy link

I made a modification to the 1.0 version of the library to remove dependencies on Software serial and replaced it with "Serial1" to use hardware serial provided by Adafruit feather.

Sometimes, the library returns PM1.0 value of 16973 (i.e. 0x424d)... is this a side effect of switching to hardware serial or is this an issue with the core library code? As I understand it, 0x424d is a special value of some kind.

I also intermittently receive other oddly high (but random) values, which I assume are value transcription errors as well.

Any information would be appreciated.

Modified code, below:

#include "pms.h"

////////////////////////////////////////

#if defined NOMINMAX

#if defined min
#undef min
#endif
template <class T> inline const T& __attribute__((always_inline)) min(const T& a, const T& b);
template <class T> inline const T& __attribute__((always_inline)) min(const T& a, const T& b) {
  return !(b < a) ? a : b;
}

#endif

////////////////////////////////////////

inline void __attribute__((always_inline)) swapEndianBig16(uint16_t *x) {
  constexpr union {
    // endian.test16 == 0x0001 for low endian
    // endian.test16 == 0x0100 for big endian
    // should be properly optimized by compiler
    uint16_t test16;
    uint8_t test8[2];
  } endian = { .test8 = { 1,0 } };

  if (endian.test16 != 0x0100) {
    uint8_t hi = (*x & 0xff00) >> 8;
    uint8_t lo = (*x & 0xff);
    *x = lo << 8 | hi;
  }
}

////////////////////////////////////////

void sumBuffer(uint16_t *sum, const uint8_t *buffer, uint16_t cnt) {
  for (; cnt > 0; --cnt, ++buffer) {
    *sum += *buffer;
  }
}

inline void sumBuffer(uint16_t *sum, const uint16_t data) {
  *sum += (data & 0xFF) + (data >> 8);
}

////////////////////////////////////////

void Pms5003::setTimeout(const decltype(timeout) timeout) {
  Serial1.setTimeout(timeout);
  this->timeout = timeout;
};

decltype(Pms5003::timeout) Pms5003::getTimeout(void) const {
  return timeout;
};

Pms5003::Pms5003() : passive(tribool(unknown)), sleep(tribool(unknown)) {
#if defined PMS_DYNAMIC
  begin();
#endif
};

Pms5003::~Pms5003() {
#if defined PMS_DYNAMIC
  end();
#endif
}

bool Pms5003::begin(void)
{
  Serial1.setTimeout(Pms5003::timeoutPassive);
  Serial1.begin(9600);
  
  return true;
};

void Pms5003::end(void) {
  Serial1.end();
};

size_t Pms5003::available(void) {
  while (Serial1.available()) {
    if (Serial1.peek() != sig[0]) {
      Serial1.read();
    } else {
      break;
    }
  }
  return static_cast<size_t>(Serial1.available());
}

Pms5003::PmsStatus Pms5003::read(pmsData *data, const size_t nData, const uint8_t dataSize) {

  if (available() < (dataSize + 2) * sizeof(pmsData) + sizeof(sig)) {
    return noData;
  }

  Serial1.read(); // Value is equal to sig[0]. There is no need to check the value, it was checked by prior peek()

  if (Serial1.read() != sig[1]) // The rest of the buffer will be invalidated during the next read attempt
    return readError;

  uint16_t sum{ 0 };
  sumBuffer(&sum, (uint8_t *)&sig, sizeof(sig));

  pmsData thisFrameLen{ 0x1c };
  if (Serial1.readBytes((uint8_t*)&thisFrameLen, sizeof(thisFrameLen)) != sizeof(thisFrameLen)) {
    return readError;
  };

  if (thisFrameLen % 2 != 0) {
    return frameLenMismatch;
  }
  sumBuffer(&sum, thisFrameLen);

  const decltype(thisFrameLen) maxFrameLen{ 2 * 0x1c };    // arbitrary

  swapEndianBig16(&thisFrameLen);
  if (thisFrameLen > maxFrameLen) {
    return frameLenMismatch;
  }

  size_t toRead{ min(thisFrameLen - 2, nData * sizeof(pmsData)) };
  if (data == nullptr) {
    toRead = 0;
  }

  if (toRead) {
    if (Serial1.readBytes((uint8_t*)data, toRead) != toRead) {
      return readError;
    }
    sumBuffer(&sum, (uint8_t*)data, toRead);

    for (size_t i = 0; i < nData; ++i) {
      swapEndianBig16(&data[i]);
    }
  }

  pmsData crc;
  for (; toRead < thisFrameLen; toRead += 2) {
    if (Serial1.readBytes((uint8_t*)&crc, sizeof(crc)) != sizeof(crc)) {
      return readError;
    };

    if (toRead < thisFrameLen - 2)
      sumBuffer(&sum, crc);
  }

  swapEndianBig16(&crc);

  if (sum != crc) {
    return sumError;
  }

  return OK;
}

void Pms5003::flushInput(void) {
  
}

bool Pms5003::waitForData(const unsigned int maxTime, const size_t nData) {
  const auto t0 = millis();
  if (nData == 0) {
    for (; (millis() - t0) < maxTime; delay(1)) {
      if (Serial1.available()) {
        return true;
      }
    }
    return Serial1.available();
  }

  for (; (millis() - t0) < maxTime; delay(1)) {
    if (available() >= nData) {
      return true;
    }
  }
  return available() >= nData;
}

bool Pms5003::write(const PmsCmd cmd) {
  static_assert(sizeof(cmd) >= 3, "Wrong definition of PmsCmd (too short)");

  if ((cmd != cmdReadData) && (cmd != cmdWakeup)) {
    flushInput();
  }

  if (Serial1.write(sig, sizeof(sig)) != sizeof(sig)) {
    return false;
  }
  const size_t cmdSize = 3;
  if (Serial1.write((uint8_t*)&cmd, cmdSize) != cmdSize) {
    return false;
  }

  uint16_t sum{ 0 };
  sumBuffer(&sum, sig, sizeof(sig));
  sumBuffer(&sum, (uint8_t*)&cmd, cmdSize);
  swapEndianBig16(&sum);
  if (Serial1.write((uint8_t*)&sum, sizeof(sum)) != sizeof(sum)) {
    return false;
  }

  switch (cmd) {
    case cmdModePassive:
      passive = tribool(true);
      break;
    case cmdModeActive:
      passive = tribool(false);
      break;
    case cmdSleep:
      sleep = tribool(true);
      break;
    case cmdWakeup:
      sleep = tribool(false);
      passive = tribool(false);
      // waitForData(wakeupTime);
      break;
    default:
      break;
  }
  if ((cmd != cmdReadData) && (cmd != cmdWakeup)) {
    const auto responseFrameSize = 8;
    if (!waitForData(ackTimeout, responseFrameSize)) {
      
      return true;
    }
    Pms5003::pmsData response = 0xCCCC;
    read(&response, 1, 1);
  }

  /*
    if ((cmd != cmdReadData) && (cmd != cmdWakeup)) {
      const auto responseFrameSize = 8;
      if (!waitForData(ackTimeout, responseFrameSize)) {
        Serial1.flushInput();
        return false;
      }
      Pms5003::pmsData response = 0xCCCC;
      if (read(&response, 1, 1) != OK) {
        return false;
      }
      if ((response >> 8) != (cmd & 0xFF)) {
        return false;
      }
    }
  */

  return true;
}

const char *Pms5003::getMetrics(const pmsIdx idx) {
  return idx < nValues_PmsDataNames ? Pms5003::metrics[idx] : "???";
}

const char *Pms5003::getDataNames(const pmsIdx idx) {
  return idx < nValues_PmsDataNames ? Pms5003::dataNames[idx] : "???";
}

const char * Pms5003::errorMsg[nValues_PmsStatus]{
  "OK",
  "noData",
  "readError",
  "frameLenMismatch",
  "sumError"
};

const char *Pms5003::metrics[]{
  "mcg/m3",
  "mcg/m3",
  "mcg/m3",

  "mcg/m3",
  "mcg/m3",
  "mcg/m3",

  "/0.1L",
  "/0.1L",
  "/0.1L",
  "/0.1L",
  "/0.1L",
  "/0.1L",

  "???"
};

const char *Pms5003::dataNames[]{
  "PM1.0, CF=1",
  "PM2.5, CF=1",
  "PM10.  CF=1",
  "PM1.0",
  "PM2.5",
  "PM10.",

  "Particles > 0.3 micron",
  "Particles > 0.5 micron",
  "Particles > 1.0 micron",
  "Particles > 2.5 micron",
  "Particles > 5.0 micron",
  "Particles > 10. micron",

  "Reserved_0"
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant