Statistics
| Revision:

root / RF12 / examples / RF12demo / RF12demo.pde

History | View | Annotate | Download (22.2 KB)

1
//>>> The latest version of this code can be found at https://github.com/jcw/ !!
2
3
// Configure some values in EEPROM for easy config of the RF12 later on.
4
// 2009-05-06 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
5
// $Id: RF12demo.pde 7763 2011-12-11 01:28:16Z jcw $
6
7
// this version adds flash memory support, 2009-11-19
8
9
#include <Ports.h>
10
#include <RF12.h>
11
#include <util/crc16.h>
12
#include <util/parity.h>
13
#include <avr/eeprom.h>
14
#include <avr/pgmspace.h>
15
16
#define DATAFLASH   1   // check for presence of DataFlash memory on JeeLink
17
#define FLASH_MBIT  16  // support for various dataflash sizes: 4/8/16 Mbit
18
19
#define LED_PIN     9   // activity LED, comment out to disable
20
21
#define COLLECT 0x20 // collect mode, i.e. pass incoming without sending acks
22
23
static unsigned long now () {
24
    // FIXME 49-day overflow
25
    return millis() / 1000;
26
}
27
28
static void activityLed (byte on) {
29
#ifdef LED_PIN
30
    pinMode(LED_PIN, OUTPUT);
31
    digitalWrite(LED_PIN, !on);
32
#endif
33
}
34
35
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36
// RF12 configuration setup code
37
38
typedef struct {
39
    byte nodeId;
40
    byte group;
41
    char msg[RF12_EEPROM_SIZE-4];
42
    word crc;
43
} RF12Config;
44
45
static RF12Config config;
46
47
static char cmd;
48
static byte value, stack[RF12_MAXDATA], top, sendLen, dest, quiet;
49
static byte testbuf[RF12_MAXDATA], testCounter;
50
51
static void addCh (char* msg, char c) {
52
    byte n = strlen(msg);
53
    msg[n] = c;
54
}
55
56
static void addInt (char* msg, word v) {
57
    if (v >= 10)
58
        addInt(msg, v / 10);
59
    addCh(msg, '0' + v % 10);
60
}
61
62
static void saveConfig () {
63
    // set up a nice config string to be shown on startup
64
    memset(config.msg, 0, sizeof config.msg);
65
    strcpy(config.msg, " ");
66
    
67
    byte id = config.nodeId & 0x1F;
68
    addCh(config.msg, '@' + id);
69
    strcat(config.msg, " i");
70
    addInt(config.msg, id);
71
    if (config.nodeId & COLLECT)
72
        addCh(config.msg, '*');
73
    
74
    strcat(config.msg, " g");
75
    addInt(config.msg, config.group);
76
    
77
    strcat(config.msg, " @ ");
78
    static word bands[4] = { 315, 433, 868, 915 };
79
    word band = config.nodeId >> 6;
80
    addInt(config.msg, bands[band]);
81
    strcat(config.msg, " MHz ");
82
    
83
    config.crc = ~0;
84
    for (byte i = 0; i < sizeof config - 2; ++i)
85
        config.crc = _crc16_update(config.crc, ((byte*) &config)[i]);
86
87
    // save to EEPROM
88
    for (byte i = 0; i < sizeof config; ++i) {
89
        byte b = ((byte*) &config)[i];
90
        eeprom_write_byte(RF12_EEPROM_ADDR + i, b);
91
    }
92
    
93
    if (!rf12_config())
94
        Serial.println("config save failed");
95
}
96
97
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
98
// OOK transmit code
99
100
// Turn transmitter on or off, but also apply asymmetric correction and account
101
// for 25 us SPI overhead to end up with the proper on-the-air pulse widths.
102
// With thanks to JGJ Veken for his help in getting these values right.
103
static void ookPulse(int on, int off) {
104
    rf12_onOff(1);
105
    delayMicroseconds(on + 150);
106
    rf12_onOff(0);
107
    delayMicroseconds(off - 200);
108
}
109
110
static void fs20sendBits(word data, byte bits) {
111
    if (bits == 8) {
112
        ++bits;
113
        data = (data << 1) | parity_even_bit(data);
114
    }
115
    for (word mask = bit(bits-1); mask != 0; mask >>= 1) {
116
        int width = data & mask ? 600 : 400;
117
        ookPulse(width, width);
118
    }
119
}
120
121
static void fs20cmd(word house, byte addr, byte cmd) {
122
	byte sum = 6 + (house >> 8) + house + addr + cmd;
123
	for (byte i = 0; i < 3; ++i) {
124
		fs20sendBits(1, 13);
125
		fs20sendBits(house >> 8, 8);
126
		fs20sendBits(house, 8);
127
		fs20sendBits(addr, 8);
128
		fs20sendBits(cmd, 8);
129
		fs20sendBits(sum, 8);
130
		fs20sendBits(0, 1);
131
		delay(10);
132
	}
133
}
134
135
static void kakuSend(char addr, byte device, byte on) {
136
    int cmd = 0x600 | ((device - 1) << 4) | ((addr - 1) & 0xF);
137
    if (on)
138
        cmd |= 0x800;
139
    for (byte i = 0; i < 4; ++i) {
140
        for (byte bit = 0; bit < 12; ++bit) {
141
            ookPulse(375, 1125);
142
            int on = bitRead(cmd, bit) ? 1125 : 375;
143
            ookPulse(on, 1500 - on);
144
        }
145
		ookPulse(375, 375);
146
		delay(11); // approximate
147
    }
148
}
149
150
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
151
// DataFlash code
152
153
#if DATAFLASH
154
155
#define DF_ENABLE_PIN   8           // PB0
156
157
#if FLASH_MBIT == 4
158
// settings for 0.5 Mbyte flash in JLv2
159
#define DF_BLOCK_SIZE   16          // number of pages erased at same time
160
#define DF_LOG_BEGIN    32          // first 2 blocks reserved for future use
161
#define DF_LOG_LIMIT    0x0700      // last 64k is not used for logging
162
#define DF_MEM_TOTAL    0x0800      // 2048 pages, i.e. 0.5 Mbyte
163
#define DF_DEVICE_ID    0x1F44      // see AT25DF041A datasheet
164
#define DF_PAGE_ERASE   0x20        // erase one block of flash memory
165
#endif
166
167
#if FLASH_MBIT == 8
168
// settings for 1 Mbyte flash in JLv2
169
#define DF_BLOCK_SIZE   16          // number of pages erased at same time
170
#define DF_LOG_BEGIN    32          // first 2 blocks reserved for future use
171
#define DF_LOG_LIMIT    0x0F00      // last 64k is not used for logging
172
#define DF_MEM_TOTAL    0x1000      // 4096 pages, i.e. 1 Mbyte
173
#define DF_DEVICE_ID    0x1F45      // see AT26DF081A datasheet
174
#define DF_PAGE_ERASE   0x20        // erase one block of flash memory
175
#endif
176
177
#if FLASH_MBIT == 16
178
// settings for 2 Mbyte flash in JLv3
179
#define DF_BLOCK_SIZE   256         // number of pages erased at same time
180
#define DF_LOG_BEGIN    512         // first 2 blocks reserved for future use
181
#define DF_LOG_LIMIT    0x1F00      // last 64k is not used for logging
182
#define DF_MEM_TOTAL    0x2000      // 8192 pages, i.e. 2 Mbyte
183
#define DF_DEVICE_ID    0x2020      // see M25P16 datasheet
184
#define DF_PAGE_ERASE   0xD8        // erase one block of flash memory
185
#endif
186
187
// structure of each page in the log buffer, size must be exactly 256 bytes
188
typedef struct {
189
    byte data [248];
190
    word seqnum;
191
    long timestamp;
192
    word crc;
193
} FlashPage;
194
195
// structure of consecutive entries in the data area of each FlashPage
196
typedef struct {
197
    byte length;
198
    byte offset;
199
    byte header;
200
    byte data[RF12_MAXDATA];
201
} FlashEntry;
202
203
static FlashPage dfBuf;     // for data not yet written to flash
204
static word dfLastPage;     // page number last written
205
static byte dfFill;         // next byte available in buffer to store entries
206
207
static byte df_present () {
208
    return dfLastPage != 0;
209
}
210
211
static void df_enable () {
212
    // digitalWrite(ENABLE_PIN, 0);
213
    bitClear(PORTB, 0);
214
}
215
216
static void df_disable () {
217
    // digitalWrite(ENABLE_PIN, 1);
218
    bitSet(PORTB, 0);
219
}
220
221
static byte df_xfer (byte cmd) {
222
    SPDR = cmd;
223
    while (!bitRead(SPSR, SPIF))
224
        ;
225
    return SPDR;
226
}
227
228
void df_command (byte cmd) {
229
    for (;;) {
230
        cli();
231
        df_enable();
232
        df_xfer(0x05); // Read Status Register
233
        byte status = df_xfer(0);
234
        df_disable();
235
        sei();
236
        // don't wait for ready bit if there is clearly no dataflash connected
237
        if (status == 0xFF || (status & 1) == 0)
238
            break;
239
    }    
240
241
    cli();
242
    df_enable();
243
    df_xfer(cmd);
244
}
245
246
static void df_deselect () {
247
    df_disable();
248
    sei();
249
}
250
251
static void df_writeCmd (byte cmd) {
252
    df_command(0x06); // Write Enable
253
    df_deselect();
254
    df_command(cmd);
255
}
256
257
void df_read (word block, word off, void* buf, word len) {
258
    df_command(0x03); // Read Array (Low Frequency)
259
    df_xfer(block >> 8);
260
    df_xfer(block);
261
    df_xfer(off);
262
    for (word i = 0; i < len; ++i)
263
        ((byte*) buf)[(byte) i] = df_xfer(0);
264
    df_deselect();
265
}
266
267
void df_write (word block, const void* buf) {
268
    df_writeCmd(0x02); // Byte/Page Program
269
    df_xfer(block >> 8);
270
    df_xfer(block);
271
    df_xfer(0);
272
    for (word i = 0; i < 256; ++i)
273
        df_xfer(((const byte*) buf)[(byte) i]);
274
    df_deselect();
275
}
276
277
// wait for current command to complete
278
void df_flush () {
279
    df_read(0, 0, 0, 0);
280
}
281
282
static void df_wipe () {
283
    Serial.println("DF W");
284
    
285
    df_writeCmd(0xC7); // Chip Erase
286
    df_deselect();
287
    df_flush();
288
}
289
290
static void df_erase (word block) {
291
    Serial.print("DF E ");
292
    Serial.println(block);
293
    
294
    df_writeCmd(DF_PAGE_ERASE); // Block Erase
295
    df_xfer(block >> 8);
296
    df_xfer(block);
297
    df_xfer(0);
298
    df_deselect();
299
    df_flush();
300
}
301
302
static word df_wrap (word page) {
303
    return page < DF_LOG_LIMIT ? page : DF_LOG_BEGIN;
304
}
305
306
static void df_saveBuf () {
307
    if (dfFill == 0)
308
        return;
309
310
    dfLastPage = df_wrap(dfLastPage + 1);
311
    if (dfLastPage == DF_LOG_BEGIN)
312
        ++dfBuf.seqnum; // bump to next seqnum when wrapping
313
    
314
    // set remainder of buffer data to 0xFF and calculate crc over entire buffer
315
    dfBuf.crc = ~0;
316
    for (byte i = 0; i < sizeof dfBuf - 2; ++i) {
317
        if (dfFill <= i && i < sizeof dfBuf.data)
318
            dfBuf.data[i] = 0xFF;
319
        dfBuf.crc = _crc16_update(dfBuf.crc, dfBuf.data[i]);
320
    }
321
    
322
    df_write(dfLastPage, &dfBuf);
323
    dfFill = 0;
324
    
325
    // wait for write to finish before reporting page, seqnum, and time stamp
326
    df_flush();
327
    Serial.print("DF S ");
328
    Serial.print(dfLastPage);
329
    Serial.print(' ');
330
    Serial.print(dfBuf.seqnum);
331
    Serial.print(' ');
332
    Serial.println(dfBuf.timestamp);
333
    
334
    // erase next block if we just saved data into a fresh block
335
    // at this point in time dfBuf is empty, so a lengthy erase cycle is ok
336
    if (dfLastPage % DF_BLOCK_SIZE == 0)
337
        df_erase(df_wrap(dfLastPage + DF_BLOCK_SIZE));
338
}
339
340
static void df_append (const void* buf, byte len) {
341
    //FIXME the current logic can't append incoming packets during a save!
342
343
    // fill in page time stamp when appending to a fresh page
344
    if (dfFill == 0)
345
        dfBuf.timestamp = now();
346
    
347
    long offset = now() - dfBuf.timestamp;
348
    if (offset >= 255 || dfFill + 1 + len > sizeof dfBuf.data) {
349
        df_saveBuf();
350
351
        dfBuf.timestamp = now();
352
        offset = 0;
353
    }
354
355
    // append new entry to flash buffer
356
    dfBuf.data[dfFill++] = offset;
357
    memcpy(dfBuf.data + dfFill, buf, len);
358
    dfFill += len;
359
}
360
361
// go through entire log buffer to figure out which page was last saved
362
static void scanForLastSave () {
363
    dfBuf.seqnum = 0;
364
    dfLastPage = DF_LOG_LIMIT - 1;
365
    // look for last page before an empty page
366
    for (word page = DF_LOG_BEGIN; page < DF_LOG_LIMIT; ++page) {
367
        word currseq;
368
        df_read(page, sizeof dfBuf.data, &currseq, sizeof currseq);
369
        if (currseq != 0xFFFF) {
370
            dfLastPage = page;
371
            dfBuf.seqnum = currseq + 1;
372
        } else if (dfLastPage == page - 1)
373
            break; // careful with empty-filled-empty case, i.e. after wrap
374
    }
375
}
376
377
static void df_initialize () {
378
    // assumes SPI has already been initialized for the RFM12B
379
    df_disable();
380
    pinMode(DF_ENABLE_PIN, OUTPUT);
381
    df_command(0x9F); // Read Manufacturer and Device ID
382
    word info = df_xfer(0) << 8;
383
    info |= df_xfer(0);
384
    df_deselect();
385
386
    if (info == DF_DEVICE_ID) {
387
        df_writeCmd(0x01);  // Write Status Register ...
388
        df_xfer(0);         // ... Global Unprotect
389
        df_deselect();
390
391
        scanForLastSave();
392
        
393
        Serial.print("DF I ");
394
        Serial.print(dfLastPage);
395
        Serial.print(' ');
396
        Serial.println(dfBuf.seqnum);
397
    
398
        // df_wipe();
399
        df_saveBuf(); //XXX
400
    }
401
}
402
403
static void discardInput () {
404
    while (Serial.read() >= 0)
405
        ;
406
}
407
408
static void df_dump () {
409
    struct { word seqnum; long timestamp; word crc; } curr;
410
    discardInput();
411
    for (word page = DF_LOG_BEGIN; page < DF_LOG_LIMIT; ++page) {
412
        if (Serial.read() >= 0)
413
            break;
414
        // read marker from page in flash
415
        df_read(page, sizeof dfBuf.data, &curr, sizeof curr);
416
        if (curr.seqnum == 0xFFFF)
417
            continue; // page never written to
418
        Serial.print(" df# ");
419
        Serial.print(page);
420
        Serial.print(" : ");
421
        Serial.print(curr.seqnum);
422
        Serial.print(' ');
423
        Serial.print(curr.timestamp);
424
        Serial.print(' ');
425
        Serial.println(curr.crc);
426
    }
427
}
428
429
static word scanForMarker (word seqnum, long asof) {
430
    word lastPage = 0;
431
    struct { word seqnum; long timestamp; } last, curr;
432
    last.seqnum = 0xFFFF;
433
    // go through all the pages in log area of flash
434
    for (word page = DF_LOG_BEGIN; page < DF_LOG_LIMIT; ++page) {
435
        // read seqnum and timestamp from page in flash
436
        df_read(page, sizeof dfBuf.data, &curr, sizeof curr);
437
        if (curr.seqnum == 0xFFFF)
438
            continue; // page never written to
439
        if (curr.seqnum >= seqnum && curr.seqnum < last.seqnum) {
440
            last = curr;
441
            lastPage = page;
442
        }
443
        if (curr.seqnum == last.seqnum && curr.timestamp <= asof)
444
            lastPage = page;
445
    }
446
    return lastPage;
447
}
448
449
static void df_replay (word seqnum, long asof) {
450
    word page = scanForMarker(seqnum, asof);
451
    Serial.print("r: page ");
452
    Serial.print(page);
453
    Serial.print(' ');
454
    Serial.println(dfLastPage);
455
    discardInput();
456
    word savedSeqnum = dfBuf.seqnum;
457
    while (page != dfLastPage) {
458
        if (Serial.read() >= 0)
459
            break;
460
        page = df_wrap(page + 1);
461
        df_read(page, 0, &dfBuf, sizeof dfBuf); // overwrites ram buffer!
462
        if (dfBuf.seqnum == 0xFFFF)
463
            continue; // page never written to
464
        // skip and report bad pages
465
        word crc = ~0;
466
        for (word i = 0; i < sizeof dfBuf; ++i)
467
            crc = _crc16_update(crc, dfBuf.data[i]);
468
        if (crc != 0) {
469
            Serial.print("DF C? ");
470
            Serial.print(page);
471
            Serial.print(' ');
472
            Serial.println(crc);
473
            continue;
474
        }
475
        // report each entry as "R seqnum time <data...>"
476
        byte i = 0;
477
        while (i < sizeof dfBuf.data && dfBuf.data[i] < 255) {
478
            if (Serial.available())
479
                break;
480
            Serial.print("R ");
481
            Serial.print(dfBuf.seqnum);
482
            Serial.print(' ');
483
            Serial.print(dfBuf.timestamp + dfBuf.data[i++]);
484
            Serial.print(' ');
485
            Serial.print((int) dfBuf.data[i++]);
486
            byte n = dfBuf.data[i++];
487
            while (n-- > 0) {
488
                Serial.print(' ');
489
                Serial.print((int) dfBuf.data[i++]);
490
            }
491
            Serial.println();
492
        }
493
        // at end of each page, report a "DF R" marker, to allow re-starting
494
        Serial.print("DF R ");
495
        Serial.print(page);
496
        Serial.print(' ');
497
        Serial.print(dfBuf.seqnum);
498
        Serial.print(' ');
499
        Serial.println(dfBuf.timestamp);
500
    }
501
    dfFill = 0; // ram buffer is no longer valid
502
    dfBuf.seqnum = savedSeqnum + 1; // so next replay will start at a new value
503
    Serial.print("DF E ");
504
    Serial.print(dfLastPage);
505
    Serial.print(' ');
506
    Serial.print(dfBuf.seqnum);
507
    Serial.print(' ');
508
    Serial.println(millis());
509
}
510
511
#else // DATAFLASH
512
513
#define df_present() 0
514
#define df_initialize()
515
#define df_dump()
516
#define df_replay(x,y)
517
#define df_erase(x)
518
519
#endif
520
521
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
522
523
char helpText1[] PROGMEM = 
524
    "\n"
