summaryrefslogtreecommitdiff
path: root/Ports.h
blob: eb78c8d80d237ed95d325b04808284e96ee902d7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
// 2009-02-13 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php

#ifndef Ports_h
#define Ports_h

/// @file
/// Ports library definitions.

#if ARDUINO >= 100
#include <Arduino.h> // Arduino 1.0
#else
#include <WProgram.h> // Arduino 0022
#endif
#include <stdint.h>
#include <avr/pgmspace.h>
//#include <util/delay.h>

// tweak this to switch ATtiny84 etc to new Arduino 1.0+ conventions
// see http://arduino.cc/forum/index.php/topic,51984.msg371307.html#msg371307
// and http://forum.jeelabs.net/node/1567
#if ARDUINO >= 100
#define WRITE_RESULT size_t
#else
#define WRITE_RESULT void
#endif

/// Interface for JeeNode Ports - see the wiki docs for
/// [JeeNodes](http://jeelabs.net/projects/hardware/wiki/JeeNode) and
/// [pinouts](http://jeelabs.net/projects/hardware/wiki/Pinouts).
/// The Ports class is a thin wrapper around the Arduino's digitalRead(),
/// digitalWrite(), analogRead(), etc. functions. It was designed to simplify
/// the use of the four standard port headers on JeeNodes.
class Port {
protected:
	/// The port number is a small integer mathing the hardware port used.
    /// Port 0 is special, it designates the I2C hardware pins on a JeeNode.
    uint8_t portNum;

#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
	/// @return Arduino digital pin number of a Port's D pin (uint8_t).
    inline uint8_t digiPin() const
        { return 0; }
	/// @return Arduino digital pin number of a Port's A pin (uint8_t).
    inline uint8_t digiPin2() const
        { return 2; }
	/// @return Arduino digital pin number of the I pin on all Ports (uint8_t).
    static uint8_t digiPin3()
        { return 1; }
    /// @return Arduino analog pin number of a Port's A pin (uint8_t).
    inline uint8_t anaPin() const
        { return 0; }
#elif defined(__AVR_ATtiny84__)
	/// @return Arduino digital pin number of a Port's D pin (uint8_t).
    inline uint8_t digiPin() const
        { return 12 - 2 * portNum; }
	/// @return Arduino digital pin number of a Port's A pin (uint8_t).
    inline uint8_t digiPin2() const
        { return 11 - 2 * portNum; }
	/// @return Arduino digital pin number of the I pin on all Ports (uint8_t).
    static uint8_t digiPin3()
        { return 3; }
    /// @return Arduino analog pin number of a Port's A pin (uint8_t).
    inline uint8_t anaPin() const
        { return 11 - 2 * portNum; }
#elif defined(__AVR_ATmega2560__)
	/// @return Arduino digital pin number of a Port's D pin (uint8_t).
    inline uint8_t digiPin() const
        { return portNum ? portNum + 3 : 20; }
	/// @return Arduino digital pin number of a Port's A pin (uint8_t).
    inline uint8_t digiPin2() const
        { return portNum ? portNum + 13 : 21; }
	/// @return Arduino digital pin number of the I pin on all Ports (uint8_t).
    static uint8_t digiPin3()
        { return 3; }
    /// @return Arduino analog pin number of a Port's A pin (uint8_t).
    inline uint8_t anaPin() const
        { return portNum - 1; }
#else
	/// @return Arduino digital pin number of a Port's D pin (uint8_t).
    inline uint8_t digiPin() const
        { return portNum ? portNum + 3 : 18; }
	/// @return Arduino digital pin number of a Port's A pin (uint8_t).
    inline uint8_t digiPin2() const
        { return portNum ? portNum + 13 : 19; }
	/// @return Arduino digital pin number of the I pin on all Ports (uint8_t).
    static uint8_t digiPin3()
        { return 3; }
    /// @return Arduino analog pin number of a Port's A pin (uint8_t).
    inline uint8_t anaPin() const
        { return portNum - 1; }
#endif

public:
	///Contructor for a Port.
    inline Port (uint8_t num) : portNum (num) {}

    // DIO pin

