SDS011 contrib, problem with installation

Absolutely not.

But looking up on the net I see that the sds node requires that baud rate. This is the currently configuration:

How can i understand what is the right value?

In that case why are you using a serial node too? The (very brief) description in the readme suggests that it drives the port directly. You don't need the serial node at all.

the sds node had a plug for input data and one for output data (am I wrong?)

image

So i supposed that it needs an input, and i tried with the serial node.

Now i just cut the serial node, but the debug show me an error:

Could be the answer?

Ok. Looking at the code for the node it seems it expects an input payload containing a command.

See

this turn on a lamp in my head....

look at the patch that i've post in my previous message. Have I to substitute the patch code with this one that yuo find out?

No idea. The diff just shows the whole file so hard to tell what you have changed. I would just use an inject node to send in a string with a relevant command

Ok, start to seems something good..... but it seems to have problems with output payload.

I have no idea how that device works. I would try getStatus. Or setActive.

whit status node that aims on the sds011, the result is `[error] [rpi-sds011:SDS011] TypeError: Cannot read property 'command' of undefine.

How can i use setActive node?

I saw this video: https://www.youtube.com/watch?v=8xyMKAJxDvg

that it seems to be similar to my case, and my setting is the same.

I think that the problem is the code, or something in my rasp configuration.

Without having used this node before, looking at your debugging output you’ve set up your inject node with the default setting. AsDave describes, try setting it to injecting a string type with as value the command, such as setActive. Again, no experience with the device or node, but you’re currently injecting a timestamp, which is the default setting for the inject node.

looking up for some flows with this node, i'm starting to understand how it works.
anyway, i've tried as you suggest, but...

[info] [rpi-sds011:SDS011] Command: setActive Parameter: undefined


I should really go to sleep now, but one last answer: start reading in the code here: https://github.com/ikhayainkomishi/node-red-contrib-sds011/blob/20646ae9e20af6d38ac26d91a40a456e218a98d5/sds011-node/sds011-node.js#L39

It shows that if msg.payload is a string it will use that as command. Some commands however require a parameter for input. To input that, instead set the inject node to JSON mode, and write an object as

{
  "command": "command you want to use",
  "parameter": "input to the command"
}

Don’t copy it directly, it has smart quotes as I’m typing on my phone and I’m too tired to fix it. The link has all commands that are defined.

thanks for help.

Don't be disappointed, I understand what you trying to say but i don't understand what i have to put in the JS injection. I tried :stuck_out_tongue:

image

same eror on row 2 if i put 'setActive' or "setActive".

I'm following your example, i'm going to sleep too.

hi!

may help?

You input JSON, hence JSON format. What's in your screenshot is not JSON, nor would it even be valid JavaScript. See my post above. I'll edit it now to replace the smart quotes by actual quotes so you can at least somewhat copy-paste it. JSON means brackets on start and end, and double quoted strings for keys, followed by a colon, and another double quoted string (in this case) for the value, and at the end of the line a comma except for the last line.

Meaning that to use the object/JSON mode in a inject node, you have to write valid JSON to it. Try to rewrite those commands following those rules instead.

Also if it's my first time with JSON and nodered, i try to paste a variant of your code.

{
  "command": "SetActive",
  "parameter": "input to the command"
}

I suppose that I have to set the parameter. But i really don't know how, also because i'm not sure that i something that the operator has to do.
Anyway, I studied the sds011-node.js code, but i can't find a idea to modify the parameter input.

good day folks.

The problem was in the serial port... in the rasp there are 4 ports, and just trying to put the usb plug in the right spot, it works. Now, new challenge: the sensor takes data every second, but i want to set that it takes data every 5 minutes.

Here's the code:
Fisrt, i try to set the row 80 code:

case 'setContinuousMode':
          setWorkingPeriod(0);
          break;

with

  case 'setContinuousMode':
          setWorkingPeriod(5);
          break;

but it doesn't work. Anyway, I feel that the point of my question is here:

// 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);
}
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(0);
          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} ]);
}

Help? :slight_smile: :sweat_smile:

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