525
    "Available commands:" "\n"
526
    "  <nn> i     - set node ID (standard node ids are 1..26)" "\n"
527
    "               (or enter an uppercase 'A'..'Z' to set id)" "\n"
528
    "  <n> b      - set MHz band (4 = 433, 8 = 868, 9 = 915)" "\n"
529
    "  <nnn> g    - set network group (RFM12 only allows 212, 0 = any)" "\n"
530
    "  <n> c      - set collect mode (advanced, normally 0)" "\n"
531
    "  t          - broadcast max-size test packet, with ack" "\n"
532
    "  ...,<nn> a - send data packet to node <nn>, with ack" "\n"
533
    "  ...,<nn> s - send data packet to node <nn>, no ack" "\n"
534
    "  <n> l      - turn activity LED on PB1 on or off" "\n"
535
    "  <n> q      - set quiet mode (1 = don't report bad packets)" "\n"
536
    "Remote control commands:" "\n"
537
    "  <hchi>,<hclo>,<addr>,<cmd> f     - FS20 command (868 MHz)" "\n"
538
    "  <addr>,<dev>,<on> k              - KAKU command (433 MHz)" "\n"
539
;
540
char helpText2[] PROGMEM = 
541
    "Flash storage (JeeLink only):" "\n"
542
    "  d                                - dump all log markers" "\n"