    /// Set the pin mode of a Port's D pin. The mode() function member sets the
    /// I/O data direction of the DIO pin associated with a specific port.
    /// @param value INPUT or OUTPUT.
    inline void mode(uint8_t value) const
        { pinMode(digiPin(), value); }
    /// Reads the value of a Port's D pin.
    /// @return High or Low.
    inline uint8_t digiRead() const
        { return digitalRead(digiPin()); }
	/// Write High or Low to a Port's D pin.
    /// @param value High or Low.
    inline void digiWrite(uint8_t value) const
        { return digitalWrite(digiPin(), value); }
    /// Writes a PWM value to a Port's D pin.
    inline void anaWrite(uint8_t val) const
        { analogWrite(digiPin(), val); }
    /// Applies the Arduino pulseIn() function on a Port's D pin.
    inline uint32_t pulse(uint8_t state, uint32_t timeout =1000000L) const
        { return pulseIn(digiPin(), state, timeout); }
    
    // AIO pin

    /// Set the pin mode of a Port's A pin. The mode2() function member sets
    /// the I/O data direction of the AIO pin associated with a specific port.
    /// @param value INPUT or OUTPUT.
    inline void mode2(uint8_t value) const
        { pinMode(digiPin2(), value); }
    /// Reads an analog value from a Port's A pin.
    /// @return int [0..1023]
    inline uint16_t anaRead() const
        { return analogRead(anaPin()); }        
	/// Reads the value of a Port's A pin.
    /// @return High or Low.
    inline uint8_t digiRead2() const
        { return digitalRead(digiPin2()); }
    /// Write High or Low to a Port's A pin.
    /// @param value High or Low.
    inline void digiWrite2(uint8_t value) const
        { return digitalWrite(digiPin2(), value); }
	/// Applies the Arduino pulseIn() function on a Port's A pin.
    /// @see http://arduino.cc/en/Reference/pulseIn for more details.
    inline uint32_t pulse2(uint8_t state, uint32_t timeout =1000000L) const
        { return pulseIn(digiPin2(), state, timeout); }
        
    // IRQ pin (INT1, shared across all ports)

    /// Set the pin mode of the I pin on all Ports. The mode3() function member
    /// sets the I/O direction of the IRQ pin associated with a specific port.
    /// Note that this is the same pin on all ports.
    /// @param value INPUT or OUTPUT.
    static void mode3(uint8_t value)
        { pinMode(digiPin3(), value); }
    /// Reads the value of the I pin on all Ports.
    /// @return High or Low.
    static uint8_t digiRead3()
        { return digitalRead(digiPin3()); }
    /// Writes the value of the I pin on all Ports.
    /// @param value High or Low.
    static void digiWrite3(uint8_t value)
        { return digitalWrite(digiPin3(), value); }
    /// Writes a PWM value to the I pin of all Ports.
    static void anaWrite3(uint8_t val)
        { analogWrite(digiPin3(), val); }
    
    // both pins: data on DIO, clock on AIO

    /// Does Arduino shiftOut() with data on D and clock on A pin of the Port.
    inline void shift(uint8_t bitOrder, uint8_t value) const
        { shiftOut(digiPin(), digiPin2(), bitOrder, value); }
    uint16_t shiftRead(uint8_t bitOrder, uint8_t count =8) const;
    void shiftWrite(uint8_t bitOrder, uint16_t value, uint8_t count =8) const;
};

/// These objects represent remote nodes connected via wireless.
/// Requires the RemotePort and RemoteHandler classes.
class RemoteNode {
public: 
    /// @struct Data
    /// %Data structure exchanged to implement RemoteNode functionality.
    typedef struct {
        uint8_t flags, modes, digiIO, anaOut[2];
        uint16_t anaIn[4]; // only bits 0..11 used
    } Data;

    RemoteNode (char id, uint8_t band, uint8_t group =0);
    
    void poll(uint16_t msecs);

    friend class RemoteHandler;
    friend class RemotePort;
private:
    uint8_t nid;
    uint32_t lastPoll;
    Data data;
};

/// A remote handler is able to deal with information from remote nodes.
class RemoteHandler {
public:
    static void setup(uint8_t id, uint8_t band, uint8_t group =0);
    static uint8_t poll(RemoteNode& node, uint8_t send);
};

/// A remote port is like a local port, bot connected to a remote node.
class RemotePort : protected Port {
    RemoteNode& node;

