Statistics
| Revision:

root / EtherCard / examples / etherNode / etherNode.pde

History | View | Annotate | Download (9.2 KB)

1
//>>> The latest version of this code can be found at https://github.com/jcw/ !!
2
3
// Arduino demo sketch for testing RFM12B + ethernet
4
// Listens for RF12 messages and displays valid messages on a webpage
5
// Memory usage exceeds 1K, so use Atmega328 or decrease history/buffers
6
//
7
// This sketch is derived from RF12eth.pde:
8
// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php
9
//
10
// The EtherCard library is based on Guido Socher's driver, licensed as GPL2.
11
//
12
// Mods bij jcw, 2010-05-20
13
 
14
#include <EtherCard.h>
15
#include <Ports.h>
16
#include <RF12.h>
17
#include <avr/eeprom.h>
18
19
#define DEBUG   1   // set to 1 to display free RAM on web page
20
#define SERIAL  0   // set to 1 to show incoming requests on serial port
21
22
#define CONFIG_EEPROM_ADDR ((byte*) 0x10)
23
24
// configuration, as stored in EEPROM
25
struct Config {
26
    byte band;
27
    byte group;
28
    byte collect;
29
    word refresh;
30
    byte valid; // keep this as last byte
31
} config;
32
33
// ethernet interface mac address - must be unique on your network
34
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
35
36
// buffer for an outgoing data packet
37
static byte outBuf[RF12_MAXDATA], outDest;
38
static char outCount = -1;
39
40
#define NUM_MESSAGES  10    // Number of messages saved in history
41
#define MESSAGE_TRUNC 15    // Truncate message payload to reduce memory use
42
43
static BufferFiller bfill;  // used as cursor while filling the buffer
44
45
static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record
46
static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history
47
static byte next_msg;       // pointer to next rf12rcvd line
48
static word msgs_rcvd;      // total number of lines received modulo 10,000
49
50
byte Ethernet::buffer[1000];   // tcp/ip send and receive buffer
51
52
static void loadConfig() {
53
    for (byte i = 0; i < sizeof config; ++i)
54
        ((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i);
55
    if (config.valid != 253) {
56
        config.valid = 253;
57
        config.band = 8;
58
        config.group = 1;
59
        config.collect = 1;
60
        config.refresh = 5;
61
    }
62
    byte freq = config.band == 4 ? RF12_433MHZ :
63
                config.band == 8 ? RF12_868MHZ :
64
                                   RF12_915MHZ;
65
    rf12_initialize(31, freq, config.group);
66
}
67
68
static void saveConfig() {
69
    for (byte i = 0; i < sizeof config; ++i)
70
        eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]);
71
}
72
73
#if DEBUG
74
static int freeRam () {
75
  extern int __heap_start, *__brkval; 
76
  int v; 
77
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
78
}
79
#endif
80
81
void setup(){
82
#if SERIAL
83
    Serial.begin(57600);
84
    Serial.println("\n[etherNode]");
85
#endif
86
    loadConfig();
87
    
88
    if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
89
      Serial.println( "Failed to access Ethernet controller");
90
    if (!ether.dhcpSetup())
91
      Serial.println("DHCP failed");
92
#if SERIAL
93
    ether.printIp("IP: ", ether.myip);
94
#endif
95
}
96
97
char okHeader[] PROGMEM = 
98
    "HTTP/1.0 200 OK\r\n"
99
    "Content-Type: text/html\r\n"
100
    "Pragma: no-cache\r\n"
101
;
102
103
static void homePage(BufferFiller& buf) {
104
    word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915;
105
    buf.emit_p(PSTR("$F\r\n"
106
        "<meta http-equiv='refresh' content='$D'/>"
107
        "<title>RF12 etherNode - $D MHz, group $D</title>" 
108
        "RF12 etherNode - $D MHz, group $D "
109
            "- <a href='c'>configure</a> - <a href='s'>send packet</a>"
110
        "<h3>Last $D messages:</h3>"
111
        "<pre>"), okHeader, config.refresh, mhz, config.group,
112
                                            mhz, config.group, NUM_MESSAGES);
113
    for (byte i = 0; i < NUM_MESSAGES; ++i) {
114
        byte j = (next_msg + i) % NUM_MESSAGES;
115
        if (history_len[j] > 0) {
116
            word n = msgs_rcvd - NUM_MESSAGES + i;
117
            buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
118
                                n/1000, (n/100) % 10, (n/10) % 10, n % 10);
119
            for (byte k = 0; k < history_len[j]; ++k)
120
                buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
121
        }
122
    }
123
    long t = millis() / 1000;
124
    word h = t / 3600;
125
    byte m = (t / 60) % 60;
126
    byte s = t % 60;
