Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / libwireless / lib / xbee.c @ 1428

History | View | Annotate | Download (22.1 KB)

1 242 bcoltin
/**
2
 * Copyright (c) 2007 Colony Project
3 409 emarinel
 *
4 242 bcoltin
 * Permission is hereby granted, free of charge, to any person
5
 * obtaining a copy of this software and associated documentation
6
 * files (the "Software"), to deal in the Software without
7
 * restriction, including without limitation the rights to use,
8
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the
10
 * Software is furnished to do so, subject to the following
11
 * conditions:
12 409 emarinel
 *
13 242 bcoltin
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15 409 emarinel
 *
16 242 bcoltin
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
 * OTHER DEALINGS IN THE SOFTWARE.
24
 **/
25
26
/**
27
 * @file xbee.c
28
 * @brief XBee Interface
29
 *
30
 * Implementation of low level communication with the XBee in API mode.
31
 *
32
 * @author Brian Coltin, Colony Project, CMU Robotics Club
33
 **/
34
35 17 bcoltin
#include "xbee.h"
36
#include "wl_defs.h"
37
38
#ifndef ROBOT
39
40
#include <fcntl.h>
41
#include <unistd.h>
42
#include <pthread.h>
43
#include <errno.h>
44 309 emarinel
#include <termios.h>
45 17 bcoltin
46
#else
47
48
#include <serial.h>
49
#include <avr/interrupt.h>
50
51
#endif
52
53
#include <stdio.h>
54
#include <stdlib.h>
55
#include <string.h>
56
57
#define XBEE_FRAME_START 0x7E
58 581 jknichel
#define XBEE_GET_PACKET_TIMEOUT 1000
59 17 bcoltin
60
/*Frame Types*/
61
#define XBEE_FRAME_STATUS 0x8A
62
#define XBEE_FRAME_AT_COMMAND 0x08
63
#define XBEE_FRAME_AT_COMMAND_RESPONSE 0x88
64
#define XBEE_FRAME_TX_REQUEST_64 0x00
65
#define XBEE_FRAME_TX_REQUEST_16 0x01
66
#define XBEE_FRAME_TX_STATUS XBEE_TX_STATUS
67
#define XBEE_FRAME_RX_64 0x80
68
#define XBEE_FRAME_RX_16 XBEE_RX
69
70
/*Internal Function Prototypes*/
71
72
/*I/O Functions*/
73 397 emarinel
static int xbee_send(char* buf, int size);
74 418 emarinel
static int xbee_send_string(char* c);
75 17 bcoltin
76
#ifndef ROBOT
77 418 emarinel
static int xbee_read(char* buf, int size);
78 17 bcoltin
#endif
79
80
/*Command Mode Functions
81
 * Called during initialization.
82
 */
83 418 emarinel
static int xbee_enter_command_mode(void);
84
static int xbee_exit_command_mode(void);
85
static int xbee_enter_api_mode(void);
86 581 jknichel
static int xbee_wait_for_string(char* s, int len);
87
static int xbee_wait_for_ok(void);
88 17 bcoltin
89
/*API Mode Functions*/
90
91 418 emarinel
static int xbee_handle_packet(char* packet, int len);
92
static void xbee_handle_at_command_response(char* command, char result, char* extra, int extraLen);
93
static void xbee_handle_status(char status);
94
static int xbee_verify_checksum(char* packet, int len);
95
static char xbee_compute_checksum(char* packet, int len);
96
static int xbee_send_frame(char* buf, int len);
97
static int xbee_send_read_at_command(char* command);
98
static int xbee_send_modify_at_command(char* command, char* value);
99 17 bcoltin
100
/*Global Variables*/
101
102
#ifndef ROBOT
103 399 emarinel
static char* xbee_com_port = XBEE_PORT_DEFAULT;
104
static int xbee_stream;
105
static pthread_t* xbee_listen_thread;
106 17 bcoltin
#endif
107
108 336 bcoltin
// TODO: is this a good size?
109 736 bcoltin
#define XBEE_BUFFER_SIZE        128
110
#define PACKET_BUFFER_SIZE        108
111 336 bcoltin
// a buffer for data received from the XBee
112
char arrival_buf[XBEE_BUFFER_SIZE];
113
// location of last unread byte in buffer
114
volatile int buffer_last = 0;
115
// first unread byte in buffer
116
volatile int buffer_first = 0;
117 17 bcoltin
118 336 bcoltin
119 17 bcoltin
//used to store packets as they are read
120 736 bcoltin
static char xbee_buf[PACKET_BUFFER_SIZE];
121 418 emarinel
static int currentBufPos = 0;
122 17 bcoltin
123
//XBee status
124 418 emarinel
static unsigned int xbee_panID = XBEE_PAN_DEFAULT;
125
static unsigned int xbee_pending_panID = XBEE_PAN_DEFAULT;
126
static int xbee_channel = XBEE_CHANNEL_DEFAULT;
127
static int xbee_pending_channel = XBEE_CHANNEL_DEFAULT;
128 581 jknichel
static volatile unsigned int xbee_address = 0;
129 17 bcoltin
130
/*Function Implementations*/
131
132
#ifdef ROBOT
133
134
/**
135
 * Interrupt for the robot. Adds bytes received from the xbee
136 336 bcoltin
 * to the buffer.
137 17 bcoltin
 **/
