// Arduino demo sketch for testing RFM12B + ethernet // 2010-05-20 http://opensource.org/licenses/mit-license.php // Listens for RF12 messages and displays valid messages on a webpage // Memory usage exceeds 1K, so use Atmega328 or decrease history/buffers // // This sketch is derived from RF12eth.pde: // May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php #include #include #include #define DEBUG 1 // set to 1 to display free RAM on web page #define SERIAL 0 // set to 1 to show incoming requests on serial port #define CONFIG_EEPROM_ADDR ((byte*) 0x10) // configuration, as stored in EEPROM struct Config { byte band; byte group; byte collect; word refresh; byte valid; // keep this as last byte } config; // ethernet interface mac address - must be unique on your network static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; // buffer for an outgoing data packet static byte outBuf[RF12_MAXDATA], outDest; static char outCount = -1; #define NUM_MESSAGES 10 // Number of messages saved in history #define MESSAGE_TRUNC 15 // Truncate message payload to reduce memory use static BufferFiller bfill; // used as cursor while filling the buffer static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history static byte next_msg; // pointer to next rf12rcvd line static word msgs_rcvd; // total number of lines received modulo 10,000 byte Ethernet::buffer[1000]; // tcp/ip send and receive buffer static void loadConfig() { for (byte i = 0; i < sizeof config; ++i) ((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i); if (config.valid != 253) { config.valid = 253; config.band = 8; config.group = 1; config.collect = 1; config.refresh = 5; } byte freq = config.band == 4 ? RF12_433MHZ : config.band == 8 ? RF12_868MHZ : RF12_915MHZ; rf12_initialize(31, freq, config.group); } static void saveConfig() { for (byte i = 0; i < sizeof config; ++i) eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]); } #if DEBUG static int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } #endif void setup(){ #if SERIAL Serial.begin(57600); Serial.println("\n[etherNode]"); #endif loadConfig(); if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) Serial.println( "Failed to access Ethernet controller"); if (!ether.dhcpSetup()) Serial.println("DHCP failed"); #if SERIAL ether.printIp("IP: ", ether.myip); #endif } const char okHeader[] PROGMEM = "HTTP/1.0 200 OK\r\n" "Content-Type: text/html\r\n" "Pragma: no-cache\r\n" ; static void homePage(BufferFiller& buf) { word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915; buf.emit_p(PSTR("$F\r\n" "" "RF12 etherNode - $D MHz, group $D" "RF12 etherNode - $D MHz, group $D " "- configure - send packet" "

Last $D messages:

" "
"), okHeader, config.refresh, mhz, config.group,
                                            mhz, config.group, NUM_MESSAGES);
    for (byte i = 0; i < NUM_MESSAGES; ++i) {
        byte j = (next_msg + i) % NUM_MESSAGES;
        if (history_len[j] > 0) {
            word n = msgs_rcvd - NUM_MESSAGES + i;
            buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
                                n/1000, (n/100) % 10, (n/10) % 10, n % 10);
            for (byte k = 0; k < history_len[j]; ++k)
                buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
        }
    }
    long t = millis() / 1000;
    word h = t / 3600;
    byte m = (t / 60) % 60;
    byte s = t % 60;
    buf.emit_p(PSTR(
        "
" "Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10); #if DEBUG buf.emit_p(PSTR(" ($D bytes free)"), freeRam()); #endif } static int getIntArg(const char* data, const char* key, int value =-1) { char temp[10]; if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0) value = atoi(temp); return value; } static void configPage(const char* data, BufferFiller& buf) { // pick up submitted data, if present if (data[6] == '?') { byte b = getIntArg(data, "b"); byte g = getIntArg(data, "g"); byte c = getIntArg(data, "c", 0); word r = getIntArg(data, "r"); if (1 <= g && g <= 250 && 1 <= r && r <= 3600) { // store values as new settings config.band = b; config.group = g; config.collect = c; config.refresh = r; saveConfig(); // re-init RF12 driver loadConfig(); // clear history memset(history_len, 0, sizeof history_len); // redirect to the home page buf.emit_p(PSTR( "HTTP/1.0 302 found\r\n" "Location: /\r\n" "\r\n")); return; } } // else show a configuration form buf.emit_p(PSTR("$F\r\n" "

Server node configuration

" "
" "

" "Freq band (4, 8, or 9)
" "Net group (1..250)
" "Collect mode: " "Don't send ACKs

" "Refresh rate (1..3600 seconds)" "

" "" "
"), okHeader, config.band, config.group, config.collect ? "CHECKED" : "", config.refresh); } static void sendPage(const char* data, BufferFiller& buf) { // pick up submitted data, if present const char* p = strstr(data, "b="); byte d = getIntArg(data, "d"); if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) { // prepare to send data as soon as possible in loop() outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0; outCount = 0; // convert the input string to a number of decimal data bytes in outBuf ++p; while (*p != 0 && *p != '&') { outBuf[outCount] = 0; while ('0' <= *++p && *p <= '9') outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0'); ++outCount; } #if SERIAL Serial.print("Send to "); Serial.print(outDest, DEC); Serial.print(':'); for (byte i = 0; i < outCount; ++i) { Serial.print(' '); Serial.print(outBuf[i], DEC); } Serial.println(); #endif // redirect to home page buf.emit_p(PSTR( "HTTP/1.0 302 found\r\n" "Location: /\r\n" "\r\n")); return; } // else show a send form buf.emit_p(PSTR("$F\r\n" "

Send a wireless data packet

" "
" "

" "Data bytes (decimal)
" "Destination node " "(1..31, or 0 to broadcast)
" "

" "" "
"), okHeader); } void loop(){ word len = ether.packetReceive(); word pos = ether.packetLoop(len); // check if valid tcp data is received if (pos) { bfill = ether.tcpOffset(); char* data = (char *) Ethernet::buffer + pos; #if SERIAL Serial.println(data); #endif // receive buf hasn't been clobbered by reply yet if (strncmp("GET / ", data, 6) == 0) homePage(bfill); else if (strncmp("GET /c", data, 6) == 0) configPage(data, bfill); else if (strncmp("GET /s", data, 6) == 0) sendPage(data, bfill); else bfill.emit_p(PSTR( "HTTP/1.0 401 Unauthorized\r\n" "Content-Type: text/html\r\n" "\r\n" "

401 Unauthorized

")); ether.httpServerReply(bfill.position()); // send web page data } // RFM12 loop runner, don't report acks if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) { history_rcvd[next_msg][0] = rf12_hdr; for (byte i = 0; i < rf12_len; ++i) if (i < MESSAGE_TRUNC) history_rcvd[next_msg][i+1] = rf12_data[i]; history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1 : MESSAGE_TRUNC+1; next_msg = (next_msg + 1) % NUM_MESSAGES; msgs_rcvd = (msgs_rcvd + 1) % 10000; if (RF12_WANTS_ACK && !config.collect) { #if SERIAL Serial.println(" -> ack"); #endif rf12_sendStart(RF12_ACK_REPLY, 0, 0); } } // send a data packet out if requested if (outCount >= 0 && rf12_canSend()) { rf12_sendStart(outDest, outBuf, outCount, 1); outCount = -1; } }