127
    buf.emit_p(PSTR(
128
        "</pre>"
129
        "Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10);
130
#if DEBUG
131
    buf.emit_p(PSTR(" ($D bytes free)"), freeRam());
132
#endif
133
}
134
135
static int getIntArg(const char* data, const char* key, int value =-1) {
136
    char temp[10];
137
    if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0)
138
        value = atoi(temp);
139
    return value;
140
}
141
142
static void configPage(const char* data, BufferFiller& buf) {
143
    // pick up submitted data, if present
144
    if (data[6] == '?') {
145
        byte b = getIntArg(data, "b");
146
        byte g = getIntArg(data, "g");
147
        byte c = getIntArg(data, "c", 0);
148
        word r = getIntArg(data, "r");
149
        if (1 <= g && g <= 250 && 1 <= r && r <= 3600) {
150
            // store values as new settings
151
            config.band = b;
152
            config.group = g;
153
            config.collect = c;
154
            config.refresh = r;
155
            saveConfig();
156
            // re-init RF12 driver
157
            loadConfig();
158
            // clear history
159
            memset(history_len, 0, sizeof history_len);
160
            // redirect to the home page
161
            buf.emit_p(PSTR(
162
                "HTTP/1.0 302 found\r\n"
163
                "Location: /\r\n"
164
                "\r\n"));
165
            return;
166
        }
167
    }
168
    // else show a configuration form
169
    buf.emit_p(PSTR("$F\r\n"
170
        "<h3>Server node configuration</h3>"
171
        "<form>"
172
          "<p>"
173
    "Freq band <input type=text name=b value='$D' size=1> (4, 8, or 9)<br>"
174
    "Net group <input type=text name=g value='$D' size=3> (1..250)<br>"
175
    "Collect mode: <input type=checkbox name=c value='1' $S> "
176
        "Don't send ACKs<br><br>"
177
    "Refresh rate <input type=text name=r value='$D' size=4> (1..3600 seconds)"
178
          "</p>"
179
          "<input type=submit value=Set>"
180
        "</form>"), okHeader, config.band, config.group,
181
                    config.collect ? "CHECKED" : "",
182
                    config.refresh);
183
}
184
185
static void sendPage(const char* data, BufferFiller& buf) {
186
    // pick up submitted data, if present
187
    const char* p = strstr(data, "b=");
188
    byte d = getIntArg(data, "d");
189
    if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) {
190
        // prepare to send data as soon as possible in loop()
191
        outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0;
192
        outCount = 0;
193
        // convert the input string to a number of decimal data bytes in outBuf
194
        ++p;
195
        while (*p != 0 && *p != '&') {
196
            outBuf[outCount] = 0;
197
            while ('0' <= *++p && *p <= '9')
198
                outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0');
199
            ++outCount;
200
        }
201
#if SERIAL
202
        Serial.print("Send to ");
203
        Serial.print(outDest, DEC);
204
        Serial.print(':');
205
        for (byte i = 0; i < outCount; ++i) {
206
            Serial.print(' ');
207
            Serial.print(outBuf[i], DEC);
208
        }
209
        Serial.println();
210
#endif
211
        // redirect to home page
212
        buf.emit_p(PSTR(
213
            "HTTP/1.0 302 found\r\n"
214
            "Location: /\r\n"
215
            "\r\n"));
216
        return;
217
    }
218
    // else show a send form
219
    buf.emit_p(PSTR("$F\r\n"
220
        "<h3>Send a wireless data packet</h3>"
221
        "<form>"
222
          "<p>"
223
    "Data bytes <input type=text name=b size=50> (decimal)<br>"
224
    "Destination node <input type=text name=d size=3> "
225
        "(1..31, or 0 to broadcast)<br>"
226
          "</p>"
227
          "<input type=submit value=Send>"
228
        "</form>"), okHeader);
229
}
230
231
void loop(){
232
    word len = ether.packetReceive();
233
    word pos = ether.packetLoop(len);
234
    // check if valid tcp data is received
235
    if (pos) {
236
        bfill = ether.tcpOffset();
237
        char* data = (char *) Ethernet::buffer + pos;
238
#if SERIAL
239
        Serial.println(data);
240
#endif
241
        // receive buf hasn't been clobbered by reply yet
242
        if (strncmp("GET / ", data, 6) == 0)
243
            homePage(bfill);
244
        else if (strncmp("GET /c", data, 6) == 0)
245
            configPage(data, bfill);
246
        else if (strncmp("GET /s", data, 6) == 0)
247
            sendPage(data, bfill);
248
        else
249
            bfill.emit_p(PSTR(
250
                "HTTP/1.0 401 Unauthorized\r\n"
251
                "Content-Type: text/html\r\n"
252
                "\r\n"
253
                "<h1>401 Unauthorized</h1>"));  
254
        ether.httpServerReply(bfill.position()); // send web page data
255
    }
256
257
    // RFM12 loop runner, don't report acks
258
    if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) {
259
        history_rcvd[next_msg][0] = rf12_hdr;
260
        for (byte i = 0; i < rf12_len; ++i)
261
            if (i < MESSAGE_TRUNC) 
262
                history_rcvd[next_msg][i+1] = rf12_data[i];
263
        history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1
264
                                                         : MESSAGE_TRUNC+1;
265
        next_msg = (next_msg + 1) % NUM_MESSAGES;
266
        msgs_rcvd = (msgs_rcvd + 1) % 10000;
267
268
        if (RF12_WANTS_ACK && !config.collect) {
269
#if SERIAL
270
            Serial.println(" -> ack");
271
#endif
272
            rf12_sendStart(RF12_ACK_REPLY, 0, 0);
273
        }
274
    }
275
    
276
    // send a data packet out if requested
277
    if (outCount >= 0 && rf12_canSend()) {
278
        rf12_sendStart(outDest, outBuf, outCount, 1);
279
        outCount = -1;
280
    }
281
}