]> the.earth.li Git - temper-clone.git/blob - main.c
Add initial temperature reading via DS18B20 device
[temper-clone.git] / main.c
1 /*
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.
4  *
5  * Copyright 2018 Jonathan McDowell <noodles@earth.li>
6  *
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.
11  *
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.
16  *
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/>.
19  */
20 #include <stdbool.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <avr/eeprom.h>
24 #include <avr/interrupt.h>
25 #include <avr/io.h>
26 #include <avr/wdt.h>
27 #include <util/delay.h>
28
29 #include <avr/pgmspace.h>
30 #include "usbdrv.h"
31 #include "libs-device/osccal.h"
32
33 #include "w1.h"
34
35 typedef struct {
36         uint8_t modifier;
37         uint8_t reserved;
38         uint8_t keycode[6];
39 } keyboard_report_t;
40 keyboard_report_t keyboard_report;
41
42 uint8_t temp_state = 0;
43
44 uint8_t temp_report[8];
45 bool have_temp_int = false;
46
47 #define NUM_LOCK        1
48 #define CAPS_LOCK       2
49 #define SCROLL_LOCK     4
50 volatile static uchar ledstate = 0xff;
51 static uchar idlerate;
52
53 /* We populate this with the 1-wire device ROM ID when we get it */
54 int serno_str[] = {
55         USB_STRING_DESCRIPTOR_HEADER(16),
56         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
57         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
58 };
59
60 #define USB_INDEX_KEYBOARD      0
61 #define USB_INDEX_MOUSE         1
62
63 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
64         0x05, 0x01,             // USAGE_PAGE (Generic Desktop)
65         0x09, 0x06,             // USAGE (Keyboard)
66         0xa1, 0x01,             // COLLECTION (Application)
67         0x85, 0x01,             //   REPORT_ID (1)
68         0x05, 0x07,             //   USAGE_PAGE (Keyboard) (Key Codes)
69         0x19, 0xe0,             //   USAGE_MINIMUM (Keyboard LeftControl)(224)
70         0x29, 0xe7,             //   USAGE_MAXIMUM (Keyboard Right GUI)(231)
71         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
72         0x25, 0x01,             //   LOGICAL_MAXIMUM (1)
73         0x75, 0x01,             //   REPORT_SIZE (1)
74         0x95, 0x08,             //   REPORT_COUNT (8)
75         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
76         0x95, 0x01,             //   REPORT_COUNT (1)
77         0x75, 0x08,             //   REPORT_SIZE (8)
78         0x81, 0x01,             //   INPUT (Cnst,Arr,Abs) ; Reserved byte
79         0x95, 0x03,             //   REPORT_COUNT (3)
80         0x75, 0x01,             //   REPORT_SIZE (1)
81         0x05, 0x08,             //   USAGE_PAGE (LEDs)
82         0x19, 0x01,             //   USAGE_MINIMUM (Num Lock)
83         0x29, 0x03,             //   USAGE_MAXIMUM (Scroll Lock)
84         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; LED report
85         0x95, 0x05,             //   REPORT_COUNT (5)
86         0x75, 0x01,             //   REPORT_SIZE (1)
87         0x91, 0x01,             //   OUTPUT (Cnst,Arr,Abs)
88         0x95, 0x05,             //   REPORT_COUNT (5)
89         0x75, 0x08,             //   REPORT_SIZE (8)
90         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
91         0x25, 0xff,             //   LOGICAL_MAXIMUM (255)
92         0x05, 0x07,             //   USAGE_PAGE (Keyboard)(Key Codes)
93         0x19, 0x00,             //   USAGE_MINIMUM (Reserved (no event))(0)
94         0x29, 0xff,             //   USAGE_MAXIMUM (Keyboard Application)(null)
95         0x81, 0x00,             //   INPUT (Data,Ary,Abs)
96         0xc0                    // END_COLLECTION
97 };
98
99 PROGMEM const char usbHidMouseReportDescriptor[] = {
100         0x06, 0x00, 0xff,       // USAGE_PAGE (65280)
101         0x09, 0x01,             // USAGE (1)
102         0xa1, 0x01,             // COLLECTION (Application)
103         0x09, 0x01,             // USAGE (1)
104         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
105         0x26, 0xff, 0x00,       //   LOGICAL_MAXIMUM (255)
106         0x75, 0x08,             //   REPORT_SIZE (8)
107         0x95, 0x08,             //   REPORT_COUNT (8)
108         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
109         0x09, 0x01,             // USAGE (Pointer)
110         0x95, 0x08,             //   REPORT_COUNT (8)
111         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; Modifier byte
112         0x05, 0x0c,             // USAGE_PAGE (Consumer)
113         0x09, 0x00,             //   USAGE (0)
114         0x15, 0x80,             //   LOGICAL_MINIMUM (128)
115         0x25, 0x7f,             //   LOGICAL_MAXIMUM (127)
116         0x75, 0x08,             //   REPORT_SIZE (8)
117         0x95, 0x08,             //   REPORT_COUNT (8)
118         0xb1, 0x02,             //   FEATURE (Data,Var,Abs)
119         0xc0                    // END_COLLECTION
120 };
121
122 /* USB configuration descriptor */
123 PROGMEM const char usbDescriptorConfiguration[] = {
124         9,                              /* sizeof(usbDescriptorConfiguration) */
125         USBDESCR_CONFIG,                /* descriptor type */
126         /* total length of data returned (including inlined descriptors) */
127         18 + 9 + 9 + 7 + 9 + 7, 0,
128         2,                              /* number of interfaces in this cfg */
129         1,                              /* index of this configuration */
130         0,                              /* configuration name string index */
131         (1 << 7) | USBATTR_REMOTEWAKE,  /* attributes */
132         USB_CFG_MAX_BUS_POWER/2,        /* max USB current in 2mA units */
133
134         /* Keyboard interface descriptor follows inline: */
135
136         /* sizeof(usbDescrInterface): length of descriptor in bytes */
137         9,
138         USBDESCR_INTERFACE,     /* descriptor type */
139         0,                      /* index of this interface */
140         0,                      /* alternate setting for this interface */
141         /* endpoints excl 0: number of endpoint descriptors to follow */
142         1,
143         3,                      /* Interface class: HID */
144         1,                      /* SubClass: Boot Interface */
145         1,                      /* Interface protocol: Keyboard */
146         0,                      /* string index for interface */
147         9,                      /* sizeof(usbDescrHID) */
148         USBDESCR_HID,           /* descriptor type: HID */
149         0x10, 0x01,             /* BCD representation of HID version */
150         0x00,                   /* target country code */
151         /* number of HID Report (or other HID class) Descriptors to follow */
152         0x01,
153         0x22,                   /* descriptor type: report */
154         sizeof(usbHidKeyboardReportDescriptor),
155         0,              /* total length of report descriptor */
156
157         /* endpoint descriptor for endpoint 1 */
158         7,                              /* sizeof(usbDescrEndpoint) */
159         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
160         (char)0x81,                     /* IN endpoint number 1 */
161         0x03,                           /* attrib: Interrupt endpoint */
162         8, 0,                           /* maximum packet size */
163         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
164
165         /* Mouse interface descriptor follows inline: */
166
167         /* sizeof(usbDescrInterface): length of descriptor in bytes */
168         9,
169         USBDESCR_INTERFACE,     /* descriptor type */
170         1,                      /* index of this interface */
171         0,                      /* alternate setting for this interface */
172         /* endpoints excl 0: number of endpoint descriptors to follow */
173         1,
174         3,                      /* Interface class: HID */
175         1,                      /* SubClass: Boot Interface */
176         2,                      /* Interface protocol: Mouse */
177         0,                      /* string index for interface */
178         9,                      /* sizeof(usbDescrHID) */
179         USBDESCR_HID,           /* descriptor type: HID */
180         0x10, 0x01,             /* BCD representation of HID version */
181         0x00,                   /* target country code */
182         /* number of HID Report (or other HID class) Descriptors to follow */
183         0x01,
184         0x22,                   /* descriptor type: report */
185         /* total length of report descriptor */
186         sizeof(usbHidMouseReportDescriptor), 0,
187
188         /* endpoint descriptor for endpoint 2 */
189         7,                              /* sizeof(usbDescrEndpoint) */
190         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
191         (char)0x82,                     /* IN endpoint number 2 */
192         0x03,                           /* attrib: Interrupt endpoint */
193         8, 0,                           /* maximum packet size */
194         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
195 };
196
197 inline char hexdigit(unsigned int i)
198 {
199         return (i < 10) ? ('0' + i) : ('A' - 10 + i);
200 }
201
202 /* Look for a 1-Wire device and use its ROMID to set the serial ID */
203 void set_serial(void)
204 {
205         uint8_t buf[8];
206         uint8_t i;
207
208         if (!w1_reset()) {
209                 return;
210         }
211
212         w1_write(0x33);         /* READ ROM */
213         w1_read(buf, 8);
214
215         for (i = 0; i < 8; i++) {
216                 serno_str[i * 2 + 1] = hexdigit(buf[i] >> 4);
217                 serno_str[i * 2 + 2] = hexdigit(buf[i] & 0xF);
218         }
219 }
220
221 uint16_t read_temp(void)
222 {
223         uint8_t buf[9];
224
225         cli();
226         if (!w1_reset()) {
227                 return 0xFFFF;
228                 sei();
229         }
230
231         w1_write(0xCC);         /* SKIP ROM */
232         w1_write(0x44);         /* Convert T */
233
234         do {
235                 w1_read(buf, 1);
236         } while (buf[0] != 0xFF);
237
238         if (!w1_reset()) {
239                 return 0xFFFF;
240                 sei();
241         }
242
243         w1_write(0xCC);         /* SKIP ROM */
244         w1_write(0xBE);         /* Read Scratchpad */
245         w1_read(buf, 9);
246         sei();
247
248         return buf[2] << 8 | buf[1];
249 }
250
251 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
252 {
253         if (rq->wValue.bytes[1] == USBDESCR_STRING &&
254                         rq->wValue.bytes[0] == 3) {
255                 usbMsgPtr = (usbMsgPtr_t) serno_str;
256                 return sizeof(serno_str);
257         } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
258                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
259                         /* "Mouse" */
260                         usbMsgPtr = (usbMsgPtr_t)
261                                 &usbDescriptorConfiguration[43];
262                 } else {
263                         /* "Keyboard" */
264                         usbMsgPtr = (usbMsgPtr_t)
265                                 &usbDescriptorConfiguration[18];
266                 }
267         } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
268                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
269                         usbMsgPtr = (usbMsgPtr_t)
270                                 &usbHidMouseReportDescriptor[0];
271                         return sizeof(usbHidMouseReportDescriptor);
272                 } else {
273                         usbMsgPtr = (usbMsgPtr_t)
274                                 &usbHidKeyboardReportDescriptor[0];
275                         return sizeof(usbHidKeyboardReportDescriptor);
276                 }
277         }
278
279         return 0;
280 }
281
282 usbMsgLen_t usbFunctionSetup(uchar data[8])
283 {
284         usbRequest_t *rq = (void *) data;
285
286         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
287                 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
288                         /* No keys pressed, no mouse event */
289                         usbMsgPtr = (void *) &keyboard_report;
290                         keyboard_report.modifier = 0;
291                         keyboard_report.reserved = 0;
292                         keyboard_report.keycode[0] = 0;
293                         return sizeof(keyboard_report);
294                 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
295                         if (rq->wIndex.word == USB_INDEX_MOUSE) {
296                                 return (rq->wLength.word == 8) ?
297                                                 USB_NO_MSG : 0;
298                         } else {
299                                 /* Keyboard */
300                                 return (rq->wLength.word == 1) ?
301                                                 USB_NO_MSG : 0;
302                         }
303                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
304                         usbMsgPtr = &idlerate;
305                         return 1;
306                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
307                         idlerate = rq->wValue.bytes[1];
308                 }
309         }
310
311         return 0;
312 }
313
314 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
315 {
316         if (len == 1) {
317                 PORTB |= 1 << PB1; // LED on
318                 if (data[0] != ledstate) {
319                         ledstate = data[0];
320
321                         if (ledstate & CAPS_LOCK) {
322                                 PORTB |= 1 << PB1; // LED on
323                         } else {
324                                 PORTB &= ~(1 << PB1); // LED off
325                         }
326                 }
327                 return 1;
328         } else if (len == 8) {
329                 /* Silently consume if unexpected*/
330                 if (data[0] != 1)
331                         return 8;
332                 if ((data[4] | data[5] | data[6] | data[7]) != 0)
333                         return 8;
334
335                 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
336                         /* Temperature query */
337                         memset(temp_report, 0, 8);
338                         temp_state = 1;
339                         temp_report[0] = 0x80;
340                         temp_report[1] = 2;
341                 } else if (data[1] == 0x82 && data[2] == 0x77 &&
342                                 data[3] == 1) {
343                         /* Initialisation Query #1 */
344                         have_temp_int = true;
345                         temp_report[0] = 0x82;
346                         temp_report[1] = 1;
347                         temp_report[2] = 0;
348                 } else if (data[1] == 0x86 && data[2] == 0xff &&
349                                 data[3] == 1) {
350                         /* Initialisation Query #2 */
351                         have_temp_int = true;
352                         memcpy(temp_report, "TEMPerF1", 8);
353                 }
354
355                 return 8;
356         }
357
358         return 0;
359 }
360
361 int main(void)
362 {
363         unsigned char i;
364         uint8_t buf[9];
365
366         wdt_enable(WDTO_1S);
367
368         w1_setup();
369         set_serial();
370
371         usbInit();
372         usbDeviceDisconnect();
373
374         i = 0;
375         while (--i) {
376                 wdt_reset();
377                 _delay_ms(1);
378         }
379
380         usbDeviceConnect();
381
382         /* PB1 as output for LED */
383         DDRB |= 1 << PB1;
384
385         sei(); /* We're ready to go; enable interrupts */
386
387         keyboard_report.modifier = 0;
388         keyboard_report.reserved = 0;
389         keyboard_report.keycode[0] = 0;
390
391         while (1) {
392                 wdt_reset();
393                 usbPoll();
394
395                 if (have_temp_int && usbInterruptIsReady3()) {
396                         usbSetInterrupt3((void *) temp_report,
397                                         sizeof(temp_report));
398                         if (temp_report[0] == 'T') {
399                                 /* Hack up second response for 0x86 query */
400                                 memcpy(temp_report, ".2", 2);
401                         } else {
402                                 have_temp_int = false;
403                         }
404                 }
405
406                 if (temp_state == 1) {
407                         if (w1_reset()) {
408                                 temp_state = 2;
409                         } else {
410                                 temp_report[2] = 0xFF;
411                                 temp_report[3] = 0xFF;
412                                 have_temp_int = true;
413                                 temp_state = 0;
414                         }
415                 } else if (temp_state == 2) {
416                         w1_write(0xCC);         /* SKIP ROM */
417                         temp_state = 3;
418                 } else if (temp_state == 3) {
419                         w1_write(0x44);         /* Convert T */
420                         temp_state = 4;
421                 } else if (temp_state == 4) {
422                         if (w1_read_byte() == 0xFF)
423                                 temp_state = 5;
424                 } else if (temp_state == 5) {
425                         if (w1_reset()) {
426                                 temp_state = 6;
427                         } else {
428                                 temp_report[2] = 0xFF;
429                                 temp_report[3] = 0xFE;
430                                 have_temp_int = true;
431                                 temp_state = 0;
432                         }
433                 } else if (temp_state == 6) {
434                         w1_write(0xCC);         /* SKIP ROM */
435                         temp_state = 7;
436                 } else if (temp_state == 7) {
437                         w1_write(0xBE);         /* Read Scratchpad */
438                         temp_state = 8;
439                 } else if (temp_state > 7 && temp_state < 17) {
440                         buf[temp_state - 8] = w1_read_byte();
441                         temp_state++;
442                 } else if (temp_state == 17) {
443                         temp_report[2] = buf[1] << 4 | buf[0] >> 4;
444                         temp_report[3] = buf[0] << 4;
445                         have_temp_int = true;
446                         temp_state = 0;
447                 }
448         }
449 }