Statistics
| Revision:

root / EtherCard / examples / etherNode / etherNode.pde

History | View | Annotate | Download (9.2 KB)

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