Statistics
| Revision:

root / RF12 / examples / RF12demo / RF12demo.pde

History | View | Annotate | Download (22.2 KB)

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