138 86 bcoltin
#ifndef FIREFLY
139 346 bcoltin
ISR(USART1_RX_vect)
140
{
141
        char c = UDR1;
142
        arrival_buf[buffer_last] = c;
143
        int t = buffer_last + 1;
144
        if (t == XBEE_BUFFER_SIZE)
145
                t = 0;
146
        if (t == buffer_first)
147
        {
148 736 bcoltin
                WL_DEBUG_PRINT("\nOut of space in buffer.\n");
149 346 bcoltin
        }
150
        buffer_last = t;
151 17 bcoltin
}
152 86 bcoltin
#else
153 346 bcoltin
SIGNAL(SIG_USART0_RECV)
154
{
155
        char c = UDR0;
156
        arrival_buf[buffer_last] = c;
157
        int t = buffer_last + 1;
158
        if (t == XBEE_BUFFER_SIZE)
159
                t = 0;
160
        if (t == buffer_first)
161
        {
162
                WL_DEBUG_PRINT("Out of space in buffer.\n");
163
        }
164
        buffer_last = t;
165 86 bcoltin
}
166
#endif
167 17 bcoltin
168
#else
169
170 418 emarinel
// Computer code
171
172 17 bcoltin
/**
173
 * Thread that listens to the xbee.
174
 **/
175 418 emarinel
static void* listen_to_xbee(void* x)
176 346 bcoltin
{
177
        char c;
178
        while (1)
179
        {
180 418 emarinel
                if (xbee_read(&c, 1) != 0) {
181 736 bcoltin
                        WL_DEBUG_PRINT("xbee_read failed.\n");
182 418 emarinel
                        return NULL;
183
                }
184 717 jknichel
185 346 bcoltin
                arrival_buf[buffer_last] = c;
186
                int t = buffer_last + 1;
187
                if (t == XBEE_BUFFER_SIZE)
188
                        t = 0;
189
                if (t == buffer_first)
190
                {
191
                        WL_DEBUG_PRINT("Out of space in buffer.\n");
192
                }
193
                buffer_last = t;
194 409 emarinel
195
                usleep(1000);
196 346 bcoltin
        }
197 418 emarinel
198
        return NULL;
199 17 bcoltin
}
200
201
#endif
202
203
/**
204
 * Initializes the XBee library so that other functions may be used.
205
 **/
