From 70570add8918865d23b06aecd1209e55e52ee5b6 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Mon, 28 May 2018 22:56:37 +0100 Subject: [PATCH] Add initial 433MHz / USB Relay code This pretends to be a dcttech.com 4 port relay board but actually sends the appropriate 433MHz signals to control 4 Energenie power sockets. --- main.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++++ usbconfig.h | 26 +++--- 2 files changed, 261 insertions(+), 14 deletions(-) create mode 100644 main.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..d6a5095 --- /dev/null +++ b/main.c @@ -0,0 +1,249 @@ +/* + * Basic firmware to control Some Energenie 433MHz sockets (ENER002-4) + * as if they were a www.dcttech.com 4 port USB Relay board + * + * Copyright 2018 Jonathan McDowell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include +#include "usbdrv.h" +#include "libs-device/osccal.h" + +#define CMD_ALL_ON 0xfe +#define CMD_ALL_OFF 0xfc +#define CMD_ON 0xff +#define CMD_OFF 0xfd +#define CMD_SET_SERIAL 0xfa + +/* Energenie 24 bit address , last 4 all 0 for code */ +#define ENER_ADDR 0x123450 + +uchar serno_read = 0; +uchar serno[6]; +unsigned long cmd = 0; +int repeat = 0; + +PROGMEM const char usbHidReportDescriptor[22] = { + 0x06, 0x00, 0xff, /* USAGE PAGE (Generic Desktop) */ + 0x09, 0x01, /* USAGE (Vendor Usage 1) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x09, 0x00, /* USAGE (Undefined) */ + 0xb2, 0x02, 0x01, /* FEATURE (Data, Var, Abs, Buf) */ + 0xc0 /* END_COLLECTION */ +}; + +void fetch_serno(void) +{ + if (!serno_read) { + eeprom_read_block(serno, 0, 6); + if (serno[0] == 0xff) { + /* If the EEPROM is blank, return a default serial # */ + serno[0] = 'U'; + serno[1] = 'N'; + serno[2] = 'S'; + serno[3] = 'E'; + serno[4] = 'T'; + serno[5] = 0; + } + serno_read = 1; + } +} + +void update_serno(uchar *buf, uchar len) +{ + uchar i; + + /* + * I have no idea why this gets stored 3 times, but the original + * firmware does it. + */ + eeprom_write_block(buf, (void *) 0x00, len); + eeprom_write_block(buf, (void *) 0x40, len); + eeprom_write_block(buf, (void *) 0x80, len); + + for (i = 0; i < 6; i++) { + serno[i] = buf[i]; + } +} + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *rq = (void *) data; + + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { + if ((rq->bRequest == USBRQ_HID_GET_REPORT) || + (rq->bRequest == USBRQ_HID_SET_REPORT)) { + return 0xFF; + } + } + + return 0; +} + +uchar usbFunctionRead(uchar *data, uchar len) +{ + uchar i; + + if (len != 0) { + fetch_serno(); + for (i = 0; i < 6; i++) { + data[i] = serno[i]; + } + data[6] = data[7] = 0; + if (PORTB & (1 << PB0)) { + data[7] = 1; + } + return len; + } + + return 0; +} + +uchar usbFunctionWrite(uchar *data, uchar len) +{ + if (data[0] == CMD_ALL_ON) { + cmd = ENER_ADDR | 0xd; + repeat = 5; + } else if (data[0] == CMD_ALL_OFF) { + cmd = ENER_ADDR | 0xc; + repeat = 5; + } else if (data[0] == CMD_ON) { + switch (data[1]) { + case 1: + cmd = ENER_ADDR | 0xf; + repeat = 5; + break; + case 2: + cmd = ENER_ADDR | 0x7; + repeat = 5; + break; + case 3: + cmd = ENER_ADDR | 0xb; + repeat = 5; + break; + case 4: + cmd = ENER_ADDR | 0x3; + repeat = 5; + break; + default: + break; + } + } else if (data[0] == CMD_OFF) { + switch (data[1]) { + case 1: + cmd = ENER_ADDR | 0xe; + repeat = 5; + break; + case 2: + cmd = ENER_ADDR | 0x6; + repeat = 5; + break; + case 3: + cmd = ENER_ADDR | 0xa; + repeat = 5; + break; + case 4: + cmd = ENER_ADDR | 0x2; + repeat = 5; + break; + default: + break; + } + } else if (data[0] == CMD_SET_SERIAL) { + update_serno(&data[1], 6); + } + + return len; +} + +void t433_transmit_bit(bool value) +{ + PORTB |= 1 << PB0; + if (value) + _delay_us(600); + else + _delay_us(200); + + PORTB &= ~(1 << PB0); + if (value) + _delay_us(200); + else + _delay_us(600); +} + +void t433_send(unsigned long code, unsigned int length) +{ + int i; + + cli(); + for (i = length - 1; i >= 0; i--) { + if (code & (1L << i)) { + t433_transmit_bit(true); + } else { + t433_transmit_bit(false); + } + } + /* Send a sync bit */ + PORTB |= 1 << PB0; + _delay_us(200); + PORTB &= ~(1 << PB0); + sei(); + _delay_ms(30); +} + +int __attribute__((noreturn)) main(void) +{ + unsigned char i; + + wdt_enable(WDTO_1S); + + usbInit(); + usbDeviceDisconnect(); + + i = 0; + while (--i) { + wdt_reset(); + _delay_ms(1); + } + + usbDeviceConnect(); + + /* Set the 433MHz transmitter bit to output mode */ + DDRB |= (1 << PB0); + PORTB &= (1 << PB0); + + sei(); /* We're ready to go; enable interrupts */ + + while (1) { + wdt_reset(); + usbPoll(); + if (cmd) { + t433_send(cmd, 24); + if (--repeat == 0) + cmd = 0; + } + } +} diff --git a/usbconfig.h b/usbconfig.h index 207af65..656ddb8 100644 --- a/usbconfig.h +++ b/usbconfig.h @@ -76,7 +76,7 @@ section at the end of this file). /* --------------------------- Functional Range ---------------------------- */ -#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 /* Define this to 1 if you want to compile a version with two endpoints: The * default control endpoint 0 and an interrupt-in endpoint (any other endpoint * number). @@ -111,7 +111,7 @@ section at the end of this file). * (e.g. HID), but never want to send any data. This option saves a couple * of bytes in flash memory and the transmit buffers in RAM. */ -#define USB_CFG_INTR_POLL_INTERVAL 10 +#define USB_CFG_INTR_POLL_INTERVAL 20 /* If you compile a version with endpoint 1 (interrupt-in), this is the poll * interval. The value is in milliseconds and must not be less than 10 ms for * low speed devices. @@ -125,12 +125,12 @@ section at the end of this file). * The value is in milliamperes. [It will be divided by two since USB * communicates power requirements in units of 2 mA.] */ -#define USB_CFG_IMPLEMENT_FN_WRITE 0 +#define USB_CFG_IMPLEMENT_FN_WRITE 1 /* Set this to 1 if you want usbFunctionWrite() to be called for control-out * transfers. Set it to 0 if you don't need it and want to save a couple of * bytes. */ -#define USB_CFG_IMPLEMENT_FN_READ 0 +#define USB_CFG_IMPLEMENT_FN_READ 1 /* Set this to 1 if you need to send control replies which are generated * "on the fly" when usbFunctionRead() is called. If you only want to send * data from a static buffer, set it to 0 and return the data from @@ -229,7 +229,7 @@ section at the end of this file). * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand * the implications! */ -#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */ +#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* = 0x05dc = 1500 */ /* This is the ID of the product, low byte first. It is interpreted in the * scope of the vendor ID. If you have registered your own VID with usb.org * or if you have licensed a PID from somebody else, define it here. Otherwise @@ -243,8 +243,8 @@ section at the end of this file). #define USB_CFG_DEVICE_VERSION 0x00, 0x01 /* Version number of the device: Minor number first, then major number. */ -#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' -#define USB_CFG_VENDOR_NAME_LEN 8 +#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 'd', 'c', 't', 't', 'e', 'c', 'h', '.', 'c', 'o', 'm' +#define USB_CFG_VENDOR_NAME_LEN 15 /* These two values define the vendor name returned by the USB device. The name * must be given as a list of characters under single quotes. The characters * are interpreted as Unicode (UTF-16) entities. @@ -253,8 +253,8 @@ section at the end of this file). * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for * details. */ -#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' -#define USB_CFG_DEVICE_NAME_LEN 8 +#define USB_CFG_DEVICE_NAME 'U', 'S', 'B', 'R', 'e', 'l', 'a', 'y', '4' +#define USB_CFG_DEVICE_NAME_LEN 9 /* Same as above for the device name. If you don't want a device name, undefine * the macros. See the file USB-IDs-for-free.txt before you assign a name if * you use a shared VID/PID. @@ -268,12 +268,12 @@ section at the end of this file). * to fine tune control over USB descriptors such as the string descriptor * for the serial number. */ -#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */ +#define USB_CFG_DEVICE_CLASS 0 #define USB_CFG_DEVICE_SUBCLASS 0 /* See USB specification if you want to conform to an existing device class. * Class 0xff is "vendor specific". */ -#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */ +#define USB_CFG_INTERFACE_CLASS 3 /* HID */ #define USB_CFG_INTERFACE_SUBCLASS 0 #define USB_CFG_INTERFACE_PROTOCOL 0 /* See USB specification if you want to conform to an existing device class or @@ -281,7 +281,7 @@ section at the end of this file). * HID class is 3, no subclass and protocol required (but may be useful!) * CDC class is 2, use subclass 2 and protocol 1 for ACM */ -/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22 /* Define this to the length of the HID report descriptor, if you implement * an HID device. Otherwise don't define it or define it to 0. * If you use this define, you must add a PROGMEM character array named @@ -346,8 +346,6 @@ section at the end of this file). * }; */ -#define CONFIG_DESCRIPTOR_SIZE 41 - #define USB_CFG_DESCR_PROPS_DEVICE 0 #define USB_CFG_DESCR_PROPS_CONFIGURATION 0 #define USB_CFG_DESCR_PROPS_STRINGS 0 -- 2.39.5