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"
38 keyboard_report_t keyboard_report;
40 uint8_t temp_report[8];
41 bool have_temp_int = false;
46 volatile static uchar ledstate = 0xff;
47 static uchar idlerate;
49 /* We populate this with the 1-wire device ROM ID when we get it */
51 USB_STRING_DESCRIPTOR_HEADER(16),
52 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
53 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
56 #define USB_INDEX_KEYBOARD 0
57 #define USB_INDEX_MOUSE 1
59 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
60 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
61 0x09, 0x06, // USAGE (Keyboard)
62 0xa1, 0x01, // COLLECTION (Application)
63 0x85, 0x01, // REPORT_ID (1)
64 0x05, 0x07, // USAGE_PAGE (Keyboard) (Key Codes)
65 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224)
66 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231)
67 0x15, 0x00, // LOGICAL_MINIMUM (0)
68 0x25, 0x01, // LOGICAL_MAXIMUM (1)
69 0x75, 0x01, // REPORT_SIZE (1)
70 0x95, 0x08, // REPORT_COUNT (8)
71 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
72 0x95, 0x01, // REPORT_COUNT (1)
73 0x75, 0x08, // REPORT_SIZE (8)
74 0x81, 0x01, // INPUT (Cnst,Arr,Abs) ; Reserved byte
75 0x95, 0x03, // REPORT_COUNT (3)
76 0x75, 0x01, // REPORT_SIZE (1)
77 0x05, 0x08, // USAGE_PAGE (LEDs)
78 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
79 0x29, 0x03, // USAGE_MAXIMUM (Scroll Lock)
80 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED report
81 0x95, 0x05, // REPORT_COUNT (5)
82 0x75, 0x01, // REPORT_SIZE (1)
83 0x91, 0x01, // OUTPUT (Cnst,Arr,Abs)
84 0x95, 0x05, // REPORT_COUNT (5)
85 0x75, 0x08, // REPORT_SIZE (8)
86 0x15, 0x00, // LOGICAL_MINIMUM (0)
87 0x25, 0xff, // LOGICAL_MAXIMUM (255)
88 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes)
89 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event))(0)
90 0x29, 0xff, // USAGE_MAXIMUM (Keyboard Application)(null)
91 0x81, 0x00, // INPUT (Data,Ary,Abs)
92 0xc0 // END_COLLECTION
95 PROGMEM const char usbHidMouseReportDescriptor[] = {
96 0x06, 0x00, 0xff, // USAGE_PAGE (65280)
97 0x09, 0x01, // USAGE (1)
98 0xa1, 0x01, // COLLECTION (Application)
99 0x09, 0x01, // USAGE (1)
100 0x15, 0x00, // LOGICAL_MINIMUM (0)
101 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
102 0x75, 0x08, // REPORT_SIZE (8)
103 0x95, 0x08, // REPORT_COUNT (8)
104 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte
105 0x09, 0x01, // USAGE (Pointer)
106 0x95, 0x08, // REPORT_COUNT (8)
107 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; Modifier byte
108 0x05, 0x0c, // USAGE_PAGE (Consumer)
109 0x09, 0x00, // USAGE (0)
110 0x15, 0x80, // LOGICAL_MINIMUM (128)
111 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
112 0x75, 0x08, // REPORT_SIZE (8)
113 0x95, 0x08, // REPORT_COUNT (8)
114 0xb1, 0x02, // FEATURE (Data,Var,Abs)
115 0xc0 // END_COLLECTION
118 /* USB configuration descriptor */
119 PROGMEM const char usbDescriptorConfiguration[] = {
120 9, /* sizeof(usbDescriptorConfiguration) */
121 USBDESCR_CONFIG, /* descriptor type */
122 /* total length of data returned (including inlined descriptors) */
123 18 + 9 + 9 + 7 + 9 + 7, 0,
124 2, /* number of interfaces in this cfg */
125 1, /* index of this configuration */
126 0, /* configuration name string index */
127 (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */
128 USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */
130 /* Keyboard interface descriptor follows inline: */
132 /* sizeof(usbDescrInterface): length of descriptor in bytes */
134 USBDESCR_INTERFACE, /* descriptor type */
135 0, /* index of this interface */
136 0, /* alternate setting for this interface */
137 /* endpoints excl 0: number of endpoint descriptors to follow */
139 3, /* Interface class: HID */
140 1, /* SubClass: Boot Interface */
141 1, /* Interface protocol: Keyboard */
142 0, /* string index for interface */
143 9, /* sizeof(usbDescrHID) */
144 USBDESCR_HID, /* descriptor type: HID */
145 0x10, 0x01, /* BCD representation of HID version */
146 0x00, /* target country code */
147 /* number of HID Report (or other HID class) Descriptors to follow */
149 0x22, /* descriptor type: report */
150 sizeof(usbHidKeyboardReportDescriptor),
151 0, /* total length of report descriptor */
153 /* endpoint descriptor for endpoint 1 */
154 7, /* sizeof(usbDescrEndpoint) */
155 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
156 (char)0x81, /* IN endpoint number 1 */
157 0x03, /* attrib: Interrupt endpoint */
158 8, 0, /* maximum packet size */
159 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
161 /* Mouse interface descriptor follows inline: */
163 /* sizeof(usbDescrInterface): length of descriptor in bytes */
165 USBDESCR_INTERFACE, /* descriptor type */
166 1, /* index of this interface */
167 0, /* alternate setting for this interface */
168 /* endpoints excl 0: number of endpoint descriptors to follow */
170 3, /* Interface class: HID */
171 1, /* SubClass: Boot Interface */
172 2, /* Interface protocol: Mouse */
173 0, /* string index for interface */
174 9, /* sizeof(usbDescrHID) */
175 USBDESCR_HID, /* descriptor type: HID */
176 0x10, 0x01, /* BCD representation of HID version */
177 0x00, /* target country code */
178 /* number of HID Report (or other HID class) Descriptors to follow */
180 0x22, /* descriptor type: report */
181 /* total length of report descriptor */
182 sizeof(usbHidMouseReportDescriptor), 0,
184 /* endpoint descriptor for endpoint 2 */
185 7, /* sizeof(usbDescrEndpoint) */
186 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
187 (char)0x82, /* IN endpoint number 2 */
188 0x03, /* attrib: Interrupt endpoint */
189 8, 0, /* maximum packet size */
190 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
193 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
195 if (rq->wValue.bytes[1] == USBDESCR_STRING &&
196 rq->wValue.bytes[0] == 3) {
197 usbMsgPtr = (usbMsgPtr_t) serno_str;
198 return sizeof(serno_str);
199 } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
200 if (rq->wIndex.word == USB_INDEX_MOUSE) {
202 usbMsgPtr = (usbMsgPtr_t)
203 &usbDescriptorConfiguration[43];
206 usbMsgPtr = (usbMsgPtr_t)
207 &usbDescriptorConfiguration[18];
209 } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
210 if (rq->wIndex.word == USB_INDEX_MOUSE) {
211 usbMsgPtr = (usbMsgPtr_t)
212 &usbHidMouseReportDescriptor[0];
213 return sizeof(usbHidMouseReportDescriptor);
215 usbMsgPtr = (usbMsgPtr_t)
216 &usbHidKeyboardReportDescriptor[0];
217 return sizeof(usbHidKeyboardReportDescriptor);
224 usbMsgLen_t usbFunctionSetup(uchar data[8])
226 usbRequest_t *rq = (void *) data;
228 if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
229 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
230 /* No keys pressed, no mouse event */
231 usbMsgPtr = (void *) &keyboard_report;
232 keyboard_report.modifier = 0;
233 keyboard_report.reserved = 0;
234 keyboard_report.keycode[0] = 0;
235 return sizeof(keyboard_report);
236 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
237 if (rq->wIndex.word == USB_INDEX_MOUSE) {
238 return (rq->wLength.word == 8) ?
242 return (rq->wLength.word == 1) ?
245 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
246 usbMsgPtr = &idlerate;
248 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
249 idlerate = rq->wValue.bytes[1];
256 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
259 if (data[0] != ledstate) {
262 if (ledstate & CAPS_LOCK) {
263 PORTB |= 1 << PB1; // LED on
265 PORTB &= ~(1 << PB1); // LED off
269 } else if (len == 8) {
270 /* Silently consume if unexpected*/
273 if ((data[4] | data[5] | data[6] | data[7]) != 0)
276 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
277 /* Temperature query */
278 have_temp_int = true;
279 temp_report[0] = 0x80;
282 temp_report[2] = 0xF;
283 temp_report[3] = 0x0;
285 } else if (data[1] == 0x82 && data[2] == 0x77 &&
287 /* Initialisation Query #1 */
288 have_temp_int = true;
289 temp_report[0] = 0x82;
292 } else if (data[1] == 0x86 && data[2] == 0xff &&
294 /* Initialisation Query #2 */
295 have_temp_int = true;
296 memcpy(temp_report, "TEMPerF1", 8);
312 usbDeviceDisconnect();
322 /* PB1 as output for LED */
325 sei(); /* We're ready to go; enable interrupts */
327 keyboard_report.modifier = 0;
328 keyboard_report.reserved = 0;
329 keyboard_report.keycode[0] = 0;
335 if (have_temp_int && usbInterruptIsReady3()) {
336 usbSetInterrupt3((void *) temp_report,
337 sizeof(temp_report));
338 if (temp_report[0] == 'T') {
339 /* Hack up second response for 0x86 query */
340 memcpy(temp_report, ".2", 2);
342 have_temp_int = false;