    inline uint8_t pinBit() const
        { return portNum - 1; }
    inline uint8_t pinBit2() const
        { return portNum + 3; }
public:
    RemotePort (RemoteNode& remote, uint8_t num) : Port (num), node (remote) {}

    void mode(uint8_t value) const;
    uint8_t digiRead() const;
    void digiWrite(uint8_t value) const;
    void anaWrite(uint8_t val) const;
    
    void mode2(uint8_t value) const;    
    uint16_t anaRead() const;
    uint8_t digiRead2() const;
    void digiWrite2(uint8_t value) const;    
};

/// Can be used to drive a software (bit-banged) I2C bus via a Port interface.
/// @todo Speed up the I2C bit I/O, it's far too slow right now.
class PortI2C : public Port {
    uint8_t uswait;
#if 0
// speed test with fast hard-coded version for Port 1:
    inline void hold() const
        { _delay_us(1); }
    inline void sdaOut(uint8_t value) const
        { bitWrite(DDRD, 4, !value); bitWrite(PORTD, 4, value); }
    inline uint8_t sdaIn() const
        { return bitRead(PORTD, 4); }
    inline void sclHi() const
        { hold(); bitWrite(PORTC, 0, 1); }
    inline void sclLo() const
        { hold(); bitWrite(PORTC, 0, 0); }
public:
    enum { KHZMAX, KHZ400, KHZ100, KHZ_SLOW };
#else
    inline void hold() const
        { delayMicroseconds(uswait); }
    inline void sdaOut(uint8_t value) const
        { mode(!value); digiWrite(value); }
    inline uint8_t sdaIn() const
        { return digiRead(); }
    inline void sclHi() const
        { hold(); digiWrite2(1); }
    inline void sclLo() const
        { hold(); digiWrite2(0); }
public:
    enum { KHZMAX = 1, KHZ400 = 2, KHZ100 = 9 };
#endif
    
    /// Creates an instance of class PortI2C
    /// @param num port number corresponding to physical JeeNode port number.
    /// @param rate in microseconds - time delay between bits? (not quite!)
    PortI2C (uint8_t num, uint8_t rate =KHZMAX);
    
    /// Initalize I2C communication on a JeeNode port.
    /// @param addr I2C address of device with which to communicate
    /// @returns 1 if communication succeeded, 0 otherwise
    uint8_t start(uint8_t addr) const;
    /// Terminate transmission on an I2C connection.
    void stop() const;
    /// Send one byte of data to the currently address I2C device.
    /// @param data the data byte to send out
    /// @returns 1 if device acknowledged write, 0 if device did not respond
    uint8_t write(uint8_t data) const;
    /// Read a byte using I2C protocol on a JeeNode port.
    /// @param last pass 1 to signal the last byte read in this bus transaction
    /// @returns data (byte) read from the I2C device
    uint8_t read(uint8_t last) const;
};

/// Each device on the I2C bus needs to be defined using a DeviceI2C instance.
class DeviceI2C {
    const PortI2C& port;
    uint8_t addr;
    
public:
    DeviceI2C(const PortI2C& p, uint8_t me) : port (p), addr (me << 1) {}
    
    /// see if a device answers at an I2C address
    bool isPresent() const;
    
    /// Create a start condition on the I2C bus, and set things up for sending
    /// data to this device.
    /// @returns true if acknowledged by the slave device.
    uint8_t send() const
        { return port.start(addr); }
    /// Create a start condition on the I2C bus, and set things up for receiving
    /// data from this device.
    /// @returns true if acknowledged.
    uint8_t receive() const
        { return port.start(addr | 1); }
    /// Create a stop condition on the I2C bus, ending the current transfer.
    void stop() const
        { port.stop(); }
    /// Write a byte to the currently addressed device. Must be preceded by a
    /// proper PortI2C start() call.
    /// @param data Data byte to be sent.
    /// @returns true if the device acknowledged the byte (accepts more data).
    uint8_t write(uint8_t data) const
        { return port.write(data); }
    /// Read a byte from the currently addressed device. Must be preceded by a
    /// proper PortI2C start() call.
    /// @param last Indicates whether this is the last byte to read. Used to
    ///             respond to the write with a positive or negative ack. 
    ///             Pass 1 if reading the last byte, otherwise pass 0.
    uint8_t read(uint8_t last) const
        { return port.read(last); }
        
