mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 09:36:59 +00:00
199 lines
5.7 KiB
C++
199 lines
5.7 KiB
C++
/*-------------------------------------------------------------------------
|
||
Arduino library to ...
|
||
|
||
Written by Jochen Krapf,
|
||
contributions by ... and other members of the open
|
||
source community.
|
||
|
||
-------------------------------------------------------------------------
|
||
This file is part of the MechInputs library.
|
||
|
||
MechInputs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU Lesser General Public License as
|
||
published by the Free Software Foundation, either version 3 of
|
||
the License, or (at your option) any later version.
|
||
|
||
MechInputs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU Lesser General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Lesser General Public
|
||
License along with MechInputs. If not, see
|
||
<http://www.gnu.org/licenses/>.
|
||
-------------------------------------------------------------------------*/
|
||
//#ifdef ESP8266 // Needed for precompile issues.
|
||
#include "jkSDS011.h"
|
||
|
||
CjkSDS011::CjkSDS011(ESPEasySerialPort port, int16_t pinRX, int16_t pinTX)
|
||
{
|
||
_sws = ! ( pinRX < 0 || pinRX == 3 );
|
||
_pm2_5 = NAN;
|
||
_pm10_ = NAN;
|
||
_available = false;
|
||
_pm2_5avr = 0;
|
||
_pm10_avr = 0;
|
||
_avr = 0;
|
||
_data.SetPacketLength(10);
|
||
_command.SetPacketLength(19);
|
||
_working_period = -1;
|
||
_sleepmode_active = false;
|
||
_serial = new (std::nothrow) ESPeasySerial(port, pinRX, pinTX);
|
||
if (_serial != nullptr)
|
||
_serial->begin(9600);
|
||
}
|
||
|
||
CjkSDS011::~CjkSDS011() {
|
||
delete _serial;
|
||
}
|
||
|
||
void CjkSDS011::SendCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
|
||
_command[0] = 0xAA; // Head
|
||
_command[1] = 0xB4; // Command ID
|
||
_command[2] = byte1; // Data Byte 1
|
||
_command[3] = byte2; // Data Byte 2
|
||
_command[4] = byte3; // Data Byte 3
|
||
_command[5] = 0; // Data Byte 4
|
||
_command[6] = 0; // Data Byte 5
|
||
_command[7] = 0; // Data Byte 6
|
||
_command[8] = 0; // Data Byte 7
|
||
_command[9] = 0; // Data Byte 8
|
||
_command[10] = 0; // Data Byte 9
|
||
_command[11] = 0; // Data Byte 10
|
||
_command[12] = 0; // Data Byte 11
|
||
_command[13] = 0; // Data Byte 12
|
||
_command[14] = 0; // Data Byte 13
|
||
_command[15] = 0xFF; // Device ID byte 1, FF: All sensor response
|
||
_command[16] = 0xFF; // Device ID byte 2, FF: All sensor response
|
||
|
||
uint8_t checksum = 0;
|
||
for (uint8_t i=2; i< 17; ++i) {
|
||
checksum += _command[i];
|
||
}
|
||
_command[17] = checksum; // Checksum
|
||
_command[18] = 0xAB; // Tail
|
||
|
||
for (int i = 0; i < 19; ++i) {
|
||
_serial->write(_command[i]);
|
||
}
|
||
}
|
||
|
||
void CjkSDS011::SetSleepMode(bool enabled) {
|
||
uint8_t databyte3 = enabled ? 0 : 1;
|
||
SendCommand(6, 1, databyte3);
|
||
}
|
||
|
||
void CjkSDS011::SetWorkingPeriod(int minutes) {
|
||
// Data byte 1: 8
|
||
// Data byte 2: 0: query the current mode
|
||
// 1: set mode
|
||
// Data byte 3: 0:continuous(default)
|
||
// 1-30minute:work 30 seconds and sleep n*60-30 seconds
|
||
if (minutes < 0 || minutes > 30) return;
|
||
// Working period is stored in the flash of the sensor. Only write to change.
|
||
const int currentWorkingPeriod = GetWorkingPeriod();
|
||
if (minutes != currentWorkingPeriod)
|
||
SendCommand(8, 1, minutes);
|
||
// In some cases the Sensor is set to "report query"-mode, this makes sure to set active reporting of values, otherwise the sensor won't work.
|
||
Process();
|
||
SendCommand(2, 1, 0);
|
||
}
|
||
|
||
int CjkSDS011::GetWorkingPeriod() {
|
||
// Data byte 1: 8
|
||
// Data byte 2: 0: query the current mode
|
||
// 1: set mode
|
||
// Data byte 3: 0:continuous(default)
|
||
// 1-30minute:work 30 seconds and sleep n*60-30 seconds
|
||
SendCommand(8, 0, 0);
|
||
Process();
|
||
return _working_period;
|
||
}
|
||
|
||
void CjkSDS011::ParseCommandReply() {
|
||
switch(_data[2]) {
|
||
case 6: // Enable/Disable sleep mode.
|
||
if (_data[3] == 0)
|
||
_sleepmode_active = _data[4];
|
||
break;
|
||
case 8: // Set/Get working period
|
||
if (_data[3] == 0)
|
||
_working_period = _data[4];
|
||
break;
|
||
default:
|
||
// Not implemented.
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CjkSDS011::Process()
|
||
{
|
||
int serial_available = _serial->available();
|
||
while (serial_available > 0)
|
||
{
|
||
--serial_available;
|
||
_data.AddData(_serial->read());
|
||
|
||
if (_data[0] == 0xAA && _data[9] == 0xAB) // correct packet frame?
|
||
{
|
||
uint8_t checksum = 0;
|
||
for (uint8_t i=2; i<= 7; i++)
|
||
checksum += _data[i];
|
||
if (checksum != _data[8])
|
||
continue;
|
||
|
||
switch(_data[1]) {
|
||
case 0xC0: // SDS011 or SDS018?
|
||
_pm2_5 = (float)((_data[3] << 8) | _data[2]) * 0.1;
|
||
_pm10_ = (float)((_data[5] << 8) | _data[4]) * 0.1;
|
||
_available = true;
|
||
break;
|
||
case 0xCF: // SDS198?
|
||
_pm2_5 = (float)((_data[5] << 8) | _data[4]);
|
||
_pm10_ = (float)((_data[3] << 8) | _data[2]);
|
||
_available = true;
|
||
break;
|
||
case 0xC5: // Reply on command ID 0xB4
|
||
ParseCommandReply();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
if (_available) {
|
||
_pm2_5avr += _pm2_5;
|
||
_pm10_avr += _pm10_;
|
||
_avr++;
|
||
_data.Clear();
|
||
return;
|
||
}
|
||
}
|
||
if (serial_available == 0) {
|
||
serial_available = _serial->available();
|
||
}
|
||
}
|
||
}
|
||
|
||
boolean CjkSDS011::available()
|
||
{
|
||
boolean ret = _available;
|
||
_available = false;
|
||
return ret;
|
||
}
|
||
|
||
boolean CjkSDS011::ReadAverage(float &pm25, float &pm10)
|
||
{
|
||
if (_avr)
|
||
{
|
||
pm25 = _pm2_5avr / _avr;
|
||
pm10 = _pm10_avr / _avr;
|
||
_pm2_5avr = 0;
|
||
_pm10_avr = 0;
|
||
_avr = 0;
|
||
return true;
|
||
}
|
||
pm25 = NAN;
|
||
pm10 = NAN;
|
||
return false;
|
||
}
|
||
// #endif
|