206 399 emarinel
int xbee_lib_init()
207 346 bcoltin
{
208 744 bcoltin
        WL_DEBUG_PRINT("in xbee_init\n");
209 581 jknichel
#ifdef ROBOT
210 17 bcoltin
211 346 bcoltin
        //enable the receiving interrupt
212 412 emarinel
#ifdef FIREFLY
213 346 bcoltin
        UCSR0B |= _BV(RXCIE) | _BV(RXEN);
214 412 emarinel
#else
215 887 bcoltin
#ifdef BAYBOARD
216
        UCSR1B |= _BV(RXCIE1);
217
#else
218 346 bcoltin
        UCSR1B |= _BV(RXCIE);
219 412 emarinel
#endif
220 887 bcoltin
#endif
221 346 bcoltin
        sei();
222 412 emarinel
#else
223 346 bcoltin
        xbee_stream = open(xbee_com_port, O_RDWR);
224
        if (xbee_stream == -1/* || lockf(xbee_stream, F_TEST, 0) != 0*/)
225
        {
226 736 bcoltin
                WL_DEBUG_PRINT("Failed to open connection to XBee on port ");
227
                WL_DEBUG_PRINT_INT(xbee_com_port);
228
                WL_DEBUG_PRINT(".\n");
229 346 bcoltin
                return -1;
230 581 jknichel
        } else {
231 736 bcoltin
          WL_DEBUG_PRINT("Successfully opened connection to XBee on port ");
232
                WL_DEBUG_PRINT_INT(xbee_com_port);
233
                WL_DEBUG_PRINT(".\n");
234 346 bcoltin
        }
235 409 emarinel
236 346 bcoltin
        // set baud rate, etc. correctly
237
        struct termios options;
238 309 emarinel
239 346 bcoltin
        tcgetattr(xbee_stream, &options);
240
        cfsetispeed(&options, B9600);
241
        cfsetospeed(&options, B9600);
242
        options.c_iflag &= ~ICRNL;
243
        options.c_oflag &= ~OCRNL;
244
        options.c_cflag |= (CLOCAL | CREAD);
245
        options.c_cflag &= ~PARENB;
246
        options.c_cflag &= ~CSTOPB;
247
        options.c_cflag &= ~CSIZE;
248
        options.c_cflag |= CS8;
249
        options.c_lflag &= ~ICANON;
250
        options.c_cc[VMIN] = 1;
251
        options.c_cc[VTIME] = 50;
252 309 emarinel
253 346 bcoltin
        if (tcsetattr(xbee_stream, TCSANOW, &options))
254
        {
255 736 bcoltin
                WL_DEBUG_PRINT("Error setting attributes.\n");
256 346 bcoltin
                return -1;
257
        }
258 309 emarinel
259 409 emarinel
        xbee_listen_thread = (pthread_t*)malloc(sizeof(pthread_t));
260 346 bcoltin
        if (xbee_listen_thread == NULL)
261
        {
262 736 bcoltin
                WL_DEBUG_PRINT("Malloc failed.\n");
263 346 bcoltin
                return -1;
264
        }
265 409 emarinel
266
        int ret = pthread_create(xbee_listen_thread, NULL, listen_to_xbee, NULL);
267 346 bcoltin
        if (ret)
268
        {
269 736 bcoltin
                WL_DEBUG_PRINT("Failed to create listener thread.\n");
270 346 bcoltin
                return -1;
271
        }
272 412 emarinel
#endif
273 409 emarinel
274 744 bcoltin
        WL_DEBUG_PRINT("Entering command mode.\n");
275 613 jknichel
276 418 emarinel
        if (xbee_enter_command_mode() != 0) {
277
                return -1;
278
        }
279 409 emarinel
280 744 bcoltin
        WL_DEBUG_PRINT("Entered command mode.\n");
281
282 418 emarinel
        if (xbee_enter_api_mode() != 0) {
283
                return -1;
284
        }
285
286 744 bcoltin
        WL_DEBUG_PRINT("Entered api mode.\n");
287
288 418 emarinel
        if (xbee_exit_command_mode() != 0) {
289 931 tachim
             return -1;
290 418 emarinel
        }
291 931 tachim
292 744 bcoltin
        WL_DEBUG_PRINT("Left command mode.\n");
293
294 418 emarinel
        if (xbee_send_read_at_command("MY")) {
295
                return -1;
296
        }
297 744 bcoltin
        WL_DEBUG_PRINT("Getting ATMY address.\n");
298 418 emarinel
299 581 jknichel
#ifndef ROBOT
300
        int i;
301
        for (i = 0; xbee_address == 0 && i < XBEE_GET_PACKET_TIMEOUT; i++) {
302
          ret = xbee_get_packet(NULL);
303
304
          usleep(1000);
305 931 tachim
306
/*           if (ret == -1) { */
307
/*             WL_DEBUG_PRINT("xbee_get_packet(NULL) failed.\n"); */
308
/*             return -1; */
309
/*           } */
310 581 jknichel
        }
311
#else
312 346 bcoltin
        //wait to return until the address is set
313 717 jknichel
  //TODO: this shouldn't wait indefinitely.  There should be some sort of reasonable timeout
314
  // so if the address is never set right, an error can be returned instead of having the
315
  // robot hang forever
316 581 jknichel
        while (xbee_address == 0) {
317
          xbee_get_packet(NULL);
318
        }
319
#endif
320 744 bcoltin
        WL_DEBUG_PRINT("Got ATMY address.\n");
321 309 emarinel
322 581 jknichel
#ifndef ROBOT
323
        if (i == XBEE_GET_PACKET_TIMEOUT) { // We timed-out.
324
325 736 bcoltin
          WL_DEBUG_PRINT("xbee_get_packet timed out.\n");
326 581 jknichel
          return -1;
327
        } else {
328
          return 0;
329
        }
330
#else
331 346 bcoltin
        return 0;
332 581 jknichel
#endif
333 17 bcoltin
}
334
335
/**
336
 * Call when finished using the XBee library. This releases
337
 * all sued resources.
338
 **/