    void setAddress(uint8_t me)
        { addr = me << 1; }
};

/// The millisecond timer can be used for timeouts up to 60000 milliseconds.
/// Setting the timeout to zero disables the timer.
///
/// * for periodic use, poll the timer object with "if (timer.poll(123)) ..."
/// * for one-shot use, call "timer.set(123)" and poll as "if (timer.poll())"

class MilliTimer {
    word next;
    byte armed;
public:
    MilliTimer () : armed (0) {}
    
    /// poll until the timer fires
    /// @param ms Periodic repeat rate of the time, omit for a one-shot timer.
    byte poll(word ms =0);
    /// Return the number of milliseconds before the timer will fire
    word remaining() const;
    /// Returns true if the timer is not armed
    byte idle() const { return !armed; }
    /// set the one-shot timeout value
    /// @param ms Timeout value. Timer stops once the timer has fired.
    void set(word ms);
};

/// Low-power utility code using the Watchdog Timer (WDT). Requires a WDT
/// interrupt handler, e.g. EMPTY_INTERRUPT(WDT_vect);
class Sleepy {
public:
    /// start the watchdog timer (or disable it if mode < 0)
    /// @param mode Enable watchdog trigger after "16 << mode" milliseconds 
    ///             (mode 0..9), or disable it (mode < 0).
    /// @note If you use this function, you MUST included a definition of a WDT
    /// interrupt handler in your code. The simplest is to include this line:
    ///
    ///     ISR(WDT_vect) { Sleepy::watchdogEvent(); }
    ///
    /// This will get called when the watchdog fires.
    static void watchdogInterrupts (char mode);
    
    /// enter low-power mode, wake up with watchdog, INT0/1, or pin-change
    static void powerDown ();

    /// flushes pending data in Serial and then enter low-power mode, wake up
    /// with watchdog, INT0/1, or pin-change
    static void flushAndPowerDown ();
    
    /// Spend some time in low-power mode, the timing is only approximate.
    /// @param msecs Number of milliseconds to sleep, in range 0..65535.
    /// @returns 1 if all went normally, or 0 if some other interrupt occurred
    /// @note If you use this function, you MUST included a definition of a WDT
    /// interrupt handler in your code. The simplest is to include this line:
    ///
    ///     ISR(WDT_vect) { Sleepy::watchdogEvent(); }
    ///
    /// This will get called when the watchdog fires.
    static byte loseSomeTime (word msecs);

    /// This must be called from your watchdog interrupt code.
    static void watchdogEvent();

    /// enter idle state
    static void idle();
        
    /// Idle a while waiting for a relevant interrupt
    static unsigned int idleSomeTime(unsigned int secs);
    
    /// Idle forever waiting for an interrupt
    static unsigned int idleTimer(byte prescale); 

    /// Watchdog Timer Prescale intervals
    ///     w w w w
    ///     d d d d
    ///     p p p p
    ///     3 2 1 0
    /// 0x0 0 0 0 0 2K (2048) cycles 16 ms
    /// 0x1 0 0 0 1 4K (4096) cycles 32 ms
    /// 0x2 0 0 1 0 8K (8192) cycles 64 ms
    /// 0x3 0 0 1 1 16K (16384) cycles 0.125 s
    /// 0x4 0 1 0 0 32K (32768) cycles 0.25 s
    /// 0x5 0 1 0 1 64K (65536) cycles 0.5 s
    /// 0x6 0 1 1 0 128K (131072) cycles 1.0 s
    /// 0x7 0 1 1 1 256K (262144) cycles 2.0 s
    /// 0x8 1 0 0 0 512K (524288) cycles 4.0 s
    /// 0x9 1 0 0 1 1024K (1048576) cycles 8.0 s
    /// All other values are reserved
            
    /// Power down forever waiting for an interrupt
    static unsigned int pwrDownTimer(byte prescale);

};

/// simple task scheduler for times up to 6000 seconds
class Scheduler {
    word* tasks;
    word remaining;
    byte maxTasks;
    MilliTimer ms100;
public:
    /// initialize for a specified maximum number of tasks
    Scheduler (byte max);
    Scheduler (word* buf, byte max);

