matthewwall / weewx-hp3000

weewx driver for fine offset hp3000 temperature/humidity sensors
GNU General Public License v3.0
5 stars 5 forks source link

Pipe Error #3

Closed antonskv closed 6 years ago

antonskv commented 6 years ago

Hi Matt,

It took some time to get somewhere. I followed instructions from other two tickets, cloned weewx usb branch, ran ./setup.py install. My weewx is located in /home/weewx

Then i followed instructions as per the README. It was still giving weeusb not found. I figured it was something about paths that was missing. I added lines into your driver in the beginning:

import sys
sys.path.append('/home/weewx')
sys.path.append('/home/weewx/bin')

I suppose this should work as well, for some reason it doesn't: export PYTHONPATH=$PYTHONPATH:/home/weewx/bin:/home/weewx

I haven't coded Python before, so maybe i'm doing something wrong. I have WS-3000-X3 connected through USB datacable to Raspberry Pi. This is the output from syslog when i plug it in:

Feb 3 19:57:49 localhost kernel: [202842.134086] usb 1-1.4: USB disconnect, device number 5 Feb 3 19:57:53 localhost kernel: [202846.264314] usb 1-1.4: new full-speed USB device number 7 using dwc_otg Feb 3 19:57:53 localhost kernel: [202846.397659] usb 1-1.4: New USB device found, idVendor=0483, idProduct=5750 Feb 3 19:57:53 localhost kernel: [202846.397668] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3 Feb 3 19:57:53 localhost kernel: [202846.397672] usb 1-1.4: Product: By viewtool Feb 3 19:57:53 localhost kernel: [202846.397676] usb 1-1.4: Manufacturer: MyUSB_HID Feb 3 19:57:53 localhost kernel: [202846.397680] usb 1-1.4: SerialNumber: 8C6487D20934 Feb 3 19:57:53 localhost kernel: [202846.400775] hid-generic 0003:0483:5750.0006: hiddev0,hidraw0: USB HID v1.10 Device [MyUSB_HID By viewtool] on usb-3f980000.usb-1.4/input0 Feb 3 19:57:54 localhost mtp-probe: checking bus 1, device 7: "/sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4" Feb 3 19:57:54 localhost mtp-probe: bus: 1, device: 7 was not an MTP device

When i try to run wee_device, script just dies, i looked at the source, i can't understand why there is no output saying something more than just crickets. I did run wee_config --reconfigure with your driver specified.

pi@raspberrypi:/home/weewx $ ./bin/wee_device 
Using configuration file /home/weewx/weewx.conf
pi@raspberrypi:/home/weewx $ 

When i try to run your script directly it gives me a "pipe error":

pi@raspberrypi:/home/weewx $ sudo PYTHONPATH=bin python bin/user/hp3000.py
Traceback (most recent call last):
  File "./user/hp3000.py", line 663, in <module>
    s.send_sequence()
  File "./user/hp3000.py", line 525, in send_sequence
    self.send_cmd(0x06)
  File "./user/hp3000.py", line 522, in send_cmd
    self.write([0x7b, cmd, 0x40, 0x7d])
  File "./user/hp3000.py", line 498, in write
    return self._write(buf)
  File "/home/weewx/bin/weeusb/usbhid_pyusb1.py", line 83, in _write
    timeout=timeout)
  File "/usr/lib/python2.7/dist-packages/usb/core.py", line 1043, in ctrl_transfer
    self.__get_timeout(timeout))
  File "/usr/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 883, in ctrl_transfer
    timeout))
  File "/usr/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 595, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 32] Pipe error

I see that my EP IN and OUT for this device is 0x82 and 0x01 instead of 0x82 and 0x02 that your driver defines inside the script. So i tried adjusting it to 0x01 value, but still got "Pipe Error".

My device shows up as /dev/usb/hiddev1 and here is output from lsusb -v:

Bus 001 Device 007: ID 0483:5750 STMicroelectronics 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0483 STMicroelectronics
  idProduct          0x5750 
  bcdDevice            2.00
  iManufacturer           1 MyUSB_HID
  iProduct                2 By viewtool
  iSerial                 3 8C6487D20934
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xc0
      Self Powered
    MaxPower              300mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      33
          Report Descriptor: (length is 33)
            Item(Global): Usage Page, data= [ 0x8c ] 140
                            Bar Code Scanner Page (POS)
            Item(Local ): Usage, data= [ 0x01 ] 1
                            (null)
            Item(Main  ): Collection, data= [ 0x01 ] 1
                            Application
            Item(Local ): Usage, data= [ 0x03 ] 3
                            (null)
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x00 0xff ] 65280
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x40 ] 64
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Local ): Usage, data= [ 0x04 ] 4
                            (null)
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x00 0xff ] 65280
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x40 ] 64
            Item(Main  ): Output, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               2
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               2
Device Status:     0x0001
  Self Powered

I run a small boutique Data Center, and i need to get 3 temperature/humidity readings. Outside, inside cold side, and inside hot side. I don't need historical data, just the live numbers. I have those 3 wireless sensors in place, and central piece is reading them fine.

My app that reads data from equipment is written in NodeJS. I was going to just use weewx to write current data to a JSON file at like 30 second interval, and NodeJS app to read the file and take stats along with other data and push to ElasticSearch.

Since you decoded the protocol this device uses, perhaps you would know if there is a way to just read the data directly from the device using something like https://github.com/tessel/node-usb?

I mean that library should allow claiming a USB device and sending commands to specific hw address and receiving temperature data back from it. But even just making it work through weewx would be already a huge help.

Thanks for making this driver btw, i've been breaking my head for days how to get this done, i'm as far along as ordering Rasperry Zero with sensor boards to make custom temperature/humidity devices. But that might be unnecessary work, if only i could get this thing working =))

Cheers.

antonskv commented 6 years ago

OK, i was able to directly interact with device though NodeJS USB module. But i'd like to thank you for providing the groundwork for it.

I would've never been able to decode the stream of crap coming out of it, without having your code as a reference.

These bits sent to the usb interface gave me what i need: [0x7b, 0x03, 0x40, 0x7d]

Response raw returns: 123, 0, 34, 97, 0, 248, 24, 0, 204, 31, 127, 255, 255, 127, 255, 255, 127, 255, 255, 127, 255, 255, 127, 255, 255, 64, 125, 0, 64, 125, 44, 0, 100, 64, 125, 0, 17, 52, 2, 19, 91, 111, 50, 180, 118, 22, 252, 75, 130, 231, 38, 145, 108, 52, 62, 225, 213, 101, 135, 36, 224, 37, 85, 166

Sensor 1 Bit 2: Temp 1 Bit 3: Humidity 1

Sensor 2 Bit 5: Temp 2 Bit 6: Humidity 2

Sensor 3 Bit 8: Temp 3 Bit 9: Humidity 3

Temperatures divided by 10, gives actual celcius value of the temperature on the sensor.

Cheers!

partoneoftwo commented 5 years ago

This is really awesome Anonskv.

Would you mind sharing your NodeJS script?

OK, i was able to directly interact with device though NodeJS USB module. But i'd like to thank you for providing the groundwork for it.

I would've never been able to decode the stream of crap coming out of it, without having your code as a reference.

These bits sent to the usb interface gave me what i need: [0x7b, 0x03, 0x40, 0x7d]

Response raw returns: 123, 0, 34, 97, 0, 248, 24, 0, 204, 31, 127, 255, 255, 127, 255, 255, 127, 255, 255, 127, 255, 255, 127, 255, 255, 64, 125, 0, 64, 125, 44, 0, 100, 64, 125, 0, 17, 52, 2, 19, 91, 111, 50, 180, 118, 22, 252, 75, 130, 231, 38, 145, 108, 52, 62, 225, 213, 101, 135, 36, 224, 37, 85, 166

Sensor 1 Bit 2: Temp 1 Bit 3: Humidity 1

Sensor 2 Bit 5: Temp 2 Bit 6: Humidity 2

