├── examples ├── network_scan │ └── network_scan.ino ├── basic_tx │ └── basic_tx.ino ├── max_tx │ └── max_tx.ino ├── buffered │ └── buffered.ino ├── basic_rx │ └── basic_rx.ino ├── remote_sensing │ └── remote_sensing.ino ├── query_xbee │ └── query_xbee.ino └── interactive │ └── interactive.ino ├── xbee_sam.h ├── xbee_atmega.h ├── README.md ├── LICENSE ├── XbeeWifi.h └── XbeeWifi.cpp /examples/network_scan/network_scan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File network_scan.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate network scan 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.0 9 | */ 10 | #include 11 | 12 | // These are the pins that we are using to connect to the Xbee 13 | #define XBEE_RESET 20 14 | #define XBEE_ATN 2 15 | #define XBEE_SELECT SS 16 | #define XBEE_DOUT 23 17 | 18 | // XBee object 19 | XbeeWifi xbee; 20 | 21 | // Receive and output scan data 22 | void inbound_scan(uint8_t encryption_mode, int rssi, char *ssid) 23 | { 24 | Serial.print(F("Scan Information, SSID=")); 25 | Serial.print(ssid); 26 | Serial.print(F(", RSSI=-")); 27 | Serial.print(rssi, DEC); 28 | Serial.print(F("dB, Security=")); 29 | switch(encryption_mode) { 30 | case XBEE_SEC_ENCTYPE_NONE : Serial.println(F("none")); break; 31 | case XBEE_SEC_ENCTYPE_WEP : Serial.println(F("wep")); break; 32 | case XBEE_SEC_ENCTYPE_WPA : Serial.println(F("wpa")); break; 33 | case XBEE_SEC_ENCTYPE_WPA2 : Serial.println(F("wpa2")); break; 34 | default : Serial.println(F("Unknown")); break; 35 | } 36 | } 37 | 38 | // Keep track of 10 second intervals at which point we'll initiate scans 39 | unsigned long next_scan; 40 | 41 | // Setup routine 42 | void setup() 43 | { 44 | // Serial at 57600 45 | Serial.begin(57600); 46 | 47 | // Initialize the xbee 48 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 49 | 50 | if (!result) { 51 | // Something failed 52 | Serial.println(F("XBee Init Failed")); 53 | while (true) { /* Loop forever - game over */} 54 | } else { 55 | // Register for incoming scan data 56 | xbee.register_scan_callback(inbound_scan); 57 | 58 | Serial.println("XBee found and configured"); 59 | } 60 | 61 | next_scan = millis(); 62 | 63 | } 64 | 65 | // Main run loop 66 | void loop() 67 | { 68 | // Initiate a scan every ten seconds 69 | if (millis() >= next_scan) { 70 | if (xbee.initiateScan()) { 71 | Serial.println(F("Scan initiated")); 72 | } else { 73 | Serial.println(F("Failed to initiate scan")); 74 | } 75 | next_scan = millis() + 10000; 76 | } 77 | 78 | // Process the xbee 79 | xbee.process(); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /xbee_sam.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File xbee_sam.h 3 | * 4 | * Synopsis Support macros for SAM based chips (Arduino DUE) 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 1.0 9 | * 10 | * License This software is released under the terms of the Mozilla Public License (MPL) version 2.0 11 | * Full details of licensing terms can be found in the "LICENSE" file, distributed with this code 12 | * 13 | * Instructions Tweak the content below to address specific requirements of the Arduino DUE 14 | */ 15 | #ifndef __XBEESAM_H__ 16 | #define __XBEESAM_H__ 17 | 18 | /* Define ARCH_SAM which will be used elsewhere when instructions 19 | specific to this board type are needed */ 20 | #define ARCH_SAM 21 | 22 | /* The SPI_BUS_DIVISOR can be any value 0..255 23 | 24 | Take the master clock (typically 84Mhz) and divide by this number 25 | this will be your SPI bus speed 26 | 27 | For example, with a 84Mhz clock, a value of 84 gives a 1Mhz clock 28 | 29 | According to datasheet the Xbee Wifi unit supports up to 3.5Mhz SPI 30 | bus which would equate to a divisor of 24 */ 31 | #define SPI_BUS_DIVISOR 84u 32 | 33 | /* The Arduino DUE supports four chip selects, three of which are on real pins 34 | the fourth of which is not available. These are: 35 | 36 | BOARD_SPI_SS0 Digital 10 (internal pin #77) 37 | BOARD_SPI_SS1 Digital 4 (internal pin #87) 38 | BOARD_SPI_SS2 Digital 52 (internal pin #86) 39 | BOARD_SPI_SS3 (Not exposed on board? Need to check this..., internal pin #78) 40 | 41 | However, this library allows you to use any pin you wish by disassociating 42 | the real chip select pin from the PIO controller and controlling chip select 43 | manually. Still, the controller has to think it's using a pin. The code will 44 | make it think it's using BOARD_SPI_SS3 unless you actually use one of the intended 45 | CS pins. This is ideal, since this pin is not exposed on the board. 46 | 47 | You can change this if you wish here, although there is likely no need to do so */ 48 | #define SPI_CS_DEFAULT BOARD_SPI_SS3 49 | 50 | /* Define the maximum size of our working buffers 51 | The maximum size of a UDP data portion on a 1500 byte MTU (ethernet) 52 | network seems like it's a reasonable choice given we have enough 53 | memory on this platform */ 54 | #define XBEE_BUFSIZE 1472 55 | 56 | /* Insert a NOP loop of this many iterations after asserting and prior to clearing CS 57 | Needed for stability at higher SPI clock frequencies */ 58 | #define NOP_COUNT 1 59 | 60 | /* For assistance with debugging, the F(xxx) macro assists in embedding 61 | strings in progmem.. But we don't do this on the SAM so the F(xxx) macro 62 | just does nothing except pass it's content straight through */ 63 | #define F(str) (str) 64 | 65 | #endif // __XBEESAM_H__ 66 | -------------------------------------------------------------------------------- /xbee_atmega.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File xbee_atmega.h 3 | * 4 | * Synopsis Support macros for ATMEGA based chips (Uno, etc..) 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 1.0 9 | * 10 | * License This software is released under the terms of the Mozilla Public License (MPL) version 2.0 11 | * Full details of licensing terms can be found in the "LICENSE" file, distributed with this code 12 | * 13 | * Instructions Tweak the content below to address specific requirements of the non ARM based Arduinos 14 | */ 15 | #ifndef __XBEEATMEGA_H__ 16 | #define __XBEEATMEGA_H__ 17 | 18 | /* Define ARCH_ATMEGA which will be used elsewhere when instructions 19 | specific to this board type are needed */ 20 | #define ARCH_ATMEGA 21 | 22 | /* 23 | The SPI_BUS_DIVISOR can be one of the following values: 24 | 2,4,8,16,32,64,128 25 | 26 | Take the master clock (typically 16Mhz) and divide by this number 27 | this will be your SPI bus speed 28 | 29 | For example, with a 16Mhz clock, a value of 8 gives a 1Mhz clock 30 | 31 | According to datasheet the Xbee Wifi unit supports up to 3.5Mhz SPI 32 | bus. 33 | 34 | */ 35 | #define SPI_BUS_DIVISOR 8 36 | 37 | /* Buffer size - keep it small on this platform 38 | You can reduce this if you're running low on DRAM, but if that's 39 | the case you're likely already in trouble... 40 | Keep this value >=48 bytes as an absolute minimum */ 41 | #define XBEE_BUFSIZE 128 42 | 43 | /* Implementation of various speeds, don't mess with this */ 44 | #if SPI_BUS_DIVISOR == 2 45 | // FCPU/2 (8Mhz typical) 46 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) 47 | #define XBEE_SPSR (1 << SPI2X) 48 | #elif SPI_BUS_DIVISOR == 4 49 | // FCPU/4 (4Mhz typical) 50 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) 51 | #define XBEE_SPSR 0x00 52 | #elif SPI_BUS_DIVISOR == 8 53 | // FCPU/8 (2Mhz typical) 54 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) | (1 << SPR0) 55 | #define XBEE_SPSR (1 << SPI2X) 56 | #elif SPI_BUS_DIVISOR == 32 57 | // FCPU/32 (500Khz typical) 58 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) | (1 << SPR1) 59 | #define XBEE_SPSR (1 << SPI2X) 60 | #elif SPI_BUS_DIVISOR == 64 61 | // FCPU/64 (250Khz typical) 62 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) | (1 << SPR1) 63 | #define XBEE_SPSR 0x00 64 | #elif SPI_BUS_DIVISOR == 128 65 | // FCPU/128 (125Khz typical) 66 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0) 67 | #define XBEE_SPSR 0x00 68 | #else 69 | // FCPU/16 (1Mhz typical, default) 70 | #define XBEE_SPCR (1 << SPE) | (1 << MSTR) | (1 << SPR0) 71 | #define XBEE_SPSR 0x00 72 | #endif 73 | 74 | /* Delay for post CS assert and pre CS retract */ 75 | #define NOP_COUNT 1 76 | 77 | /* For assistance with debugging, the F(xxx) macro assists in embedding 78 | strings in progmem */ 79 | class __FlashStringHelper; 80 | #define F(str) reinterpret_cast<__FlashStringHelper *>(PSTR(str)) 81 | 82 | #endif // __XBEEATMEGA_H__ 83 | -------------------------------------------------------------------------------- /examples/basic_tx/basic_tx.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File basic_tx.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate transmission of data 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.0 9 | */ 10 | #include 11 | 12 | // These are the pins that we are using to connect to the Xbee 13 | #define XBEE_RESET 20 14 | #define XBEE_ATN 2 15 | #define XBEE_SELECT SS 16 | #define XBEE_DOUT 23 17 | 18 | // These are the configuration parameters we're going to use 19 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 20 | #define CONFIG_SSID "Example" // SSID 21 | #define CONFIG_KEY "whatever" // Password 22 | 23 | // Create an xbee object to handle things for us 24 | XbeeWifi xbee; 25 | 26 | unsigned long int next_tx; 27 | 28 | // Setup routine 29 | void setup() 30 | { 31 | // Serial at 57600 32 | Serial.begin(57600); 33 | 34 | // Initialize the xbee 35 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 36 | 37 | if (result) { 38 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 39 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 40 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 41 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 42 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 43 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 44 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 45 | } 46 | } 47 | 48 | if (!result) { 49 | // Something failed 50 | Serial.println(F("XBee Init Failed")); 51 | while (true) { /* Loop forever - game over */} 52 | } else { 53 | Serial.println("XBee found and configured"); 54 | } 55 | 56 | next_tx = millis(); 57 | } 58 | 59 | 60 | // Main run loop 61 | // Transmit "Hello World" on UDP, to 192.168.1.150 every 1 minute (using port 12345) 62 | void loop() 63 | { 64 | // Just keep calling the process method on the xbee object 65 | xbee.process(); 66 | 67 | if (millis() > next_tx) { 68 | if (xbee.last_status != XBEE_MODEM_STATUS_JOINED) { 69 | Serial.println(F("Not yet up and running")); 70 | } else { 71 | Serial.println(F("Transmitting now")); 72 | 73 | // Create an s_txoptions object to describe the port, protocol and behaviors 74 | s_txoptions txopts; 75 | txopts.dest_port=12345; 76 | txopts.source_port=12345; 77 | txopts.protocol = XBEE_NET_IPPROTO_UDP; 78 | 79 | // Create a binary IP address representation 80 | char ip[] = { 192, 168, 1, 150 }; 81 | 82 | // Transmit the frame 83 | if (!xbee.transmit((uint8_t *)ip, &txopts, (uint8_t *)"Hello World", 11)) { 84 | Serial.println(F("Transmit failed")); 85 | } else { 86 | Serial.println(F("Transmit OK")); 87 | } 88 | } 89 | 90 | // Repeat in one minute 91 | next_tx = millis() + 1000L; 92 | } 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /examples/max_tx/max_tx.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File basic_tx.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate transmission of data 5 | * at the highest possible rate 6 | * 7 | * Author Chris Bearman 8 | * 9 | * Version 1.0 10 | */ 11 | #include 12 | 13 | // These are the pins that we are using to connect to the Xbee 14 | #define XBEE_RESET 15 15 | #define XBEE_ATN 2 16 | #define XBEE_SELECT SS 17 | #define XBEE_DOUT 23 18 | 19 | // These are the configuration parameters we're going to use 20 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 21 | #define CONFIG_SSID "Example" // SSID 22 | #define CONFIG_KEY "whatever" // Password 23 | 24 | // Create an xbee object to handle things for us 25 | XbeeWifi xbee; 26 | 27 | // Setup routine 28 | 29 | // Size and buffer to hold our test data 30 | #define TEST_FRAME_SIZE 1024 31 | char testFrame[TEST_FRAME_SIZE]; 32 | 33 | void setup() 34 | { 35 | // Serial at 57600 36 | Serial.begin(57600); 37 | 38 | // Initialize the xbee 39 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 40 | 41 | if (result) { 42 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 43 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 44 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 45 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 46 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 47 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 48 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 49 | } 50 | } 51 | 52 | if (!result) { 53 | // Something failed 54 | Serial.println("XBee Init Failed"); 55 | while (true) { /* Loop forever - game over */} 56 | } else { 57 | Serial.println("XBee found and configured"); 58 | } 59 | 60 | // Set up data in test frame 61 | for (int i = 0 ; i < TEST_FRAME_SIZE; i++) { 62 | testFrame[i] = 65 + (i % 26); //(A thru Z, repeating) 63 | } 64 | 65 | } 66 | 67 | // To help split lines 68 | int split = 0; 69 | 70 | // Main run loop 71 | // Transmit "Hello World" on UDP, to 192.168.1.150 every 1 minute (using port 12345) 72 | void loop() 73 | { 74 | // Process the XBEE 75 | xbee.process(); 76 | 77 | if (xbee.last_status != XBEE_MODEM_STATUS_JOINED) { 78 | Serial.println("Not yet up and running"); 79 | delay(1000); 80 | } else { 81 | // Create an s_txoptions object to describe the port, protocol and behaviors 82 | s_txoptions txopts; 83 | txopts.dest_port=12345; 84 | txopts.source_port=12345; 85 | txopts.leave_open=true; 86 | txopts.protocol = XBEE_NET_IPPROTO_TCP; // Change to UDP if desired 87 | 88 | // Create a binary IP address representation 89 | char ip[] = { 192, 168, 1, 150 }; 90 | 91 | // Transmit the frame 92 | // You could turn off frame confirmation (add false as fifth option) but you'll overwhelm the device and loose 93 | // most data unless you can tweak your transmits to exactly the best intervals... 94 | if (xbee.transmit((uint8_t *)ip, &txopts, (uint8_t *)testFrame, TEST_FRAME_SIZE)) { 95 | // Send "o" to serial for sent Okay 96 | Serial.print("o"); 97 | } else { 98 | // "X" if send failed 99 | Serial.print("X"); 100 | } 101 | 102 | // Start a new line every sixty or so characters 103 | if ((++split % 60) == 0) { 104 | Serial.println(""); 105 | } 106 | } 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /examples/buffered/buffered.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File buffered.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate reception of data 5 | * using a buffered methodology 6 | * 7 | * Best run on something with >4K memory, such as a Mega 8 | * or a Due 9 | * 10 | * Author Chris Bearman 11 | * 12 | * Version 1.0 13 | */ 14 | #include 15 | 16 | // These are the pins that we are using to connect to the Xbee 17 | #define XBEE_RESET 15 18 | #define XBEE_ATN 2 19 | #define XBEE_SELECT SS 20 | #define XBEE_DOUT 23 21 | #define PIN_LED 7 22 | 23 | // These are the configuration parameters we're going to use 24 | #define CONFIG_PAYLOAD XBEE_NET_IPPROTO_TCP // Expecting TCP connections 25 | #define CONFIG_PORT 12350 // To this port 26 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 27 | #define CONFIG_SSID "Example" // SSID 28 | #define CONFIG_KEY "whatever" // Password 29 | 30 | // Create a buffered Xbee object with a whopping 1K (1024 bytes) 31 | // of cache space. 32 | XbeeWifiBuffered xbee(1024); 33 | 34 | // Receives inbound modem status updates and outputs to serial 35 | void inbound_status(uint8_t status) 36 | { 37 | Serial.println(); 38 | Serial.print(F("Modem status, received code ")); 39 | Serial.print(status, DEC); 40 | Serial.print(F(" (")); 41 | switch(status) { 42 | case XBEE_MODEM_STATUS_RESET : Serial.print(F("Reset or power on")); break; 43 | case XBEE_MODEM_STATUS_WATCHDOG_RESET : Serial.print(F("Watchdog reset")); break; 44 | case XBEE_MODEM_STATUS_JOINED : Serial.print(F("Joined")); break; 45 | case XBEE_MODEM_STATUS_NO_LONGER_JOINED : Serial.print(F("No longer joined")); break; 46 | case XBEE_MODEM_STATUS_IP_CONFIG_ERROR : Serial.print(F("IP configuration error")); break; 47 | case XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON : Serial.print(F("Send or join without connecting first")); break; 48 | case XBEE_MODEM_STATUS_AP_NOT_FOUND : Serial.print(F("AP not found")); break; 49 | case XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED : Serial.print(F("Key not configured")); break; 50 | case XBEE_MODEM_STATUS_SSID_NOT_FOUND : Serial.print(F("SSID not found")); break; 51 | case XBEE_MODEM_STATUS_FAILED_WITH_SECURITY : Serial.print(F("Failed to join with security enabled")); break; 52 | case XBEE_MODEM_STATUS_INVALID_CHANNEL : Serial.print(F("Invalid channel")); break; 53 | case XBEE_MODEM_STATUS_FAILED_TO_JOIN : Serial.print(F("Failed to join AP")); break; 54 | default : Serial.print(F("Unknown Status Code")); break; 55 | } 56 | Serial.println(F(")")); 57 | } 58 | 59 | // Setup routine 60 | void setup() 61 | { 62 | // Serial at 57600 63 | Serial.begin(57600); 64 | 65 | // Initialize the xbee 66 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 67 | 68 | if (result) { 69 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 70 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 71 | result &= xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, CONFIG_PAYLOAD); 72 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 73 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 74 | result &= xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, CONFIG_PORT); 75 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 76 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 77 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 78 | } 79 | } 80 | 81 | if (!result) { 82 | // Something failed 83 | Serial.println(F("XBee Init Failed")); 84 | while (true) { /* Loop forever - game over */} 85 | } else { 86 | // Register for status callbacks 87 | xbee.register_status_callback(inbound_status); 88 | 89 | Serial.println("XBee found and configured"); 90 | } 91 | } 92 | 93 | // Main run loop 94 | void loop() 95 | { 96 | // Whilst there is data buffered in the xbee, read it and output it to serial 97 | while (xbee.available()) { 98 | Serial.print((char)xbee.read()); 99 | } 100 | 101 | // Check to see if we've had buffer overruns 102 | // and report on them, should they occur 103 | if (xbee.overran()) { 104 | Serial.println("** Overran **"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/basic_rx/basic_rx.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File basic_rx.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate reception of data 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.0 9 | */ 10 | #include 11 | 12 | // These are the pins that we are using to connect to the Xbee 13 | #define XBEE_RESET 20 14 | #define XBEE_ATN 2 15 | #define XBEE_SELECT SS 16 | #define XBEE_DOUT 23 17 | 18 | // These are the configuration parameters we're going to use 19 | #define CONFIG_PAYLOAD XBEE_NET_IPPROTO_TCP // Expecting TCP connections 20 | #define CONFIG_PORT 12350 // To this port 21 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 22 | #define CONFIG_SSID "Example" // SSID 23 | #define CONFIG_KEY "whatever" // Password 24 | 25 | // Create an xbee object to handle things for us 26 | XbeeWifi xbee; 27 | 28 | // Helper function converts binary IP address to textual form 29 | // returning said text as an static buffer reference (so non-reentrant) 30 | char * ipstr(uint8_t *ip) 31 | { 32 | static char ipstrbuf[32]; 33 | sprintf(ipstrbuf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 34 | return ipstrbuf; 35 | } 36 | 37 | // Receives inbound IP data and outputs to serial 38 | void inbound_ip(unsigned char *data, int len, s_rxinfo *info) 39 | { 40 | Serial.println(); 41 | Serial.print(F("Inbound data from ")); 42 | Serial.print(ipstr(info->source_addr)); 43 | Serial.print(":"); 44 | Serial.print(info->source_port, DEC); 45 | Serial.println(":"); 46 | data[len] = 0; 47 | Serial.println((char *)data); 48 | Serial.println(); 49 | } 50 | 51 | // Receives inbound modem status updates and outputs to serial 52 | void inbound_status(uint8_t status) 53 | { 54 | Serial.println(); 55 | Serial.print(F("Modem status, received code ")); 56 | Serial.print(status, DEC); 57 | Serial.print(F(" (")); 58 | switch(status) { 59 | case XBEE_MODEM_STATUS_RESET : Serial.print(F("Reset or power on")); break; 60 | case XBEE_MODEM_STATUS_WATCHDOG_RESET : Serial.print(F("Watchdog reset")); break; 61 | case XBEE_MODEM_STATUS_JOINED : Serial.print(F("Joined")); break; 62 | case XBEE_MODEM_STATUS_NO_LONGER_JOINED : Serial.print(F("No longer joined")); break; 63 | case XBEE_MODEM_STATUS_IP_CONFIG_ERROR : Serial.print(F("IP configuration error")); break; 64 | case XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON : Serial.print(F("Send or join without connecting first")); break; 65 | case XBEE_MODEM_STATUS_AP_NOT_FOUND : Serial.print(F("AP not found")); break; 66 | case XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED : Serial.print(F("Key not configured")); break; 67 | case XBEE_MODEM_STATUS_SSID_NOT_FOUND : Serial.print(F("SSID not found")); break; 68 | case XBEE_MODEM_STATUS_FAILED_WITH_SECURITY : Serial.print(F("Failed to join with security enabled")); break; 69 | case XBEE_MODEM_STATUS_INVALID_CHANNEL : Serial.print(F("Invalid channel")); break; 70 | case XBEE_MODEM_STATUS_FAILED_TO_JOIN : Serial.print(F("Failed to join AP")); break; 71 | default : Serial.print(F("Unknown Status Code")); break; 72 | } 73 | Serial.println(F(")")); 74 | } 75 | 76 | // Setup routine 77 | void setup() 78 | { 79 | // Serial at 57600 80 | Serial.begin(57600); 81 | 82 | // Initialize the xbee 83 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 84 | 85 | if (result) { 86 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 87 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 88 | result &= xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, CONFIG_PAYLOAD); 89 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 90 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 91 | result &= xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, CONFIG_PORT); 92 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 93 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 94 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 95 | } 96 | } 97 | 98 | if (!result) { 99 | // Something failed 100 | Serial.println(F("XBee Init Failed")); 101 | while (true) { /* Loop forever - game over */} 102 | } else { 103 | // Register for incoming data 104 | xbee.register_ip_data_callback(inbound_ip); 105 | xbee.register_status_callback(inbound_status); 106 | 107 | Serial.println("XBee found and configured"); 108 | } 109 | } 110 | 111 | // Main run loop 112 | void loop() 113 | { 114 | // Just keep calling the process method on the xbee object 115 | // When things happen, it will call our callback routines 116 | xbee.process(); 117 | } 118 | 119 | 120 | -------------------------------------------------------------------------------- /examples/remote_sensing/remote_sensing.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File remote_sensing.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate remote sampling 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.0 9 | */ 10 | #include 11 | 12 | // These are the pins that we are using to connect to the Xbee 13 | #define XBEE_RESET 20 14 | #define XBEE_ATN 2 15 | #define XBEE_SELECT SS 16 | #define XBEE_DOUT 23 17 | 18 | // These are the configuration parameters we're going to use 19 | #define CONFIG_PAYLOAD XBEE_NET_IPPROTO_TCP // Expecting TCP connections 20 | #define CONFIG_PORT 12350 // To this port 21 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 22 | #define CONFIG_SSID "Example" // SSID 23 | #define CONFIG_KEY "whatever" // Password 24 | 25 | // Create an xbee object to handle things for us 26 | XbeeWifi xbee; 27 | 28 | // Helper function converts binary IP address to textual form 29 | // returning said text as an static buffer reference (so non-reentrant) 30 | char * ipstr(uint8_t *ip) 31 | { 32 | static char ipstrbuf[32]; 33 | sprintf(ipstrbuf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 34 | return ipstrbuf; 35 | } 36 | 37 | void inbound_sample(s_sample *sample) 38 | { 39 | Serial.print(F("Sample Inbound from IP ")); 40 | Serial.print(ipstr(sample->source_addr)); 41 | Serial.print(F(": DM=0x")); 42 | Serial.print(sample->digital_mask, HEX); 43 | Serial.print(F(", DS=0x")); 44 | Serial.print(sample->digital_samples, HEX); 45 | Serial.print(F(", AM=0x")); 46 | Serial.print(sample->analog_mask, HEX); 47 | Serial.print(F(", AS=0x")); 48 | Serial.println(sample->analog_samples, HEX); 49 | 50 | } 51 | 52 | // Receives inbound modem status updates and outputs to serial 53 | void inbound_status(uint8_t status) 54 | { 55 | Serial.println(); 56 | Serial.print(F("Modem status, received code ")); 57 | Serial.print(status, DEC); 58 | Serial.print(F(" (")); 59 | switch(status) { 60 | case XBEE_MODEM_STATUS_RESET : Serial.print(F("Reset or power on")); break; 61 | case XBEE_MODEM_STATUS_WATCHDOG_RESET : Serial.print(F("Watchdog reset")); break; 62 | case XBEE_MODEM_STATUS_JOINED : Serial.print(F("Joined")); break; 63 | case XBEE_MODEM_STATUS_NO_LONGER_JOINED : Serial.print(F("No longer joined")); break; 64 | case XBEE_MODEM_STATUS_IP_CONFIG_ERROR : Serial.print(F("IP configuration error")); break; 65 | case XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON : Serial.print(F("Send or join without connecting first")); break; 66 | case XBEE_MODEM_STATUS_AP_NOT_FOUND : Serial.print(F("AP not found")); break; 67 | case XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED : Serial.print(F("Key not configured")); break; 68 | case XBEE_MODEM_STATUS_SSID_NOT_FOUND : Serial.print(F("SSID not found")); break; 69 | case XBEE_MODEM_STATUS_FAILED_WITH_SECURITY : Serial.print(F("Failed to join with security enabled")); break; 70 | case XBEE_MODEM_STATUS_INVALID_CHANNEL : Serial.print(F("Invalid channel")); break; 71 | case XBEE_MODEM_STATUS_FAILED_TO_JOIN : Serial.print(F("Failed to join AP")); break; 72 | default : Serial.print(F("Unknown Status Code")); break; 73 | } 74 | Serial.println(F(")")); 75 | } 76 | 77 | // Setup routine 78 | void setup() 79 | { 80 | // Serial at 57600 81 | Serial.begin(57600); 82 | 83 | // Initialize the xbee 84 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 85 | 86 | if (result) { 87 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 88 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 89 | result &= xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, CONFIG_PAYLOAD); 90 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 91 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 92 | result &= xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, CONFIG_PORT); 93 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 94 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 95 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 96 | } 97 | } 98 | 99 | if (!result) { 100 | // Something failed 101 | Serial.println(F("XBee Init Failed")); 102 | while (true) { /* Loop forever - game over */} 103 | } else { 104 | // Register for incoming data 105 | xbee.register_sample_callback(inbound_sample); 106 | xbee.register_status_callback(inbound_status); 107 | 108 | Serial.println("XBee found and configured"); 109 | } 110 | } 111 | 112 | // Main run loop 113 | void loop() 114 | { 115 | // Just keep calling the process method on the xbee object 116 | // When things happen, it will call our callback routines 117 | xbee.process(); 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /examples/query_xbee/query_xbee.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File query_xbee.ino 3 | * 4 | * Synopsis Simple example sketch to demonstrate AT query of Xbee 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.0 9 | */ 10 | #include 11 | 12 | // These are the pins that we are using to connect to the Xbee 13 | #define XBEE_RESET 20 14 | #define XBEE_ATN 2 15 | #define XBEE_SELECT SS 16 | #define XBEE_DOUT 23 17 | 18 | // These are the configuration parameters we're going to use 19 | #define CONFIG_PAYLOAD XBEE_NET_IPPROTO_TCP // Expecting TCP connections 20 | #define CONFIG_PORT 12350 // To this port 21 | #define CONFIG_ENCMODE XBEE_SEC_ENCTYPE_WPA2 // Network type is WPA2 encrypted 22 | #define CONFIG_SSID "Example" // SSID 23 | #define CONFIG_KEY "password" // Password 24 | 25 | // Create an xbee object to handle things for us 26 | XbeeWifi xbee; 27 | 28 | // Track 5 second status reporting intervals 29 | unsigned long next_status; 30 | 31 | // Helper function converts binary IP address to textual form 32 | // returning said text as an static buffer reference (so non-reentrant) 33 | char * ipstr(uint8_t *ip) 34 | { 35 | static char ipstrbuf[32]; 36 | sprintf(ipstrbuf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 37 | return ipstrbuf; 38 | } 39 | 40 | // Interrogate Xbee for basic network status 41 | void show_status() 42 | { 43 | Serial.println(); 44 | 45 | uint8_t buf[XBEE_BUFSIZE]; 46 | int len; 47 | if (xbee.at_query(XBEE_AT_ADDR_IPADDR, buf, &len, XBEE_BUFSIZE - 1)) { 48 | buf[len] = 0; 49 | Serial.print(F("IP Address : ")); 50 | Serial.println((char *)buf); 51 | } else { 52 | Serial.println(F("IP Query fail")); 53 | } 54 | if (xbee.at_query(XBEE_AT_ADDR_NETMASK, buf, &len, XBEE_BUFSIZE - 1)) { 55 | buf[len] = 0; 56 | Serial.print(F("Netmask : ")); 57 | Serial.println((char *)buf); 58 | } else { 59 | Serial.println(F("NM Query fail")); 60 | } 61 | if (xbee.at_query(XBEE_AT_ADDR_GATEWAY, buf, &len, XBEE_BUFSIZE - 1)) { 62 | buf[len] = 0; 63 | Serial.print(F("Gateway : ")); 64 | Serial.println((char *) buf); 65 | } else { 66 | Serial.println(F("GW Query fail")); 67 | } 68 | 69 | if (xbee.at_query(XBEE_AT_DIAG_ASSOC_INFO, buf, &len, 1)) { 70 | Serial.print(F("Operation State : ")); 71 | switch(buf[0]) { 72 | case XBEE_DIAG_ASSOC_INSV : Serial.println(F("In Service")); break; 73 | case XBEE_DIAG_ASSOC_INIT_INPROG : Serial.println(F("Init In Progress")); break; 74 | case XBEE_DIAG_ASSOC_DISCONNECTING : Serial.println(F("Disconnecting")); break; 75 | case XBEE_DIAG_ASSOC_SSID_NOT_FOUND : Serial.println(F("SSID Not Found")); break; 76 | case XBEE_DIAG_ASSOC_SSID_NOT_CONFIGURED : Serial.println(F("SSID Not Configured")); break; 77 | case XBEE_DIAG_ASSOC_JOIN_FAILED : Serial.println(F("Join Failed")); break; 78 | case XBEE_DIAG_ASSOC_PENDING_DHCP : Serial.println(F("Pending DHCP")); break; 79 | case XBEE_DIAG_ASSOC_JOINED_IN_SETUP : Serial.println(F("Joined, Setup In Progress")); break; 80 | case XBEE_DIAG_ASSOC_SCANNING : Serial.println(F("Scanning")); break; 81 | default : Serial.println(F("Unknown Code")); break; 82 | } 83 | } else { 84 | Serial.println(F("STATE query fail")); 85 | } 86 | } 87 | 88 | // Setup routine 89 | void setup() 90 | { 91 | // Serial at 57600 92 | Serial.begin(57600); 93 | 94 | // Initialize the xbee 95 | bool result = xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 96 | 97 | if (result) { 98 | // Initialization okay so far, send setup parameters - if anything fails, result goes false 99 | result &= xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 100 | result &= xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, CONFIG_PAYLOAD); 101 | result &= xbee.at_cmd_str(XBEE_AT_NET_SSID, CONFIG_SSID); 102 | result &= xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 103 | result &= xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, CONFIG_PORT); 104 | result &= xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, CONFIG_ENCMODE); 105 | if (CONFIG_ENCMODE != XBEE_SEC_ENCTYPE_NONE) { 106 | result &= xbee.at_cmd_str(XBEE_AT_SEC_KEY, CONFIG_KEY); 107 | } 108 | } 109 | 110 | if (!result) { 111 | // Something failed 112 | Serial.println(F("XBee Init Failed")); 113 | while (true) { /* Loop forever - game over */} 114 | } else { 115 | Serial.println("XBee found and configured"); 116 | next_status = millis(); 117 | } 118 | } 119 | 120 | // Main run loop 121 | void loop() 122 | { 123 | // Every two seconds, show status 124 | if (millis() >= next_status) { 125 | show_status(); 126 | next_status = millis() + 2000; 127 | } 128 | xbee.process(); 129 | } 130 | 131 | 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arduino library for XBEE Wifi module, using SPI communication 2 | ============================================================= 3 | 4 | Notes: 5 | 6 | This library is not for use with non-wifi (Zigbee or other) XBEE modules. It is also not for use with the UART mode of the Wifi XBEE module. It implements the SPI interface to the XBEE Wifi module only. The SPI interface is optimal to utilize the full network functionality of the Wifi XBEE at the expense of GPIO lines used for the SPI bus integration. 7 | 8 | Note that using integrating with the Xbee Wifi using SPI is a demanding job for a small microcontroller. Simple communications problems are often better solved UART interface to the device. This library does not provide support for the much simpler requirements of integration through the UART interface - it is soley dedicated to integration using the more complex SPI interface. 9 | 10 | I am no longer actively maintaining this library. I will generally answer questions if time permits, however, please be aware you're going to be largely on your own. 11 | 12 | Feb 17 2015 13 | Since this library was published a couple of years ago, Digi has released a new version of the Xbee wifi. Although I believe this library will work with the new chip, there will at the very least be some unsupported functions, such as the new cloud management interactions provided by the device. If time permits in the near future, I will purchase one of the new variants and update the library to support it. 14 | 15 | Hardware Configuration 16 | ====================== 17 | Arduino XBEE 18 | MISO <---> MISO (required) 19 | MOSI <---> MOSI (required) 20 | SCLK <---> SCLK (required) 21 | SS or other pin <---> CS (required) 22 | digital pin <---> ATN (required) 23 | digital pin <---> RESET (optional) 24 | digital pin <---> DOUT (optional) 25 | 26 | The optional pin connections ARE optionally used to force the XBEE module into SPI mode during initialization. If not provided then the XBEE must be pre-configured (using XCTU or other methods) with the correct pin assignments for SPI operation before use with this library. It is important to note that the Xbee is not directly compatible with 5v Arduino devices. The Xbee requires 3.3v power and signals. If using a 5v Arduino, you *MUST* provide a 3.3v power supply for the Xbee and you *MUST* convert the signal leverl for *ALL* connections to 3.3v using appropriate level shifting hardware. 27 | 28 | Make sure and pay attention to the instructions on power supply design in the Xbee Wifi manual - particuarly the capacitor recommendations. When running with an Arduino based board powered at 3.3v I have encounted issues tripping BOD (brown out) resets on the microcontroller. Lowering the BOD threshold in the microcontrolelr fuses as well as providing plenty (1000uF+) of capacitance on the power feed is advisible. 29 | 30 | This library supports both Arduino Uno (and similar) boards based on ATMEGA chipset and Arduino Due based on SAM chipset. Support for Due is new and is likely less stable at this time. 31 | 32 | SPI Bus Speed 33 | ============= 34 | The Xbee Wifi chip (per spec sheet) supports a maximum SPI bus speed of 3.5Mhz. 35 | 36 | The SPI bus speed is set by a macro definition in either: 37 | xbee_atmega.h For Uno and similar (ATMEGA chipset based) boards 38 | xbee_sam.h For Due 39 | 40 | The default SPI bus speed I am using is 1Mhz. This is very conservative (and assumes a 16Mhz clock for ATMEGA devices, 84Mhz clock for Arduino DUE). The clock speed definition can be easily changed by altering the clock divisor setting in either of the above files. Just read the comments and change the SPI_BUS_DIVISOR macro, per the instructions. 41 | 42 | Technicaly 3.5Mhz can be achieved, exactly, on Arduino Due - I have tested this without issue. 43 | 44 | Due to the lower primary clock speed and more limited divisor options on the ATMEGA based boards, you are probably limited to using 2Mhz as a maximum unless you're using a non-standard crystal. I have tried overclocking to 4Mhz without success. 45 | 46 | Primary Functions 47 | ================= 48 | Send / Receive IP packets to/from any IP address using both native IPv4 and application compatability modes (port 0xBEE) as provided by the Wifi XBEE device. 49 | Issue AT (control) commands to the local XBEE and remote XBEE devices 50 | Receive data samples from remote XBEE devices 51 | Remove modem status indications from local XBEE device 52 | Initiate and receive active network scan data from local XBEE device 53 | 54 | Installation 55 | ============ 56 | Create a directory called XbeeWifi under sketches/libraries - where sketches is your base sketches directory. Check out this GIT repository to the new directory. 57 | 58 | Restart Arduino IDE. You should now be able to import the XbeeWifi library and find XbeeWifi examle sketches. 59 | 60 | Connecting to the Xbee 61 | ====================== 62 | 63 | FIrst of all figure out which pins you are using on your Arduino. 64 | 65 | #define XBEE_RESET 20 66 | #define XBEE_ATN 2 67 | #define XBEE_SELECT SS 68 | #define XBEE_DOUT 23 69 | 70 | Import the XbeeWifi library into your sketch. Create an XbeeWifi object: 71 | 72 | XbeeWifi xbee; 73 | 74 | Initialize the Xbee from your setup routine: 75 | 76 | if (!xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT)) { 77 | // Failed to initialize 78 | } 79 | 80 | 81 | Configuring the Xbee 82 | ==================== 83 | The XbeeWifi library provides functions for you to make AT calls to the Xbee. These are used to configure the device. Here is a typical configuration that might be used to configure the Xbee for connection to a typical WPA2 encrypted home network: 84 | 85 | xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE); 86 | xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, XBEE_NET_IPPROTO_TCP); 87 | xbee.at_cmd_str(XBEE_AT_NET_SSID, "my_network"); 88 | xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP); 89 | xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, 12345); 90 | xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, XBEE_SEC_ENCTYPE_WPA2); 91 | xbee.at_cmd_str(XBEE_AT_SEC_KEY, "MyVerySecretPassphrase"); 92 | 93 | Each at_cmd_xxxx function returns a boolean (true or false) to indicate success / failure. By default each command will wait for confirmation from the Xbee before returning. Other parameters on these functions allow you to skip confirmation if you wish. 94 | 95 | Note that remote versions of these comamnds are available for issuing at commands on remote Xbees. 96 | 97 | Servicing the Xbee 98 | ================== 99 | You must call xbee.prorcess() continuously - typically once during each iteration of your loop() method. You must call this method frequently since it services any inbound data from the Xbee. Failure to call this method frequently will result in SPI buffer overruns and loss of data. The process method will in turn call your registered callback methods as and when data is available for them. 100 | 101 | Registering For Callbacks 102 | ========================= 103 | Assuming you want to receive data from the Xbee, you will want to register for one or more of four possible callback functions. If you don't register one or more of these callbacks then any inbound data associated with them is silently discarded. 104 | 105 | IP Data Reception 106 | ================= 107 | 108 | To register a function to receive inbound IP data register a function of the following prototype: 109 | 110 | void my_ip_inbound_function(uint8_t *data, int len, s_rxinfo *info); 111 | 112 | Using the register_ip_data_callback method: 113 | 114 | xbee.register_ip_data_callback(my_ip_inbound_function); 115 | 116 | Your function will be called when you run the process() method if there is IP data inbound. The data is provided in the data parameter, and the length of the data in the len function. The info structure contains information about the data, such as which IP:Port sent it. 117 | 118 | The maximum amount of data received in any call is defined by XBEE_BUFSIZE macro (define in xbee_atmega.h). The default for ATMEGA based platforms is 128 bytes, meaning if more than 128 bytes come in in a single packet, it will be framented and delivered to you in multiple calls. 119 | 120 | Increase the size of this if memory allows and you are expecting larger packets. Decrease as low as 48 bytes if you're tragically short on DRAM. 121 | 122 | For Arduino Due users, this is defined in xbee_sam.h at a default of 1472 bytes which should be enough to convey most packets in a single call (based on a 1500 MTU less minimum headers). 123 | 124 | If it's necessary to reconstruct the entire packet, you must buffer it up and track the reassembly. The following elements of the info structure contain information to assist with reassembly: 125 | 126 | sequence 127 | .. Starts at zero for first segment of each packet. Increments for each successive segment. 128 | total_packet_length 129 | .. The total size of the incoming packet 130 | current_offset 131 | .. Starts at zero for first segment. Increments to indicate of the current offset of the incoming segment within the overall packet. 132 | final 133 | .. Indicates that this is the final segment to be sent for this packet 134 | checksum_error 135 | .. Indicates that a checksum error occurred on this packet 136 | 137 | Note that the checksum error will be only set to true on the final packet (final = true) because we don't know until that point. If the source / destination port is 0xBEE then this would indicate a packet received by the Xbee application compatability mode, which exclusively uses this port. 138 | 139 | Modem Status Reception 140 | ====================== 141 | To register for modem status updates, register a function of the following prototype: 142 | 143 | void my_modem_status_function(uint8_t status); 144 | 145 | Using the register_status_callback method 146 | 147 | xbee.register_status_callback(my_modem_status_function); 148 | 149 | The status value received can be compared against one of the following defined values: 150 | 151 | XBEE_MODEM_STATUS_RESET 0x00 152 | XBEE_MODEM_STATUS_WATCHDOG_RESET 0x01 153 | XBEE_MODEM_STATUS_JOINED 0x02 154 | XBEE_MODEM_STATUS_NO_LONGER_JOINED 0x03 155 | XBEE_MODEM_STATUS_IP_CONFIG_ERROR 0x04 156 | XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON 0x82 157 | XBEE_MODEM_STATUS_AP_NOT_FOUND 0x83 158 | XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED 0x84 159 | XBEE_MODEM_STATUS_SSID_NOT_FOUND 0x87 160 | XBEE_MODEM_STATUS_FAILED_WITH_SECURITY 0x88 161 | XBEE_MODEM_STATUS_INVALID_CHANNEL 0x8A 162 | XBEE_MODEM_STATUS_FAILED_TO_JOIN 0x8E 163 | 164 | Remote Data Sample callback 165 | =========================== 166 | The remote sample callback is used to receive remote IO data samples from a remote Xbee. Should such a sample arrive, it will be dispatched using this callback. 167 | 168 | To register for sample callback, register a function of the following prototype: 169 | 170 | void my_sample_callback(s_sample *sample); 171 | 172 | Using the register_sample_callback method 173 | 174 | xbee.register_sample_callback(sample); 175 | 176 | The contents of the sample structure contain the various sample fields as defined in the Xbee documentation. Namely masks and data representations for the various possible sampled IO ports. 177 | 178 | Network Scan callback 179 | --------------------- 180 | 181 | If you wish to scan for networks you must register the scan callback. 182 | 183 | To register for scan callback, register a function of the following prototype: 184 | 185 | void my_callback(uint8_t encryption_mode, int rssi, char *ssid); 186 | 187 | Using the register_scan_callback method 188 | 189 | xbee.register_scan_callback(my_scan_callback); 190 | 191 | To scan for networks you must then call initiateScan() 192 | 193 | xbee.initiateScan() 194 | 195 | Your callback function will be called (by process) for each network found. 196 | 197 | Note that initiating a network scan will force the Xbee to reset it's network parameters, causing you to disconnect from any connected network. You must reconfigure the network settings of the Xbee using appropriate AT commands after using the scan should you wish it to reconnect. 198 | 199 | 200 | Stack Safety 201 | ============ 202 | 203 | Both sending AT commands and transmiting data typically involve receiving a confirmation on the SPI bus once the operation completes. Since the SPI bus is serial and other data may be pending, please consider the following restrictions: 204 | 205 | AT commands will be aborted automatically and not sent. The at functions will return false. 206 | 207 | Transmission of data will be forced as unacknowledged. 208 | 209 | Why? Because other data may be pending on the SPI bus that data must be serviced before the confirmation of the AT command or transmission can occur. Accordingly, this could cause recursion of your callback functions and possible stack overflow and certainly complex behaviors. 210 | 211 | Although it is possible to transmit (without confirmation) from inside a callback, it is not advised since it would be very hard to prevent SPI buffer overruns. 212 | 213 | 214 | Buffered Reception 215 | ================== 216 | The callback pattern described above is used because: 217 | a) We don't have much memory to work with 218 | b) We cannot predict what packets (IP data, modem status etc..) may be inbound at any point in time from the device. 219 | 220 | So unless we want to buffer up data, which we probably don't have the memory to do, we need to be flexible in when the data is delivered. Hence the callback pattern. 221 | 222 | HOWEVER... If you really REALLY dislike callbacks, and have a simple application case, an alternative implementation is also provided. The XbeeWifiBuffered class is very similar to XbeeWifi, however, when constructing the object you specify a number of bytes (say 1024) to use as a buffer. You don't register for IP data callback, instead, you use the "available" and "read" methods to read data from the buffer. 223 | 224 | This is a more risk approach, althogh much easier to code to: 225 | 226 | Problems: 227 | 1. You're chewing up memory 228 | .. Less of a problem on bigger boards with more DRAM such as Mega or Due. 229 | 2. More risk of data loss 230 | .. If a packet comes in that exceeds the buffer size, some data WILL be discarded 231 | .. Other cases can cause data loss if you're not servicing the object fast enough. 232 | 233 | See the "buffered" example sketch for more information on using this mode. 234 | 235 | However, consider that if you're considering this approach you might be better off using the serial (non SPI) mode of the Xbee. 236 | 237 | Note that you do not have to call "process" when using this object. The calls to available() and read() will ensure that the bus is serviced. 238 | 239 | You should always read all data on every application loop to avoid potential data loss! I.E. keep read()ing until available() returns false. 240 | 241 | All other functions are unmodified (i.e. use callbacks for scanning, modem status etc..). It is recommended to empty the buffer PRIOR to using any other command (such as issuing an AT command operation). Reason here is that using one of these other commands may require the buffer on the Xbee to be flushed out to get to the new command response, possibly overwhelming the receive buffer if data is still pending. 242 | 243 | Optimizations 244 | ============= 245 | This is a pretty large library. Arduino and avr-gcc are good at optimizing out unused methods, however, due to the callback nature of the library some functions will be included even when they are not needed. 246 | 247 | A set of #defines are provided in the XbeeWifi.h function that can be uncommented to reduce the size of the final binary, at the expense of loosing some functionality. 248 | 249 | You will find a list of these optional defines commented out at the top of the .h file. Simple uncomment then to limit the functionality and reduce sketch size. 250 | 251 | 252 | Limitations 253 | =========== 254 | Support for the Arduino DUE is now provided. It is experimental at this time. 255 | 256 | Adding other devices to the SPI bus at the same time as the XbeeWifi, using this library should be supported - but is not tested. 257 | 258 | I have outstanding questions on whether it is possible for a packet to be dispatched (Xbee -> Arduino) on the SPI bus during the transmission of a packet (Arduino -> Xbee). It appears that this does not occur. I have not found an instance of the ATTN line being asserted, or the reception of a 0x7E (start byte) from the Xbee during transmission of a packet. A good test for this is sending a transmission from the xbee to it's own IP. This behaves mostly as expected- although there appears to be an Xbee bug on receipt - the received packet comes back over SPI but the IP address is all zeros and the data is corrupt. I will send an email to Digi about this minor problem, as well as questions over the details of the SPI bus implementation. 259 | 260 | Since there is no obvious instance where an XBEE -> Arduino transmission commences AFTER the Arduino asserts the chip select, this case is not handled by the library (which is a relief because that would require buffering and extra RAM consumption). 261 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /examples/interactive/interactive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * File interactive.ino 3 | * 4 | * Synopsis Comprehensive interactive Xbee demo 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Instructions You will need a microprocessor with a decent amount of flash to fit this sketch 9 | * since it contains many progmem strings and much functionality. 10 | * 11 | * Download it and connect ot serial monitor at 57600. 12 | * 13 | * Type help for information 14 | * 15 | * Use the set command to set parameters (i.e. set ipmode dhcp) 16 | * 17 | * Received data appears on screen as it arrives. 18 | * Transmit data to configured endpoint with transmit command. 19 | * Parameters are preserved in eeprom so that configuration is retained (excluding Arduino Due) 20 | * 21 | * Version 2.0 22 | */ 23 | 24 | #include 25 | #ifndef ARCH_SAM 26 | #include 27 | #endif 28 | 29 | // Number of bytes to use for general purpose buffers 30 | #define MAX_BUF 512 31 | 32 | // Definitions for endpoint types 33 | #define ENDPOINT_RAW 0 34 | #define ENDPOINT_APP 1 35 | 36 | // Defintions for what pins we're using for hwat 37 | #define XBEE_RESET 15 38 | #define XBEE_ATN 2 39 | #define XBEE_SELECT SS 40 | #define XBEE_DOUT 23 41 | #define PIN_LED 7 42 | 43 | // Misc definitions 44 | #define CONFIGURED 0xBEEE 45 | #define MAX_SSID 32 46 | #define MAX_PSK 63 47 | #define MAX_IP 4 48 | 49 | // Structure to hold our configuration 50 | typedef struct { 51 | uint16_t config_marker; 52 | uint8_t ssid[MAX_SSID + 1]; 53 | uint8_t psk[MAX_PSK +1]; 54 | uint8_t encmode; 55 | uint8_t ipmode; 56 | uint8_t ip[MAX_IP]; 57 | uint8_t nm[MAX_IP]; 58 | uint8_t gw[MAX_IP]; 59 | uint16_t port; 60 | uint8_t dest_ip[MAX_IP]; 61 | uint16_t dest_port; 62 | uint8_t payload; 63 | uint8_t endpoint; 64 | } s_config; 65 | 66 | // Current configuration 67 | s_config config; 68 | 69 | // Xbee object 70 | XbeeWifi xbee; 71 | 72 | // Display help 73 | void help() 74 | { 75 | Serial.println(F("Help Information...")); 76 | Serial.println(F("To show current operational status: show status")); 77 | Serial.println(F("To view current configuration: show config")); 78 | Serial.println(F("To change any configuration item: set ")); 79 | Serial.println(F("To restore to default configuration: defaults")); 80 | Serial.println(F("To initiate scan: scan (Must not be connected)")); 81 | Serial.println(F("To reset XBee: reset")); 82 | Serial.println(F("To transmit a phrase to configured endpoint: tx ")); 83 | Serial.println(); 84 | } 85 | 86 | // Print a binary IP to serial 87 | void print_ip(uint8_t *ip, bool lf=true) 88 | { 89 | Serial.print(ipstr(ip)); 90 | if (lf) Serial.println(""); 91 | } 92 | 93 | // Read input from serial 94 | // If reprompt = true indicates that we don't want to read anything but should 95 | // redisplay the prompt and currently pending line contents since something poluted the display 96 | // Call whenever serial reports input. 97 | // Returns NULL unless something got read, in which case an internal buffer is returned with the input string 98 | char *read_input(bool reprompt = false) 99 | { 100 | static char last[MAX_BUF]; 101 | static char current[MAX_BUF]; 102 | static int pos = 0; 103 | 104 | if (reprompt) { 105 | Serial.print("->"); 106 | for (int i = 0 ; i < pos; i++) { 107 | Serial.print((char)current[i]); 108 | } 109 | return NULL; 110 | } 111 | 112 | if (Serial.available()) { 113 | char c = Serial.read(); 114 | if ((c == '\n' || c == '\r') && pos) { 115 | memcpy(last, current, MAX_BUF); 116 | last[pos] = 0; 117 | pos = 0; 118 | 119 | Serial.print("\r\n"); 120 | 121 | return last; 122 | } 123 | 124 | if (c == 0x08 /* backspace */ && pos > 0) { 125 | pos--; 126 | Serial.print(c); 127 | Serial.print(' '); 128 | Serial.print(c); 129 | } 130 | 131 | if (pos >= MAX_BUF - 1) { 132 | Serial.println(F("Sorry - input too long - trashed")); 133 | pos = 0; 134 | } else { 135 | if (c >= 32) { 136 | current[pos++] = c; 137 | Serial.print(c); 138 | } 139 | } 140 | } 141 | return NULL; 142 | } 143 | 144 | // Write configuration to EEPROM 145 | void write_configuration() { 146 | #ifndef ARCH_SAM 147 | eeprom_write_block(&config, 0, sizeof(s_config)); 148 | #endif 149 | } 150 | 151 | // Initialize configuration in memory AND EEPROM 152 | void init_configuration() { 153 | memset(&config, 0, sizeof(s_config)); 154 | 155 | // Set key initial values 156 | config.config_marker = CONFIGURED; 157 | config.ipmode = XBEE_NET_ADDRMODE_DHCP; 158 | config.encmode = XBEE_SEC_ENCTYPE_NONE; 159 | strcpy((char *) config.ssid, "mynetwork"); 160 | config.port = 12345; 161 | config.dest_port = 12345; 162 | config.ip[0] = config.gw[0] = config.dest_ip[0] = 192; 163 | config.ip[1] = config.gw[1] = config.dest_ip[1] = 168; 164 | config.ip[2] = config.gw[2] = config.dest_ip[2] = 1; 165 | config.ip[3] = 254; 166 | config.dest_ip[3] = 253; 167 | config.gw[3] = 1; 168 | config.nm[0] = config.nm[1] = config.nm[2] = 255; 169 | config.payload = XBEE_NET_IPPROTO_TCP; 170 | config.endpoint = ENDPOINT_RAW; // Raw IP 171 | 172 | // Write to EEPROM 173 | write_configuration(); 174 | } 175 | 176 | // Load configuration from EEPROM to memory 177 | // If EEPROM memory did not hold our config, then an initial configuration is deployed 178 | void load_configuration() 179 | { 180 | #ifdef ARCH_SAM 181 | init_configuration(); 182 | #else 183 | eeprom_read_block(&config, 0, sizeof(s_config)); 184 | if (config.config_marker != CONFIGURED) init_configuration(); 185 | #endif 186 | } 187 | 188 | // Apply in memory configuration to Xbee 189 | void apply_configuration() 190 | { 191 | if (!xbee.at_cmd_byte(XBEE_AT_NET_TYPE, XBEE_NET_TYPE_IBSS_INFRASTRUCTURE)) Serial.println(F("Set Infrastructure Mode Failed")); 192 | if (!xbee.at_cmd_byte(XBEE_AT_NET_IPPROTO, config.payload)) Serial.println(F("Set payload Failed")); 193 | 194 | if (!xbee.at_cmd_str(XBEE_AT_NET_SSID, (char *)config.ssid)) Serial.println(F("Set SSID Failed")); 195 | if (config.ipmode == XBEE_NET_ADDRMODE_DHCP) { 196 | if (!xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_DHCP)) Serial.println(F("Set DHCP Failed")); 197 | } else { 198 | if (!xbee.at_cmd_byte(XBEE_AT_NET_ADDRMODE, XBEE_NET_ADDRMODE_STATIC)) Serial.println(F("Set STATIC Failed")); 199 | if (!xbee.at_cmd_str(XBEE_AT_ADDR_IPADDR, ipstr(config.ip))) Serial.println(F("Set IP Failed")); 200 | if (!xbee.at_cmd_str(XBEE_AT_ADDR_NETMASK, ipstr(config.nm))) Serial.println(F("Set Netmask Failed")); 201 | if (!xbee.at_cmd_str(XBEE_AT_ADDR_GATEWAY, ipstr(config.gw))) Serial.println(F("Set Gateway Failed")); 202 | } 203 | if (!xbee.at_cmd_short(XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT, config.port)) Serial.println(F("Set Port Failed")); 204 | if (!xbee.at_cmd_byte(XBEE_AT_SEC_ENCTYPE, config.encmode)) Serial.println(F("Set Encryption Mode Failed")); 205 | if (!xbee.at_cmd_str(XBEE_AT_SEC_KEY, (char *)config.psk)) Serial.println(F("Set Encryption Key Failed")); 206 | } 207 | 208 | // Setup algorithm 209 | void setup() 210 | { 211 | // Initialize serial 212 | Serial.begin(57600); 213 | 214 | // Initiailize XBEE 215 | if (!xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT)) { 216 | // Oops - failed 217 | // Flash the LED in three pulse groups 218 | pinMode(PIN_LED, OUTPUT); 219 | Serial.println(F("Failed to find the Xbee?")); 220 | while (true) { 221 | for(int i = 0; i < 3; i ++) { 222 | digitalWrite(PIN_LED, HIGH); 223 | delay(100); 224 | digitalWrite(PIN_LED, LOW); 225 | delay(100); 226 | } 227 | delay(400); 228 | } 229 | } 230 | 231 | // Load the EEPROM configuration 232 | load_configuration(); 233 | 234 | // Apply it to xbee 235 | apply_configuration(); 236 | 237 | // Welcome Message 238 | Serial.println(F("XBee Wifi Simple Controller Example")); 239 | Serial.println(F("Type help for instructions")); 240 | 241 | // Register callbacks 242 | xbee.register_ip_data_callback(ip_data_inbound); 243 | xbee.register_status_callback(status_inbound); 244 | xbee.register_scan_callback(scan_inbound); 245 | xbee.register_sample_callback(sample_inbound); 246 | 247 | // Display initial prompt 248 | Serial.print("->"); 249 | } 250 | 251 | // Inbound sample data, output to serial 252 | void sample_inbound(s_sample *sample) 253 | { 254 | Serial.println(); 255 | Serial.print(F("Sample Inbound from IP ")); 256 | Serial.print(ipstr(sample->source_addr)); 257 | Serial.print(F(": DM=0x")); 258 | Serial.print(sample->digital_mask, HEX); 259 | Serial.print(F(", DS=0x")); 260 | Serial.print(sample->digital_samples, HEX); 261 | Serial.print(F(", AM=0x")); 262 | Serial.print(sample->analog_mask, HEX); 263 | Serial.print(F(", AS=0x")); 264 | Serial.println(sample->analog_samples, HEX); 265 | read_input(true); 266 | 267 | } 268 | 269 | // Inbound scan data, output to serial 270 | void scan_inbound(uint8_t encryption_mode, int rssi, char *ssid) 271 | { 272 | Serial.println(); 273 | Serial.print(F("Scan Information, SSID=")); 274 | Serial.print(ssid); 275 | Serial.print(F(", RSSI=-")); 276 | Serial.print(rssi, DEC); 277 | Serial.print(F("dB, Security=")); 278 | switch(encryption_mode) { 279 | case XBEE_SEC_ENCTYPE_NONE : Serial.println(F("none")); break; 280 | case XBEE_SEC_ENCTYPE_WEP : Serial.println(F("wep")); break; 281 | case XBEE_SEC_ENCTYPE_WPA : Serial.println(F("wpa")); break; 282 | case XBEE_SEC_ENCTYPE_WPA2 : Serial.println(F("wpa2")); break; 283 | default : Serial.println(F("Unknown")); break; 284 | } 285 | read_input(true); 286 | } 287 | 288 | // Inbound status information, output to serial 289 | void status_inbound(uint8_t status) 290 | { 291 | Serial.println(); 292 | Serial.print(F("Modem status, received code ")); 293 | Serial.print(status, DEC); 294 | Serial.print(F(" (")); 295 | switch(status) { 296 | case XBEE_MODEM_STATUS_RESET : Serial.print(F("Reset or power on")); break; 297 | case XBEE_MODEM_STATUS_WATCHDOG_RESET : Serial.print(F("Watchdog reset")); break; 298 | case XBEE_MODEM_STATUS_JOINED : Serial.print(F("Joined")); break; 299 | case XBEE_MODEM_STATUS_NO_LONGER_JOINED : Serial.print(F("No longer joined")); break; 300 | case XBEE_MODEM_STATUS_IP_CONFIG_ERROR : Serial.print(F("IP configuration error")); break; 301 | case XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON : Serial.print(F("Send or join without connecting first")); break; 302 | case XBEE_MODEM_STATUS_AP_NOT_FOUND : Serial.print(F("AP not found")); break; 303 | case XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED : Serial.print(F("Key not configured")); break; 304 | case XBEE_MODEM_STATUS_SSID_NOT_FOUND : Serial.print(F("SSID not found")); break; 305 | case XBEE_MODEM_STATUS_FAILED_WITH_SECURITY : Serial.print(F("Failed to join with security enabled")); break; 306 | case XBEE_MODEM_STATUS_INVALID_CHANNEL : Serial.print(F("Invalid channel")); break; 307 | case XBEE_MODEM_STATUS_FAILED_TO_JOIN : Serial.print(F("Failed to join AP")); break; 308 | default : Serial.print(F("Unknown Status Code")); break; 309 | } 310 | Serial.println(F(")")); 311 | read_input(true); 312 | } 313 | 314 | // Inbound IP data - output to serial 315 | void ip_data_inbound(unsigned char *data, int len, s_rxinfo *info) 316 | { 317 | Serial.println(); 318 | Serial.print(F("Inbound data from ")); 319 | Serial.print(ipstr(info->source_addr)); 320 | Serial.print(":"); 321 | Serial.print(info->source_port, DEC); 322 | Serial.print(" "); 323 | Serial.print(info->current_offset, DEC); 324 | Serial.print("/"); 325 | Serial.print(info->total_packet_length, DEC); 326 | Serial.println(":"); 327 | data[len] = 0; 328 | Serial.println((char *)data); 329 | Serial.println(); 330 | 331 | // Cause the prompt and current input line to redisplay 332 | read_input(true); 333 | } 334 | 335 | // Convert IP to string and return as internal buffer 336 | char * ipstr(uint8_t *ip) 337 | { 338 | static char ipstrbuf[32]; 339 | sprintf(ipstrbuf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 340 | return ipstrbuf; 341 | } 342 | 343 | 344 | // Parse string as IP and write to config parameter as 4 byte binary 345 | bool parse_as_ip(char *value, uint8_t *config) 346 | { 347 | for (int i = 0; i < strlen(value); i ++) { 348 | if (!(value[i] == '.' || isdigit(value[i]))) return false; 349 | } 350 | char *o1 = strtok(value, "."); 351 | char *o2 = strtok(NULL, "."); 352 | char *o3 = strtok(NULL, "."); 353 | char *o4 = strtok(NULL, "."); 354 | 355 | if (!o1 || !o2 || !o3 || !o4) return false; 356 | 357 | config[0] = atoi(o1); 358 | config[1] = atoi(o2); 359 | config[2] = atoi(o3); 360 | config[3] = atoi(o4); 361 | 362 | return true; 363 | 364 | } 365 | 366 | // Show configuration 367 | void show_config() 368 | { 369 | Serial.print(F("ipmode : ")); 370 | Serial.print(config.ipmode ? F("static") : F("dhcp")); 371 | Serial.println(F(" (dhcp, static)")); 372 | if (config.ipmode) { 373 | Serial.print(F("ipaddr : ")); 374 | print_ip(config.ip); 375 | Serial.print(F("netmask : ")); 376 | print_ip(config.nm); 377 | Serial.print(F("gateway : ")); 378 | print_ip(config.gw); 379 | } 380 | Serial.print(F("port : ")); 381 | Serial.println(config.port, DEC); 382 | Serial.print(F("destip : ")); 383 | print_ip(config.dest_ip); 384 | Serial.print(F("destport : ")); 385 | Serial.println(config.dest_port, DEC); 386 | Serial.print(F("payload : ")); 387 | Serial.print(config.payload == XBEE_NET_IPPROTO_TCP ? F("tcp") : F("udp")); 388 | Serial.println(F(" (tcp, udp)")); 389 | Serial.print(F("endpoint : ")); 390 | Serial.print(config.endpoint == ENDPOINT_RAW ? F("raw") : F("app")); 391 | Serial.println(F(" (raw, app)")); 392 | Serial.print(F("ssid : ")); 393 | Serial.println((char *)config.ssid); 394 | Serial.print(F("encryption : ")); 395 | switch(config.encmode) { 396 | case XBEE_SEC_ENCTYPE_NONE : Serial.print(F("none")); break; 397 | case XBEE_SEC_ENCTYPE_WEP : Serial.print(F("wep")); break; 398 | case XBEE_SEC_ENCTYPE_WPA : Serial.print(F("wpa")); break; 399 | case XBEE_SEC_ENCTYPE_WPA2 : Serial.print(F("wpa2")); break; 400 | } 401 | Serial.println(F(" (none, wep, wpa, wpa2)")); 402 | if (config.encmode != XBEE_SEC_ENCTYPE_NONE) { 403 | Serial.print(F("password : ")); 404 | Serial.println((char *)config.psk); 405 | } 406 | Serial.println(); 407 | } 408 | 409 | // When we get a "set" command, parse it to update input 410 | void update_config(char *input) 411 | { 412 | bool apply_needed = false; 413 | char *cmd = strtok(input, " "); 414 | char *parm = strtok(NULL, " "); 415 | char *value = strtok(NULL, " "); 416 | 417 | if (strcmp(cmd, "set") == 0 && parm && value) { 418 | bool valid = false; 419 | if (strcmp(parm, "ipmode") == 0) { 420 | if (strcmp(value, "dhcp") == 0) { 421 | config.ipmode = XBEE_NET_ADDRMODE_DHCP; 422 | apply_needed = valid = true; 423 | } else if (strcmp(value, "static") == 0) { 424 | config.ipmode = XBEE_NET_ADDRMODE_STATIC; 425 | apply_needed = valid = true; 426 | } 427 | } else if (strcmp(parm, "ipaddr") ==0) { 428 | if (parse_as_ip(value, config.ip)) apply_needed = valid = true; 429 | } else if (strcmp(parm, "gateway") ==0) { 430 | if (parse_as_ip(value, config.gw)) apply_needed = valid = true; 431 | } else if (strcmp(parm, "netmask") ==0) { 432 | if (parse_as_ip(value, config.nm)) apply_needed = valid = true; 433 | } else if (strcmp(parm, "destip") ==0) { 434 | if (parse_as_ip(value, config.dest_ip)) valid = true; 435 | } else if (strcmp(parm, "port") ==0) { 436 | config.port = atoi(value); 437 | apply_needed = valid = true; 438 | } else if (strcmp(parm, "destport") ==0) { 439 | config.dest_port = atoi(value); 440 | valid = true; 441 | } else if (strcmp(parm, "payload") ==0) { 442 | if (strcmp(value, "tcp") == 0) { 443 | config.payload = XBEE_NET_IPPROTO_TCP; 444 | apply_needed = valid = true; 445 | } else if (strcmp(value, "udp") == 0) { 446 | config.payload = XBEE_NET_IPPROTO_UDP; 447 | apply_needed = valid = true; 448 | } 449 | } else if (strcmp(parm, "endpoint") == 0) { 450 | if (strcmp(value, "raw") == 0) { 451 | config.endpoint = ENDPOINT_RAW; 452 | valid = true; 453 | } else if (strcmp(value, "app") == 0) { 454 | config.endpoint = ENDPOINT_APP; 455 | valid = true; 456 | } 457 | } else if (strcmp(parm, "ssid") ==0) { 458 | strncpy((char *)config.ssid, value, MAX_SSID); 459 | apply_needed = valid = true; 460 | } else if (strcmp(parm, "password") ==0) { 461 | strncpy((char *)config.psk, value, MAX_PSK); 462 | apply_needed = valid = true; 463 | } else if (strcmp(parm, "encryption") ==0) { 464 | if (strcmp(value, "none") == 0) { 465 | config.encmode = XBEE_SEC_ENCTYPE_NONE; 466 | apply_needed = valid = true; 467 | } else if (strcmp(value, "wep") == 0) { 468 | config.encmode = XBEE_SEC_ENCTYPE_WEP; 469 | apply_needed = valid = true; 470 | } else if (strcmp(value, "wpa") == 0) { 471 | config.encmode = XBEE_SEC_ENCTYPE_WPA; 472 | apply_needed = valid = true; 473 | } else if (strcmp(value, "wpa2") == 0) { 474 | config.encmode = XBEE_SEC_ENCTYPE_WPA2; 475 | apply_needed = valid = true; 476 | } 477 | } 478 | if (!valid) { 479 | Serial.println(F("huh?")); 480 | } else { 481 | if (apply_needed) { 482 | write_configuration(); 483 | apply_configuration(); 484 | } else { 485 | write_configuration(); 486 | } 487 | Serial.println("ok"); 488 | } 489 | } else { 490 | Serial.println(F("huh?")); 491 | } 492 | } 493 | 494 | // Show system status by querying the xbee 495 | void show_status() 496 | { 497 | uint8_t buf[XBEE_BUFSIZE]; 498 | int len; 499 | if (xbee.at_query(XBEE_AT_ADDR_IPADDR, buf, &len, XBEE_BUFSIZE - 1)) { 500 | buf[len] = 0; 501 | Serial.print(F("IP Address : ")); 502 | Serial.println((char *)buf); 503 | } else { 504 | Serial.println(F("IP Query fail")); 505 | } 506 | if (xbee.at_query(XBEE_AT_ADDR_NETMASK, buf, &len, XBEE_BUFSIZE - 1)) { 507 | buf[len] = 0; 508 | Serial.print(F("Netmask : ")); 509 | Serial.println((char *)buf); 510 | } else { 511 | Serial.println(F("NM Query fail")); 512 | } 513 | if (xbee.at_query(XBEE_AT_ADDR_GATEWAY, buf, &len, XBEE_BUFSIZE - 1)) { 514 | buf[len] = 0; 515 | Serial.print(F("Gateway : ")); 516 | Serial.println((char *) buf); 517 | } else { 518 | Serial.println(F("GW Query fail")); 519 | } 520 | 521 | if (xbee.at_query(XBEE_AT_DIAG_ASSOC_INFO, buf, &len, 1)) { 522 | Serial.print(F("Operation State : ")); 523 | switch(buf[0]) { 524 | case XBEE_DIAG_ASSOC_INSV : Serial.println(F("In Service")); break; 525 | case XBEE_DIAG_ASSOC_INIT_INPROG : Serial.println(F("Init In Progress")); break; 526 | case XBEE_DIAG_ASSOC_DISCONNECTING : Serial.println(F("Disconnecting")); break; 527 | case XBEE_DIAG_ASSOC_SSID_NOT_FOUND : Serial.println(F("SSID Not Found")); break; 528 | case XBEE_DIAG_ASSOC_SSID_NOT_CONFIGURED : Serial.println(F("SSID Not Configured")); break; 529 | case XBEE_DIAG_ASSOC_JOIN_FAILED : Serial.println(F("Join Failed")); break; 530 | case XBEE_DIAG_ASSOC_PENDING_DHCP : Serial.println(F("Pending DHCP")); break; 531 | case XBEE_DIAG_ASSOC_JOINED_IN_SETUP : Serial.println(F("Joined, Setup In Progress")); break; 532 | case XBEE_DIAG_ASSOC_SCANNING : Serial.println(F("Scanning")); break; 533 | default : Serial.println(F("Unknown Code")); break; 534 | } 535 | } else { 536 | Serial.println(F("STATE query fail")); 537 | } 538 | 539 | Serial.println(); 540 | 541 | } 542 | 543 | // Main run loop 544 | void loop() 545 | { 546 | // Read input and if we get something, act upon it 547 | char *input = read_input(); 548 | if (input) { 549 | if (strncmp(input, "set ", 4) == 0) { 550 | update_config(input); 551 | } else if (strcmp(input, "show config") == 0) { 552 | show_config(); 553 | } else if (strcmp(input, "reset") == 0) { 554 | Serial.println("ok"); 555 | xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 556 | apply_configuration(); 557 | } else if (strcmp(input, "defaults") == 0) { 558 | Serial.println("ok"); 559 | xbee.init(XBEE_SELECT, XBEE_ATN, XBEE_RESET, XBEE_DOUT); 560 | init_configuration(); 561 | apply_configuration(); 562 | } else if (strcmp(input, "show status") == 0) { 563 | show_status(); 564 | } else if (strncmp(input, "tx ", 3) == 0) { 565 | s_txoptions txopt; 566 | txopt.dest_port = config.dest_port; 567 | txopt.source_port = config.port; 568 | txopt.protocol = config.payload; 569 | txopt.leave_open = true; 570 | Serial.print("Transmitting..."); 571 | int res = xbee.transmit(config.dest_ip, &txopt, (uint8_t *)input +3, strlen((char *)input +3), true, config.endpoint == ENDPOINT_APP ? true : false); 572 | Serial.println(res ? "ok" : "fail"); 573 | } else if (strcmp(input, "scan") == 0) { 574 | Serial.println(F("ok (recommend reset after scan since you will be disconnected")); 575 | if (!xbee.initiateScan()) Serial.println(F("Failed to start scan, error")); 576 | } else if (strcmp(input, "help") == 0) { 577 | help(); 578 | } else { 579 | Serial.println(F("huh?")); 580 | } 581 | 582 | // Redisplay prompt 583 | Serial.println(); 584 | Serial.print("->"); 585 | } 586 | 587 | // Process the xbee SPI bus 588 | xbee.process(); 589 | } 590 | 591 | -------------------------------------------------------------------------------- /XbeeWifi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File xbeewifi.h 3 | * 4 | * Synopsis Support for Xbee Wifi (XB24-WF...) modules through SPI bus API 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.1 9 | * 10 | * License This software is released under the terms of the Mozilla Public License (MPL) version 2.0 11 | * Full details of licensing terms can be found in the "LICENSE" file, distributed with this code 12 | * 13 | * Hardware Minimum connections required: 14 | * SPI Bus (MISO, MOSI, SCK) 15 | * Chip select line 16 | * Attention line 17 | * Additional connections desired: 18 | * RESET line 19 | * DOUT line 20 | * 21 | * Inclusion of GPIO connections to Xbee RESET and DOUT line allows automatic startup in 22 | * SPI mode, regardless of Xbee configuration. These lines may be omitted from the hardware 23 | * desgign but in this case the Xbee must be pre-configured for SPI operation 24 | * 25 | * Take care to operate the XBee correctly at 3.3v. If using a 5v Arduino, ensure necessary 26 | * hardware is included to provide 5v <-> 3.3v logic level conversion for all connections 27 | * 28 | * Instructions Create a new instance of this class (XbeeWifi) 29 | * Call init method. Must include digital pin numbers for CS (chip select) and ATTN (attention) 30 | * lines. Ideally also provide digital pin numbers for DOUT and RESET lines. 31 | * Inclusion of DOUT and RESET lines will cause the XBee to automatically reset into SPI mode 32 | * upon calling init 33 | * 34 | * Call at_cmd_xxxx methods to issue AT commands 35 | * Call register_ methods to register callback functions for asynchronous operations : 36 | * IP Data Reception 37 | * Modem Status Reception 38 | * Network Scan Reception 39 | * Remote Data Sample Reception 40 | * 41 | * Call process method frequently to ensure that incoming data and notifications are 42 | * processed and dispatched to your callback functions and that the SPI bus doesn't overflow 43 | * from excess pending data to be delivered 44 | * 45 | * Callback functions should *NEVER* call any method on this object since this can cause 46 | * recursive behaviors and stack overflow (crash). 47 | * 48 | * If you've got lots of memory and want a buffered data feed, check out the XbeeWifiBuffered 49 | * class instead. 50 | */ 51 | #ifndef __XBEEWIFI_H 52 | #define __XBEEWIFI_H 53 | #include 54 | 55 | // Set up a macro depending on architecture 56 | #ifdef __SAM3X8E__ 57 | #define ARCH_SAM 58 | #include "xbee_sam.h" 59 | #else 60 | #define ARCH_ATMEGA 61 | #include "xbee_atmega.h" 62 | #endif 63 | 64 | // The compiler is good at optimizing out unused methods, however, certain methods are implicitly used 65 | // to support incoming data that is of unknown type even if that data is then discarded 66 | // If you know you won't be using certain subsystems, then you can uncomment one or more of the following lines 67 | // to further optimize code size 68 | 69 | // If you won't be using network scan, uncomment XBEE_OMIT_SCAN 70 | // #define XBEE_OMIT_SCAN 71 | 72 | // If you won't be receiving data at all 73 | // #define XBEE_OMIT_RX_DATA 74 | 75 | // If you won't be using remote data sampling, uncomment XBEE_OMIT_SAMPLE 76 | // #define XBEE_OMIT_RX_SAMPLE 77 | 78 | // If you want to omit support for Xbee compatability mode, uncomment XBEE_OMIT_COMPAT_MODE 79 | // #define XBEE_OMIT_COMPAT_MODE 80 | 81 | // Definitions of the various API frame types 82 | #define XBEE_API_FRAME_TX64 0x00 83 | #define XBEE_API_FRAME_REMOTE_CMD_REQ 0x07 84 | #define XBEE_API_FRAME_ATCMD 0x08 85 | #define XBEE_API_FRAME_ATCMD_QUEUED 0x09 86 | #define XBEE_API_FRAME_TX_IPV4 0x20 87 | #define XBEE_API_FRAME_RX64_INDICATOR 0x80 88 | #define XBEE_API_FRAME_REMOTE_CMD_RESP 0x87 89 | #define XBEE_API_FRAME_ATCMD_RESP 0x88 90 | #define XBEE_API_FRAME_TX_STATUS 0x89 91 | #define XBEE_API_FRAME_MODEM_STATUS 0x8A 92 | #define XBEE_API_FRAME_IO_DATA_SAMPLE_RX 0x8F 93 | #define XBEE_API_FRAME_RX_IPV4 0xB0 94 | 95 | // Modem Status Values that could be passed to the 96 | // status callback function 97 | #define XBEE_MODEM_STATUS_RESET 0x00 98 | #define XBEE_MODEM_STATUS_WATCHDOG_RESET 0x01 99 | #define XBEE_MODEM_STATUS_JOINED 0x02 100 | #define XBEE_MODEM_STATUS_NO_LONGER_JOINED 0x03 101 | #define XBEE_MODEM_STATUS_IP_CONFIG_ERROR 0x04 102 | #define XBEE_MODEM_STATUS_S_OR_J_WITHOUT_CON 0x82 103 | #define XBEE_MODEM_STATUS_AP_NOT_FOUND 0x83 104 | #define XBEE_MODEM_STATUS_PSK_NOT_CONFIGURED 0x84 105 | #define XBEE_MODEM_STATUS_SSID_NOT_FOUND 0x87 106 | #define XBEE_MODEM_STATUS_FAILED_WITH_SECURITY 0x88 107 | #define XBEE_MODEM_STATUS_INVALID_CHANNEL 0x8A 108 | #define XBEE_MODEM_STATUS_FAILED_TO_JOIN 0x8E 109 | 110 | // Definitions of AT commands for addressing 111 | #define XBEE_AT_ADDR_DEST_ADDR "DL" 112 | #define XBEE_AT_ADDR_IPADDR "MY" 113 | #define XBEE_AT_ADDR_NETMASK "MK" 114 | #define XBEE_AT_ADDR_GATEWAY "GW" 115 | #define XBEE_AT_ADDR_SERNO_HIGH "SH" 116 | #define XBEE_AT_ADDR_SERNO_LOW "SL" 117 | #define XBEE_AT_ADDR_NODEID "NI" 118 | #define XBEE_AT_ADDR_DEST_PORT "DE" 119 | #define XBEE_AT_ADDR_SERIAL_COM_SERVICE_PORT "C0" 120 | #define XBEE_AT_ADDR_DEV_TYPE_ID "DD" 121 | #define XBEE_AT_ADDR_MAX_RF_PAYLOAD_BYTES "NP" 122 | 123 | // Definitions of AT commands for Network parameters 124 | #define XBEE_AT_NET_SSID "ID" 125 | #define XBEE_AT_NET_TYPE "AH" 126 | #define XBEE_AT_NET_IPPROTO "IP" 127 | #define XBEE_AT_NET_ADDRMODE "MA" 128 | #define XBEE_AT_NET_TCP_TIMEOUT "TM" 129 | 130 | // Options associated with network commands 131 | #define XBEE_NET_TYPE_IBSS_JOINER 0x00 132 | #define XBEE_NET_TYPE_IBSS_CREATOR 0x01 133 | #define XBEE_NET_TYPE_IBSS_INFRASTRUCTURE 0x02 134 | 135 | #define XBEE_NET_IPPROTO_UDP 0x00 136 | #define XBEE_NET_IPPROTO_TCP 0x01 137 | 138 | #define XBEE_NET_ADDRMODE_DHCP 0x00 139 | #define XBEE_NET_ADDRMODE_STATIC 0x01 140 | 141 | // Definition of AT commands for Security 142 | #define XBEE_AT_SEC_ENCTYPE "EE" 143 | #define XBEE_AT_SEC_KEY "PK" 144 | 145 | // Options associated with security commands 146 | #define XBEE_SEC_ENCTYPE_NONE 0x00 147 | #define XBEE_SEC_ENCTYPE_WPA 0x01 148 | #define XBEE_SEC_ENCTYPE_WPA2 0x02 149 | #define XBEE_SEC_ENCTYPE_WEP 0x03 150 | 151 | // Definition of AT commands for RF control 152 | #define XBEE_AT_RF_POWER_LEVEL "PL" 153 | #define XBEE_AT_RF_CHANNEL "CH" 154 | #define XBEE_AT_RF_BITRATE "BR" 155 | 156 | // Options associated with RF commands 157 | #define XBEE_RF_BITRATE_AUTO 0x00 158 | #define XBEE_RF_BITRATE_1MBPS 0x01 159 | #define XBEE_RF_BITRATE_2MBPS 0x02 160 | #define XBEE_RF_BITRATE_5MBPS 0x03 161 | #define XBEE_RF_BITRATE_11MBPS 0x04 162 | #define XBEE_RF_BITRATE_6MBPS 0x05 163 | #define XBEE_RF_BITRATE_9MBPS 0x06 164 | #define XBEE_RF_BITRATE_12MBPS 0x07 165 | #define XBEE_RF_BITRATE_18MBPS 0x08 166 | #define XBEE_RF_BITRATE_24MBPS 0x09 167 | #define XBEE_RF_BITRATE_36_BMPS 0x0A 168 | #define XBEE_RF_BITRATE_48_MBPS 0x0B 169 | #define XBEE_RF_BITRATE_54_MBPS 0x0C 170 | #define XBEE_RF_BITRATE_MCS0 0x0D 171 | #define XBEE_RF_BITRATE_MCS1 0x0E 172 | #define XBEE_RF_BITRATE_MCS2 0x0F 173 | #define XBEE_RF_BITRATE_MCS3 0x10 174 | #define XBEE_RF_BITRATE_MCS4 0x11 175 | #define XBEE_RF_BITRATE_MCS5 0x12 176 | #define XBEE_RF_BITRATE_MCS6 0x13 177 | #define XBEE_RF_BITRATE_MCS7 0x14 178 | 179 | // Definition of AT commands for diagnostics 180 | #define XBEE_AT_DIAG_FIRMWARE_VERSION "VR" 181 | #define XBEE_AT_DIAG_HARDWARE_VERSION "HV" 182 | #define XBEE_AT_DIAG_ASSOC_INFO "AI" 183 | #define XBEE_AT_DIAG_ACTIVE_SCAN "AS" 184 | #define XBEE_AT_DIAG_TEMPERATURE "TP" 185 | #define XBEE_AT_DIAG_CONFIG_CODE "CK" 186 | #define XBEE_AT_DIAG_SUPPLY_VOLTAGE "%V" 187 | #define XBEE_AT_DIAG_RSSI "DB" 188 | 189 | // Options associated with diagnostic commands 190 | #define XBEE_DIAG_ASSOC_INSV 0x00 191 | #define XBEE_DIAG_ASSOC_INIT_INPROG 0x01 192 | #define XBEE_DIAG_ASSOC_DISCONNECTING 0x13 193 | #define XBEE_DIAG_ASSOC_SSID_NOT_FOUND 0x22 194 | #define XBEE_DIAG_ASSOC_SSID_NOT_CONFIGURED 0x23 195 | #define XBEE_DIAG_ASSOC_JOIN_FAILED 0x27 196 | #define XBEE_DIAG_ASSOC_PENDING_DHCP 0x41 197 | #define XBEE_DIAG_ASSOC_JOINED_IN_SETUP 0x42 198 | #define XBEE_DIAG_ASSOC_SCANNING 0xFF 199 | 200 | // Definition of AT commands for serial control 201 | #define XBEE_AT_SERIAL_API_ENABLE "AP" 202 | #define XBEE_AT_SERIAL_INTERFACE_DATA_RATE "BD" 203 | #define XBEE_AT_SERIAL_SERIAL_PARITY "NB" 204 | #define XBEE_AT_SERIAL_STOP_BITS "SB" 205 | #define XBEE_AT_SERIAL_PACKET_TIMEOUT "RO" 206 | #define XBEE_AT_SERIAL_DIO7_CONFIG "D7" 207 | #define XBEE_AT_SERIAL_DIO6_CONFIG "D6" 208 | 209 | // Options associated with serial control commands 210 | #define XBEE_SERIAL_API_TRANSPARENT 0x00 211 | #define XBEE_SERIAL_API_ENABLE_NOESC 0x01 212 | #define XBEE_SERIAL_API_ENABLE_ESC 0x02 213 | 214 | #define XBEE_SERIAL_DATA_RATE_1200 0x00 215 | #define XBEE_SERIAL_DATA_RATE_2400 0x01 216 | #define XBEE_SERIAL_DATA_RATE_4800 0x02 217 | #define XBEE_SERIAL_DATA_RATE_9600 0x03 218 | #define XBEE_SERIAL_DATA_RATE_19200 0x04 219 | #define XBEE_SERIAL_DATA_RATE_38400 0x05 220 | #define XBEE_SERIAL_DATA_RATE_57600 0x06 221 | #define XBEE_SERIAL_DATA_RATE_115200 0x07 222 | #define XBEE_SERIAL_DATA_RATE_230400 0x08 223 | 224 | #define XBEE_SERIAL_PARITY_NONE 0x00 225 | #define XBEE_SERIAL_PARITY_EVENT 0x01 226 | #define XBEE_SERIAL_PARITY_ODD 0x02 227 | 228 | #define XBEE_SERIAL_STOPBITS_1 0x00 229 | #define XBEE_SERIAL_STOPBITS_2 0x01 230 | 231 | #define XBEE_SERIAL_DIO7_DISABLED 0x00 232 | #define XBEE_SERIAL_DIO7_CTS 0x01 233 | #define XBEE_SERIAL_DIO7_DIGITAL_IN 0x03 234 | #define XBEE_SERIAL_DIO7_DIGITAL_OUT_LOW 0x04 235 | #define XBEE_SERIAL_DIO7_DIGITAL_OUT_HIGH 0x05 236 | #define XBEE_SERIAL_DIO7_RS485_TX_ENABLE_HIGH 0x06 237 | #define XBEE_SERIAL_DIO7_RS485_TX_ENABLE_LOW 0x07 238 | 239 | #define XBEE_SERIAL_DIO6_DISABLED 0x00 240 | #define XBEE_SERIAL_DIO6_RTS 0x01 241 | #define XBEE_SERIAL_DIO6_DIGITAL_IN 0x03 242 | #define XBEE_SERIAL_DIO6_DIGITAL_OUT_LOW 0x04 243 | #define XBEE_SERIAL_DIO6_DIGITAL_OUT_HIGH 0x05 244 | 245 | // Definition of AT commands for IO control 246 | #define XBEE_AT_IO_FORCE_SAMPLE "IS" 247 | #define XBEE_AT_IO_SAMPLE_RATE "IR" 248 | #define XBEE_AT_IO_DIGITAL_CHANGE_DETECTION "IC" 249 | #define XBEE_AT_IO_SAMPLE_FROM_SLEEP_RATE "IF" 250 | #define XBEE_AT_IO_DIO10_CONFIG "P0" 251 | #define XBEE_AT_IO_DIO11_CONFIG "P1" 252 | #define XBEE_AT_IO_DIO12_CONFIG "P2" 253 | #define XBEE_AT_IO_DOUT_CONFIG "P3" 254 | #define XBEE_AT_IO_DIN_CONFIG "P4" 255 | #define XBEE_AT_IO_AD0_DIO0_CONFIG "D0" 256 | #define XBEE_AT_IO_AD1_DIO1_CONFIG "D1" 257 | #define XBEE_AT_IO_AD2_DIO2_CONFIG "D2" 258 | #define XBEE_AT_IO_AD3_DIO3_CONFIG "D3" 259 | #define XBEE_AT_IO_DIO4_CONFIG "D4" 260 | #define XBEE_AT_IO_DIO5_CONFIG "D5" 261 | #define XBEE_AT_IO_DIO8_CONFIG "D8" 262 | #define XBEE_AT_IO_DIO9_CONFIG "D9" 263 | #define XBEE_AT_IO_ASSOC_LED_BLINK_TIME "LT" 264 | #define XBEE_AT_IO_PULLUP "PR" 265 | #define XBEE_AT_IO_PULL_DIRECTION "PD" 266 | #define XBEE_AT_IO_ANALOG_VOLTAGE_REF "AV" 267 | #define XBEE_AT_IO_PWM0_DUTY_CYCLE "M0" 268 | #define XBEE_AT_IO_PWM1_DUTY_CYCLE "M1" 269 | 270 | // Options associated with IO commands 271 | #define XBEE_IO_DISABLED 0x00 272 | #define XBEE_IO_ENABLED 0x01 273 | #define XBEE_IO_ANALOG_INPUT 0x02 274 | #define XBEE_IO_DIGITAL_INPUT_MONITORED 0x03 275 | #define XBEE_IO_DIGITAL_INPUT_DEFAULT_LOW 0x04 276 | #define XBEE_IO_DIGITAL_INPUT_DEFAULT_HIGH 0x05 277 | #define XBEE_IO_SPI_MISO 0X01 278 | #define XBEE_IO_SPI_ATTN 0x01 279 | #define XBEE_IO_SPI_CLK 0x01 280 | #define XBEE_IO_SPI_SELECT 0x01 281 | #define XBEE_IO_SPI_MOSI 0x01 282 | #define XBEE_IO_ASSOC_INDICATOR 0x01 283 | #define XBEE_IO_SLEEP_REQ 0x01 284 | #define XBEE_IO_ON_SLEED_INDICATOR 0x01 285 | #define XBEE_IO_AVREF_1_25V 0x00 286 | #define XBEE_IO_AVREF_2_5V 0x01 287 | 288 | // Definition of AT commands for AT command options 289 | #define XBEE_AT_CMD_OPT_CMD_MODE_TIMEOUT "CT" 290 | #define XBEE_AT_CMD_OPT_EXIT_COMMAND_MODE "CN" 291 | #define XBEE_AT_CMD_OPT_GUARD_TIMES "GT" 292 | #define XBEE_AT_CMD_OPT_CMD_MODE_CHAR "CC" 293 | 294 | // Definition of AT commands for sleep commands 295 | #define XBEE_AT_SLEEP_MODE "SM" 296 | #define XBEE_AT_SLEEP_PERIOD "SP" 297 | #define XBEE_AT_SLEEP_OPTIONS "SO" 298 | #define XBEE_AT_WAKE_HOST "WH" 299 | #define XBEE_AT_WAKE_TIME "ST" 300 | 301 | // Options associated with sleep commands 302 | #define XBEE_SLEEP_NO_SLEEP 0x00 303 | #define XBEE_SLEEP_PIN_SLEEP 0x01 304 | #define XBEE_SLEEP_CYCLIC_SLEEP 0x04 305 | #define XBEE_SLEEP_CYCLIC_SLEEP_PIN_WAKE 0x05 306 | 307 | // Definition of AT commands for execution 308 | #define XBEE_AT_EXEC_APPLY_CHANGES "AC" 309 | #define XBEE_AT_EXEC_WRITE "WR" 310 | #define XBEE_AT_EXEC_RESTORE_DEFAULTS "RE" 311 | #define XBEE_AT_EXEC_SOFTWARE_RESET "FR" 312 | #define XBEE_AT_EXEC_NETWORK_RESET "NR" 313 | 314 | // This structure is used with the IP data callback to 315 | // report information about the incoming IP data 316 | typedef struct { 317 | uint8_t source_addr[4]; // Address from which the data originated 318 | uint16_t source_port; // Port from which the data originated 319 | uint16_t dest_port; // Port on which the data arrived. If 0xBEE, data was received using app service 320 | uint8_t protocol; // XBEE_NET_IPPROTO_UDP / TCP 321 | uint16_t sequence; // Segment number 322 | uint16_t total_packet_length; // Total length of the incoming packet 323 | uint16_t current_offset; // Current offset within the incoming packet of this segment 324 | bool final; // True for the final segment of this packet 325 | bool checksum_error; // Checksum indication flag 326 | } s_rxinfo; 327 | 328 | // Note that due to buffer size restrictions, an incoming data packet (of up to 1400 bytes length) 329 | // will be delivered in multiple calls to the ip data reception callback 330 | // The sequence number will be the same for all calls for a given packet and then incremented 331 | // for the next packet 332 | // A checksum error will only be flagged (true) on the last given call for a packet / sequence 333 | 334 | 335 | // This structure is used to provide transmission options when transmiting IP data 336 | typedef struct { 337 | uint16_t dest_port; 338 | uint16_t source_port; 339 | uint8_t protocol; // XBEE_NET_IPPROTO_UDP / TCP 340 | bool leave_open; 341 | } s_txoptions; 342 | 343 | // This packet is used for the sample reception callback to provide sample data 344 | typedef struct { 345 | uint8_t source_addr[4]; 346 | uint16_t digital_mask; 347 | uint8_t analog_mask; 348 | uint16_t digital_samples; 349 | uint16_t analog_samples; 350 | } s_sample; 351 | 352 | class XbeeWifi 353 | { 354 | public: 355 | 356 | // Constructor 357 | XbeeWifi(); 358 | 359 | // Must call before any other functions to initialize the xbee 360 | // Provide cs (required), atn (required) pins and reset (optional), dout (optional) 361 | // If reset and dout are not connected then the module will not be reset / forced into SPI mode 362 | // on init 363 | bool init(uint8_t cs, uint8_t atn, uint8_t reset = 0xFF, uint8_t dout = 0xFF); 364 | 365 | // Send AT command with data of various possible forms 366 | // atxx = Two digit string (i.e. "XY" would indicate ATXY command) 367 | // Set queued = true to delay execution until applied (per spec) 368 | // But note that queued AT commands are executed without confirmation 369 | // so errors will not be reported 370 | #ifndef XBEE_OMIT_LOCAL_AT 371 | bool at_cmd_raw(const char *atxx, uint8_t *buffer, int len, bool queued = false); 372 | bool at_cmd_str(const char *atxx, const char *buffer, bool queued = false); 373 | bool at_cmd_byte(const char *atxx, uint8_t byte, bool queued = false); 374 | bool at_cmd_short(const char *atxx, uint16_t twobyte, bool queued = false); 375 | bool at_cmd_noparm(const char *atxx, bool queued = false); 376 | 377 | // Query an AT parameter 378 | // Provide a buffer (parmval) and it's length (maxlen) 379 | // Will return parmlen indicating the number of bytes read back into the buffer 380 | bool at_query(const char *atxx, uint8_t *parmval, int *parmlen, int maxlen); 381 | #endif 382 | 383 | // Equivalent AT set / get methods for targetting a remote device 384 | // as described by the IP address (of form uint8_t ip[4]) 385 | // Set apply=true (default) to cause the remote device to immediately apply the command 386 | // Use apply=false to defer application until subsequent apply operation 387 | #ifndef XBEE_OMIT_REMOTE_AT 388 | bool at_remcmd_raw(uint8_t *ip, const char *atxx, uint8_t *buffer, int len, bool apply = true); 389 | bool at_remcmd_str(uint8_t *ip, const char *atxx, const char *buffer, bool apply = true); 390 | bool at_remcmd_byte(uint8_t *ip, const char *atxx, uint8_t byte, bool apply = true); 391 | bool at_remcmd_short(uint8_t *ip, const char *atxx, uint16_t twobyte, bool apply = true); 392 | bool at_remcmd_noparm(uint8_t *ip, const char *atxx, bool apply = true); 393 | bool at_remquery(uint8_t *ip, const char *atxx, uint8_t *parmval, int *parmlen, int maxlen); 394 | #endif 395 | 396 | // Provide a reference of the last modem status 397 | volatile uint8_t last_status; 398 | 399 | // The following functions define callbacks for asynchronous data delivery 400 | // To stop delivery (and discard data) of any given type 401 | // set the associated callback to it's default (NULL) 402 | 403 | // Register a callback to receive incoming IP data 404 | // Callback should be of following form: 405 | // void my_callback(uint8_t *data, int len, s_rxinfo *info) 406 | #ifndef XBEE_OMIT_RX_DATA 407 | void register_ip_data_callback(void (*func)(uint8_t *, int, s_rxinfo *)); 408 | #endif 409 | 410 | // Register callback for modem status indications 411 | // Callback should be of following form: 412 | // void my_callback(uint8_t status) 413 | void register_status_callback(void (*func)(uint8_t)); 414 | 415 | // Register a callback for network scan returns 416 | // Callback should be of following form: 417 | // void my_callback(uint8_t encryption_mode, int rssi, char *ssid) 418 | #ifndef XBEE_OMIT_SCAN 419 | void register_scan_callback(void (*func)(uint8_t, int, char *)); 420 | #endif 421 | 422 | // Register a callback for remote data sample reception 423 | // Callback should be of following form: 424 | // void my_callback(s_sample *sampledata) 425 | #ifndef XBEE_OMIT_RX_SAMPLE 426 | void register_sample_callback(void (*func)(s_sample *)); 427 | #endif 428 | 429 | // Call as often as possible to check for inbound data 430 | // Will trigger register_ip_data_callback to receive and process any inbound data 431 | void process(bool rx_one_packet_only = false); 432 | 433 | // Transmit data to an endpoint 434 | // ip should be the binary form (uint8_t[4]) IP address 435 | // addr should be transmission options indicating port assignments and such. May be null when useAppService is true 436 | // data and len provide the data to be transmitted 437 | // Leave confirm=true to block for confirmation of delivery (TCP) 438 | // Set useAppService to true to use the compatability mode (64bit) app service to transmit the data to the 0xBEE port 439 | bool transmit(const uint8_t *ip, s_txoptions *addr, uint8_t *data, int len, bool confirm = true, bool useAppService = false); 440 | 441 | // Initiate a network scan 442 | // Will cause the registered scan callback to be called with information about APs that are heard 443 | // Causes network reset! Connection will be downed and will need to be reconfigured (or xBee reset if appropriate) 444 | #ifndef XBEE_OMIT_SCAN 445 | bool initiateScan(); 446 | #endif 447 | 448 | protected: 449 | #ifndef XBEE_OMIT_RX_data 450 | virtual void dispatch(uint8_t *data, int len, s_rxinfo *info); 451 | #endif 452 | 453 | private: 454 | // This is the actual method that does all AT processing 455 | bool at_cmd(const char *atxx, const uint8_t *parmval, int parmlen, void *returndata, int *returnlen, bool queued); 456 | 457 | // And this is the equivalent for remote commands 458 | bool at_remcmd(uint8_t ip[4], const char *atxx, const uint8_t *parmval, int parmlen, void *returndata, int *returnlen, bool apply); 459 | 460 | // Read from SPI, single byte 461 | uint8_t read(); 462 | 463 | // Write to SPI buffer of given length 464 | void write(const uint8_t *data, int len); 465 | 466 | // Receive an API frame, providing type, length and data to a max of bufsize 467 | // If bufsize is < len then data will be truncated 468 | int rx_frame(uint8_t *frame_type, unsigned int *len, uint8_t *data, int bufsize, unsigned long atn_wait_ms = 5000L, bool return_status = false, bool single_ip_rx_only = false); 469 | 470 | // Transmit an API frame of specified type, length and data 471 | void tx_frame(uint8_t type, unsigned int len, uint8_t *data); 472 | 473 | // Start / End SPI operation 474 | void spiStart(); 475 | void spiEnd(); 476 | 477 | // Perform the actual TX/RX on SPI bus 478 | uint8_t rxtx(uint8_t data); 479 | 480 | // Read and dispatch an inbound IP packet 481 | #ifndef XBEE_OMIT_RX_DATA 482 | void rx_ip(unsigned int len, uint8_t frame_type); 483 | #endif 484 | 485 | // Read and dispatch inbound sample packet 486 | #ifndef XBEE_OMIT_RX_SAMPLE 487 | void rx_sample(unsigned int len); 488 | #endif 489 | 490 | // Read and dispatch an inbound modem status packet 491 | void rx_modem_status(unsigned int len); 492 | 493 | // Wait for ATN to be asserted to a maximum period (millisecs) 494 | // Returns true on proper assert, false on timeout 495 | bool wait_atn(unsigned long int max_millis = 5000L); 496 | 497 | // Flush all content from the incoming SPI buffer (i.e. read until ATN de-asserts) 498 | void flush_spi(); 499 | 500 | // Our internal records of our pin assignments 501 | uint8_t pin_cs; 502 | uint8_t pin_atn; 503 | uint8_t pin_dout; 504 | uint8_t pin_reset; 505 | #ifdef ARCH_SAM 506 | uint8_t pin_cs_actual; 507 | uint8_t spi_ch; 508 | #endif 509 | 510 | // RX seq 511 | #ifndef XBEE_OMIT_RX_DATA 512 | uint16_t rx_seq; 513 | #endif 514 | 515 | // The function pointer for IP callback 516 | #ifndef XBEE_OMIT_RX_DATA 517 | void (*ip_data_func)(uint8_t *, int, s_rxinfo *); 518 | #endif 519 | 520 | // The function pointer for modem status callback 521 | void (*modem_status_func)(uint8_t); 522 | 523 | // The function pointer for scan callback 524 | #ifndef XBEE_OMIT_SCAN 525 | void (*scan_func)(uint8_t, int, char *); 526 | #endif 527 | 528 | // The function pointer for sample callback 529 | #ifndef XBEE_OMIT_RX_SAMPLE 530 | void (*sample_func)(s_sample *); 531 | #endif 532 | 533 | // The next ATID to use for sequencing AT comamnd responses 534 | uint8_t next_atid; 535 | 536 | #ifndef XBEE_OMIT_SCAN 537 | // Handles incoming active scan data (AT responses to AS command) 538 | void handleActiveScan(uint8_t *buf, int len); 539 | #endif 540 | 541 | // Track RX callback depth 542 | uint8_t callback_depth; 543 | 544 | #ifdef ARCH_ATMEGA 545 | // To be nice about things, we reset SPCR after using it, copy of SPCR held here 546 | // Ditto SPSR - which is in fact just the SPI2X bit which is the only writable bit here 547 | uint8_t spcr_copy; 548 | uint8_t spsr_copy; 549 | #endif 550 | 551 | // True when we have the Xbee Chip Select asserted 552 | bool spiRunning; 553 | 554 | // True to prevent SPI bus from being de-selected by endSpi function 555 | // in some cases 556 | bool spiLocked; 557 | 558 | }; 559 | 560 | #ifndef XBEE_OMIT_RX_DATA 561 | // The XbeeWifiBuffered class is a derivative class that provides 562 | // buffered access to the incoming IP data 563 | // 564 | // This is problematic, which is why the main XbeeWifi class does not 565 | // buffer data but dispatches it asynchronously via callback 566 | // 567 | // But on larger Arduinos (particuarly the Due) you could use this 568 | // class and assign some memory as a buffer into which incoming packets 569 | // are captured and then read them using the "available", "read" methods 570 | // 571 | // Strictly speaking if you want to do this you're probably better off 572 | // usign the UART on the Xbee to read data instead of SPI 573 | // Still - there might be a use case for this 574 | class XbeeWifiBuffered : public XbeeWifi 575 | { 576 | public: 577 | // Must provide a desired buffer size when constructing 578 | // If at any time incoming data is in excess of this buffer size, you will lose data 579 | // This is why you should probably be using the Xbee serial service instead of SPI 580 | XbeeWifiBuffered(uint16_t bufsize); 581 | 582 | // Destructor since we use dynamic allocation 583 | ~XbeeWifiBuffered(); 584 | 585 | // Returns the number of available bytes 586 | bool available(); 587 | 588 | // Reads the next byte. Always returns 0 if no bytes were available 589 | uint8_t read(); 590 | 591 | // Peeks the next byte. Always returns 0 if no bytes are in the buffer 592 | uint8_t peek(); 593 | 594 | // Flush all items out of the buffer 595 | void flush(); 596 | 597 | // Returns true if a buffer overrun has occurred (and resets the overrun 598 | // state to false unless reset is marked false) 599 | bool overran(bool reset = true); 600 | 601 | protected: 602 | // We will rewrite the XbeeWifi::dispatch method to capture the incoming data 603 | // into the FIFO buffer 604 | virtual void dispatch(uint8_t *data, int len, s_rxinfo *info); 605 | 606 | private: 607 | // Move register_ip_data_callback to private space 608 | // This is not callable from the buffered version of the class 609 | void register_ip_data_callback(void (*func)(uint8_t *, int, s_rxinfo *)); 610 | 611 | // The buffer 612 | uint8_t *buffer; 613 | 614 | // Declared size of the buffer 615 | uint16_t bufsize; 616 | 617 | // Head of the buffer 618 | uint16_t head; 619 | 620 | // Tail of the buffer 621 | uint16_t tail; 622 | 623 | // The number of bytes currently in the buffer 624 | uint16_t size; 625 | 626 | // Flag when a buffer overrun has occurred here 627 | bool buffer_overrun; 628 | }; 629 | #endif 630 | 631 | #endif /* __XBEE_WIFI_H_ */ 632 | -------------------------------------------------------------------------------- /XbeeWifi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File xbeewifi.cpp 3 | * 4 | * Synopsis Support for Xbee Wifi (XB24-WF...) modules through SPI bus 5 | * 6 | * Author Chris Bearman 7 | * 8 | * Version 2.1 9 | * 10 | * License This software is released under the terms of the Mozilla Public License (MPL) version 2.0 11 | * Full details of licensing terms can be found in the "LICENSE" file, distributed with this code 12 | * 13 | * Instructions See xbeewifi.h 14 | */ 15 | #include "XbeeWifi.h" 16 | #include 17 | 18 | // Debugging... 19 | // Uncomment the following line to enable debug output to serial 20 | // When using debug, caller is responsible for initializing Serial interface 21 | 22 | //#define XBEE_ENABLE_DEBUG 23 | 24 | // Debug functions 25 | #ifdef XBEE_ENABLE_DEBUG 26 | // Debug is enabled, XBEE_DEBUG is a simple macro that just inserts the code within it's parameter 27 | #define XBEE_DEBUG(x) (x) 28 | #else 29 | // Debug is not enabled, the XBEE_DEBUG becomes a NOP macro that essentially discards it's parameter 30 | #define XBEE_DEBUG(x) 31 | #endif 32 | 33 | // The following codes are returned by the rx_frame method, and used internally within this module 34 | #define RX_SUCCESS 0 35 | #define RX_FAIL_WAITING_FOR_ATN -1 36 | #define RX_FAIL_INVALID_START_BYTE -2 37 | #define RX_FAIL_TRUNCATED -3 38 | #define RX_FAIL_CHECKSUM -4 39 | 40 | // Constructor (default) 41 | XbeeWifi::XbeeWifi() : 42 | last_status(XBEE_MODEM_STATUS_RESET), 43 | #ifndef XBEE_OMIT_RX_DATA 44 | rx_seq(0), 45 | ip_data_func(NULL), 46 | #endif 47 | modem_status_func(NULL), 48 | #ifndef XBEE_OMIT_SCAN 49 | scan_func(NULL), 50 | #endif 51 | #ifndef XBEE_OMIT_RX_SAMPLE 52 | sample_func(NULL), 53 | #endif 54 | next_atid(0), 55 | callback_depth(0), 56 | #ifdef ARCH_ATMEGA 57 | spcr_copy(SPCR), 58 | spsr_copy(SPSR), 59 | #endif 60 | spiRunning(false), 61 | spiLocked(false) 62 | { 63 | } 64 | 65 | // Write a buffer of given length to SPI 66 | // Writing multiple bytes from a single function is optimal from a SPI bus usage perspective 67 | void XbeeWifi::write(const uint8_t *data, int len) 68 | { 69 | uint8_t rxbyte; 70 | XBEE_DEBUG(Serial.print(F("Write"))); 71 | XBEE_DEBUG(Serial.println(len, DEC)); 72 | 73 | // Output data 74 | for (int i = 0; i < len; i++) { 75 | #ifdef XBEE_ENABLE_DEBUG 76 | if (digitalRead(pin_atn) == LOW) Serial.println("ATN Asserted during write"); 77 | #endif 78 | XBEE_DEBUG(Serial.print(F("OUT 0x"))); 79 | XBEE_DEBUG(Serial.println(data[i], HEX)); 80 | rxbyte = rxtx(data[i]); 81 | } 82 | } 83 | 84 | // Set up for SPI operation, assert chip select 85 | void XbeeWifi::spiStart() 86 | { 87 | if (spiRunning) return; 88 | spiRunning = true; 89 | XBEE_DEBUG(Serial.println("SPI Start")); 90 | delay(1); 91 | #ifdef ARCH_ATMEGA 92 | spcr_copy = SPCR; 93 | spsr_copy = SPSR; 94 | SPCR = XBEE_SPCR; 95 | SPSR = XBEE_SPSR; 96 | #endif 97 | digitalWrite(pin_cs, LOW); 98 | #if NOP_COUNT > 0 99 | for (int i = 0 ; i < NOP_COUNT; i++) __asm__("nop\n\t"); 100 | #endif 101 | } 102 | 103 | // Clean up from SPI operation, de-assert chip select, unless SPI has been locked 104 | void XbeeWifi::spiEnd() 105 | { 106 | if (!spiRunning || spiLocked) return; 107 | #if NOP_COUNT > 0 108 | for (int i = 0 ; i < NOP_COUNT; i++) __asm__("nop\n\t"); 109 | #endif 110 | spiRunning = false; 111 | XBEE_DEBUG(Serial.println("SPI End")); 112 | digitalWrite(pin_cs, HIGH); 113 | #ifdef ARCH_ATMEGA 114 | SPCR = spcr_copy; 115 | SPSR = spsr_copy; 116 | #endif 117 | } 118 | 119 | uint8_t XbeeWifi::rxtx(uint8_t data) 120 | { 121 | uint8_t rx; 122 | #ifdef ARCH_ATMEGA 123 | SPDR = data; 124 | while(!(SPSR & (1<SPI_SR & SPI_SR_TDRE) == 0) { }; 129 | SPI_INTERFACE->SPI_TDR = ((uint32_t) SPI_PCS(spi_ch) | (uint32_t) data); 130 | while ((SPI_INTERFACE->SPI_SR & SPI_SR_RDRF) == 0) { }; 131 | rx = (SPI_INTERFACE->SPI_RDR & 0xFF); 132 | #endif 133 | return rx; 134 | } 135 | 136 | // Read a buffer of given length from SPI 137 | // Reading multiple bytes in a single function is again optimal 138 | uint8_t XbeeWifi::read() 139 | { 140 | // A read is accomplished by transmitting a meaningless byte 141 | uint8_t data = rxtx(0x00); 142 | XBEE_DEBUG(Serial.print("IN 0x")); 143 | XBEE_DEBUG(Serial.println(data, HEX)); 144 | 145 | // Return the data 146 | return data; 147 | } 148 | 149 | // Initialize the XBEE 150 | bool XbeeWifi::init(uint8_t cs, uint8_t atn, uint8_t reset, uint8_t dout) 151 | { 152 | // Capture pin assignments for later use 153 | pin_cs = cs; 154 | pin_atn = atn; 155 | pin_reset = reset; 156 | pin_dout = dout; 157 | 158 | // Output details for debugging 159 | XBEE_DEBUG(Serial.print(F("CS = "))); 160 | XBEE_DEBUG(Serial.print(pin_cs, DEC)); 161 | XBEE_DEBUG(Serial.print(F(", ATN = "))); 162 | XBEE_DEBUG(Serial.print(pin_atn, DEC)); 163 | XBEE_DEBUG(Serial.print(F(", DOUT = "))); 164 | XBEE_DEBUG(Serial.print(pin_dout, DEC)); 165 | XBEE_DEBUG(Serial.print(F(", RST = "))); 166 | XBEE_DEBUG(Serial.println(pin_reset, DEC)); 167 | 168 | // Set correct states for SPI lines 169 | #ifdef ARCH_ATMEGA 170 | // Don't want to do this on the DUE 171 | pinMode(MOSI, OUTPUT); 172 | pinMode(MISO, INPUT); 173 | pinMode(SCK, OUTPUT); 174 | pinMode(SS, OUTPUT); // SS *MUST* be OUTPUT, even if not used as the select line 175 | #endif 176 | pinMode(pin_cs, OUTPUT); 177 | digitalWrite(pin_cs, HIGH); 178 | 179 | // Set correct state for other signal lines 180 | pinMode(pin_atn, INPUT); 181 | digitalWrite(pin_atn, HIGH); // Pull-up 182 | 183 | #ifdef ARCH_SAM 184 | // Set up SPI 185 | PIO_Configure( 186 | g_APinDescription[PIN_SPI_MOSI].pPort, 187 | g_APinDescription[PIN_SPI_MOSI].ulPinType, 188 | g_APinDescription[PIN_SPI_MOSI].ulPin, 189 | g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); 190 | PIO_Configure( 191 | g_APinDescription[PIN_SPI_MISO].pPort, 192 | g_APinDescription[PIN_SPI_MISO].ulPinType, 193 | g_APinDescription[PIN_SPI_MISO].ulPin, 194 | g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); 195 | PIO_Configure( 196 | g_APinDescription[PIN_SPI_SCK].pPort, 197 | g_APinDescription[PIN_SPI_SCK].ulPinType, 198 | g_APinDescription[PIN_SPI_SCK].ulPin, 199 | g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); 200 | SPI_Configure(SPI_INTERFACE, SPI_INTERFACE_ID, SPI_MR_MSTR | SPI_MR_PS | SPI_MR_MODFDIS); 201 | SPI_Enable(SPI_INTERFACE); 202 | 203 | // pin_cs actual is the pin that the SPI controller is using for CS, it may - or may not - be the same pin 204 | // that we're actually using... 205 | pin_cs_actual = (pin_cs == BOARD_SPI_SS0 || pin_cs == BOARD_SPI_SS1 || pin_cs == BOARD_SPI_SS2 || pin_cs == BOARD_SPI_SS3) ? pin_cs : SPI_CS_DEFAULT; 206 | 207 | /* 208 | We are NOT associating the CS pin to the SPI controller. This seems to work okay since we handle CS manually in all cases... 209 | If we were associating the actual CS pin, we'd do this... 210 | uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(pin_cs_actual); 211 | PIO_Configure( 212 | g_APinDescription[spiPin].pPort, 213 | g_APinDescription[spiPin].ulPinType, 214 | g_APinDescription[spiPin].ulPin, 215 | g_APinDescription[spiPin].ulPinConfiguration); 216 | */ 217 | 218 | // Set up SPI control register 219 | // 0x02 = SPI Mode 0 (CPOL = 0, CPHA = 0) 220 | SPI_ConfigureNPCS(SPI_INTERFACE, spi_ch, 0x02 | SPI_CSR_SCBR(SPI_BUS_DIVISOR) | SPI_CSR_DLYBCT(1)); 221 | #endif 222 | 223 | // Do we have pin assignments for RESET and DOUT? 224 | if (pin_reset != 0xFF && pin_dout != 0xFF) { 225 | // Yes - we can do a reset on the XBee and force the device into SPI mode 226 | 227 | // Tristate the reset pin 228 | pinMode(pin_reset, INPUT); 229 | 230 | // Set DOUT to OUTPUT and bring it low 231 | pinMode(pin_dout, OUTPUT); 232 | digitalWrite(pin_dout, LOW); 233 | 234 | // Set RESET to OUTPUT and go LOW to reset the chip 235 | // now that DOUT is low which forces SPI mode 236 | pinMode(pin_reset, OUTPUT); 237 | digitalWrite(pin_reset, LOW); 238 | 239 | // Stay in reset for 1/10 sec to ensure the device gets the message 240 | delay(100); 241 | 242 | // Take XBEE out of reset, still leaving DOUT LOW 243 | // by tri-moding the reset pin and applying internal pullup 244 | pinMode(pin_reset, INPUT); 245 | digitalWrite(pin_reset, HIGH); 246 | 247 | // We expect to see ATN go high to confirm SPI mode 248 | if (!wait_atn()) { 249 | // ATN did not go high 250 | XBEE_DEBUG(Serial.println(F("No ATN assert on reset"))); 251 | return false; 252 | } 253 | 254 | // Tristate DOUT pin, we're done with it 255 | pinMode(pin_dout, INPUT); 256 | 257 | // The reset / force SPI auto-queues a status frame 258 | // so go ahead and read it 259 | uint8_t buf[XBEE_BUFSIZE]; 260 | uint8_t type; 261 | unsigned int len; 262 | 263 | // Normally rx_frame consumes and dispatches modem status frames separately 264 | // however we explicitly request it to be returned in this case 265 | int result = rx_frame(&type, &len, buf, XBEE_BUFSIZE, 5000L, true); 266 | 267 | if (result == RX_SUCCESS && type == XBEE_API_FRAME_MODEM_STATUS) { 268 | // Good status frame - we have an Xbee talking to us! 269 | return true; 270 | } else { 271 | XBEE_DEBUG(Serial.println(F("****** Failure rx status"))); 272 | return false; 273 | } 274 | } else { 275 | // Don't have assignments for RESET and DOUT so we have to assume 276 | // that the XBee has already been correctly pre-configured for SPI 277 | return true; 278 | } 279 | } 280 | 281 | // Transmit a SPI API frame 282 | // type should be the type of frame (XBEE_API_FRAME_.....) 283 | // data (of length len) should be all data within the frame, excluding frame id, length or checksum 284 | void XbeeWifi::tx_frame(uint8_t type, unsigned int len, uint8_t *data) 285 | { 286 | // Grab the SPI bus ASAP 287 | spiStart(); 288 | 289 | // It is prudent to check for incoming frames cached, or otherwise we'd loose / corrupt 290 | // them as we transmit ours. By locking the SPI bus we prevent it from being released 291 | // after a packet is received (if a packet is received) 292 | spiLocked = true; 293 | process(); 294 | 295 | // Safe now to send our packet and we must also make sure and unlock the SPI 296 | // bus so that when we deassert CS (spiEnd) later, it will in fact deassert 297 | spiLocked = false; 298 | 299 | // Calculate the proper checksum (sum of all bytes - type onward) subtracted from 0xFF 300 | uint8_t cs = type; 301 | for (unsigned int i = 0; i < len; i++) { 302 | cs += data[i]; 303 | } 304 | cs = 0xff - cs; 305 | 306 | // Set up the header 307 | uint8_t hdr[4]; 308 | hdr[0] = 0x7e; // Start indicator 309 | hdr[1] = (((len + 1) >> 8) & 0xff); // Length MSB 310 | hdr[2] = ((len + 1) & 0xff); // Length LSB 311 | hdr[3] = type; // API Frame Type 312 | 313 | // Send 314 | write(hdr, 4); // Write header 315 | write(data, len); // Write the data to SPI 316 | write(&cs, 1); // And the checksum 317 | spiEnd(); 318 | } 319 | 320 | // Receive a SPI API frame 321 | // Typically this is used to receive AT response frame 322 | // It will trigger the asynchronous functions responsible for receiving other frame types as needed 323 | // to ensure those frames get processed 324 | int XbeeWifi::rx_frame(uint8_t *frame_type, unsigned int *len, uint8_t *data, int bufsize, unsigned long atn_wait_ms, bool return_status, bool single_ip_rx_only) 325 | { 326 | // Before we do anything else, set the received length to zero 327 | // for the case where we don't sucesfully receive anything 328 | *len = 0; 329 | 330 | // This will be our working received length 331 | unsigned int rxlen = 0; 332 | 333 | // This will be the current character read 334 | uint8_t in; 335 | 336 | // This will be the type of packet (API frame type) 337 | uint8_t type; 338 | 339 | // This will be our truncation flag 340 | bool truncated = false; 341 | 342 | // Repeat this operation until we receive a returnable packet 343 | do { 344 | // Wait on ATN 345 | if (!wait_atn(atn_wait_ms)) { 346 | if (atn_wait_ms > 0) { 347 | XBEE_DEBUG(Serial.println(F("****** Failed in rx_frame waiting for ATN"))); 348 | } 349 | return RX_FAIL_WAITING_FOR_ATN; 350 | } 351 | 352 | // Read start byte 353 | spiStart(); 354 | in = read(); 355 | if (in != 0x7E) { 356 | XBEE_DEBUG(Serial.println(F("****** Failed in rx_frame, invalid start byte"))); 357 | flush_spi(); 358 | spiEnd(); 359 | return RX_FAIL_INVALID_START_BYTE; 360 | } 361 | 362 | // Read length (MSB and LSB) and combine 363 | rxlen = ((read() << 8 | read()) - 1); // -1 because we do not include type in our length 364 | XBEE_DEBUG(Serial.print(F("rx_frame Length Of "))); 365 | XBEE_DEBUG(Serial.print(rxlen, HEX)); 366 | XBEE_DEBUG(Serial.println(F(" bytes"))); 367 | 368 | // Read frame type 369 | type = read(); 370 | XBEE_DEBUG(Serial.print(F("Read type 0x"))); 371 | XBEE_DEBUG(Serial.println(type, HEX)); 372 | 373 | uint8_t cs, cs_incoming; 374 | 375 | switch(type) { 376 | #ifndef XBEE_OMIT_RX_DATA 377 | case XBEE_API_FRAME_RX_IPV4 : 378 | #ifndef XBEE_OMIT_COMPAT_MODE 379 | case XBEE_API_FRAME_RX64_INDICATOR : 380 | #endif 381 | XBEE_DEBUG(Serial.println(F("Is IP reception, route to rx_ip"))); 382 | // This is an IP V4 RX packet, which can be very long and requires 383 | // special handling due to memory constraints 384 | // So process that here, it's not the packet we're directly interested in 385 | rx_ip(rxlen, type); 386 | break; 387 | #endif 388 | 389 | #ifndef XBEE_OMIT_RX_SAMPLE 390 | case XBEE_API_FRAME_IO_DATA_SAMPLE_RX : 391 | // This is a remote sample, which may arrive at any time 392 | // again - we're not interested int it directly - it will process 393 | // it's own async callbacks as needed 394 | rx_sample(rxlen); 395 | break; 396 | #endif 397 | 398 | case XBEE_API_FRAME_MODEM_STATUS : 399 | // This is a modem status frame, which crops up from time to time 400 | // We will return this if explicitly requested, otherwise it's routed 401 | // to it's own asynchronous handler 402 | if (!return_status) { 403 | XBEE_DEBUG(Serial.println(F("IS MS, route to rx_modem_status"))); 404 | rx_modem_status(rxlen); 405 | break; 406 | } 407 | 408 | // If return_status == true, we drop through to the next case 409 | // which processes the modem status frame like any other 410 | 411 | case XBEE_API_FRAME_TX_STATUS : 412 | case XBEE_API_FRAME_REMOTE_CMD_RESP : 413 | case XBEE_API_FRAME_ATCMD_RESP : 414 | // We want to handle and return this frame 415 | 416 | cs = type; 417 | for (unsigned int i = 0 ; i < rxlen; i++) { 418 | in = read(); 419 | cs += in; 420 | if (i < (unsigned int) bufsize) { 421 | data[i] = in; 422 | } else { 423 | truncated = true; 424 | } 425 | } 426 | // Complete checksum calculation 427 | cs = 0xFF - cs; 428 | 429 | // Read incoming checksum (last byte of packet) 430 | cs_incoming = read(); 431 | 432 | // Set up returned values 433 | *len = (rxlen > (unsigned int) bufsize) ? bufsize : rxlen; 434 | *frame_type = type; 435 | 436 | // And report appropriate status in return value 437 | spiEnd(); 438 | if (truncated) { 439 | XBEE_DEBUG(Serial.println(F("****** RX fail, truncation"))); 440 | return RX_FAIL_TRUNCATED; 441 | } else if (cs != cs_incoming) { 442 | XBEE_DEBUG(Serial.println(F("****** RX fail, checksum"))); 443 | XBEE_DEBUG(Serial.print(F("RX CS 0x"))); 444 | XBEE_DEBUG(Serial.println(cs_incoming, HEX)); 445 | XBEE_DEBUG(Serial.print(F("CALC CS 0x"))); 446 | XBEE_DEBUG(Serial.println(cs, HEX)); 447 | return RX_FAIL_CHECKSUM; 448 | } else { 449 | return RX_SUCCESS; 450 | } 451 | //break; (implied by returns) 452 | 453 | default : 454 | // This is an unexpected (possibly new, unsupported) frame 455 | // Drop it with debug 456 | XBEE_DEBUG(Serial.print(F("**** RX DROP Unsupported frame, type : 0x"))); 457 | XBEE_DEBUG(Serial.println(type, HEX)); 458 | for (unsigned int i = 0 ; i < rxlen + 1; i++) { 459 | #ifdef XBEE_ENABLE_DEBUG 460 | uint8_t dropped = read(); 461 | XBEE_DEBUG(Serial.print(F("Dropping : 0x"))); 462 | XBEE_DEBUG(Serial.println(dropped, HEX)); 463 | #else 464 | read(); 465 | #endif 466 | } 467 | 468 | break; 469 | 470 | } 471 | spiEnd(); 472 | } while(true); // Break out via return statement 473 | } 474 | 475 | // Dispatch an AT CMD with raw buffer as parameter 476 | bool XbeeWifi::at_cmd_raw(const char *atxx, uint8_t *buffer, int len, bool queued) 477 | { 478 | return at_cmd(atxx, buffer, len, NULL, 0, queued); 479 | } 480 | 481 | // Same - to remote 482 | bool XbeeWifi::at_remcmd_raw(uint8_t *ip, const char *atxx, uint8_t *buffer, int len, bool apply) 483 | { 484 | return at_remcmd(ip, atxx, buffer, len, NULL, 0, apply); 485 | } 486 | 487 | // Dispatch an AT CMD with string buffer as parameter 488 | bool XbeeWifi::at_cmd_str(const char *atxx, const char *buffer, bool queued) 489 | { 490 | return at_cmd(atxx, (uint8_t *)buffer, strlen(buffer), NULL, 0, queued); 491 | } 492 | 493 | // Same - to remote 494 | bool XbeeWifi::at_remcmd_str(uint8_t *ip, const char *atxx, const char *buffer, bool apply) 495 | { 496 | return at_remcmd(ip, atxx, (uint8_t *)buffer, strlen(buffer), NULL, 0, apply); 497 | } 498 | 499 | // Dispatch an AT CMD with single byte parameter 500 | bool XbeeWifi::at_cmd_byte(const char *atxx, uint8_t byte, bool queued) 501 | { 502 | return at_cmd(atxx, &byte, 1, NULL, 0, queued); 503 | } 504 | 505 | // Same - to remote 506 | bool XbeeWifi::at_remcmd_byte(uint8_t *ip, const char *atxx, uint8_t byte, bool apply) 507 | { 508 | return at_remcmd(ip, atxx, &byte, 1, NULL, 0, apply); 509 | } 510 | 511 | // Dispatch an AT CMD with word paramter 512 | bool XbeeWifi::at_cmd_short(const char *atxx, uint16_t twobyte, bool queued) 513 | { 514 | uint16_t swap = ((twobyte >>8) | ((twobyte & 0xFF) << 8)); 515 | return at_cmd(atxx, (uint8_t *) &swap, 2, NULL, 0, queued); 516 | } 517 | 518 | // Same - to remote 519 | bool XbeeWifi::at_remcmd_short(uint8_t *ip, const char *atxx, uint16_t twobyte, bool apply) 520 | { 521 | uint16_t swap = ((twobyte >>8) | ((twobyte & 0xFF) << 8)); 522 | return at_remcmd(ip, atxx, (uint8_t *) &swap, 2, NULL, 0, apply); 523 | } 524 | 525 | // Dispatch a non parameterized AT CMD 526 | bool XbeeWifi::at_cmd_noparm(const char *atxx, bool queued) 527 | { 528 | return at_cmd(atxx, NULL, 0, NULL, 0, queued); 529 | } 530 | 531 | // Same to remote 532 | bool XbeeWifi::at_remcmd_noparm(uint8_t *ip, const char *atxx, bool apply) 533 | { 534 | return at_remcmd(ip, atxx, NULL, 0, NULL, 0, apply); 535 | } 536 | 537 | // AT command processor backend 538 | // atxx = char[2+] where first two characters are the AT code 539 | // parmval = the parameter value 540 | // parmlen = and it's length 541 | // returndata = buffer for returned data (or NULL if not interested) 542 | // returnlen = size of return data buffer 543 | // queued = true means use the queued (non immediate) AT operation 544 | bool XbeeWifi::at_cmd(const char *atxx, const uint8_t *parmval, int parmlen, void *returndata, int *returnlen, bool queued) 545 | { 546 | XBEE_DEBUG(Serial.print(F("Run AT Query "))); 547 | XBEE_DEBUG(Serial.print(atxx[0])); 548 | XBEE_DEBUG(Serial.print(atxx[1])); 549 | XBEE_DEBUG(Serial.println()); 550 | 551 | // If we are in RX callback, then we can't do ATs 552 | if (callback_depth > 0) { 553 | XBEE_DEBUG(Serial.println(F("***** AT Reject - in RX callback"))); 554 | return false; 555 | } 556 | 557 | // Parameter must be able to fit into our maximum allowable buffer size 558 | if (parmlen > XBEE_BUFSIZE - 3) { 559 | XBEE_DEBUG(Serial.println(F("****** Too big AT"))); 560 | return false; 561 | } 562 | 563 | 564 | // If this is an immediate operation, increment the atid - mostly for debug reasons 565 | if (!queued) { 566 | next_atid++; 567 | if (next_atid == 0) next_atid++; 568 | } 569 | 570 | // Construct packet 571 | uint8_t buf[XBEE_BUFSIZE]; 572 | buf[0] = queued ? 0x00 : next_atid; 573 | buf[1] = atxx[0]; 574 | buf[2] = atxx[1]; 575 | memcpy(buf + 3, parmval, parmlen); 576 | 577 | // Transmit 578 | tx_frame(queued ? XBEE_API_FRAME_ATCMD_QUEUED : XBEE_API_FRAME_ATCMD, parmlen + 3, buf); 579 | 580 | // If this was immediate, then we are expecting an AT response 581 | // Unless this was an AS (active scan) which we handle as a strange special case 582 | if (!queued && (atxx[0] != 'A' || atxx[1] != 'S')) { 583 | // AT response expected 584 | uint8_t type; 585 | unsigned int len; 586 | int res; 587 | if ((res = rx_frame(&type, &len, buf, XBEE_BUFSIZE)) == RX_SUCCESS) { 588 | if (type == XBEE_API_FRAME_ATCMD_RESP && buf[0] == next_atid && buf[3] == 0) { 589 | // Correct frame type and success code found 590 | if (returndata != NULL) { 591 | // Caller wants the parameter returned 592 | *returnlen = (len - 4); 593 | memcpy(returndata, buf + 4, *returnlen > XBEE_BUFSIZE ? XBEE_BUFSIZE : *returnlen); 594 | } 595 | return true; 596 | } else { 597 | // Failure - either not the correct type of packet 598 | // or wrong ATID or non success indication 599 | XBEE_DEBUG(Serial.println(F("****** Failed AT CMD RESP"))); 600 | } 601 | } else { 602 | // Failed to receive a packet 603 | XBEE_DEBUG(Serial.print(F("****** Failed AT CMD RESP, error "))); 604 | XBEE_DEBUG(Serial.println(res, DEC)); 605 | } 606 | 607 | // Something failed, return false 608 | return false; 609 | } else { 610 | // Was queued or was an active scan which we handle separately 611 | // so we don't have a response to look for so we have to assume success 612 | return true; 613 | } 614 | } 615 | 616 | // This is the equivalent back end for AT command processing for remote nodes 617 | bool XbeeWifi::at_remcmd(uint8_t *ip, const char *atxx, const uint8_t *parmval, int parmlen, void *returndata, int *returnlen, bool apply) 618 | { 619 | XBEE_DEBUG(Serial.print(F("Run AT Query, remote "))); 620 | XBEE_DEBUG(Serial.print(atxx[0])); 621 | XBEE_DEBUG(Serial.print(atxx[1])); 622 | XBEE_DEBUG(Serial.println()); 623 | 624 | // Parameter must be able to fit into our maximum allowable buffer size 625 | if (parmlen > XBEE_BUFSIZE - 15) { 626 | XBEE_DEBUG(Serial.println(F("****** Too big AT"))); 627 | return false; 628 | } 629 | 630 | // Increment ATID 631 | next_atid++; 632 | if (next_atid == 0) next_atid++; 633 | 634 | // Construct packet 635 | uint8_t buf[XBEE_BUFSIZE]; 636 | buf[0] = next_atid; 637 | memset(buf + 1, 0, 4); 638 | memcpy(buf + 5, ip, 4); 639 | buf[9] = apply ? 0x02 : 0x00; 640 | buf[10] = atxx[0]; 641 | buf[11] = atxx[1]; 642 | memcpy(buf + 12, parmval, parmlen); 643 | 644 | // Transmit 645 | tx_frame(XBEE_API_FRAME_REMOTE_CMD_REQ, parmlen + 12, buf); 646 | 647 | // If this was immediate, then we are expecting an AT response 648 | // Unless this was an AS (active scan) which we handle as a strange special case 649 | // REMAT response expected 650 | uint8_t type; 651 | unsigned int len; 652 | int res; 653 | if ((res = rx_frame(&type, &len, buf, XBEE_BUFSIZE)) == RX_SUCCESS) { 654 | if (type == XBEE_API_FRAME_REMOTE_CMD_RESP && 655 | buf[0] == next_atid && 656 | buf[11] == 0 && 657 | memcmp(ip, buf + 5, 4) == 0) { 658 | // Correct frame type, success code and ip 659 | if (returndata != NULL) { 660 | // Caller wants the parameter returned 661 | *returnlen = (len - 12); 662 | memcpy(returndata, buf + 12, *returnlen > XBEE_BUFSIZE ? XBEE_BUFSIZE : *returnlen); 663 | } 664 | return true; 665 | } else { 666 | XBEE_DEBUG(Serial.println(F("****** Failed AT/REM CMD RESP"))); 667 | } 668 | } else { 669 | XBEE_DEBUG(Serial.print(F("****** Failed AT/REM CMD RESP, error "))); 670 | XBEE_DEBUG(Serial.println(res, DEC)); 671 | } 672 | return false; 673 | } 674 | 675 | // Query an AT for it's parameter value 676 | // atxx = char[2+] coptaining AT seuqence in first two positions 677 | // parmval is the paramever value buffer for return 678 | // parmlen will return the length of the parameter 679 | // if maxlen < parmlen then parmval will be truncated 680 | bool XbeeWifi::at_query(const char *atxx, uint8_t *parmval, int *parmlen, int maxlen) 681 | { 682 | char retbuf[XBEE_BUFSIZE]; 683 | int len; 684 | 685 | if (at_cmd(atxx, NULL, 0, retbuf, &len, false)) { 686 | *parmlen = len; 687 | memcpy(parmval, retbuf, len > maxlen ? maxlen : len); 688 | return true; 689 | } else { 690 | XBEE_DEBUG(Serial.println(F("****** Failed AT QRY"))); 691 | return false; 692 | } 693 | } 694 | 695 | // Equivalent for remote device 696 | bool XbeeWifi::at_remquery(uint8_t *ip, const char *atxx, uint8_t *parmval, int *parmlen, int maxlen) 697 | { 698 | char retbuf[XBEE_BUFSIZE]; 699 | int len; 700 | 701 | if (at_remcmd(ip, atxx, NULL, 0, retbuf, &len, true)) { 702 | *parmlen = len; 703 | memcpy(parmval, retbuf, len > maxlen ? maxlen : len); 704 | return true; 705 | } else { 706 | XBEE_DEBUG(Serial.println(F("**** Failed ATREMQRY"))); 707 | return false; 708 | } 709 | } 710 | 711 | // Wait until atn asserts, for a given maximum number of milliseconds 712 | // returns true if assert is found 713 | // Call with max_mllis = 0 to get a simple true/false on whether ATN is currently asserted 714 | bool XbeeWifi::wait_atn(unsigned long int max_millis) 715 | { 716 | if (max_millis > 0) { 717 | XBEE_DEBUG(Serial.println(F("Waiting for ATN"))); 718 | } 719 | unsigned long int sanity = millis() + max_millis; 720 | int atn; 721 | do { 722 | atn = digitalRead(pin_atn); 723 | if (atn == LOW) return true; 724 | if (millis() >= sanity) return false; 725 | } while(true); 726 | } 727 | 728 | // Flush SPI until ATN de-asserts, meaning XBEE has no queued data 729 | // This is an emergency recovery function to ensure resync of the SPI bus at the potential loss 730 | // of much data 731 | // If something really goes off the rails, this function can be used to nudge things back on track 732 | // It should never be hit in normal operation unless we have software errors or possibly noise on the SPI bus 733 | void XbeeWifi::flush_spi() 734 | { 735 | while(digitalRead(pin_atn) == LOW) { 736 | #ifdef XBEE_ENABLE_DEBUG 737 | uint8_t in = read(); 738 | #else 739 | read(); 740 | #endif 741 | XBEE_DEBUG(Serial.print(F("Flushed one from spi: 0x"))); 742 | XBEE_DEBUG(Serial.println(in, HEX)); 743 | } 744 | } 745 | 746 | // Register a callback for IP data delivery 747 | #ifndef XBEE_OMIT_RX_DATA 748 | void XbeeWifi::register_ip_data_callback(void (*func)(uint8_t *, int, s_rxinfo *)) 749 | { 750 | ip_data_func = func; 751 | } 752 | #endif 753 | 754 | // Register a callback for status (modem status) delivery 755 | void XbeeWifi::register_status_callback(void (*func)(uint8_t)) 756 | { 757 | modem_status_func = func; 758 | } 759 | 760 | // Register a callback for active scan data delivery 761 | #ifndef XBEE_OMIT_SCAN 762 | void XbeeWifi::register_scan_callback(void (*func)(uint8_t, int, char *)) 763 | { 764 | scan_func = func; 765 | } 766 | #endif 767 | 768 | // Register a callback for remote sample data delivery 769 | #ifndef XBEE_OMIT_RX_SAMPLE 770 | void XbeeWifi::register_sample_callback(void (*func)(s_sample *)) 771 | { 772 | sample_func = func; 773 | } 774 | #endif 775 | 776 | // This method should be called repeatedly by the run loop to ensure 777 | // that the SPI bus is serviced in an expeditious manner to prevent overruns 778 | // and ensure timely delivery of asynchronous callbacks 779 | void XbeeWifi::process(bool rx_one_packet_only) 780 | { 781 | int res; 782 | unsigned int len; 783 | uint8_t buf[XBEE_BUFSIZE]; 784 | uint8_t type; 785 | do { 786 | // Receive frames with zero timeout 787 | // Since we're not currently expecting an exlicit response to anything 788 | // this simply serves to dispatch our asynchronous frames 789 | res = rx_frame(&type, &len, buf, XBEE_BUFSIZE, 0, false, true); 790 | 791 | // IP / Status / Sample packets are already handled, the only thing we need to handle here 792 | // is AT response to active scan 793 | #ifndef XBEE_OMIT_SCAN 794 | if (type == XBEE_API_FRAME_ATCMD_RESP && res == RX_SUCCESS) { 795 | handleActiveScan(buf, len); 796 | } 797 | #endif 798 | 799 | // Keep doing this until we get a report of timeout (0 length of course) waiting 800 | // for ATN, meaning ATN is no longer asserted and the SPI bus is empty 801 | } while(res != RX_FAIL_WAITING_FOR_ATN); 802 | } 803 | 804 | // Receive a remote sample packet 805 | // Packet must have already been read to type before calling with length of remaining data 806 | #ifndef XBEE_OMIT_RX_SAMPLE 807 | void XbeeWifi::rx_sample(unsigned int len) 808 | { 809 | XBEE_DEBUG(Serial.print(F("RX Sample len 0x"))); 810 | XBEE_DEBUG(Serial.println(len, HEX)); 811 | 812 | // Create new empty sample record 813 | s_sample sample; 814 | memset(&sample, 0, sizeof(s_sample)); 815 | 816 | // Initiate checksum processing 817 | uint8_t cs = XBEE_API_FRAME_IO_DATA_SAMPLE_RX; 818 | 819 | // Keep reading all data, reading in the appropriate values as they are reached 820 | for (unsigned int pos = 0 ; pos < len; pos++) { 821 | uint8_t incoming = read(); 822 | cs += incoming; 823 | switch(pos) { 824 | case 4 : sample.source_addr[0] = incoming; break; 825 | case 5 : sample.source_addr[1] = incoming; break; 826 | case 6 : sample.source_addr[2] = incoming; break; 827 | case 7 : sample.source_addr[3] = incoming; break; 828 | case 11 : sample.digital_mask |= ((uint16_t) incoming) << 8; break; 829 | case 12 : sample.digital_mask |= incoming; break; 830 | case 13 : sample.analog_mask = incoming; break; 831 | case 14 : sample.digital_samples |= ((uint8_t) incoming) << 8; break; 832 | case 15 : sample.digital_samples |= incoming; 833 | case 16 : sample.analog_samples |= ((uint8_t) incoming) << 8; break; 834 | case 17 : sample.analog_samples |= incoming; break; 835 | } 836 | } 837 | 838 | // Read and validate checksum 839 | uint8_t incoming_cs = read(); 840 | cs = 0xff - cs; 841 | if (incoming_cs != cs) { 842 | // Invalid checksum, ignore this packet 843 | XBEE_DEBUG(Serial.print(F("Incoming sample CS mismatch RX=0x"))); 844 | XBEE_DEBUG(Serial.print(incoming_cs, HEX)); 845 | XBEE_DEBUG(Serial.print(F(", CALC=0x"))); 846 | XBEE_DEBUG(Serial.println(cs, HEX)); 847 | } else { 848 | // Valid checksum, dispatch this sample to the callback, if registered 849 | XBEE_DEBUG(Serial.println(F("Sample dispatch"))); 850 | if (sample_func) sample_func(&sample); 851 | } 852 | } 853 | #endif 854 | 855 | // Receive an IP packet (either IPv4 or compatability IP packet) 856 | // Must have read to frame type and call with both frame type and length 857 | #ifndef XBEE_OMIT_RX_DATA 858 | void XbeeWifi::rx_ip(unsigned int len, uint8_t frame_type) 859 | { 860 | uint8_t buf[XBEE_BUFSIZE + 1]; // Leave 1 byte for user termination with \0 for safety 861 | int bufpos = 0; 862 | int pos = 4; 863 | 864 | // Create a new IP data record 865 | s_rxinfo info; 866 | memset(&info, 0, sizeof(s_rxinfo)); 867 | 868 | // Set total length of packet 869 | info.total_packet_length = len - 0x0A; 870 | 871 | // Initialize checksum processing 872 | uint8_t cs = frame_type; 873 | 874 | // If this is an application compatability IP packet we assert source and dest port 875 | // of 0xBEE as defined by spec 876 | #ifndef XBEE_OMIT_COMPAT_MODE 877 | if (frame_type != XBEE_API_FRAME_RX_IPV4) { 878 | info.source_port = info.dest_port = 0xBEE; 879 | XBEE_DEBUG(Serial.println(F("RX APPMODE"))); 880 | } else { 881 | XBEE_DEBUG(Serial.println(F("RX RAW"))); 882 | } 883 | #endif 884 | 885 | // Read data and process in based on packet type 886 | do { 887 | uint8_t inbound = read(); 888 | XBEE_DEBUG(Serial.print(F("Inbound PKT Data 0x"))); 889 | XBEE_DEBUG(Serial.println(inbound, HEX)); 890 | cs += inbound; 891 | #ifndef XBEE_OMIT_COMPAT_MODE 892 | if (frame_type == XBEE_API_FRAME_RX_IPV4) { 893 | #endif 894 | // Handle IPV4 header info 895 | switch(pos) { 896 | case 4 : info.source_addr[0] = inbound; break; 897 | case 5 : info.source_addr[1] = inbound; break; 898 | case 6 : info.source_addr[2] = inbound; break; 899 | case 7 : info.source_addr[3] = inbound; break; 900 | case 8 : info.dest_port |= (((uint16_t) inbound) << 8); break; 901 | case 9 : info.dest_port |= ((uint16_t) inbound); break; 902 | case 10 : info.source_port |= (((uint16_t) inbound) << 8); break; 903 | case 11 : info.source_port |= ((uint16_t) inbound); break; 904 | case 12 : info.protocol = inbound; break; 905 | } 906 | #ifndef XBEE_OMIT_COMPAT_MODE 907 | } else { 908 | // Handle application compatability header info 909 | switch(pos) { 910 | case 7 : info.source_addr[0] = inbound; break; 911 | case 8 : info.source_addr[1] = inbound; break; 912 | case 9 : info.source_addr[2] = inbound; break; 913 | case 10 : info.source_addr[3] = inbound; break; 914 | } 915 | } 916 | #endif 917 | 918 | if (pos > 0x0D) { 919 | // Past the header - reading actual packet data now 920 | if (bufpos == XBEE_BUFSIZE && len > 1) { 921 | // We've exhausted our inbound buffer, we must dispatch 922 | // this buffer now, even though we haven't had chance to check 923 | // the checksum unless of course this was the last byte 924 | // in which case we still defer 925 | dispatch(buf, bufpos, &info); 926 | info.current_offset += bufpos; 927 | bufpos = 0; 928 | } 929 | buf[bufpos++] = inbound; 930 | } 931 | pos++; 932 | } while (--len > 0); 933 | 934 | // Complete checksum processing 935 | uint8_t inbound_cs = read(); 936 | cs = 0xFF - cs; 937 | if (inbound_cs != cs) { 938 | XBEE_DEBUG(Serial.println(F("****** CS Fail inbound rx"))); 939 | info.checksum_error = true; 940 | } 941 | 942 | // The last packet for a given sequence will always set the checksum error 943 | // if it occured 944 | // Dispatch the IP data to the callback function - if defined 945 | info.final = true; 946 | if (bufpos > 0) dispatch(buf, bufpos, &info); 947 | rx_seq++; 948 | } 949 | #endif 950 | 951 | // Receive modem status packet 952 | void XbeeWifi::rx_modem_status(unsigned int len) 953 | { 954 | // Length SHOULD be a single byte 955 | if (len != 1) { 956 | // Oops.... 957 | // Read the frame out and ignore 958 | for (unsigned int i = 0 ; i < len + 1; i++) read(); 959 | XBEE_DEBUG(Serial.println(F("Non 1 length on incoming modem status frame"))); 960 | } else { 961 | uint8_t status = read(); 962 | uint8_t cs = 0xFF - (uint8_t) (status + XBEE_API_FRAME_MODEM_STATUS); 963 | uint8_t incoming_cs = read(); 964 | if (incoming_cs == cs) { 965 | // Record last status 966 | last_status = status; 967 | // Dispatch status 968 | if (modem_status_func) modem_status_func(status); 969 | } else { 970 | // Bad checksum - discard 971 | XBEE_DEBUG(Serial.println(F("Checksum mismatch on incoming modem status frame"))); 972 | } 973 | } 974 | } 975 | 976 | // Transmits data of length to the address and with options specified in addr struct 977 | // If confirm=true (default) then send confirmation is requested and reflected in 978 | // the return from this function 979 | // useAppService=true would be used to use the application compatability (0xBEE port) method, true for 980 | // raw IPV4 981 | // When using app compat mode, addr can be null because it is unused 982 | bool XbeeWifi::transmit(const uint8_t *ip, s_txoptions *addr, uint8_t *data, int len, bool confirm, bool useAppService) 983 | { 984 | // Okay - let's grab the SPI bus and LOCK it (so that other agents within 985 | // this code base cannot release it) 986 | // This way we (hopefully) stop the Xbee from queuing up any inbound frames 987 | spiStart(); 988 | spiLocked=true; 989 | 990 | // But it's possible that Xbee may have already had such frames pending 991 | // .. So deal with them 992 | if (digitalRead(pin_atn) == LOW) { 993 | XBEE_DEBUG(Serial.print(F("ATN asserted before transmit, call process"))); 994 | process(); 995 | } 996 | 997 | // Okay - the SPI bus should now be clear of incoming data 998 | // we are safe to send our own data... 999 | // We still have the SPI bus chip select locked. 1000 | // Unlock it so that when the transmit releases it, it is actually released 1001 | spiLocked = false; 1002 | 1003 | XBEE_DEBUG(Serial.print(F("XMIT frame of length "))); 1004 | XBEE_DEBUG(Serial.println(len, DEC)); 1005 | XBEE_DEBUG(Serial.print(F("XMIT mode : "))); 1006 | XBEE_DEBUG(Serial.println(useAppService ? F("APP") : F("RAW"))); 1007 | 1008 | // If we're in the RX callback, we cannot risk confirmation, so we force confirm=false 1009 | if (callback_depth > 0) { 1010 | confirm = false; 1011 | XBEE_DEBUG(Serial.print(F("Transmit during RX callback - force no confirmation"))); 1012 | } 1013 | 1014 | // Attempt to send nothing will be considered an error 1015 | if (len <= 0) return false; 1016 | 1017 | // Set up the header for the data 1018 | uint8_t hdrbuf[15]; 1019 | 1020 | // If we've been asked for confirmation, then we'll be needing 1021 | // an atid 1022 | if (confirm) { 1023 | next_atid++; 1024 | if (next_atid == 0) next_atid++; 1025 | } 1026 | 1027 | // Construct the header 1028 | #ifndef XBEE_OMIT_COMPAT_MODE 1029 | int hdrlen = useAppService ? 0x0E : 0x0F; 1030 | #else 1031 | int hdrlen = 0x0E; 1032 | #endif 1033 | int offset = 0; 1034 | hdrbuf[offset++] = 0x7E; // Start byte 1035 | hdrbuf[offset++] = (len + hdrlen - 3) >> 8; // Length MSB 1036 | hdrbuf[offset++] = (len + hdrlen - 3) & 0xFF; // Length LSB 1037 | #ifndef XBEE_OMIT_COMPAT_MODE 1038 | hdrbuf[offset++] = useAppService ? XBEE_API_FRAME_TX64 : XBEE_API_FRAME_TX_IPV4; // Frame type 1039 | #else 1040 | hdrbuf[offset++] = XBEE_API_FRAME_TX_IPV4; 1041 | #endif 1042 | hdrbuf[offset++] = confirm ? next_atid : 0x00; // ATID (or 00 if no confirm required) 1043 | if (useAppService) { 1044 | hdrbuf[offset++] = 0x00; 1045 | hdrbuf[offset++] = 0x00; 1046 | hdrbuf[offset++] = 0x00; 1047 | hdrbuf[offset++] = 0x00; 1048 | } 1049 | hdrbuf[offset++] = ip[0]; // IP Address... 1050 | hdrbuf[offset++] = ip[1]; 1051 | hdrbuf[offset++] = ip[2]; 1052 | hdrbuf[offset++] = ip[3]; 1053 | #ifndef XBEE_OMIT_COMPAT_MODE 1054 | if (!useAppService) { 1055 | #endif 1056 | hdrbuf[offset++] = addr->dest_port >> 8; // Dest port MSB 1057 | hdrbuf[offset++] = addr->dest_port & 0xFF; // Dest port LSB 1058 | hdrbuf[offset++] = addr->source_port >> 8; // Source port MSB 1059 | hdrbuf[offset++] = addr->source_port & 0xFF; // Source port LSB 1060 | hdrbuf[offset++] = addr->protocol == XBEE_NET_IPPROTO_TCP ? XBEE_NET_IPPROTO_TCP : XBEE_NET_IPPROTO_UDP; 1061 | hdrbuf[offset++] = addr->leave_open ? 0x00 : 0x01; // TCP Leave open / immediate close 1062 | #ifndef XBEE_OMIT_COMPAT_MODE 1063 | } else { 1064 | hdrbuf[offset++] = 0x00; 1065 | } 1066 | #endif 1067 | 1068 | // Write the header, and then the data to SPI 1069 | spiStart(); 1070 | write(hdrbuf, hdrlen); 1071 | write(data, len); 1072 | 1073 | // Calculate the checksum 1074 | uint8_t cs = 0; 1075 | int i; 1076 | for(i = 3; i < hdrlen; i ++) cs += hdrbuf[i]; 1077 | for (i = 0; i < len; i ++) cs += data[i]; 1078 | cs = 0xFF - cs; 1079 | 1080 | // Write the checksum 1081 | write(&cs, 1); 1082 | spiEnd(); 1083 | 1084 | // If asked to confirm we sent a packet with a non-zero ATID 1085 | // and must now listen for a response 1086 | if (confirm) { 1087 | uint8_t type; 1088 | unsigned int len; 1089 | uint8_t buf[XBEE_BUFSIZE]; 1090 | // Attempt to receive a frame - use a long timeout for ATN (1 minute) 1091 | if (rx_frame(&type, &len, buf, XBEE_BUFSIZE, 60000L) == RX_SUCCESS) { 1092 | if (type != XBEE_API_FRAME_TX_STATUS) { 1093 | // Did not get the expected frame back 1094 | // Probably not the best idea, but clean out the SPI bus 1095 | XBEE_DEBUG(Serial.println(F("****** Receive of frame not TX status"))); 1096 | flush_spi(); 1097 | return false; 1098 | } 1099 | if (buf[0] != next_atid) { 1100 | // ATID mismatch 1101 | // Very weird - clean out the SPI bus 1102 | XBEE_DEBUG(Serial.println(F("****** Receive of frame, ATID mismatch"))); 1103 | flush_spi(); 1104 | return false; 1105 | } 1106 | if (buf[1] != 0x00) { 1107 | // Transmission operation success, but failed to transmit 1108 | XBEE_DEBUG(Serial.print(F("****** TX Failure, code="))); 1109 | XBEE_DEBUG(Serial.println(buf[1], HEX)); 1110 | return false; 1111 | } 1112 | } else { 1113 | // ATN Timeout or structural problem with received frame 1114 | XBEE_DEBUG(Serial.println(F("****** RX TX Status frame failed RX"))); 1115 | flush_spi(); 1116 | return false; 1117 | } 1118 | } 1119 | 1120 | XBEE_DEBUG(Serial.println(F("Frame sent successfully"))); 1121 | return true; 1122 | } 1123 | 1124 | // Initiate active scan 1125 | // Note that network reset will occur meaning association to any AP will be lost 1126 | #ifndef XBEE_OMIT_SCAN 1127 | bool XbeeWifi::initiateScan() 1128 | { 1129 | // Initiate network reset 1130 | if (!at_cmd_noparm(XBEE_AT_EXEC_NETWORK_RESET)) return false; 1131 | 1132 | // Wait for effect (probably not needed - but still - why not) 1133 | delay(250); 1134 | 1135 | // Initiate active scan and return success 1136 | return at_cmd_noparm(XBEE_AT_DIAG_ACTIVE_SCAN); 1137 | } 1138 | #endif //XBEE_OMIT_SCAN 1139 | 1140 | #ifndef XBEE_OMIT_SCAN 1141 | // Handle incoming active scan data 1142 | void XbeeWifi::handleActiveScan(uint8_t *buf, int len) 1143 | { 1144 | XBEE_DEBUG(Serial.println(F("Handle active scan"))); 1145 | char ssid[33]; 1146 | int rssi; 1147 | uint8_t encmode; 1148 | 1149 | // Check that this is an AS response status 0 1150 | if (buf[1] == 'A' && buf[2] == 'S' && buf[3] == 0x00) { 1151 | // Valid AS response frame 1152 | if (len <= 8) { 1153 | XBEE_DEBUG(Serial.println(F("AS end frame? rx"))); 1154 | } else { 1155 | 1156 | encmode = buf[6]; 1157 | rssi = (int) buf[7]; 1158 | memset(ssid, 0, 33); 1159 | memcpy(ssid, buf + 8, (len - 8) > 32 ? 32 : (len - 8)); 1160 | if (scan_func) scan_func(encmode, rssi, ssid); 1161 | } 1162 | } else { 1163 | XBEE_DEBUG(Serial.println(F("Invalid AS response frame"))); 1164 | } 1165 | } 1166 | #endif // XBEE_OMIT_SCAN (CJB) 1167 | 1168 | void XbeeWifi::dispatch(uint8_t *data, int len, s_rxinfo *info) 1169 | { 1170 | XBEE_DEBUG(Serial.println(F("Non buffered dispatch"))); 1171 | if (ip_data_func) { 1172 | callback_depth++; 1173 | ip_data_func(data, len, info); 1174 | callback_depth--; 1175 | } 1176 | } 1177 | 1178 | #ifndef XBEE_OMIT_RX_DATA 1179 | // Constructor for buffered XbeeWifi object 1180 | XbeeWifiBuffered::XbeeWifiBuffered(uint16_t bufsize) : 1181 | bufsize(bufsize), 1182 | head(0), 1183 | tail(0), 1184 | size(0), 1185 | buffer_overrun(false) 1186 | { 1187 | // Allocate memory to the buffer 1188 | buffer = (uint8_t *) malloc(bufsize); 1189 | } 1190 | 1191 | // Destructor. Can't really imagine where we'd be using it, but still 1192 | // memory allocation must be respected 1193 | XbeeWifiBuffered::~XbeeWifiBuffered() 1194 | { 1195 | if (buffer) free(buffer); 1196 | } 1197 | 1198 | 1199 | // Override the virtual dispatch function from the parent object 1200 | // and instead of dispatching data to a callback, insert it into our circular 1201 | // buffer 1202 | void XbeeWifiBuffered::dispatch(uint8_t *data, int len, s_rxinfo *info) 1203 | { 1204 | XBEE_DEBUG(Serial.print(F("(Buffered) Dispatching "))); 1205 | XBEE_DEBUG(Serial.print(len, DEC)); 1206 | XBEE_DEBUG(Serial.println(F(" bytes to FIFO queue"))); 1207 | 1208 | // For each incoming byte 1209 | for (int i = 0 ; i < len; i ++) { 1210 | // Make sure we have space 1211 | if (size == bufsize) { 1212 | // Oops, out of space. Remainder is dropped. Make sure overrun condition is flagged 1213 | XBEE_DEBUG(Serial.println(F("FIFO overrun"))); 1214 | buffer_overrun = true; 1215 | break; 1216 | } else { 1217 | // Add to head of buffer 1218 | buffer[head++] = data[i]; 1219 | if (head == bufsize) head = 0; 1220 | size++; 1221 | } 1222 | } 1223 | } 1224 | 1225 | // Returns true if we have bytes available to read 1226 | bool XbeeWifiBuffered::available() 1227 | { 1228 | if (size == 0) process(); 1229 | return (size > 0); 1230 | } 1231 | 1232 | // Returns the next character but does not remove it from the buffer 1233 | // Always returns 0 if nothing available (but of course might return 0 if 0 is in the buffer too) 1234 | uint8_t XbeeWifiBuffered::peek() 1235 | { 1236 | // If we have nothing in the buffer, it's time to process the SPI bus 1237 | if (size == 0) process(false); 1238 | 1239 | if (size == 0) { 1240 | // Still nothing, return 0 1241 | return 0; 1242 | } else { 1243 | // Return tail of buffer 1244 | return buffer[tail]; 1245 | } 1246 | } 1247 | 1248 | // Returns the next character from the buffer 1249 | // Returns 0 if nothing available (same caveat as for peek) 1250 | uint8_t XbeeWifiBuffered::read() 1251 | { 1252 | // If we have nothing in the buffer, it's time to process the SPI bus 1253 | if (size == 0) process(false); 1254 | 1255 | if (size == 0) { 1256 | // Still nothing, return 0 1257 | return 0; 1258 | } else { 1259 | // Grab the buffer tail and advance / wrap the tail pointer 1260 | uint8_t data = buffer[tail++]; 1261 | if (tail == bufsize) tail = 0; 1262 | 1263 | // Decrement the size since we just removed a byte and return the byte 1264 | size--; 1265 | return data; 1266 | } 1267 | } 1268 | 1269 | // Returns true if we overran the buffer 1270 | // Will reset state to false on every call unless reset is specified false 1271 | bool XbeeWifiBuffered::overran(bool reset) 1272 | { 1273 | uint8_t result = buffer_overrun; 1274 | if (reset) buffer_overrun = false; 1275 | return result; 1276 | } 1277 | 1278 | // Flush all content from the buffer 1279 | // Does not flush the SPI bus though.. This is deliberate. 1280 | void XbeeWifiBuffered::flush() 1281 | { 1282 | head = tail = size = 0; 1283 | } 1284 | 1285 | 1286 | #endif 1287 | --------------------------------------------------------------------------------