    /// Return next task to run, -1 if there are none ready to run, but there
    /// are tasks waiting, or -2 if there are no tasks waiting (i.e. all idle)
    char poll();
    /// same as poll, but wait for event in power-down mode.
    /// Uses Sleepy::loseSomeTime() - see comments there re requiring the
    /// watchdog timer. 
    char pollWaiting();
    
    /// set a task timer, in tenths of seconds
    void timer(byte task, word tenths);
    /// cancel a task timer
    void cancel(byte task);
    
    /// return true if a task timer is not running
    byte idle(byte task) { return tasks[task] == ~0U; }
};

/// Interface for the Blink Plug - see http://jeelabs.org/bp
class BlinkPlug : public Port {
    MilliTimer debounce;
    byte leds, lastState, checkFlags;
public:
	/// Enum containing shorthands for BlinkPlug button states.
    enum { ALL_OFF, ON1, OFF1, ON2, OFF2, SOME_ON, ALL_ON }; // for buttonCheck

    /// Constructor for the BlinkPlug class.
    /// @param port Portnumber the blinkplug is connected to.
    BlinkPlug (byte port)
        : Port (port), leds (0), lastState (0), checkFlags (0) {}
    
    void ledOn(byte mask);
    void ledOff(byte mask);
    /// @return One byte containing the state of both leds.
    byte ledState() const { return leds; }
    
    byte state();
    byte pushed(); // deprecated, don't use in combination with buttonCheck
    byte buttonCheck();
};

/// Interface for the Memory Plug - see http://jeelabs.org/mp
class MemoryPlug : public DeviceI2C {
    uint32_t nextSave;
public:
    MemoryPlug (PortI2C& port)
        : DeviceI2C (port, 0x50), nextSave (0) {}

    void load(word page, byte offset, void* buf, int count);
    void save(word page, byte offset, const void* buf, int count);
};

/// A memory stream can save and reload a stream of bytes on a MemoryPlug.
class MemoryStream {
    MemoryPlug& dev;
    word start, curr;
    char step;
    byte buffer[256], pos;
public:
    MemoryStream (MemoryPlug& plug, word page =0, char dir =1)
            : dev (plug), start (page), curr (page), step (dir), pos (0) {}
    
    long position(byte writing) const;
    byte get();
    void put(byte data);
    word flush();
    void reset();
};

/// Interface for the UART Plug - see http://jeelabs.org/up
class UartPlug : public Print {
    DeviceI2C dev;
    // avoid per-byte access, fill entire buffer instead to reduce I2C overhead
    byte rxbuf[20], in, out;

    void regSet (byte reg, byte value);
    void regRead (byte reg);
    
public:
    UartPlug (PortI2C& port, byte addr)
        : dev (port, addr), in (0), out (0) {}
        
    void begin(long);
    byte available();
    int read();
    void flush();
    virtual WRITE_RESULT write(byte);
};

/// Interface for the Dimmer Plug - see http://jeelabs.org/dp
class DimmerPlug : public DeviceI2C {
public:
    enum {
        MODE1, MODE2,
        PWM0, PWM1, PWM2, PWM3, PWM4, PWM5, PWM6, PWM7,
        PWM8, PWM9, PWM10, PWM11, PWM12, PWM13, PWM14, PWM15,
        GRPPWM, GRPFREQ,
        LEDOUT0, LEDOUT1, LEDOUT2, LEDOUT3,
        SUBADR1, SUBADR2, SUBADR3, ALLCALLADR,
    };

    DimmerPlug (PortI2C& port, byte addr)
        : DeviceI2C (port, addr) {}
    
    void begin ();
    byte getReg(byte reg) const;
    void setReg(byte reg, byte value) const;
    void setMulti(byte reg, ...) const;
};

/// Interface for the Lux Plug - see http://jeelabs.org/xp
class LuxPlug : public DeviceI2C {
    union { byte b[4]; word w[2]; } data;
public:
    enum {
        CONTROL, TIMING,
        THRESHLOWLOW, THRESHLOWHIGH, THRESHHIGHLOW, THRESHHIGHHIGH, INTERRUPT,
        LUXID = 0xA,
        DATA0LOW = 0xC, DATA0HIGH, DATA1LOW, DATA1HIGH,
    };

    LuxPlug (PortI2C& port, byte addr) : DeviceI2C (port, addr) {}