Sensor 3 Bit 8: Temp 3 Bit 9: Humidity 3

Temperatures divided by 10, gives actual celcius value of the temperature on the sensor.

Cheers!

I also thank the author of this driver for all his efforts. But for me, this whole Weewx & Weewx-hp3000 experience has been very tough. All I want is to feed a home automation system with temperatures. I think your nodejs script will work!

Just to contribute something towards the discussion; I was able to get the data out with this test script. I guess I will have to modify it slightly to make it compatible with for instance Telegraf (for Influxdb).

#!/usr/bin/env python2

import sys, traceback
import usb.core, usb.util
from time import sleep

def trimbuffer(buf):
    i = 0
    while i < len(buf):
        if (i > 0 and buf[i] == 0x7d and buf[i-1] == 0x40):
            break
        i += 1
    return buf[0:i+1]

def ws3000_write(command):
    print "ws3000_write: entry"
    VENDOR = 0x0483
    PRODUCT = 0x5750
    CONFIGURATION = 0

    try:

        print "Detecting device..."
        device = usb.core.find(idVendor=VENDOR, idProduct=PRODUCT)
        if device is None:
            print("Is the WS-3000 connected?")
            sys.exit(1)
        print("Device found")

        # Device busy if managed by the kernel
        if device.is_kernel_driver_active(CONFIGURATION):
            print("Detaching kernel driver")
            device.detach_kernel_driver(CONFIGURATION)

        # WS-3000 has only one configuration
        device.set_configuration()

        # control
        print "Sending control packet"
        #device.ctrl_transfer(0x21, 10, 0, 0, None)
        device.ctrl_transfer(0, 9, 1, 0, None)

        commands = [0x41, 0x06, 0x08, 0x09, 0x05, 0x03, 0x04]
        #commands = [0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03]
        for command in commands:
            request = [0x7b, command, 0x40, 0x7d]
            padding = [0x0] * 60
            request.extend(padding)
            print "Sending command: ", request
            device.write(0x01, request, 100)
            data = device.read(0x82, 64, 100)
            data = trimbuffer(data)
            print data
            print len(data)
            #for value in data:
            #    print hex(value)
            sleep(10)

    except:
        print "An error occurred:", sys.exc_info()
        traceback.print_exc(file=sys.stdout)

    usb.util.dispose_resources(device)

print("Starting tests...")
ws3000_write("")
print("Tests completed")

Source: https://www.mail-archive.com/weewx-user@googlegroups.com/msg09026.html

antonskv commented 5 years ago

(Apologies for delay in response, it was holidays and all that)

Oh man, i forgot about this thing, it's been so long.... haha. I picked Node (and this was my first attempt at coding in NodeJS) because it's asynchronous, and multiple things can be going on at same time.

Here is how I did it. Please mind i'm not a NodeJS programmer, so it's probably done in sub-optimal way, and feel free to improve. I did this because back in the day i was running a small air cooled datacenter. So i needed 3 temperatures, outside, interior cold side, and interior hot side (where exhaust was going out). In hindsight i probably could've configured bunch of Arduinos with sensors to do the same thing, but this station was already there, so i decided to take a crack at it, and with some annoyance i got it done.

You need to pull USB module into your Node libraries, install instructions are here: https://www.npmjs.com/package/usb

And here is my amateur-style NodeJS module, it's part of bigger app, but this should be enough as a sample. It just opens a USB device and then starts sending bits to WS3000 at whatever interval you specify in the constructor. Then endpoint on('data', ... function reads and parses the response from WS3000.

/* FILENAME: AmbientWeather3000.js */

const usb = require('usb');

