# SDS011 contrib, how to set the working period

**URL:** <https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624>\
**Category:** General\
**Created:** [10 April 2020 23:36 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624 "2020-04-10T23:36:14Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [10 April 2020 23:36 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/1 "2020-04-10T23:36:14Z")

</div>

Hi everyone,

I'm a newbie with node red. Actually, i'i'm trying to configure my SDS011 sensor node in my rapsberry pi3, you can find it here: [https://flows.nodered.org/node/node-red-contrib-sds011](https://flows.nodered.org/node/node-red-contrib-sds011)

The sensor works fine, I see the data. But it takes data every second, (seems to work in continuos mode). I want to change this situation changing the work period in about 5 minutes.

To do this, i read:

- the code (see below)
- the documentation of the sensor ([https://cdn.sparkfun.com/assets/parts/1/2/2/7/5/Laser\_Dust\_Sensor\_Control\_Protocol\_V1.3.pdf](https://cdn.sparkfun.com/assets/parts/1/2/2/7/5/Laser_Dust_Sensor_Control_Protocol_V1.3.pdf))

After reading the part 5 of the document, i tried to understand which part of the code is necesary to set the period. Probably, the answer is the function "setWorkingPeriod":

```auto
// set working period
// period: 0 for continues
// n for work 30 sec and sleep n*60-30 secs.
function setWorkingPeriod(period, deviceID=deviceId) {
  period = ('00'+(period).toString(16)).substr(-2);
  let data = '0801'+period+'00000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

```

So, i tried to put an Inject item (Payload = text, topic = setWorkingPeriod(5, deviceID)) on my board, but ativating it doesn't works.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/5/2560a60e10df5864b34fffd78d3bb315d2ea0347.png)

any helps? Thanks.

Here the code:

```auto
const SerialPort = require('serialport');

let node;
let serial;
let deviceId;

module.exports = function(RED) {
  function sds011Sensor(config) {
    RED.nodes.createNode(this, config);
    node = this;
    deviceId = 0xFFFF;
    serial = new SerialPort(config.port, {
      baudRate: 9600,
      parser: serialParser()
    });
    serial.on('open', function() {
      node.log('Serial port is open');
      node.status({fill: 'green', shape: 'dot', text: 'connected'});
    });
    serial.on('error', function(err) {
      if (err) {
        node.error('Serial port error', err);
      }
      node.status({fill: 'red', shape: 'ring', text: 'error'});
    });
    serial.on('close', function() {
      node.log('Serial port is closed');
      node.status({fill: 'red', shape: 'ring', text: 'disconnected'});
    });
    let parser = serialParser();
    serial.on('data', function(data) {
      parser(data);
    });

    node.on('input', function(msg) {
      let command;
      let parameter;
      if (msg.command === undefined) {
        command = msg.payload;
      } else {
        command = msg.command;
        parameter = msg.parameter;
      }
      node.log('Command: ' + command + ' Parameter: ' + parameter);
      switch (command) {
        case 'setDataReportingMode':
          setDataReportingMode(parameter);
          break;
        case 'setActive':
          setDataReportingMode('active');
          break;
        case 'setQuery':
          setDataReportingMode('query');
          break;
        case 'getDataReportingMode':
          getDataReportingMode();
          break;
        case 'queryData':
          queryData();
          break;
        case 'setDeviceId':
          setDeviceId(parameter);
          break;
        case 'setStatus':
          setStatus(parameter);
          break;
        case 'sleep':
          setStatus('sleep');
          break;
        case 'work':
          setStatus('work');
          break;
        case 'getStatus':
          getStatus();
          break;
        case 'setWorkingPeriod':
          setWorkingPeriod(parameter);
          break;
        case 'setContinuousMode':
          setWorkingPeriod(5);
          break;
        case 'getWorkingPeriod':
          getWorkingPeriod();
          break;
        case 'checkFirmwareVersion':
          checkFirmwareVersion();
          break;
        default:
          node.error('Unknown command: ' + msg.payload);
          break;
      }

      // get the device id of the sensor
      checkFirmwareVersion(deviceId);
    });

    node.on('close', function() {
      serial.close(function(err) {
        if (err) {
          node.error('Serial close error', err);
        }
      });
    });
  }
  RED.nodes.registerType('rpi-sds011', sds011Sensor);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#############################################################################################################################//
//# #//
//# documentation can be found at https://cdn.sparkfun.com/assets/parts/1/2/2/7/5/Laser_Dust_Sensor_Control_Protocol_V1.3.pdf #//
//# #//
//#############################################################################################################################//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function sendBuffer(buffer) {
  serial.write(buffer, 'hex');
}

// set data reporting mode
// mode: 'active' / 'query'
// deviceID: 0xFFFF for all peripherals
function setDataReportingMode(mode, deviceID=deviceId) {
  let data = '0201'+(mode==='active'?'00':'01')+'00000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// get data reporting mode
// deviceID: 0xFFFF for all peripherals
function getDataReportingMode(deviceID=deviceId) {
  let data = '02000000000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// query data command
function queryData(deviceID=deviceId) {
  let data = '04000000000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// set device ID
function setDeviceId(newDeviceID, deviceID=deviceId) {
  let data = '0500000000000000000000'+newDeviceID.toString(16).padStart(4, 0)+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// set sleep and work
// mode: 'sleep' / 'work'
function setStatus(mode, deviceID=deviceId) {
  let data = '0601'+(mode==='sleep'?'00':'01')+'00000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// get sleep and work
function getStatus(deviceID=deviceId) {
  let data = '06000000000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// set working period
// period: 0 for continues
// n for work 30 sec and sleep n*60-30 secs.
function setWorkingPeriod(period, deviceID=deviceId) {
  period = ('00'+(period).toString(16)).substr(-2);
  let data = '0801'+period+'00000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// get working period
function getWorkingPeriod(deviceID=deviceId) {
  let data = '08000000000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

// check formware version
function checkFirmwareVersion(deviceID=deviceId) {
  let data = '07000000000000000000000000'+deviceID.toString(16).padStart(4, 0);
  let crc = ('00'+(calcCrcFromData(new Buffer(data, 'hex'))).toString(16)).substr(-2);
  let buffer = new Buffer('AAB4'+data+crc+'AB', 'hex');
  sendBuffer(buffer);
}

function getNextBeginning(buffer) {
  let start = buffer.indexOf('AA', 'hex');
  if (start === -1) {
    return new Buffer(0);
  }
  return buffer.slice(start);
}

function serialParser() {
  let serialData = new Buffer(0);
  return function(buffer) {
    serialData = Buffer.concat([serialData, buffer]);

    // find message beginning
    if (serialData[0] != 0xAA) {
      serialData = getNextBeginning(serialData);
    }
    // check buffer length
    if (serialData.length < 10) {
      node.debug('buffer is not long enough: ' + buffer.length);
      return;
    }
    // check checksum and tail
    if (!checkCrc(serialData) || (serialData[9] != 0xAB)) {
      node.debug('wrong checksum/tail: ' + buffer[9]);
      serialData = serialData.slice(10);
      return;
    }
    parseMessage(serialData);
    serialData = new Buffer(0);
  }
};

// Message format:
// 0 HEADER AA
// 1 COMMAND
// 2 DATA
// 3 DATA
// 4 DATA
// 5 DATA
// 6 DATA
// 7 DATA
// 8 Check-sum
// 9 TAIL AB
function parseMessage(buffer) {
  deviceId = buffer[6] << 8 | buffer[7]
  let ret = {};
  // check command
  switch (buffer[1]) {
    case 0xC0:
      // PM values
      // 2 DATA PM2.5 Low byte
      // 3 DATA PM2.5 High byte
      // 4 DATA PM10 Low byte
      // 5 DATA PM10 High byte
      // 6 DATA Device ID Low byte
      // 7 DATA Device ID High byte

      // Extract PM values
      // PM2.5 (ug/m3) = ((PM2.5 High byte *256) + PM2.5 low byte) / 10
      // PM10 (ug/m3) = ((PM10 high byte*256) + PM10 low byte) / 10// PM2.5:
      let pm2_5 = (buffer[2] | (buffer[3] << 8)) / 10.0;
      let pm10 = (buffer[4] | (buffer[5] << 8)) / 10.0;

      sendPMValues(pm2_5, pm10);
      break;
    case 0xC5:
      switch (buffer[2]) {
        case 2:
          // data reporting mode
          // 2 DATA 2
          // 3 DATA 0: query the current mode
          // 1: set reporting mode
          // 4 DATA 0: report active mode
          // 1: report query mode
          // 5 DATA 0
          // 6 DATA Device ID Low byte
          // 7 DATA Device ID High byte

          if (buffer[4] === 0) {
            sendInfo('Sensor is in active mode');
          } else {
            sendInfo('Sensor is in query mode');
          }
          break;
        case 5:
          // set device ID
          // 2 DATA 5
          // 3 DATA 0
          // 4 DATA 0
          // 5 DATA 0
          // 6 DATA New Device ID Low byte
          // 7 DATA New Device ID High byte

          sendInfo('Sensor has a new Device ID: 0x' + buffer[6].toString(16) + buffer[7].toString(16));
          break;
        case 6:
          // set sleep and work
          // 2 DATA 6
          // 3 DATA 0: query the current mode
          // 1: set mode
          // 4 DATA 0: sleep
          // 1: work
          // 5 DATA 0
          // 6 DATA Device ID Low byte
          // 7 DATA Device ID High byte

          if (buffer[4] === 0) {
            sendInfo('Sensor is in sleep mode');
          } else {
            sendInfo('Sensor is in work mode');
          }
          break;
        case 7:
          // check firmware version
          // 2 DATA 7
          // 3 DATA Firmware version year
          // 4 DATA Firmware version month
          // 5 DATA Firmware version day
          // 6 DATA Device ID Low byte
          // 7 DATA Device ID High byte

          sendInfo('Firmware: ' + buffer[5] + '.' + buffer[4] + '.' + buffer[3]);
          break;
        case 8:
          // set working period
          // 2 DATA 8
          // 3 DATA 0: query the current mode
          // 1: set mode
          // 4 DATA 0: continues(default)
          // 1-30minute: work 30 secs and sleep n*60-30 secs
          // 5 DATA 0
          // 6 DATA Device ID Low byte
          // 7 DATA Device ID High byte

          if (buffer[4] === 0) {
            sendInfo('Sensor is in continuous mode');
          } else {
            sendInfo('Sensor works 30 seconds and sleeps for ' + (buffer[4]-1).toString() + ' minutes and 30 seconds.');
          }
          break;
        default:
          break;
      }
      break;
    default:
      sendInfo('received a unknown command: ' + buffer[1].toString(16));
      return;
  }
  return ret;
}

function calcCrcFromData(data) {
  let crc = data.reduce(function(prev, curr) {
    return prev + curr;
  });
  crc &= 0xFF;
  return crc;
}

function calcCrcFromBuffer(buffer) {
  let crc = calcCrcFromData(buffer.slice(2, 8));
  return crc;
}

function checkCrc(buffer) {
  let calcCrc = calcCrcFromBuffer(buffer);
  return calcCrc === buffer[8];
}

function sendPMValues(pm2_5, pm10) {
  let time = new Date();
  node.send([
    {payload: pm2_5, title: 'PM2.5 value', topic: 'PM2_5', description: 'PM2.5 value in ug/m3', time: time},
    {payload: pm10, title: 'PM10 value', topic: 'PM10', description: 'PM10 value in ug/m3', time: time},
    null
  ]);
}

function sendInfo(msg) {
  let time = new Date();
  node.log(msg);
  node.send([null, null, {payload: msg, time: time, deviceId: deviceId}]);
}

```

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [11 April 2020 03:52 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/2 "2020-04-11T03:52:01Z")

</div>

The readme for that mode says it is no longer maintained and to use this fork instead [https://github.com/jannikbecher/node-red-contrib-sds011](https://github.com/jannikbecher/node-red-contrib-sds011)

You may find that one already works.

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [14 April 2020 10:44 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/3 "2020-04-14T10:44:05Z")

</div>

Thanks for your answer.

Does it mean that I have to use the package.json included in taht link? Or is something deeper?

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [14 April 2020 11:07 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/4 "2020-04-14T11:07:49Z")

</div>

Actually this one looks better - as someone has started fixing things....

[optiprime](https://github.com/optiprime)/ **[node-red-contrib-sds011](https://github.com/optiprime/node-red-contrib-sds011)**

to install it you can go into your user directory (usually `~/.node-red`) and do

```auto
npm i optiprime/node-red-contrib-sds011

```

probably best talk directly to the author of that node if you need extra support.

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [14 April 2020 13:09 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/5 "2020-04-14T13:09:34Z")

</div>

Thanks. Before install this one, should I uninstall the actual node that i have in my directory?

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [14 April 2020 14:08 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/6 "2020-04-14T14:08:24Z")

</div>

always a good idea 🙂

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [14 April 2020 23:29 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/7 "2020-04-14T23:29:50Z")

</div>

Ok, i tried, but if i inject the command, it returns "Unknown command":

Here's how I build the injection: ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/a/7/a7459e5af5821c3e6abb28d430297eefc3c79c8b.png)

Here's the results. I'm curious about that "error" message at the bottom of the node.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/e/6e74c3206db2082ce54ce1eaa8bb49dd9acfc351.png)

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [15 April 2020 13:35 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/8 "2020-04-15T13:35:39Z")

</div>

The node info shows that the command need to be in the payload - not the topic.

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [16 April 2020 08:50 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/9 "2020-04-16T08:50:34Z")

</div>

Right 🙂  
I've tried with a string, but it doesn't work.

So, I tried with JSON inputs. I've made a few tests:

```auto
{
    "command:": "setWorkingPeriod(5)",
}

```

```auto
{
    "command:": "setWorkingPeriod(parameter)",
    "parameter:": 5
}

```

```auto
{
    "command:": "setWorkingPeriod()",
    "parameter:": 5
}

```

Here's an example.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/f/9f79d4d6e4d529ef1736fb56e355ffb5df014a72.png)

The output is always the same, Unknown command: [object Object].

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [16 April 2020 10:27 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/10 "2020-04-16T10:27:07Z")

</div>

try contacting the most recent author direct - I have know knowledge of this node other than trying to point you in the right direction.

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [16 April 2020 10:47 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/11 "2020-04-16T10:47:25Z")

</div>

I'm trying... but he doesn't leave a contact in his github profile ☹

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [16 April 2020 10:58 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/12 "2020-04-16T10:58:55Z")

</div>

Not helpful..... but looking at the code - hhttps://github.com/optiprime/node-red-contrib-sds011/blob/master/sds011-node/sds011-node.js#L106

you can see that msg.payload should be an object with a command and parameter properties so  
`msg.payload = { command:"setWorkingPeriod", parameter:5 }` ought to do it.

and look at that code for the other parameters, etc.

---

<div class="post-metadata">

**Author:** ![calcioscacchi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/calcioscacchi/32/16898_2.png) [@calcioscacchi](https://discourse.nodered.org/u/calcioscacchi)\
**Post date:** [16 April 2020 12:57 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/13 "2020-04-16T12:57:06Z")

</div>

Thanks, after some attempts, finally i got it 🙂

Next step: taking the output object and write it inside an excel file...  
Can you suggest some general guidelines to reach this purpose?

Many thanks for your precious help 🙂

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [16 April 2020 14:58 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/14 "2020-04-16T14:58:07Z")

</div>

you can use the csv node and provide it a template of column names and it will produce strings that you can feed to a file out node. but you may need to use the change node to move things around a bit first...

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [30 April 2020 15:04 UTC](https://discourse.nodered.org/t/sds011-contrib-how-to-set-the-working-period/24624/15 "2020-04-30T15:04:15Z")

</div>

Closed on 30 April 2020 15:04 UTC

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