339 346 bcoltin
void xbee_terminate()
340
{
341
        #ifndef ROBOT
342
        pthread_cancel(*xbee_listen_thread);
343 1428 rcahoon
    pthread_join(*xbee_listen_thread, NULL);
344 346 bcoltin
        free(xbee_listen_thread);
345
        lockf(xbee_stream, F_ULOCK, 0);
346
        close(xbee_stream);
347
        #endif
348 17 bcoltin
}
349
350
/**
351
 * Send a buffer buf of size bytes to the XBee.
352 409 emarinel
 *
353 17 bcoltin
 * @param buf the buffer of data to send
354
 * @param size the number of bytes to send
355
 **/
356 418 emarinel
static int xbee_send(char* buf, int size)
357 346 bcoltin
{
358 418 emarinel
#ifdef ROBOT
359 346 bcoltin
        int i;
360 418 emarinel
        for (i = 0; i < size; i++) {
361 346 bcoltin
                xbee_putc(buf[i]);
362 418 emarinel
        }
363
364
        return 0;
365
366
#else
367
368 346 bcoltin
        int ret = write(xbee_stream, buf, size);
369
        //success
370
        if (ret == size)
371 397 emarinel
                return 0;
372 346 bcoltin
        if (ret == -1)
373
        {
374
                //interrupted by system signal, probably timer interrupt.
375
                //just try again
376
                if (errno == 4)
377
                {
378 397 emarinel
                        return xbee_send(buf, size);
379 346 bcoltin
                }
380 755 gtress
                WL_DEBUG_PRINT("Failed to write to xbee\r\n");
381 397 emarinel
                return -1;
382 346 bcoltin
        }
383 17 bcoltin
384 346 bcoltin
        //write was interrupted after writing ret bytes
385 418 emarinel
        return xbee_send(buf + ret, size - ret);
386
#endif
387 17 bcoltin
}
388
389
/**
390
 * Sends a string to the XBee.
391
 *
392
 * @param c the string to send to the XBEE
393
 **/
394 717 jknichel
//TODO: this function is so simple, it *may* be beneficial to inline this function.  testing of if
395
// it reduces code size or not should be done to be sure.
396 418 emarinel
static int xbee_send_string(char* c)
397 346 bcoltin
{
398 418 emarinel
        return xbee_send(c, strlen(c));
399 17 bcoltin
}
400
401
#ifndef ROBOT
402 418 emarinel
static int xbee_read(char* buf, int size)
403 346 bcoltin
{
404 418 emarinel
        if (read(xbee_stream, buf, size) == -1) {
405 736 bcoltin
                WL_DEBUG_PRINT("Failed to read from xbee.\r\n");
406 418 emarinel
                return -1;
407
        }
408
409
        return 0;
410 17 bcoltin
}
411
#endif
412
413
/**
414
 * Enter into command mode.
415
 **/
416 418 emarinel
static int xbee_enter_command_mode()
417 346 bcoltin
{
418 418 emarinel
        if (xbee_send_string("+++") != 0) {
419
                return -1;
420
        }
421
422 581 jknichel
        if (xbee_wait_for_ok() != 0) {
423
          return -1;
424 736 bcoltin
        }
425 581 jknichel
          return 0;
426 17 bcoltin
}
427
428
/**
429
 * Exit from command mode.
430
 **/
431 418 emarinel
static int xbee_exit_command_mode()
432 346 bcoltin
{
433 418 emarinel
        if (xbee_send_string("ATCN\r") != 0) {
434
                return -1;
435
        }
436
437 346 bcoltin
        xbee_wait_for_ok();
438 418 emarinel
439
        return 0;
440 17 bcoltin
}
441
442
/**
443
 * Enter API mode.
444
 **/
445 418 emarinel
static int xbee_enter_api_mode()
446 346 bcoltin
{
447 418 emarinel
        if (xbee_send_string("ATAP 1\r") != 0) {
448
                return -1;
449
        }
450 346 bcoltin
        xbee_wait_for_ok();
451 418 emarinel
452
        return 0;
453 17 bcoltin
}
454
455
/**
456
 * Wait until the string "OK\r" is received from the XBee.
457
 **/
458 717 jknichel
//TODO: this function is so simple, it *may* be beneficial to inline this function.  testing of if
459
// it reduces code size or not should be done to be sure.
460 581 jknichel
static int xbee_wait_for_ok()
461 346 bcoltin
{
462 581 jknichel
        return xbee_wait_for_string("OK\r", 3);
463 17 bcoltin
}
464
465
/**
466
 * Delay until the specified string is received from
467
 * the XBee. Discards all other XBee data.
468
 *
469
 * @param s the string to receive
470
 * @param len the length of the string
471
 **/