module.exports = class AmbientWeather3000
{
    constructor(interval){

        /* @NOTE: This is AmbientWeather 3000 vendor/device ID */
        // VendorID:  0x0483 (1155)
        // ProductID: 0x5750 (22352)

        this._usbVendorId = 1155;
        this._usbProductId = 22352;
        this._interval = interval; 
        this._intervalVariable = null;

        this._device = null;
        this._deviceOpen = false;

        this._outside = {
            temp: 0,
            humidity: 0
        };

        this._coldside = {
            temp: 0,
            humidity: 0
        };

        this._hotside = {
            temp: 0,
            humidity: 0
        };

        this.startScraping();
    };

    set device(device){ this._device = device; };
    get device() { return this._device; };

    set deviceOpen(isOpen){ this._deviceOpen = isOpen; };
    get deviceOpen() { return this._deviceOpen; };

    set outside(outside) { this._outside = outside; };
    get outside() { return this._outside; };

    set coldside(coldside) {this._coldside = coldside; };
    get coldside() { return this._coldside; }

    set hotside(hotside) { this._hotside = hotside; };
    get hotside() { return this._hotside; }

    get interval() { return this._interval; };

    set intervalVariable(intervalVariable){ this._intervalVariable = intervalVariable; };
    get intervalVariable(){ return this._intervalVariable; };

    startScraping(){
        console.log('['+new Date()+'] Starting scraping AmbientWeather 3000 at ' + (this.interval / 1000).toFixed(2) + ' seconds..' );

        var _this = this;

        try
        {
            this.device = usb.findByIds(1155, 22352);

            if(this.device)
                this.device.open();
            else
                throw Error("Cannot open WS-3000 USB device");

            if (this.device.interface(0).isKernelDriverActive()) {
               this.device.interface(0).detachKernelDriver();
            }

            this.deviceOpen = true;

            this.device.interface(0).claim();
        }
        catch(e)
        {
            console.log("ERROR: Cannot open USB device for streaming: " + e);
            process.exit();
        }

        this.device.interface(0).endpoint(130).startPoll(3, 64);

        this.device.interface(0).endpoint(130).on('data', function(data){
                var fullArray = [];

                for(var i=0 ; i < data.length ; i++)
                {
                    fullArray.push(data[i]);
                }

                //console.log( "RAW: (Bit Length: "+fullArray.length+")" + fullArray.join(', ') );
                var msb1 = fullArray[1];
                var temp1 = (msb1 * 256 + fullArray[2]) / 10.0;
                var hum1 = fullArray[3];

                var msb2 = fullArray[4];
                var temp2 = (msb2 * 256 + fullArray[5]) / 10.0;
                var hum2 = fullArray[6];

                var msb3 = fullArray[7];
                var temp3 = (msb3 * 256 + fullArray[8]) / 10.0;
                var hum3 = fullArray[9];

                console.log( "[" + new Date() + "] Outside: " + temp1 + "°C " + hum1 + "% " + " | Cold-side: " + temp3 + "°C " + hum3 + "% " + " | Hot-side: " + temp2 + "°C " + hum2 + "%" );
                _this.outside = { temp: parseFloat(temp1), humidity: hum1 };
                _this.coldside = { temp: parseFloat(temp3), humidity: hum3 };
                _this.hotside = { temp: parseFloat(temp2), humidity: hum2 };

                //console.log( util.inspect(currentData) );
        }).on('error', function(error){
                console.log( "ERROR: Problem receiving data from USB: " + error );
        });

        var _this = this;

        _this.device.interface(0).endpoint(1).transfer([0x7b, 0x03, 0x40, 0x7d], function(error){
                if(error)
                    console.log("Error sending command to WS-3000: " + error);
        });

        this.intervalVariable = setInterval(function(){
            _this.device.interface(0).endpoint(1).transfer([0x7b, 0x03, 0x40, 0x7d], function(error){
                if(error)
                    console.log("Error sending command to WS-3000: " + error);
            });
        }, this.interval);
    };

};

Then whichever main script you are using it in you would do something like:

/* Include the module at the top */
const AmbientWeather3000 = require('./AmbientWeather3000');

/* Then create this object with 60000 interval or whatever interval you want */
var aw3000 = new AmbientWeather3000( 60000 );

/* Then temperatures will be available like this, you probably would put it into a loop */
console.log( 'Temp: ' + aw3000.outside.temp );
console.log( 'Humidity: ' + aw3000.outside.humidity );

Hope this is enough to get you started!