Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Вопросы и информация о скриптах для ioBroker
Antworten
andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 23.10.2017, 09:53

Датчик:
https://ru.aliexpress.com/item/Free-shi ... 6141ebe467

Адаптер:
https://ru.aliexpress.com/item/FT232RL- ... 0.0.DBxfZO

Code: Alles auswählen


var device_port = '/dev/ttyUSB2';
var buflen = 64;
var sleep_time = 200; // Sleep time between request and wait for response.
var SerialPort = require('serialport');
var ByteLength = SerialPort.parsers.ByteLength;
var ppm;

var cmd_init = new Buffer ([0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]);
var cmd_set_zero = new Buffer ([0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78]);

function toHexString(byteArray) {
  return Array.from(byteArray, function(byte) {
    return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('');
}

function co2_gd(cmd,buflen,callback)
{

    var port = new SerialPort(device_port,{
    baudrate: 9600,
    dataBits: 8,
    parity: 'none'
    });
    const parser = port.pipe(new ByteLength({length: buflen}));
    port.pipe(parser);
    port.write(cmd);
    setTimeout(readport, sleep_time);
    function readport () {
        parser.on ('data', function (result) { 
               ppm = null;
               var CRC_calc=0;
                var b2 = parseInt(toHexString(result.slice(3,4)),16);
                var b3 = parseInt(toHexString(result.slice(5,6)),16);
                var b4 = parseInt(toHexString(result.slice(7,8)),16);
                var b5 = parseInt(toHexString(result.slice(9,10)),16);
                var b6 = parseInt(toHexString(result.slice(11,12)),16);
                var b7 = parseInt(toHexString(result.slice(13,14)),16);
                var b8 = parseInt(toHexString(result.slice(15,16)),16);
                var CRC_get = parseInt(toHexString(result.slice(17,18)),16);
                CRC_calc = (255 - (b2 + b3 + b4 + b5 + b6 + b7 + b8) % 256) + 1;
                if (CRC_get != CRC_calc)
                    {
                    console.log ('CRC ERROR');
                    console.log ('result='+JSON.stringify(result));
                    console.log('b='+b2+' '+b3+' '+b4+' '+b5+' '+b6+' '+b7+' '+b8 );    
                    console.log('CRC_get='+(CRC_get));
                    console.log('CRC_calc='+(CRC_calc));
                    }
                else
                    {
                    //console.log ('CRC CO2 is  OK');
                    //console.log ('result='+JSON.stringify(result));
                    //console.log('b='+b2+' '+b3+' '+b4+' '+b5+' '+b6+' '+b7+' '+b8 );
                    //console.log('CRC_get='+(CRC_get));
                    //console.log('CRC_calc='+(CRC_calc));  
                    var Hibyte = toHexString(result.slice(5,6));
                    var Lowbyte = toHexString(result.slice(7,8));
                    ppm = parseInt(Hibyte,16)*256+parseInt(Lowbyte,16);
                    //console.log('Hibyte='+Hibyte);
                    //console.log('Lowbyte='+Hibyte);
                    //console.log('ppm='+ppm);
                    if (ppm> 5000 )
                        {
                            console.log ('CRC OK');
                            console.log('Hibyte='+Hibyte);
                            console.log('Lowbyte='+Hibyte);
                            console.log('ppm='+ppm);
                            console.log ('result='+JSON.stringify(result));
                            console.log('b='+b2+' '+b3+' '+b4+' '+b5+' '+b6+' '+b7+' '+b8 );
                            console.log('CRC_get='+(CRC_get));
                            console.log('CRC_calc='+(CRC_calc));   
                            ppm = null;
                        }
                    
                    }
                if (callback && typeof(callback) === "function") {
                callback(ppm);
                } 
      
        }); //parser.on
        port.pause();
        port.close();
        return (ppm);
    }
    
    
}  //end function co2_gd

//co2_gd(cmd_set_zero,18, function(data) {} ); //Set CO2 level as zero  (400 ppm)
 
schedule("* * * * *", function () {
    co2_gd(cmd_init,18, function(data) { 
        //console.log('CO2 ppm='+data);
        setState("javascript.0.CO2_1f_uart",data);
    });
});

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 23.12.2017, 17:12

Не работает.
8:10:37.273 [info] javascript.0 Start javascript script.js.common.co2_mh_z19
18:10:37.274 [info] javascript.0 script.js.common.co2_mh_z19: registered 0 subscriptions and 1 schedule
18:11:00.281 [info] javascript.0 script.js.common.co2_mh_z19: request={"type":"Buffer","data":[255,1,134,0,0,0,0,0,121]}
18:11:00.508 [info] javascript.0 script.js.common.co2_mh_z19: -------------------
18:11:00.509 [info] javascript.0 script.js.common.co2_mh_z19: CRC ERROR
18:11:00.510 [info] javascript.0 script.js.common.co2_mh_z19: result={"type":"Buffer","data":[255,255,134,2,142,86,134,2,142,86,4,4,58,58,137,137,205,205]}
18:11:00.510 [info] javascript.0 script.js.common.co2_mh_z19: b=2 86 2 86 4 58 137
18:11:00.510 [info] javascript.0 script.js.common.co2_mh_z19: CRC_get=205
18:11:00.511 [info] javascript.0 script.js.common.co2_mh_z19: CRC_calc=137

Добавлял отладочный вывод запросов - такое впечатление, что отсылается команда чтения два раза и потом в буфере приемном все перемешано, вот и лажа.

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 24.12.2017, 16:14

А никому не приходилось бороться со странными зависами при setTimeout в скриптах?

Переделал скрипт из темы, несколько раз получил результаты и теперь мертвые висы.

Code: Alles auswählen

  console.log('wait response');
            setTimeout(readport, 200);
            function readport() {
                                        console.log('readport enter');   

в логе вижу первый вывод в консоль - и все, глухой вис. в top висит процесс iobroker.javascript с 100% загрузкой, драйвер javascript в панели тоже останавливается. Приходится вручную процесс убивать.
Иногда по непонятной причине - проходит, результат получается нормально.

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 25.12.2017, 04:37

artko hat geschrieben:Не работает.
CRC ERROR
18:11:00.510 [info] javascript.0 script.js.common.co2_mh_z19: result={"type":"Buffer","data":[255,255,134,2,142,86,134,2,142,86,4,4,58,58,137,137,205,205]}

Добавлял отладочный вывод запросов - такое впечатление, что отсылается команда чтения два раза и потом в буфере приемном все перемешано, вот и лажа.
Дело в том что мне попадались 2 типа USB-Uart адаптера.
В первом случае ответы дублируются побайтно, а во втором - ответ дублируется целиком.
При чём у разных адаптеров разный % CRC Error.
В вашем случае побайтный дубль (как у меня) и видно - что идёт сбой ответа - не все байты попарно одинаковы.
Длина кабеля?
Уверены что нет просадки напряжения на самом датчике в моменты ИК-свечения (ток вроде 300мА)?
Какой у вас адаптер?
В первые пару минут - CRC error это нормально.
Попробуйте другой адаптер.
У меня на таком FTDI_FT232R_USB_UART_AH05SK6S CRC ошибок менее ~3 %

P.S.Мой скрипт написан для MH-Z14, на MH-Z19 ещё не проверял. Скоро проверю, сам датчик уже есть. Судя по документации логика абсолютно одинаковая.
Zuletzt geändert von andrey99986 am 25.12.2017, 05:00, insgesamt 2-mal geändert.

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 25.12.2017, 04:49

artko hat geschrieben:А никому не приходилось бороться со странными зависами при setTimeout в скриптах?

Переделал скрипт из темы, несколько раз получил результаты и теперь мертвые висы.

Code: Alles auswählen

  console.log('wait response');
            setTimeout(readport, 200);
            function readport() {
                                        console.log('readport enter');   

в логе вижу первый вывод в консоль - и все, глухой вис. в top висит процесс iobroker.javascript с 100% загрузкой, драйвер javascript в панели тоже останавливается. Приходится вручную процесс убивать.
Иногда по непонятной причине - проходит, результат получается нормально.
Какая OS?
Сколько свободной памяти показывает iobroker?

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 25.12.2017, 09:23

andrey99986 hat geschrieben: Дело в том что мне попадались 2 типа USB-Uart адаптера.
В первом случае ответы дублируются побайтно, а во втором - ответ дублируется целиком.
При чём у разных адаптеров разный % CRC Error.
В вашем случае побайтный дубль (как у меня) и видно - что идёт сбой ответа - не все байты попарно одинаковы.
Длина кабеля?
Уверены что нет просадки напряжения на самом датчике в моменты ИК-свечения (ток вроде 300мА)?
Какой у вас адаптер?
В первые пару минут - CRC error это нормально.
Попробуйте другой адаптер.
У меня на таком FTDI_FT232R_USB_UART_AH05SK6S CRC ошибок менее ~3 %

P.S.Мой скрипт написан для MH-Z14, на MH-Z19 ещё не проверял. Скоро проверю, сам датчик уже есть. Судя по документации логика абсолютно одинаковая.
адаптер прямо в порту торчит. чип CP2102. датчик - проводочки сантиметров пять. Просадку не смотрел. Два разных адаптера (одинаковых, впрочем, по чипу и схеме).
Скрипт на питоне - работает как из пушки. Ни одной ошибки.

Code: Alles auswählen

import MySQLdb
import string
import time
import serial

from datetime import datetime

ser = serial.Serial('/dev/ttyUSB0',
                      baudrate=9600,
                      bytesize=serial.EIGHTBITS,
                      parity=serial.PARITY_NONE,
                      stopbits=serial.STOPBITS_ONE,
                      timeout=1.0)
result=ser.write("\xff\x01\x86\x00\x00\x00\x00\x00\x79")
s=ser.read(9)
if s[0] == "\xff" and s[1] == "\x86":
  co2 = ord(s[2])*256 + ord(s[3])

print "co2=", co2

ts = time.time()
ts1 = int(ts * 1000)
print "ts=", ts1

db = MySQLdb.connect(host="localhost", user="xxxx", passwd="xxxxx", db="iobroker", charset="utf8")
cursor = db.cursor()
sql = """insert iobroker.ts_number(id, ts, val, ack, _from, q) values (12,%(ts)s,%(val)s,0,2,0) """ %{"ts":ts1, "val":co2}
print "sql=", sql
cursor.execute(sql)
db.commit()
db.close()

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 25.12.2017, 09:24

andrey99986 hat geschrieben:Какая OS?
Сколько свободной памяти показывает iobroker?

Ubuntu 16.04LTS.
Всего RAM - Используется: 325 Мб / Свободно: 176 Мб (9%) [Сервер: atom-nb - 9 процессов]

%Cpu(s): 8,0 us, 2,0 sy, 0,0 ni, 87,0 id, 3,0 wa, 0,0 hi, 0,0 si, 0,0 st
КиБ Mem : 2050484 total, 179628 free, 520800 used, 1350056 buff/cache
КиБ Swap: 8269820 total, 8203072 free, 66748 used. 1233352 avail Mem

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 25.12.2017, 09:45

artko hat geschrieben:
andrey99986 hat geschrieben:Какая OS?
Сколько свободной памяти показывает iobroker?

Ubuntu 16.04LTS.
Всего RAM - Используется: 325 Мб / Свободно: 176 Мб (9%) [Сервер: atom-nb - 9 процессов]

%Cpu(s): 8,0 us, 2,0 sy, 0,0 ni, 87,0 id, 3,0 wa, 0,0 hi, 0,0 si, 0,0 st
КиБ Mem : 2050484 total, 179628 free, 520800 used, 1350056 buff/cache
КиБ Swap: 8269820 total, 8203072 free, 66748 used. 1233352 avail Mem
У меня драйвер javascript при работе с ком-портами периодически самоперегружается, поэтому я скрипты работающие с ком-портами запускаю в дополнительной инстанции, чтобы сбой не повлиял на работу других скриптов. Причина где то в недрах библиотеки serialport nodejs. По идее надо дебажить и создавать issue на https://github.com/node-serialport/node ... ort/issues

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 26.12.2017, 04:17

artko hat geschrieben: адаптер прямо в порту торчит. чип CP2102. датчик - проводочки сантиметров пять. Просадку не смотрел. Два разных адаптера (одинаковых, впрочем, по чипу и схеме).
Скрипт на питоне - работает как из пушки. Ни одной ошибки.
Попробуй поменять в коде на

Code: Alles auswählen

var buflen = 9;
Ещё вероятно причина в драйвере serialport. Видно же что ответ ненормальный идёт. Сделайте дебаг вывод ответа питона и сравните с nodejs.
С питоном также дублированы байты ответа?
Когда работает с портом iobroker (nodejs) - другие скрипты на уровне OS (питон) с этим портом не работают?

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 26.12.2017, 11:48

12:25:19.724 [info] javascript.0 script.js.common.Скрипт1: registered 0 subscriptions and 1 schedule
12:26:00.729 [error] javascript.0 at co2_gd (script.js.common.Скрипт1:20:16)
12:26:00.730 [error] javascript.0 at Object. (script.js.common.Скрипт1:93:5)

20 var port = new SerialPort(device_port,{
93 co2_gd(cmd_init,18, function(data) {


с питоном ничего не дублируется, с терминалкой тоже все ок. никаких других скриптов кроме этого вообще нет. питон на время экспериментов остановил.
похоже, что-то в недрах nodejs.serialport очень странно работает.

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 26.12.2017, 12:17

artko hat geschrieben:
с питоном ничего не дублируется, с терминалкой тоже все ок. никаких других скриптов кроме этого вообще нет. питон на время экспериментов остановил.
похоже, что-то в недрах nodejs.serialport очень странно работает.

Посмотреть кто занял порт:

Code: Alles auswählen

 fuser /dev/ttyUSB1
Иногда javascript driver надо перезапустить чтобы порт освободить.

Это не помогло?

Code: Alles auswählen

var buflen = 9;

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 26.12.2017, 12:26

andrey99986 hat geschrieben: Посмотреть кто занял порт:

Code: Alles auswählen

 fuser /dev/ttyUSB1
Иногда javascript driver надо перезапустить чтобы порт освободить.
никто не занимает, ну и проверяю в процессах - уже вижу, если висит io.javascript c 100% загрузкой ядра - то ой. почему-то вешается именно при ожидании данных.
сделал себе вот такой вариант скрипта для проверки по этапам:

Code: Alles auswählen

createState('CO2_UART');

var device_port = '/dev/ttyUSB0';
var buflen = 64;
var sleep_time = 200; // Sleep time between request and wait for response.
var SerialPort = require('serialport');
var ByteLength = SerialPort.parsers.ByteLength;
var ppm;

var cmd_init = new Buffer ([0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]);
var cmd_set_zero = new Buffer ([0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78]);
//var cmd_abc_off = new Buffer ([0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86]);
//var cmd_abc_on= new Buffer ([0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6]);


function co2_gd(cmd,buflen,callback)
{
    


    var port = new SerialPort(device_port,{
    baudRate: 9600,
    dataBits: 8,
    parity: 'none',
    autoOpen: false
    });
    

    port.open(function (err) {
        if (err) {
            return console.log('Error opening port: ', err.message);
        }
        console.log('port opened');
        console.log ('request='+JSON.stringify(cmd));
    
        const parser = port.pipe(new ByteLength({length: buflen}));
        port.pipe(parser);
        
        port.write(cmd, function(err) {
            if (err) {
                return console.log('Error on write: ', err.message);
            }
            console.log('message written');
            const parser = port.pipe(new ByteLength({length: buflen}));
            port.pipe(parser);
        
    
            console.log('wait response');
            
            setTimeout(readport, 200);
            function readport() {
                                        console.log('readport enter');      
                                        parser.on ('data', function (result) { 
                                        console.log ('result='+JSON.stringify(result));
                                        var i = 0;
                                        while (i < 18)
                                        {
                  if (result[i] == result[i+1])
                  {
                      i += 2;
                  }
                  else
                  {
                      if ((i + 2) < 18)
                      {
                          if (result[i] == result[i+2])
                          {
                              console.log ('fix data at position ' + i);
                              result[i+2] = result[i+1];
                              result[i+1] = result[i];
                              i += 2;
                          }
                      }
                  }
                 console.log('fix cycle ' + i);
                }
    
                                        ppm = null;
                                        var CRC_calc=0;
                                        var CRC_get = result[16];
                                        CRC_calc = (255 - (result[2] + result[4] + result[6] + result[8] + result[10] + result[12] + result[14]) % 256) + 1;
                                        if (CRC_get != CRC_calc)
                                        {
                    console.log ('CRC ERROR');
                    console.log('CRC_get='+(CRC_get));
                    console.log('CRC_calc='+(CRC_calc));
                    }
                                        else
                                        {
                            ppm = result[4] * 256 + result[6];
                            console.log('ppm='+ppm);
                            if (ppm> 5000 )
                            {
                                console.log ('CRC OK');
                                console.log('ppm='+ppm);
                                console.log('CRC_get='+(CRC_get));
                                console.log('CRC_calc='+(CRC_calc));   
                                ppm = null;
                            }
                    }
                                        if (callback && typeof(callback) === "function") {
                                            callback(ppm);
                                        } 
                                        port.pause();
                                        port.close();
                                        return (ppm);
                                    })
            }
            });
    });
    
    
    
}  //end function co2_gd

//co2_gd(cmd_set_zero,18, function(data) {} ); //Set CO2 level as zero  (400 ppm)
 
schedule("*/15 * * * * *", function () {
    console.log('co2 request');
    co2_gd(cmd_init,18, function(data) { 
        //console.log('CO2 ppm='+data);
        setState("CO2_UART",data);
    });
});


andrey99986 hat geschrieben: Это не помогло?

Code: Alles auswählen

var buflen = 9;
неа, в таком виде как скрипт из первого сообщения - вообще не хочет работать. с ошибками.

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 27.12.2017, 04:10

artko hat geschrieben: Скрипт на питоне - работает как из пушки. Ни одной ошибки.
Что-то не видно проверки CRC на питоне. Понятно что без проверки ошибок нет :)

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 27.12.2017, 15:58

andrey99986 hat geschrieben:
artko hat geschrieben: Скрипт на питоне - работает как из пушки. Ни одной ошибки.
Что-то не видно проверки CRC на питоне. Понятно что без проверки ошибок нет :)
то был первый вариант :) питон принимает строго нужное количество байт, crc совпадает :)

andrey99986
professional
Beiträge: 198
Registriert: 24.08.2016, 11:12

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von andrey99986 » 28.12.2017, 04:12

artko hat geschrieben: то был первый вариант :) питон принимает строго нужное количество байт, crc совпадает :)
CRC вычисляется в именно в строгом количестве байт, смотрите даташит на датчик. В вашем коде на питоне проверки CRC нет.

artko
Beiträge: 8
Registriert: 23.12.2017, 15:50

Re: Скрипт для датчика CO2 MH-Z14 (MH-Z19)

Beitrag von artko » 28.12.2017, 09:36

andrey99986 hat geschrieben:
artko hat geschrieben: то был первый вариант :) питон принимает строго нужное количество байт, crc совпадает :)
CRC вычисляется в именно в строгом количестве байт, смотрите даташит на датчик. В вашем коде на питоне проверки CRC нет.
я же написал, то был первый вариант....