543
    "  <sh>,<sl>,<t3>,<t2>,<t1>,<t0> r  - replay from specified marker" "\n"
544
    "  123,<bhi>,<blo> e                - erase 4K block" "\n"
545
    "  12,34 w                          - wipe entire flash memory" "\n"
546
;
547
548
static void showString (PGM_P s) {
549
    for (;;) {
550
        char c = pgm_read_byte(s++);
551
        if (c == 0)
552
            break;
553
        if (c == '\n')
554
            Serial.print('\r');
555
        Serial.print(c);
556
    }
557
}
558
559
static void showHelp () {
560
    showString(helpText1);
561
    if (df_present())
562
        showString(helpText2);
563
    Serial.println("Current configuration:");
564
    rf12_config();
565
}
566
567
static void handleInput (char c) {
568
    if ('0' <= c && c <= '9')
569
        value = 10 * value + c - '0';
570
    else if (c == ',') {
571
        if (top < sizeof stack)
572
            stack[top++] = value;
573
        value = 0;
574
    } else if ('a' <= c && c <='z') {
575
        Serial.print("> ");
576
        Serial.print((int) value);
577
        Serial.println(c);
578
        switch (c) {
579
            default:
580
                showHelp();
581
                break;
582
            case 'i': // set node id
583
                config.nodeId = (config.nodeId & 0xE0) + (value & 0x1F);
584
                saveConfig();
585
                break;
586
            case 'b': // set band: 4 = 433, 8 = 868, 9 = 915
587
                value = value == 8 ? RF12_868MHZ :
588
                        value == 9 ? RF12_915MHZ : RF12_433MHZ;
589
                config.nodeId = (value << 6) + (config.nodeId & 0x3F);
590
                saveConfig();
591
                break;
592
            case 'g': // set network group
593
                config.group = value;
594
                saveConfig();
595
                break;
596
            case 'c': // set collect mode (off = 0, on = 1)
597
                if (value)
598
                    config.nodeId |= COLLECT;
599
                else
600
                    config.nodeId &= ~COLLECT;
601
                saveConfig();
602
                break;
603
            case 't': // broadcast a maximum size test packet, request an ack
604
                cmd = 'a';
605
                sendLen = RF12_MAXDATA;
606
                dest = 0;
607
                for (byte i = 0; i < RF12_MAXDATA; ++i)
608
                    testbuf[i] = i + testCounter;
609
                Serial.print("test ");
610
                Serial.println((int) testCounter); // first byte in test buffer
611
                ++testCounter;
612
                break;
613
            case 'a': // send packet to node ID N, request an ack
614
            case 's': // send packet to node ID N, no ack
615
                cmd = c;
616
                sendLen = top;
617
                dest = value;
618
                memcpy(testbuf, stack, top);
619
                break;
620
            case 'l': // turn activity LED on or off
621
                activityLed(value);
622
                break;
623
            case 'f': // send FS20 command: <hchi>,<hclo>,<addr>,<cmd>f
624
                rf12_initialize(0, RF12_868MHZ);
625
                activityLed(1);
626
                fs20cmd(256 * stack[0] + stack[1], stack[2], value);
627
                activityLed(0);
628
                rf12_config(); // restore normal packet listening mode
629
                break;
630
            case 'k': // send KAKU command: <addr>,<dev>,<on>k
631
                rf12_initialize(0, RF12_433MHZ);
632
                activityLed(1);
633
                kakuSend(stack[0], stack[1], value);
634
                activityLed(0);
635
                rf12_config(); // restore normal packet listening mode
636
                break;
637
            case 'd': // dump all log markers
638
                if (df_present())
639
                    df_dump();
640
                break;
641
            case 'r': // replay from specified seqnum/time marker
642
                if (df_present()) {
643
                    word seqnum = (stack[0] << 8) || stack[1];
644
                    long asof = (stack[2] << 8) || stack[3];
645
                    asof = (asof << 16) | ((stack[4] << 8) || value);
646
                    df_replay(seqnum, asof);
647
                }
648
                break;
649
            case 'e': // erase specified 4Kb block
650
                if (df_present() && stack[0] == 123) {
651
                    word block = (stack[1] << 8) | value;
652
                    df_erase(block);
653
                }
654
                break;
655
            case 'w': // wipe entire flash memory
656
                if (df_present() && stack[0] == 12 && value == 34) {
657
                    df_wipe();
658
                    Serial.println("erased");
659
                }
660
                break;
661
            case 'q': // turn quiet mode on or off (don't report bad packets)
662
                quiet = value;
663
                break;
664
        }
665
        value = top = 0;
666
        memset(stack, 0, sizeof stack);
667
    } else if ('A' <= c && c <= 'Z') {
668
        config.nodeId = (config.nodeId & 0xE0) + (c & 0x1F);
669
        saveConfig();
670
    } else if (c > ' ')
671
        showHelp();
672
}
673
674
void setup() {
675
    Serial.begin(57600);
676
    Serial.print("\n[RF12demo.8]");
677
678
    if (rf12_config()) {
679
        config.nodeId = eeprom_read_byte(RF12_EEPROM_ADDR);
680
        config.group = eeprom_read_byte(RF12_EEPROM_ADDR + 1);
681
    } else {
682
        config.nodeId = 0x41; // node A1 @ 433 MHz
683
        config.group = 0xD4;
684
        saveConfig();
685
    }
686
687
    df_initialize();
688
    
689
    showHelp();
690
}
691
692
void loop() {
693
    if (Serial.available())
694
        handleInput(Serial.read());
695
696
    if (rf12_recvDone()) {
697
        byte n = rf12_len;
698
        if (rf12_crc == 0) {
699
            Serial.print("OK");
700
        } else {
701
            if (quiet)
702
                return;
703
            Serial.print(" ?");
704
            if (n > 20) // print at most 20 bytes if crc is wrong
705
                n = 20;
706
        }
707
        if (config.group == 0) {
708
            Serial.print("G ");
709
            Serial.print((int) rf12_grp);
710
        }
711
        Serial.print(' ');
712
        Serial.print((int) rf12_hdr);
713
        for (byte i = 0; i < n; ++i) {
714
            Serial.print(' ');
715
            Serial.print((int) rf12_data[i]);
716
        }
717
        Serial.println();
718
        
719
        if (rf12_crc == 0) {
720
            activityLed(1);
721
            
722
            if (df_present())
723
                df_append((const char*) rf12_data - 2, rf12_len + 2);
724
725
            if (RF12_WANTS_ACK && (config.nodeId & COLLECT) == 0) {
726
                Serial.println(" -> ack");
727
                rf12_sendStart(RF12_ACK_REPLY, 0, 0);
728
            }
729
            
730
            activityLed(0);
731
        }
732
    }
733
734
    if (cmd && rf12_canSend()) {
735
        activityLed(1);
736
737
        Serial.print(" -> ");
738
        Serial.print((int) sendLen);
739
        Serial.println(" b");
740
        byte header = cmd == 'a' ? RF12_HDR_ACK : 0;
741
        if (dest)
742
            header |= RF12_HDR_DST | dest;
743
        rf12_sendStart(header, testbuf, sendLen);
744
        cmd = 0;
745
746
        activityLed(0);
747
    }
748
}