X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/fs20pcs/blobdiff_plain/11007e953311f6ba2c929e1a0b7315a1e25be09c..HEAD:/fs20pcs.c diff --git a/fs20pcs.c b/fs20pcs.c index 10f9718..3941d80 100644 --- a/fs20pcs.c +++ b/fs20pcs.c @@ -1,8 +1,32 @@ +/* FS20PCS libusb-driver + * + * Copyright (c) 2013 Michael Gernoth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include #include #include #include #include +#include #include #define USB_TIMEOUT 10000 @@ -10,6 +34,8 @@ #define ID_VENDOR 0x18ef #define ID_PRODUCT 0xe015 +#define EXT_TO_SEC(ext) ((1 << ((ext & 0xf0) >> 4)) * (ext & 0x0f) * 0.25) + extern char *optarg; /* Not in all libusb-1.0 versions, so we have to roll our own :-( */ @@ -108,13 +134,13 @@ int fs20pcs_send(libusb_device_handle *devh, unsigned char* send_data) int i; int ret; - err = libusb_interrupt_transfer(devh, 0x01, send_data, 11, &cnt, 5000); + err = libusb_interrupt_transfer(devh, 0x01, send_data, 11, &cnt, USB_TIMEOUT); if (err) { fprintf(stderr, "Can't send data: %s\n", usb_strerror(err)); return 0; } - err = libusb_interrupt_transfer(devh, 0x81, recv_data, sizeof(recv_data), &cnt, 5000); + err = libusb_interrupt_transfer(devh, 0x81, recv_data, sizeof(recv_data), &cnt, USB_TIMEOUT); if (err) { fprintf(stderr, "Can't receive data: %s\n", usb_strerror(err)); return 0; @@ -269,6 +295,133 @@ int parse_addr(char *ad) return addr; } +int parse_command(char *cmd) +{ + int command = -1; + char *ep; + unsigned long val; + + if (cmd == NULL) + return -1; + + if (!strcasecmp(cmd, "off")) { + command = 0x00; + } else if (!strcasecmp(cmd, "on")) { + command = 0x11; + } else if (!strcasecmp(cmd, "toggle")) { + command = 0x12; + } else if (!strcasecmp(cmd, "dimup")) { + command = 0x13; + } else if (!strcasecmp(cmd, "dimdown")) { + command = 0x14; + } else if (!strcasecmp(cmd, "on-for-timer")) { + command = 0x39; + } else { + val = strtoul(cmd, &ep, 16); + if (*ep != '\0') { + fprintf(stderr, "Not a 1 byte hexstring or alias: %s\n", cmd); + return -1; + } + + command = val & 0xff; + } + + return command; +} + +int parse_extension(char *ext) +{ + int extension = -1; + char *ep; + + if (ext == NULL) + return -1; + + if ((ext[strlen(ext)-1] == 's') || + (ext[strlen(ext)-1] == 'S')) { + double t; + double diff = 1; + uint8_t high = 0; + uint8_t low = 0; + uint8_t i; + + t = strtod(ext, &ep); + if ((*ep != 's') && (*ep != 'S')) { + fprintf(stderr, "Not a valid time in seconds: %s\n", ext); + return -1; + } + + /* time = 2^(high nibble) * (low nibble) * 0.25 , high may only be 12 */ + t /= 0.25; +#ifdef DEBUG + printf("t: %f\n", t); +#endif + + for(i = 1; i <= 0xf; i++) { + double h; + double l; + uint8_t i_l; + double d; + +#ifdef DEBUG + printf("i: 0x%01x\n", i); +#endif + + h = t / i; +#ifdef DEBUG + printf("h: %f\n", h); +#endif + + l = log2(h); +#ifdef DEBUG + printf("l: %f\n", l); +#endif + + i_l = l; + if (i_l > 12) + i_l = 12; + + d = fabs(l - i_l); + +#ifdef DEBUG + printf("d: %f\n", d); +#endif + + if (d < diff) { +#ifdef DEBUG + printf("Current best match!\n"); +#endif + diff = d; + high = i_l; + low = i; + } + } + +#ifdef DEBUG + printf("Got: %f, high: %01x, low: %01x\n", t, high, low); +#endif + + if ((high == 0) && (low == 0)) { + fprintf(stderr, "Can't find extension byte for %s!\n", ext); + return -1; + } + + extension = ((high & 0xf) << 4) | (low & 0xf); + } else { + unsigned long val; + + val = strtoul(ext, &ep, 16); + if (*ep != '\0') { + fprintf(stderr, "Not a 1 byte hexstring or time: %s\n", ext); + return -1; + } + + extension = val & 0xff; + } + + return extension; +} + void syntax(char *prog) { fprintf(stderr, "Syntax: %s options\n\n", prog); @@ -277,42 +430,44 @@ void syntax(char *prog) fprintf(stderr, "\t-x\t\tabort sending long press\n"); fprintf(stderr, "\t-h [ELV|hex]\thousecode in ELV- or hex-notation\n"); fprintf(stderr, "The following options need an housecode:\n"); - fprintf(stderr, "\t-a [ELV|hex]\tsend commands to device at specified address\n"); - fprintf(stderr, "\t-s hex\t\tsend command byte\n"); - fprintf(stderr, "\t-e hex\t\textension byte for command\n"); + fprintf(stderr, "\t-a [ELV|hex]\tdestination address\n"); + fprintf(stderr, "\t-s [hex|alias]\tsend command byte\n"); + fprintf(stderr, "\t-e [hex|time]\textension byte for command or time in seconds (e.g. 10s)\n"); fprintf(stderr, "\t-r n\t\trepeat sending n times\n"); fprintf(stderr, "\nCommand bytes (without extension byte):\n"); - fprintf(stderr, "0x00\t\tswitch off\n"); - fprintf(stderr, "0x01-0x10\tswitch on dimmed (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%)\n"); - fprintf(stderr, "0x11\t\tswitch on\n"); - fprintf(stderr, "0x12\t\ttoggle\n"); - fprintf(stderr, "0x13\t\tdimup\n"); - fprintf(stderr, "0x14\t\tdimdown\n"); - fprintf(stderr, "0x15\t\tdimup, pause, dimdown, pause, ...\n"); - fprintf(stderr, "0x16\t\tstart/stop programming internal timer\n"); - fprintf(stderr, "0x17\t\tlearn housecode/address\n"); - fprintf(stderr, "0x18\t\toff for internal timer, then back on with current level\n"); - fprintf(stderr, "0x19\t\ton (100%%) for internal timer, then off\n"); - fprintf(stderr, "0x1a\t\ton (old level) for internal timer, then off\n"); - fprintf(stderr, "0x1b\t\tfactory reset\n"); - fprintf(stderr, "0x1e\t\ton (100%%) for internal timer, then old level\n"); - fprintf(stderr, "0x1f\t\ton (old level) for internal timer, then old state\n"); + fprintf(stderr, "hex\t\talias\tdescription\n"); + fprintf(stderr, "0x00\t\toff\tswitch off\n"); + fprintf(stderr, "0x01-0x10\t\tswitch on dimmed (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%)\n"); + fprintf(stderr, "0x11\t\ton\tswitch on\n"); + fprintf(stderr, "0x12\t\ttoggle\ttoggle\n"); + fprintf(stderr, "0x13\t\tdimup\tdimup\n"); + fprintf(stderr, "0x14\t\tdimdown\tdimdown\n"); + fprintf(stderr, "0x15\t\t\tdimup, pause, dimdown, pause, ...\n"); + fprintf(stderr, "0x16\t\t\tstart/stop programming internal timer\n"); + fprintf(stderr, "0x17\t\t\tlearn housecode/address\n"); + fprintf(stderr, "0x18\t\t\toff for internal timer, then back on with current level\n"); + fprintf(stderr, "0x19\t\t\ton (100%%) for internal timer, then off\n"); + fprintf(stderr, "0x1a\t\t\ton (old level) for internal timer, then off\n"); + fprintf(stderr, "0x1b\t\t\tfactory reset\n"); + fprintf(stderr, "0x1e\t\t\ton (100%%) for internal timer, then old level\n"); + fprintf(stderr, "0x1f\t\t\ton (old level) for internal timer, then old state\n"); fprintf(stderr, "\nCommand bytes (with timer as extension byte):\n"); - fprintf(stderr, "0x20\t\tdim down to off while timer is running\n"); - fprintf(stderr, "0x21-0x30\tdim to (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%) while timer is running\n"); - fprintf(stderr, "0x31\t\tdim to last level while timer is running\n"); - fprintf(stderr, "0x32\t\tdim to off, then after timer dim to off\n"); - fprintf(stderr, "0x33\t\tdimup now and switch off after timer\n"); - fprintf(stderr, "0x34\t\tdimdown now and switch off after timer\n"); - fprintf(stderr, "0x35\t\tdimup or dimdown (toggle) and switch off after timer\n"); - fprintf(stderr, "0x36\t\tprogram internal timer\n"); - fprintf(stderr, "0x38\t\toff for timer, then back on with current level\n"); - fprintf(stderr, "0x39\t\ton (100%%) for timer, then off\n"); - fprintf(stderr, "0x3a\t\ton (old level) for timer, then off\n"); - fprintf(stderr, "0x3c\t\tprogram internal ramp-up-time\n"); - fprintf(stderr, "0x3d\t\tprogram internal ramp-down-time\n"); - fprintf(stderr, "0x3e\t\ton (100%%) for timer, then old level\n"); - fprintf(stderr, "0x3f\t\ton (old level) for timer, then old state\n"); + fprintf(stderr, "hex\t\talias\tdescription\n"); + fprintf(stderr, "0x20\t\t\tdim down to off while timer is running\n"); + fprintf(stderr, "0x21-0x30\t\tdim to (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%) while timer is running\n"); + fprintf(stderr, "0x31\t\t\tdim to last level while timer is running\n"); + fprintf(stderr, "0x32\t\t\tdim to off, then after timer dim to off\n"); + fprintf(stderr, "0x33\t\t\tdimup now and switch off after timer\n"); + fprintf(stderr, "0x34\t\t\tdimdown now and switch off after timer\n"); + fprintf(stderr, "0x35\t\t\tdimup or dimdown (toggle) and switch off after timer\n"); + fprintf(stderr, "0x36\t\t\tprogram internal timer\n"); + fprintf(stderr, "0x38\t\t\toff for timer, then back on with current level\n"); + fprintf(stderr, "0x39\ton-for-timer\ton (100%%) for timer, then off\n"); + fprintf(stderr, "0x3a\t\t\ton (old level) for timer, then off\n"); + fprintf(stderr, "0x3c\t\t\tprogram internal ramp-up-time\n"); + fprintf(stderr, "0x3d\t\t\tprogram internal ramp-down-time\n"); + fprintf(stderr, "0x3e\t\t\ton (100%%) for timer, then old level\n"); + fprintf(stderr, "0x3f\t\t\ton (old level) for timer, then old state\n"); }; enum { @@ -335,8 +490,8 @@ int main(int argc, char **argv) int err; int opt; int action = NO_ACTION; - uint8_t command = 0x00; - uint8_t extension = 0x00; + int command = -1; + int extension = 0; uint8_t repeat = 0; int addr = -1; @@ -358,8 +513,8 @@ int main(int argc, char **argv) DUPLICATE_ACTION; action = SEND_COMMAND; - command = strtoul(optarg, &ep, 16); - if (*ep != '\0') { + command = parse_command(optarg); + if (command == -1) { fprintf(stderr, "Can't parse command!\n"); exit(EXIT_FAILURE); } @@ -398,8 +553,8 @@ int main(int argc, char **argv) #endif break; case 'e': - extension = strtoul(optarg, &ep, 16); - if (*ep != '\0') { + extension = parse_extension(optarg); + if (extension == -1) { fprintf(stderr, "Can't parse extension!\n"); exit(EXIT_FAILURE); } @@ -443,15 +598,15 @@ int main(int argc, char **argv) send_data[1] = 0x07; send_data[2] = 0xf2; send_data[8] = repeat; - printf("Sending 0x%02x 0x%02x to address %d (hc: 0x%02x%02x) (repeated %d times)\n", - command, extension, addr, + printf("Sending 0x%02x 0x%02x (%.2fs) to address %d (hc: 0x%02x%02x) (repeated %d times)\n", + command, extension, EXT_TO_SEC(extension), addr, housecode[0], housecode[1], repeat); } else { send_data[1] = 0x06; send_data[2] = 0xf1; - printf("Sending 0x%02x 0x%02x to address %d (hc: 0x%02x%02x)\n", - command, extension, addr, + printf("Sending 0x%02x 0x%02x (%.2fs) to address %d (hc: 0x%02x%02x)\n", + command, extension, EXT_TO_SEC(extension), addr, housecode[0], housecode[1]); } send_data[3] = housecode[0];