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"
41 keyboard_report_t keyboard_report;
43 uint8_t temp_state = 0;
44 uint16_t last_temp = 0xFFFD;
45 #define TEMP_INTERVAL (10 * 1000)
47 uint8_t temp_report[8];
48 bool have_temp_int = false;
53 volatile static uchar ledstate = 0xff;
54 static uchar idlerate;
56 /* We populate this with the 1-wire device ROM ID when we get it */
58 USB_STRING_DESCRIPTOR_HEADER(16),
59 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
60 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
63 #define USB_INDEX_KEYBOARD 0
64 #define USB_INDEX_MOUSE 1
66 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
67 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
68 0x09, 0x06, // USAGE (Keyboard)
69 0xa1, 0x01, // COLLECTION (Application)
70 0x85, 0x01, // REPORT_ID (1)
71 0x05, 0x07, // USAGE_PAGE (Keyboard) (Key Codes)
72 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224)
73 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231)
74 0x15, 0x00, // LOGICAL_MINIMUM (0)
75 0x25, 0x01, // LOGICAL_MAXIMUM (1)
76 0x75, 0x01, // REPORT_SIZE (1)
77 0x95, 0x08, // REPORT_COUNT (8)
78 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
79 0x95, 0x01, // REPORT_COUNT (1)
80 0x75, 0x08, // REPORT_SIZE (8)
81 0x81, 0x01, // INPUT (Cnst,Arr,Abs) ; Reserved byte
82 0x95, 0x03, // REPORT_COUNT (3)
83 0x75, 0x01, // REPORT_SIZE (1)
84 0x05, 0x08, // USAGE_PAGE (LEDs)
85 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
86 0x29, 0x03, // USAGE_MAXIMUM (Scroll Lock)
87 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED report
88 0x95, 0x05, // REPORT_COUNT (5)
89 0x75, 0x01, // REPORT_SIZE (1)
90 0x91, 0x01, // OUTPUT (Cnst,Arr,Abs)
91 0x95, 0x05, // REPORT_COUNT (5)
92 0x75, 0x08, // REPORT_SIZE (8)
93 0x15, 0x00, // LOGICAL_MINIMUM (0)
94 0x25, 0xff, // LOGICAL_MAXIMUM (255)
95 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes)
96 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event))(0)
97 0x29, 0xff, // USAGE_MAXIMUM (Keyboard Application)(null)
98 0x81, 0x00, // INPUT (Data,Ary,Abs)
99 0xc0 // END_COLLECTION
102 PROGMEM const char usbHidMouseReportDescriptor[] = {
103 0x06, 0x00, 0xff, // USAGE_PAGE (65280)
104 0x09, 0x01, // USAGE (1)
105 0xa1, 0x01, // COLLECTION (Application)
106 0x09, 0x01, // USAGE (1)
107 0x15, 0x00, // LOGICAL_MINIMUM (0)
108 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
109 0x75, 0x08, // REPORT_SIZE (8)
110 0x95, 0x08, // REPORT_COUNT (8)
111 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
112 0x09, 0x01, // USAGE (Pointer)
113 0x95, 0x08, // REPORT_COUNT (8)
114 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; Modifier byte
115 0x05, 0x0c, // USAGE_PAGE (Consumer)
116 0x09, 0x00, // USAGE (0)
117 0x15, 0x80, // LOGICAL_MINIMUM (128)
118 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
119 0x75, 0x08, // REPORT_SIZE (8)
120 0x95, 0x08, // REPORT_COUNT (8)
121 0xb1, 0x02, // FEATURE (Data,Var,Abs)
122 0xc0 // END_COLLECTION
125 /* USB configuration descriptor */
126 PROGMEM const char usbDescriptorConfiguration[] = {
127 9, /* sizeof(usbDescriptorConfiguration) */
128 USBDESCR_CONFIG, /* descriptor type */
129 /* total length of data returned (including inlined descriptors) */
130 18 + 9 + 9 + 7 + 9 + 7, 0,
131 2, /* number of interfaces in this cfg */
132 1, /* index of this configuration */
133 0, /* configuration name string index */
134 (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */
135 USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */
137 /* Keyboard interface descriptor follows inline: */
139 /* sizeof(usbDescrInterface): length of descriptor in bytes */
141 USBDESCR_INTERFACE, /* descriptor type */
142 0, /* index of this interface */
143 0, /* alternate setting for this interface */
144 /* endpoints excl 0: number of endpoint descriptors to follow */
146 3, /* Interface class: HID */
147 1, /* SubClass: Boot Interface */
148 1, /* Interface protocol: Keyboard */
149 0, /* string index for interface */
150 9, /* sizeof(usbDescrHID) */
151 USBDESCR_HID, /* descriptor type: HID */
152 0x10, 0x01, /* BCD representation of HID version */
153 0x00, /* target country code */
154 /* number of HID Report (or other HID class) Descriptors to follow */
156 0x22, /* descriptor type: report */
157 sizeof(usbHidKeyboardReportDescriptor),
158 0, /* total length of report descriptor */
160 /* endpoint descriptor for endpoint 1 */
161 7, /* sizeof(usbDescrEndpoint) */
162 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
163 (char)0x81, /* IN endpoint number 1 */
164 0x03, /* attrib: Interrupt endpoint */
165 8, 0, /* maximum packet size */
166 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
168 /* Mouse interface descriptor follows inline: */
170 /* sizeof(usbDescrInterface): length of descriptor in bytes */
172 USBDESCR_INTERFACE, /* descriptor type */
173 1, /* index of this interface */
174 0, /* alternate setting for this interface */
175 /* endpoints excl 0: number of endpoint descriptors to follow */
177 3, /* Interface class: HID */
178 1, /* SubClass: Boot Interface */
179 2, /* Interface protocol: Mouse */
180 0, /* string index for interface */
181 9, /* sizeof(usbDescrHID) */
182 USBDESCR_HID, /* descriptor type: HID */
183 0x10, 0x01, /* BCD representation of HID version */
184 0x00, /* target country code */
185 /* number of HID Report (or other HID class) Descriptors to follow */
187 0x22, /* descriptor type: report */
188 /* total length of report descriptor */
189 sizeof(usbHidMouseReportDescriptor), 0,
191 /* endpoint descriptor for endpoint 2 */
192 7, /* sizeof(usbDescrEndpoint) */
193 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
194 (char)0x82, /* IN endpoint number 2 */
195 0x03, /* attrib: Interrupt endpoint */
196 8, 0, /* maximum packet size */
197 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
200 inline char hexdigit(unsigned int i)
202 return (i < 10) ? ('0' + i) : ('A' - 10 + i);
205 /* Look for a 1-Wire device and use its ROMID to set the serial ID */
206 void set_serial(void)
215 w1_write(0x33); /* READ ROM */
218 for (i = 0; i < 8; i++) {
219 serno_str[i * 2 + 1] = hexdigit(buf[i] >> 4);
220 serno_str[i * 2 + 2] = hexdigit(buf[i] & 0xF);
224 uint16_t read_temp(void)
234 w1_write(0xCC); /* SKIP ROM */
235 w1_write(0x44); /* Convert T */
239 } while (buf[0] != 0xFF);
246 w1_write(0xCC); /* SKIP ROM */
247 w1_write(0xBE); /* Read Scratchpad */
251 return buf[2] << 8 | buf[1];
254 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
256 if (rq->wValue.bytes[1] == USBDESCR_STRING &&
257 rq->wValue.bytes[0] == 3) {
258 usbMsgPtr = (usbMsgPtr_t) serno_str;
259 return sizeof(serno_str);
260 } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
261 if (rq->wIndex.word == USB_INDEX_MOUSE) {
263 usbMsgPtr = (usbMsgPtr_t)
264 &usbDescriptorConfiguration[43];
267 usbMsgPtr = (usbMsgPtr_t)
268 &usbDescriptorConfiguration[18];
270 } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
271 if (rq->wIndex.word == USB_INDEX_MOUSE) {
272 usbMsgPtr = (usbMsgPtr_t)
273 &usbHidMouseReportDescriptor[0];
274 return sizeof(usbHidMouseReportDescriptor);
276 usbMsgPtr = (usbMsgPtr_t)
277 &usbHidKeyboardReportDescriptor[0];
278 return sizeof(usbHidKeyboardReportDescriptor);
285 usbMsgLen_t usbFunctionSetup(uchar data[8])
287 usbRequest_t *rq = (void *) data;
289 if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
290 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
291 /* No keys pressed, no mouse event */
292 usbMsgPtr = (void *) &keyboard_report;
293 keyboard_report.modifier = 0;
294 keyboard_report.reserved = 0;
295 keyboard_report.keycode[0] = 0;
296 return sizeof(keyboard_report);
297 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
298 if (rq->wIndex.word == USB_INDEX_MOUSE) {
299 return (rq->wLength.word == 8) ?
303 return (rq->wLength.word == 1) ?
306 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
307 usbMsgPtr = &idlerate;
309 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
310 idlerate = rq->wValue.bytes[1];
317 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
320 PORTB |= 1 << PB1; // LED on
321 if (data[0] != ledstate) {
324 if (ledstate & CAPS_LOCK) {
325 PORTB |= 1 << PB1; // LED on
327 PORTB &= ~(1 << PB1); // LED off
331 } else if (len == 8) {
332 /* Silently consume if unexpected*/
335 if ((data[4] | data[5] | data[6] | data[7]) != 0)
338 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
339 /* Temperature query */
340 memset(temp_report, 0, 8);
341 have_temp_int = true;
342 temp_report[0] = 0x80;
344 temp_report[2] = last_temp >> 8;
345 temp_report[3] = last_temp & 0xFF;
346 } else if (data[1] == 0x82 && data[2] == 0x77 &&
348 /* Initialisation Query #1 */
349 have_temp_int = true;
350 temp_report[0] = 0x82;
353 } else if (data[1] == 0x86 && data[2] == 0xff &&
355 /* Initialisation Query #2 */
356 have_temp_int = true;
357 memcpy(temp_report, "TEMPerF1", 8);
366 void hadUsbReset(void)
368 /* Reset our state machine back to having nothing to send */
370 have_temp_int = false;
377 unsigned long last_temp_time = 0;
385 usbDeviceDisconnect();
395 /* PB1 as output for LED */
398 sei(); /* We're ready to go; enable interrupts */
400 keyboard_report.modifier = 0;
401 keyboard_report.reserved = 0;
402 keyboard_report.keycode[0] = 0;
408 if (have_temp_int && usbInterruptIsReady3()) {
409 usbSetInterrupt3((void *) temp_report,
410 sizeof(temp_report));
411 if (temp_report[0] == 'T') {
412 /* Hack up second response for 0x86 query */
413 memcpy(temp_report, ".2", 2);
415 have_temp_int = false;
419 if (temp_state == 1) {
425 } else if (temp_state == 2) {
426 w1_write(0xCC); /* SKIP ROM */
428 } else if (temp_state == 3) {
429 w1_write(0x44); /* Convert T */
431 } else if (temp_state == 4) {
434 } else if (temp_state == 5) {
440 } else if (temp_state == 6) {
441 w1_write(0xCC); /* SKIP ROM */
443 } else if (temp_state == 7) {
444 w1_write(0xBE); /* Read Scratchpad */
446 } else if (temp_state > 7 && temp_state < 17) {
447 buf[temp_state - 8] = w1_read_byte();
449 } else if (temp_state == 17) {
450 last_temp = buf[1] << 12 | buf[0] << 4;
452 last_temp_time = millis();
455 if (temp_state == 0 &&
456 (millis() - last_temp_time) > TEMP_INTERVAL) {