    /// Initialize the LuxPlug. Wait at least 1000 ms after calling this!
    void begin() {
        send();
        write(0xC0 | CONTROL);
        write(3); // power up
        stop();
    }

    ///Power down the lux plug for low power usage.
    void poweroff() {
        send();
        write(0xC0 | CONTROL);
        write(0); // power down
        stop();
    }
    
    void setGain(byte high);
    
    const word* getData();

    word calcLux(byte iGain =0, byte tInt =2) const;
};

// Interface for the HYT131 thermometer/hygrometer - see http://jeelabs.org/2012/06/30/new-hyt131-sensor/
class HYT131 : public DeviceI2C {
public:
    // Constructor for the HYT131 sensor.
    HYT131 (PortI2C& port) : DeviceI2C (port, 0x28) {}
    
    // Execute a reading; results are in tenths of degrees and percent, respectively
    // @param temp in which to store the temperature (int, tenths of degrees C)
    // @param humi in which to store the humidity (int, tenths of percent)
    // @param delayFun (optional) supply delayFun that takes ms delay as argument, for low-power waiting during reading (e.g. Sleepy::loseSomeTime()). By default, delay() is used
    void reading (int& temp, int& humi, byte (*delayFun)(word ms) =0);
};

/// Interface for the Gravity Plug - see http://jeelabs.org/gp
class GravityPlug : public DeviceI2C {
    /// Data storage for getAxes() and sensitivity()
    union { byte b[6]; int w[3]; } data;
public:
    /// Constructor for Gravity Plug.
    GravityPlug (PortI2C& port) : DeviceI2C (port, 0x38) {}

    /// Setup GravityPlug. Call during setup()
    void begin() {}
    /// Set GravityPlug sensitivity.
    /// @param range 2,4,8
    /// @param bw (optional) bandwidth.
    void sensitivity(byte range, word bw =0);

    /// Get accelleration data from GravityPlug.
    /// @return An array with 3 integers. (x,y,z) respectively.
    const int* getAxes();
    /// Read out the temperature (only for BMA150, not the older BMA020)
    /// @return temp, in half deg C steps, from -30C to +50C (i.e. times 2)
    char temperature();
};

/// Interface for the Input Plug - see http://jeelabs.org/ip
class InputPlug : public Port {
    uint8_t slow;
public:
    InputPlug (uint8_t num, uint8_t fix =0) : Port (num), slow (fix) {}
    
    void select(uint8_t channel);
};

/// Interface for the Infrared Plug - see http://jeelabs.org/ir
class InfraredPlug : public Port {
    uint8_t slot, gap, buf [40];
    char fill;
    uint32_t prev;
public:
    /// Initialize with default values for NEC protocol
    InfraredPlug (uint8_t num);
    
    /// Set slot size (us*4) and end-of-data gap (us*256)
    void configure(uint8_t slot4, uint8_t gap256 =80);
    
    /// Call this continuously or at least right after a pin change
    void poll();
    
    /// Returns number of nibbles read, or 0 if not yet ready
    uint8_t done();

    enum { UNKNOWN, NEC, NEC_REP };
    /// Try to decode a received packet, return type of packet
    /// if recognized, the receive buffer will be overwritten with the results
    uint8_t decoder(uint8_t nibbles);
    
    /// Access to the receive buffer
    const uint8_t* buffer() { return buf; }
    
    /// Send out a bit pattern, cycle time is the "slot4" config value
    void send(const uint8_t* data, uint16_t bits);
};

/// Interface for the Heading Board - see http://jeelabs.org/hb
class HeadingBoard : public PortI2C {
    DeviceI2C eeprom, adc, compass;
    Port aux;
    // keep following fields in order:
    word CC1, CC2, CC3, CC4, CC5, CC6, CC7;
    byte A, B, C, D, setReset;

    byte eepromByte(byte reg) const;
    void getConstants();
    word adcValue(byte press) const;

public:
    HeadingBoard (int num)
        : PortI2C (num), eeprom (*this, 0x50), adc (*this, 0x77),
          compass (*this, 0x30), aux (5-num), setReset (0x02) {}
    
    void begin();
    void pressure(int& temp, int& pres) const;
    void heading(int& xaxis, int& yaxis);
};