472 581 jknichel
static int xbee_wait_for_string(char* s, int len)
473 346 bcoltin
{
474 412 emarinel
        char* curr = s;
475
        while (curr - s < len) {
476
                // check if buffer is empty
477
                if (buffer_last != buffer_first) {
478
                        char c = arrival_buf[buffer_first++];
479
                        if (buffer_first == XBEE_BUFFER_SIZE) {
480
                                buffer_first = 0;
481
                        }
482 409 emarinel
483 412 emarinel
                        if (c == *curr) {
484
                                curr++;
485
                        } else {
486 581 jknichel
#ifndef ROBOT
487 613 jknichel
                          //return -1; // Computer is less forgiving.
488
                          curr = s;
489 581 jknichel
#else
490
                          curr = s;
491
#endif
492 412 emarinel
                        }
493 581 jknichel
                } // else buffer is empty.
494 409 emarinel
495 412 emarinel
#ifndef ROBOT
496 581 jknichel
                usleep(100);
497 412 emarinel
#endif
498
        }
499 581 jknichel
500
        return 0;
501 17 bcoltin
}
502
503
/**
504
 * Verifies that the packets checksum is correct.
505
 * (If the checksum is correct, the sum of the bytes
506
 * is 0xFF.)
507
 *
508
 * @param packet the packet received. This includes the first
509
 * three bytes, which are header information from the XBee.
510
 *
511
 * @param len The length of the packet received from the XBee
512
 *
513
 * @return 0 if the checksum is incorrect, nonzero
514
 * otherwise
515
 **/
516 346 bcoltin
int xbee_verify_checksum(char* packet, int len)
517
{
518
        unsigned char sum = 0;
519
        int i;
520
        for (i = 3; i < len; i++)
521
                sum += (unsigned char)packet[i];
522
        return sum == 0xFF;
523 17 bcoltin
}
524
525
/**
526
 * Returns the checksum of the given packet.
527
 *
528
 * @param buf the data for the packet to send
529
 * @param len the length of the packet in bytes
530
 *
531
 * @return the checksum of the packet, which will
532
 * become the last byte sent in the packet
533
 **/
534 346 bcoltin
char xbee_compute_checksum(char* buf, int len)
535
{
536
        int i;
537
        unsigned char sum = 0;
538
        for (i = 0; i < len; i++)
539
                sum += (unsigned char)buf[i];
540
        return 0xFF - sum;
541 17 bcoltin
}
542
543
/**
544
 * Adds header information and checksum to the given
545
 * packet and sends it. Header information includes
546
 * XBEE_FRAME_START and the packet length, as two bytes.
547
 *
548
 * @param buf the packet data
549
 * @param len the size in bytes of the packet data
550
 *
551
 **/
552 418 emarinel
static int xbee_send_frame(char* buf, int len)
553 346 bcoltin
{
554
        char prefix[3];
555
        prefix[0] = XBEE_FRAME_START;
556
        prefix[1] = (len & 0xFF00) >> 8;
557
        prefix[2] = len & 0xFF;
558
        char checksum = xbee_compute_checksum(buf, len);
559 418 emarinel
560
        if (xbee_send(prefix, 3) != 0) {
561
                return -1;
562
        }
563 581 jknichel
564 418 emarinel
        if (xbee_send(buf, len) != 0) {
565
                return -1;
566
        }
567
568
        if (xbee_send(&checksum, 1) != 0) {
569
                return -1;
570
        }
571
572
        return 0;
573 17 bcoltin
}
574
575
/**
576
 * Sends an AT command to read a parameter.
577
 *
578
 * @param command the AT command to send. For exmaple,
579
 * use ID to read the PAN ID and MY to return the XBee ID.
580
 * See the XBee reference guide for a complete listing.
581
 **/
582 717 jknichel
//TODO: this function is so simple, it *may* be beneficial to inline this function.  testing of if
583
// it reduces code size or not should be done to be sure.
584 418 emarinel
static int xbee_send_read_at_command(char* command)
585 346 bcoltin
{
586 418 emarinel
        return xbee_send_modify_at_command(command, NULL);
587 17 bcoltin
}
588
589
/**
590
 * Sends the given AT command.
591
 *
592
 * @param command the AT command to send (e.g., MY, ID)
593
 * @param value the value to pass as a parameter
594
 * (or NULL if there is no parameter)
595
 **/
596 418 emarinel
static int xbee_send_modify_at_command(char* command, char* v