2 * Basic firmware to emulate a USB TEMPer device (which uses an FM75 I2C
3 * sensor internally) using a Digispark + a DS18B20 1-wire sensor.
5 * Copyright 2018 Jonathan McDowell <noodles@earth.li>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <avr/eeprom.h>
24 #include <avr/interrupt.h>
27 #include <util/delay.h>
29 #include <avr/pgmspace.h>
31 #include "libs-device/osccal.h"
40 keyboard_report_t keyboard_report;
42 uint8_t temp_report[8];
43 bool have_temp_int = false;
48 volatile static uchar ledstate = 0xff;
49 static uchar idlerate;
51 /* We populate this with the 1-wire device ROM ID when we get it */
53 USB_STRING_DESCRIPTOR_HEADER(16),
54 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
55 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
58 #define USB_INDEX_KEYBOARD 0
59 #define USB_INDEX_MOUSE 1
61 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
62 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
63 0x09, 0x06, // USAGE (Keyboard)
64 0xa1, 0x01, // COLLECTION (Application)
65 0x85, 0x01, // REPORT_ID (1)
66 0x05, 0x07, // USAGE_PAGE (Keyboard) (Key Codes)
67 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224)
68 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231)
69 0x15, 0x00, // LOGICAL_MINIMUM (0)
70 0x25, 0x01, // LOGICAL_MAXIMUM (1)
71 0x75, 0x01, // REPORT_SIZE (1)
72 0x95, 0x08, // REPORT_COUNT (8)
73 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
74 0x95, 0x01, // REPORT_COUNT (1)
75 0x75, 0x08, // REPORT_SIZE (8)
76 0x81, 0x01, // INPUT (Cnst,Arr,Abs) ; Reserved byte
77 0x95, 0x03, // REPORT_COUNT (3)
78 0x75, 0x01, // REPORT_SIZE (1)
79 0x05, 0x08, // USAGE_PAGE (LEDs)
80 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
81 0x29, 0x03, // USAGE_MAXIMUM (Scroll Lock)
82 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED report
83 0x95, 0x05, // REPORT_COUNT (5)
84 0x75, 0x01, // REPORT_SIZE (1)
85 0x91, 0x01, // OUTPUT (Cnst,Arr,Abs)
86 0x95, 0x05, // REPORT_COUNT (5)
87 0x75, 0x08, // REPORT_SIZE (8)
88 0x15, 0x00, // LOGICAL_MINIMUM (0)
89 0x25, 0xff, // LOGICAL_MAXIMUM (255)
90 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes)
91 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event))(0)
92 0x29, 0xff, // USAGE_MAXIMUM (Keyboard Application)(null)
93 0x81, 0x00, // INPUT (Data,Ary,Abs)
94 0xc0 // END_COLLECTION
97 PROGMEM const char usbHidMouseReportDescriptor[] = {
98 0x06, 0x00, 0xff, // USAGE_PAGE (65280)
99 0x09, 0x01, // USAGE (1)
100 0xa1, 0x01, // COLLECTION (Application)
101 0x09, 0x01, // USAGE (1)
102 0x15, 0x00, // LOGICAL_MINIMUM (0)
103 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
104 0x75, 0x08, // REPORT_SIZE (8)
105 0x95, 0x08, // REPORT_COUNT (8)
106 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
107 0x09, 0x01, // USAGE (Pointer)
108 0x95, 0x08, // REPORT_COUNT (8)
109 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; Modifier byte
110 0x05, 0x0c, // USAGE_PAGE (Consumer)
111 0x09, 0x00, // USAGE (0)
112 0x15, 0x80, // LOGICAL_MINIMUM (128)
113 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
114 0x75, 0x08, // REPORT_SIZE (8)
115 0x95, 0x08, // REPORT_COUNT (8)
116 0xb1, 0x02, // FEATURE (Data,Var,Abs)
117 0xc0 // END_COLLECTION
120 /* USB configuration descriptor */
121 PROGMEM const char usbDescriptorConfiguration[] = {
122 9, /* sizeof(usbDescriptorConfiguration) */
123 USBDESCR_CONFIG, /* descriptor type */
124 /* total length of data returned (including inlined descriptors) */
125 18 + 9 + 9 + 7 + 9 + 7, 0,
126 2, /* number of interfaces in this cfg */
127 1, /* index of this configuration */
128 0, /* configuration name string index */
129 (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */
130 USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */
132 /* Keyboard interface descriptor follows inline: */
134 /* sizeof(usbDescrInterface): length of descriptor in bytes */
136 USBDESCR_INTERFACE, /* descriptor type */
137 0, /* index of this interface */
138 0, /* alternate setting for this interface */
139 /* endpoints excl 0: number of endpoint descriptors to follow */
141 3, /* Interface class: HID */
142 1, /* SubClass: Boot Interface */
143 1, /* Interface protocol: Keyboard */
144 0, /* string index for interface */
145 9, /* sizeof(usbDescrHID) */
146 USBDESCR_HID, /* descriptor type: HID */
147 0x10, 0x01, /* BCD representation of HID version */
148 0x00, /* target country code */
149 /* number of HID Report (or other HID class) Descriptors to follow */
151 0x22, /* descriptor type: report */
152 sizeof(usbHidKeyboardReportDescriptor),
153 0, /* total length of report descriptor */
155 /* endpoint descriptor for endpoint 1 */
156 7, /* sizeof(usbDescrEndpoint) */
157 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
158 (char)0x81, /* IN endpoint number 1 */
159 0x03, /* attrib: Interrupt endpoint */
160 8, 0, /* maximum packet size */
161 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
163 /* Mouse interface descriptor follows inline: */
165 /* sizeof(usbDescrInterface): length of descriptor in bytes */
167 USBDESCR_INTERFACE, /* descriptor type */
168 1, /* index of this interface */
169 0, /* alternate setting for this interface */
170 /* endpoints excl 0: number of endpoint descriptors to follow */
172 3, /* Interface class: HID */
173 1, /* SubClass: Boot Interface */
174 2, /* Interface protocol: Mouse */
175 0, /* string index for interface */
176 9, /* sizeof(usbDescrHID) */
177 USBDESCR_HID, /* descriptor type: HID */
178 0x10, 0x01, /* BCD representation of HID version */
179 0x00, /* target country code */
180 /* number of HID Report (or other HID class) Descriptors to follow */
182 0x22, /* descriptor type: report */
183 /* total length of report descriptor */
184 sizeof(usbHidMouseReportDescriptor), 0,
186 /* endpoint descriptor for endpoint 2 */
187 7, /* sizeof(usbDescrEndpoint) */
188 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
189 (char)0x82, /* IN endpoint number 2 */
190 0x03, /* attrib: Interrupt endpoint */
191 8, 0, /* maximum packet size */
192 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
195 inline char hexdigit(unsigned int i)
197 return (i < 10) ? ('0' + i) : ('A' - 10 + i);
200 /* Look for a 1-Wire device and use its ROMID to set the serial ID */
201 void set_serial(void)
210 w1_write(0x33); /* READ ROM */
213 for (i = 0; i < 8; i++) {
214 serno_str[i * 2 + 1] = hexdigit(buf[i] >> 4);
215 serno_str[i * 2 + 2] = hexdigit(buf[i] & 0xF);
219 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
221 if (rq->wValue.bytes[1] == USBDESCR_STRING &&
222 rq->wValue.bytes[0] == 3) {
223 usbMsgPtr = (usbMsgPtr_t) serno_str;
224 return sizeof(serno_str);
225 } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
226 if (rq->wIndex.word == USB_INDEX_MOUSE) {
228 usbMsgPtr = (usbMsgPtr_t)
229 &usbDescriptorConfiguration[43];
232 usbMsgPtr = (usbMsgPtr_t)
233 &usbDescriptorConfiguration[18];
235 } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
236 if (rq->wIndex.word == USB_INDEX_MOUSE) {
237 usbMsgPtr = (usbMsgPtr_t)
238 &usbHidMouseReportDescriptor[0];
239 return sizeof(usbHidMouseReportDescriptor);
241 usbMsgPtr = (usbMsgPtr_t)
242 &usbHidKeyboardReportDescriptor[0];
243 return sizeof(usbHidKeyboardReportDescriptor);
250 usbMsgLen_t usbFunctionSetup(uchar data[8])
252 usbRequest_t *rq = (void *) data;
254 if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
255 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
256 /* No keys pressed, no mouse event */
257 usbMsgPtr = (void *) &keyboard_report;
258 keyboard_report.modifier = 0;
259 keyboard_report.reserved = 0;
260 keyboard_report.keycode[0] = 0;
261 return sizeof(keyboard_report);
262 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
263 if (rq->wIndex.word == USB_INDEX_MOUSE) {
264 return (rq->wLength.word == 8) ?
268 return (rq->wLength.word == 1) ?
271 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
272 usbMsgPtr = &idlerate;
274 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
275 idlerate = rq->wValue.bytes[1];
282 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
285 if (data[0] != ledstate) {
288 if (ledstate & CAPS_LOCK) {
289 PORTB |= 1 << PB1; // LED on
291 PORTB &= ~(1 << PB1); // LED off
295 } else if (len == 8) {
296 /* Silently consume if unexpected*/
299 if ((data[4] | data[5] | data[6] | data[7]) != 0)
302 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
303 /* Temperature query */
304 have_temp_int = true;
305 temp_report[0] = 0x80;
308 temp_report[2] = 0xF;
309 temp_report[3] = 0x0;
311 } else if (data[1] == 0x82 && data[2] == 0x77 &&
313 /* Initialisation Query #1 */
314 have_temp_int = true;
315 temp_report[0] = 0x82;
318 } else if (data[1] == 0x86 && data[2] == 0xff &&
320 /* Initialisation Query #2 */
321 have_temp_int = true;
322 memcpy(temp_report, "TEMPerF1", 8);
341 usbDeviceDisconnect();
351 /* PB1 as output for LED */
354 sei(); /* We're ready to go; enable interrupts */
356 keyboard_report.modifier = 0;
357 keyboard_report.reserved = 0;
358 keyboard_report.keycode[0] = 0;
364 if (have_temp_int && usbInterruptIsReady3()) {
365 usbSetInterrupt3((void *) temp_report,
366 sizeof(temp_report));
367 if (temp_report[0] == 'T') {
368 /* Hack up second response for 0x86 query */
369 memcpy(temp_report, ".2", 2);
371 have_temp_int = false;