8 Channel USB Relay with MacHIDMBS

  1. ‹ Older
  2. 11 months ago

    Kimball L

    23 Aug 2018 Pre-Release Testers, Xojo Pro Meridian, ID, USA

    Hoo boy. I'll do my best to help explain what is going on here, but keep in mind I'm not a nodeJS dev. I can read it well enough to be dangerous is all....

    So, the section that you really care about is the write(mask) method. Here it is again, with a bunch of my own comments added to help clarify what it is doing:

    //This method will receive a number named mask.  I believe this is to select which action for the device to take, or which channel to take that action on. I'm not sure.
    write(mask) {
    //verify that the mask is in fact a number, and is in the range of 1 to 655535 (0x0001 to 0xFFFF)
        if (typeof mask !== 'number' || mask < 0 || mask > 0xFFFF) {
          throw new Error(`Invalid write mask: ${mask}`);
        }
    
    //THIS THING IS IMPORTANT: This is an array of bytes (looks like 64 of them, but I didn't count) that will be used as the basic report structure to send to the device. Note that the first byte is 0xC3 - this is likely the reportID. The rest of this method swaps out some of the bytes in this array.
        let writeCmd = [
          0xC3, 0x0E, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x49, 0x44,
          0x43, 0xEE, 0x01, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
          0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
          0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
          0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
        ];
    //So we'll define a Uint16 array 
        let arr16 = new Uint16Array(1);
    //Then put that array's buffer into an array of Uint8's. No idea why it is doing this, except that maybe it's the only way to get to the individual bytes from the mask passed in? 
        let arr8 = new Uint8Array(arr16.buffer);
    //Yeah, must be, because now we set the mask bytes into that Uint16 array, which means that we can get to the LSB and MSB (Least Significant Byte and Most Significant Byte) by accessing the arr8 (which is just peeking into the arr16)
        arr16[0] = mask;
    
    //Grab the byte from the mask at index 0 and put it into the writeCmd array at position 2 (note arrays are probably 0 based, so this would be the 3rd byte)
        writeCmd[2] = arr8[0];
    //put the byte from the mask at index 1 and put it into the writeCmd at index 3.
        writeCmd[3] = arr8[1];
    
    //OK - so far we've created what I believe is a generic report in writeCmd, then twiddled 2 of it's bytes into new values, pulled from the mask provided to this method. Now we need to do some sort of checksum operation.  This next bit declares a little addCommandChecksum function that will be used shortly.  This function is declared in a way that is likely unfamiliar to you: I'm not sure of the exact term in NodeJS for this type of thing, but I've seen them called closures, inline functions, or similar. The idea here is that in the middle of the function called write, we will declare a variable called addCommandChecksum, but the value of that variable will not be something like a string, int, or float - it'll be a function itself.  A function within a function, if you will.
    
    
        const addCommandChecksum = (cmd) => {
          let size = cmd[1];
    //There is some js-specific syntax here that I'm not entirely familiar with. Look up reduce and slice if you absolutely must know what this is doing (and I think you do)
          let checksum = cmd.slice(0, size).reduce((a, b) => a + b, 0);
    //more bit twiddling on the computed checksum
          let arr32 = new Uint32Array(1);
          let arr8 = new Uint8Array(arr32.buffer);
          arr32[0] = checksum;
    //apparently there will be 4 bytes from the checksum - looks like it is tacking those bytes onto the very end of the original cmd that will be passed into this method.
          for (let i = 0; i < 4; i++) {
            cmd[size + i] = arr8[i];
          }
    //then it spits the passed-in array back out to the calling context.
          return cmd;
        };
    
    //Ok, we are finished defining the checksum function, and are back to the main body of the write() function. Now we'll take our writeCmd array that has 2 bits twiddled into it from the mask, pass it through the checksum method defined just above, then write the results out to the device.
    
        this.hid.write(addCommandChecksum(writeCmd));
        return Promise.resolve();
      }

    So: if I were in your shoes, I'd try to find a way to get at a few values during runtime: specifically, what are the bytes in the mask variable passed into this array? What does the writeCmd look like after the bytes from mask have been swapped into it? Finally, what does the checksum look like? (i.e., what does the FINAL writeCmd look like that is actually sent to the device?

    Any chance you can get a debugger for node stuff? Or at the very least print or echo the values of variables at runtime?

    Hope this is helpful. Good luck!

  3. Thanks so much for that insight. I have it working now. I'll post some finished working code.

  4. I have this fully working in HIDAPI and MacHID, it was pretty easy after determining what the packet structure should look like.

  5. 5 weeks ago

or Sign Up to reply!