RF22
|
Sketch to provide an IP gateway for a set of RF22 radios (Datagram, ReliableDatagram, Router or Mesh) Routes UDP messages from an internet connection using an Ethernet Shield and sends them to a radio whose ID is based on the UDP port. Replies are sent back to the originating UDP address and port
// IPGateway.ino // -*- mode: C++ -*- // // This example Arduino sketch shows how to implement an RF22-IP gateway. // // UDP packets sent to this IP address will be sent to an RF22 radio. The radio will be selected // based on the destination UDP port. // eg UDP messages to port 2 will be sent to the RF22 radio with address 2. // The payload of the UDP message will be sent as data to the radio. // // If a message is sent from another radio to this one, it will be relayed to the internet. // The destination IP address and port for the messages are deduced from the senders node ID. // If a message was previouslt releayed from the internet to that id, then the reply wil be sent to the // senders IP address and port. // If there has been no recent mesasge sent to that node, the radio message will be relayed to the // defaultReplyAddress and defaultReplyPort defined below // The radio message contents are sent as the UDP payload. // // Test this with the example sketch rf22_datagram_server: // Send UDP mesage to this IP address, with UDP port 2. The mesage will be sent to the rf22_datagram_server. // Its reply will be relayed back to the original IP address and port. // For example, using sendUDP.pl // # perl sendUDP.pl -s 203.63.154.101 -p 2 -w hello // And hello back to you // // It requires hardware // RF22 radio // Arduino Uno + Ethernet Shield // or... // Arduino EtherTen // // Libraries required are: // Ethernet (included in Arduino 1.0) // EtherRaw from http://www.open.com.au/mikem/arduino/EtherRaw // RF22 from http://www.open.com.au/mikem/arduino/RF22 // // This code will NOT work with old EtherShields such as this one: // http://www.dfrobot.com/index.php?route=product/product&product_id=52 // because the old ones do not play nicely with other SPI devices (such as the RF22): they do not disable their SPI pins // when not slave selected. // Later EtherShields (such as the 05 and 06 models) are fine. // EtherTen is also fine // // Also, you MUST connect the RF22 in a slightly different way to the RF22 library documentation. // The RF22 Slave Select pin must be connected to Arduino pin 9 // (not the usual pin 10: 10 is used as slave select by the Ethernet library for the ethernet w5100 chip) // // Caution: reply speed is usually dominated by the on-the-air radio speed. With FSK_Rb2_4Fd36 modulation // and messages of 7 and 22 octets, the send-reply response time to another radio is about 180ms // // Tested with EtherTen, RF22 1.19, EtherRaw 1.0, arduino 1.0 // // Copyright (C) 2012 Mike McCauley #include <SPI.h> #include <Ethernet.h> #include <utility/socket.h> #include <EtherRaw.h> #include <RF22Datagram.h> #include <RF22.h> #define THIS_ADDRESS 1 // Singleton instance of the radio. You can use RF22Datagram, RF22ReliableDatagram // or whatever, according to whatever the other radios support RF22Datagram rf22(THIS_ADDRESS, 9); // Need different SS pin so dont conflict with EtherShield // Enter a MAC address and IP address for this gateway below. // The IP address will be dependent on your local network: MACAddress mac(0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED); IPAddress ip(203, 63, 154, 101); // If a radio message is received and ther eis no previously recorded sou8rce adress, send the message to // to this default address IPv4Address defaultReplyAddress(203, 63, 154, 29); uint16_t defaultReplyPort = 9048; uint8_t macraw_socket = 0; uint8_t udp_reply_socket = 1; void setup() { delay(100); // EtherTen likes a delay like this so the w5100 starts OK // start the Ethernet chip. You can also specifiy the internet gateway address etc here, if you want Ethernet.begin(mac.address(), ip); Serial.begin(115200); // Create a MACRAW socket to listen for incoming UDP packets // Cant use ordinary UDP socket, since they can only listen to one port at a time. socket(macraw_socket, SnMR::MACRAW, 0, 0); if (!rf22.init()) Serial.println("RF22 init failed"); // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 } // recvfrom in socket.cpp is guilty of buffer overflows // This version is specifically for MACRAW // and will only fill buf up to max of len octets // returns the amount actually copied to buf uint16_t macraw_read(SOCKET s, uint8_t *buf, uint16_t len) { uint8_t head[2]; uint16_t data_len=0; uint16_t ptr=0; if (!W5100.getRXReceivedSize(s)) return 0; ptr = W5100.readSnRX_RD(s); W5100.read_data(s, (uint8_t*)ptr, head, 2); ptr+=2; data_len = head[0]; data_len = (data_len<<8) + head[1] - 2; // Only copy as much as will fil the buffer if (len > data_len) len = data_len; W5100.read_data(s, (uint8_t*)ptr, buf, len); ptr += data_len; W5100.writeSnRX_RD(s, ptr); W5100.execCmdSn(s, Sock_RECV); return len; } // Structure for recording where requests come from and are sent to typedef struct Source { IPv4Address source; uint16_t sourcePort; uint16_t destPort; } Source; #define NUM_SOURCE_RECORDS 4 Source sources[NUM_SOURCE_RECORDS]; uint8_t next_source_record = 0; struct Source* findSourceRecord(uint16_t destPort) { uint8_t i; for (i =0; i < NUM_SOURCE_RECORDS; i++) { if (sources[i].destPort == destPort) return &sources[i]; } return NULL; } // Record the most recent sender to the dest port void recordSource(const IPv4Address& source, uint16_t sourcePort, uint16_t destPort) { Source* s = findSourceRecord(destPort); if (!s) { s = &sources[next_source_record]; next_source_record = (next_source_record + 1) % NUM_SOURCE_RECORDS; } s->source = source; s->sourcePort = sourcePort; s->destPort = destPort; } void loop() { uint16_t len; uint8_t buf[200]; // First see if anyone sent a UDP message for transmit to a radio node // Disable interrupts while we poll the w5100 // else if an RF22 interrupt occurs, the ISR may not be able to contact the RF22 due to the state of the // SPI interface at the time cli(); len = macraw_read(macraw_socket, buf, sizeof(buf)); sei(); if (len) { EthernetHeader* h = (EthernetHeader*)buf; // h->printTo(Serial); // Is it for our MAC? Ignore broadcasts if (!(h->dest() == mac)) return; // Is it IPv4? if (h->ethertype() != EthernetHeader::ETH_P_IP) return; PacketIPv4* p = (PacketIPv4*)h->payload(); // Is it really IPv4? if (p->version() != IPVERSION) return; // Is it addressed to our IP address? if (!(ip == p->dest().address())) return; // p->printTo(Serial); // Is it UDP? if (p->protocol() != PacketIPv4::IPPROTO_UDP) return; // Get UDP data PacketUDP* u = (PacketUDP*)p->payload(); // u->printTo(Serial); // RF22::printBuffer("payload:", buf, len); // Only send if its for another radio // REVISIT: if the message was for this radio, could implement command to configure the radio if (u->destPort() != THIS_ADDRESS) { // Serial.println((const char*)u->payload()); recordSource(p->source(), u->sourcePort(), u->destPort()); if (rf22.sendto(u->payload(), u->payload_length(), u->destPort())) rf22.waitPacketSent(); } } // Now see if anyone has sent us a message // And send it out on the internet uint8_t recvlen = sizeof(buf); uint8_t from; uint8_t to; if (rf22.recvfrom(buf, &recvlen, &from, &to)) { // Serial.print("got message from : 0x"); // Serial.print(from, HEX); // Serial.print(": "); // Serial.println((char*)buf); // Forward the payload to the internet cli(); socket(udp_reply_socket, SnMR::UDP, from, 0); // If we have recorded a source address/port for this radio, use it // Else use the default address/port Source* s = findSourceRecord(from); if (s) sendto(udp_reply_socket, buf, recvlen, (uint8_t*)s->source.address(), s->sourcePort); else sendto(udp_reply_socket, buf, recvlen, (uint8_t*)defaultReplyAddress.address(), defaultReplyPort); // We have to close this socket again, otherwise it will take priority over the MACRAW socket and MACRAW wont // receive UDP requests for this port again. close(udp_reply_socket); sei(); } }