Code: Alles auswählen

import MySQLdb
import string
import time
import serial

from datetime import datetime

ser = serial.Serial('/dev/ttyUSB0',
                      baudrate=9600,
                      bytesize=serial.EIGHTBITS,
                      parity=serial.PARITY_NONE,
                      stopbits=serial.STOPBITS_ONE,
                      timeout=1.0)
result=ser.write("\xff\x01\x86\x00\x00\x00\x00\x00\x79")
s=ser.read(9)

str = ""
for ch in s:
	str += hex(ord(ch))+ " "
print str
crc_get = ord(s[8])
crc_calc = (255 - (ord(s[1]) + ord(s[2]) + ord(s[3]) + ord(s[4]) + ord(s[5]) + ord(s[6]) + ord(s[7]))) % 256 + 1
print "crc calc=" + hex(crc_calc) + " crc get=" + hex(crc_calc)
if s[0] == "\xff" and s[1] == "\x86" and crc_calc == crc_get:
   co2 = ord(s[2])*256 + ord(s[3])  
   print "crc ok"
else:
   co2 = 0
   print "crc error"
print "co2=", co2

ts = time.time()
ts1 = int(ts * 1000)
print "ts=", ts1

db = MySQLdb.connect(host="localhost", user="xxxxxxx", passwd="xxxxxxx", db="iobroker", charset="utf8")
cursor = db.cursor()
sql = """insert iobroker.ts_number(id, ts, val, ack, _from, q) values (12,%(ts)s,%(val)s,0,2,0) """ %{"ts":ts1, "val":co2}
print "sql=", sql
cursor.execute(sql)
db.commit()
db.close()
вывод исполнения
0xff 0x86 0x1 0xca 0x57 0x40 0x28 0x58 0x98
crc calc=0x98 crc get=0x98
crc ok
co2= 458
ts= 1514450008318
sql= insert iobroker.ts_number(id, ts, val, ack, _from, q) values (12,1514450008318,458,0,2,0)

Antworten