/// Interface for the Modern Device 3-axis Compass board.
/// See http://shop.moderndevice.com/products/3-axis-compass
class CompassBoard : public DeviceI2C {
    int read2 (byte last);
public:
    CompassBoard (PortI2C& port) : DeviceI2C (port, 0x1E) {}

    float heading();
};

/// Interface for the Proximity Plug - see http://jeelabs.org/yp
class ProximityPlug : public DeviceI2C {
public:
    enum {
        FIFO, FAULT, TPSTATUS, TPCONFIG,
        STR1, STR2, STR3, STR4, STR5, STR6, STR7, STR8, 
        ECEMR, MNTPR, MTPR, TASPR, SCR, LPCR, SKTR,
        CONFIG, SINFO,
    };

    ProximityPlug (PortI2C& port, byte num =0)
        : DeviceI2C (port, 0x5C + num) {}
    
    void begin();
    
    void setReg(byte reg, byte value) const;
    byte getReg(byte reg) const;
};

/// Interface for the Analog Plug - see http://jeelabs.org/ap
class AnalogPlug : public DeviceI2C {
  byte config;
public:
  AnalogPlug (const PortI2C& port, byte addr =0x69)
    : DeviceI2C (port, addr), config (0x1C) {}
  
  /// Default mode is channel 1, continuous, 18-bit, gain x1
  void begin (byte mode =0x1C);
  /// Select channel (1..4), must wait to read it out (up to 270 ms for 18-bit)
  void select (byte channel);
  /// Read out 4 bytes, caller will need to shift out the irrelevant lower bits
  long reading ();
};

/// Interface for the DHT11 and DHT22 sensors, does not use floating point
class DHTxx {
  byte pin;
public:
  DHTxx (byte pinNum);
  /// Results are returned in tenths of a degree and percent, respectively.
  /// Set "precise" to true for the more accurate DHT21 and DHT22 sensors.
  bool reading (int& temp, int &humi, bool precise =false);
};

/// Interface for the Color Plug - see http://jeelabs.org/cp
class ColorPlug : public DeviceI2C {
    union { byte b[8]; word w[4]; } data;
    word chromacct[3];
public:
    enum {
        CONTROL, TIMING, INTERRUPT, INTERRUPTSOURCE, CPID, GAIN = 0x7,
        THRESHLOWLOW, THRESHLOWHIGH, THRESHHIGHLOW, THRESHHIGHHIGH,
        DATA0LOW = 0x10, DATA0HIGH, DATA1LOW, DATA1HIGH,
        DATA2LOW, DATA2HIGH, DATA3LOW, DATA3HIGH,
        BLOCKREAD = 0x4F
    };

    ColorPlug (PortI2C& port, byte addr) : DeviceI2C (port, addr) {}
    
    void begin() {
        send();
        write(0x80 | CONTROL);
        write(3); // power up
        stop();
    }
    
    void setGain(byte gain, byte prescaler);
    
    // returns four 16-bit values: red, green, blue, and clear intensities
    const word* getData();
    
    const word* chromaCCT();
};

#ifdef Stream_h // only available in recent Arduino IDE versions

/// Simple parser for input data and one-letter commands
class InputParser {
public:
    typedef struct {
        char code;      // one-letter command code
        byte bytes;     // number of bytes required as input
        void (*fun)();  // code to call for this command
    } Commands;
    
    /// Set up with a buffer of specified size
    InputParser (byte size, Commands*, Stream& =Serial);
    InputParser (byte* buf, byte size, Commands*, Stream& =Serial);
    
    /// Number of data bytes
    byte count() { return fill; }
    
    /// Call this frequently to check for incoming data
    void poll();
    
    InputParser& operator >> (char& v)      { return get(&v, 1); }
    InputParser& operator >> (byte& v)      { return get(&v, 1); }
    InputParser& operator >> (int& v)       { return get(&v, 2); }
    InputParser& operator >> (word& v)      { return get(&v, 2); }
    InputParser& operator >> (long& v)      { return get(&v, 4); }
    InputParser& operator >> (uint32_t& v)  { return get(&v, 4); }
    InputParser& operator >> (const char*& v);

private:
    InputParser& get(void*, byte);
    void reset();
    
    byte *buffer, limit, fill, top, next;
    byte instring, hexmode, hasvalue;
    uint32_t value;
    Commands* cmds;
    Stream& io;
};

#endif // Stream_h

#endif