Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / robot / colonet_dragonfly / colonet_dragonfly.c @ 936

History | View | Annotate | Download (14.5 KB)

1
/** @file colonet.c
2
 * @brief Colonet library for DRAGONFLY colony robots
3
 *
4
 * @author Eugene Marinelli
5
 * @date 10/10/07
6
 *
7
 * @bug Handler registration not tested
8
 * @bug Request reponding not implemented - only accepts commands.
9
 */
10

    
11
#include <assert.h>
12
#include <battery.h>
13
#include <colonet_defs.h>
14
#include <colonet_dragonfly.h>
15
#include <dragonfly_lib.h>
16
#include <math.h>
17
#include <string.h>
18
#include <wireless.h>
19

    
20
#define TARGET_POSITION_STOP_DISTANCE_THRESHOLD (550)
21

    
22
typedef struct {
23
  unsigned char msgId; //is this necessary?
24
  void (*handler)(void);
25
} UserHandler;
26

    
27
typedef struct {
28
  int up_x;
29
  int up_y;
30
  int low_x;
31
  int low_y;
32
} VirtualWall;
33

    
34
/* Globals (internal) */
35
static UserHandler user_handlers[USER_DEFINED_MSG_TOTAL];
36

    
37
static volatile unsigned robot_x, robot_y;
38
static volatile unsigned last_x, last_y;
39
static volatile unsigned target_x, target_y;
40
static volatile int updated_robot_pos_ready;
41
static volatile int robot_lost; //set to 1 if robot's position is not known by the server, else 0.
42
static volatile VirtualWall virtual_wall;
43

    
44
typedef enum {MOVING, MOVING_TO_POSITION, RECHARGING, NO_COMMAND} ColonetRobotCommandState;
45
static volatile ColonetRobotCommandState robot_state;
46

    
47
static volatile int virtual_wall_up_available, virtual_wall_low_available;
48

    
49
#define ERROR_MAG_TO_MOTOR_DIFFENTIAL_CONST (1)
50

    
51
/* Internal function prototypes */
52
static unsigned two_bytes_to_int(unsigned char high, unsigned char low);
53
static void colonet_handle_receive(char type, int source, unsigned char* packet, int length);
54
static void move_to_position_routine(unsigned x, unsigned y);
55
static int dist_squared(int x1, int y1, int x2, int y2);
56
static void move_routine(void);
57

    
58
static PacketGroupHandler colonet_pgh;
59

    
60
/* two_bytes_to_int(char a, char b)
61
 * Returns int of form [high][low]
62
 */
63
static unsigned int two_bytes_to_int(unsigned char high, unsigned char low) {
64
  return (high<<8) | low;
65
}
66

    
67
static int virtual_wall_available() {
68
  return virtual_wall_up_available && virtual_wall_low_available;
69
}
70

    
71
/* Public functions */
72
int colonet_init() {
73
  colonet_pgh.groupCode = COLONET_PACKET_GROUP_ID;
74
  colonet_pgh.timeout_handler = NULL;
75
  colonet_pgh.handle_response = NULL;
76
  colonet_pgh.handle_receive = colonet_handle_receive;
77
  colonet_pgh.unregister = NULL;
78

    
79
  // TODO this should return an error if wl_init has not been called yet.
80
  wl_register_packet_group(&colonet_pgh);
81

    
82
  robot_x = last_x = 0;
83
  robot_y = last_y = 0;
84
  updated_robot_pos_ready = 0;
85
  robot_lost = 1;
86

    
87
  virtual_wall_up_available = 0;
88
  virtual_wall_low_available = 0;
89

    
90
  virtual_wall.up_x = 0;
91
  virtual_wall.up_y = 0;
92
  virtual_wall.low_x = 0;
93
  virtual_wall.low_y = 0;
94

    
95
  robot_state = NO_COMMAND;
96

    
97
  return 0;
98
}
99

    
100
/* colonet_add_message
101
 * Adds a user-defined message
102
 */
103
int colonet_add_message(unsigned char msgId, void (*handler)(void)) {
104
  if(msgId < USER_DEFINED_MSG_ID_START  /* || msgId > USER_DEFINED_MSG_ID_END */){
105
    return -1;
106
  }
107

    
108
  /* Register this function in the array */
109
  user_handlers[msgId-USER_DEFINED_MSG_ID_START].msgId = msgId;
110
  user_handlers[msgId-USER_DEFINED_MSG_ID_START].handler = handler;
111

    
112
  return 0;
113
}
114

    
115
void get_absolute_position(int* x, int* y) {
116
  *x = robot_x;
117
  *y = robot_y;
118
}
119

    
120
static void report_arrived() {
121
  //usb_puts("requesting_abs_position\n");
122
  ColonetRobotServerPacket pkt;
123
  pkt.client_id = -1; //global
124
  pkt.msg_code = ROBOT_REPORT_ARRIVED_AT_POSITION;
125
  wl_send_global_packet(colonet_pgh.groupCode, 0, (char*)&pkt, sizeof(ColonetRobotServerPacket), 0);
126
}
127

    
128
void request_abs_position() {
129
  //usb_puts("requesting_abs_position\n");
130
  ColonetRobotServerPacket pkt;
131
  pkt.client_id = -1; //global
132
  pkt.msg_code = ROBOT_REQUEST_POSITION_FROM_SERVER;
133
  wl_send_global_packet(colonet_pgh.groupCode, 0, (char*)&pkt, sizeof(ColonetRobotServerPacket), 0);
134
}
135

    
136
static int dist_squared(int x1, int y1, int x2, int y2) {
137
  int dx = x1 - x2;
138
  int dy = y1 - y2;
139
  return dx*dx + dy*dy;
140
}
141

    
142
/** Differential is the intended left motor speed - right motor speed. */
143
static void set_motors_with_differential(int differential) {
144
  int ml, mr;
145

    
146
  if (differential >= 0) {
147
    /* Going left or straight. */
148
    ml = 200;
149
    mr = ml - differential;
150
  } else {
151
    /* Turning right. */
152
    mr = 200;
153
    ml = mr + differential;
154
  }
155

    
156
  int motor1_dir = mr < 0 ? 0 : 1;
157
  int motor2_dir = ml < 0 ? 0 : 1;
158

    
159
  motor1_set(motor1_dir, mr);
160
  motor2_set(motor2_dir, ml);
161
}
162

    
163
static int get_motor_differential(int e_x, int e_y, int v_x, int v_y) {
164
  //  char buf[80];
165
  //  sprintf(buf,"ex:%d ey:%d vx:%d vy:%d\n", e_x, e_y, v_x, v_y);
166
  //  usb_puts(buf);
167

    
168
  return 250;
169

    
170
  /*
171
  long e_mag = sqrt_100_approx(e_x*e_x + e_y*e_y);
172
  long v_mag = sqrt_100_approx(v_x*v_x + v_y*v_y);
173
  long e_norm_x = (1000 * e_x) / e_mag;
174
  long e_norm_y = (1000 * e_y) / e_mag;
175
  long v_norm_x = (1000 * v_x) / v_mag;
176
  long v_norm_y = (1000 * v_y) / v_mag;
177
  long e_dot_v = e_norm_x*v_norm_x + e_norm_y*v_norm_y;
178
  */
179

    
180

    
181
}
182

    
183
static int inside_virtual_wall(int x, int y) {
184
  if (virtual_wall_available()) {
185
    if (robot_x < virtual_wall.up_x || robot_x > virtual_wall.low_x || robot_y < virtual_wall.up_y
186
        || robot_y > virtual_wall.low_y) {
187
      return 0;
188
    } else {
189
      return 1;
190
    }
191
  } else {
192
    return 1;
193
  }
194
}
195

    
196

    
197
/**
198
 * Main loop which responds to commands.
199
 */
200
void colonet_run(void (*step)(void)) {
201
  char buf[80];
202

    
203
  while (1) {
204
    wl_do();
205
    request_abs_position();
206
    step();
207

    
208
    switch (robot_state) {
209
    case NO_COMMAND:
210
      break;
211

    
212
    case MOVING:
213
      if (robot_lost) {
214
        motors_off();
215
        robot_state = NO_COMMAND;
216
      } else {
217
        if (updated_robot_pos_ready) {
218
          if (!inside_virtual_wall(robot_x, robot_y)) {
219
            usb_puts("outside of boundary\n");
220
            motors_off();
221
            robot_state = NO_COMMAND;
222
          }
223

    
224
          updated_robot_pos_ready = 0;
225
        }
226
      }
227
      break;
228

    
229
    case MOVING_TO_POSITION:
230
      usb_puts("move to absolute position\n");
231

    
232
      if (robot_lost) {
233
        motors_off();
234
        robot_state = NO_COMMAND;
235
      } else {
236
        if (updated_robot_pos_ready) {
237
          updated_robot_pos_ready = 0;
238

    
239
          if (!inside_virtual_wall(robot_x, robot_y)) {
240
            usb_puts("outside of boundary\n");
241
            motors_off();
242
            robot_state = NO_COMMAND;
243
          } else {
244
            int dist = 0;
245
            if ((dist = dist_squared(robot_x, robot_y, target_x, target_y)) < TARGET_POSITION_STOP_DISTANCE_THRESHOLD) {
246
              // Reached destination.
247
              motors_off();
248
              robot_state = NO_COMMAND;
249

    
250
              // Notify server that robot arrived.
251
              report_arrived();
252
            } else {
253
              // e is the error vector (where we want to go)
254
              //char buf[80];
255
              //                sprintf(buf, "target_x:%d target_y:%d robot_x:%d robot_y:%d\n", target_x, target_y, robot_x, robot_y);
256
              //                usb_puts(buf);
257

    
258
              int e_x = (int)target_x - (int)robot_x;
259
              int e_y = (int)target_y - (int)robot_y;
260

    
261
              //                sprintf(buf, "e: (x:%d,y:%d)\n", e_x, e_y);
262
              //                usb_puts(buf);
263

    
264
              // v is the velocity vector (where we just went)
265
              int v_x = (int)robot_x - (int)last_x;
266
              int v_y = (int)robot_y - (int)last_y;
267

    
268
              //sprintf(buf, "v: (x:%d,y:%d)\n", v_x, v_y);
269
              //usb_puts(buf);
270

    
271
              if (abs(v_x) < 2 && abs(v_x) < 2) {
272
                // if we didn't move since the last check, just go forward.
273
                motor1_set(1, 180);
274
                motor2_set(1, 180);
275
              } else {
276
                int e_dot_v = e_x * v_x + e_y * v_y;
277

    
278
                // move according to algortihm
279
                int e_mag = e_x*e_x + e_y*e_y;
280
                //       int motor_differential = 180;//e_mag >> 7;
281
                int motor_differential = get_motor_differential(e_x, e_y, v_x, v_y);
282

    
283
                // p is perpendicular to v, directed to the "left" of the robot.
284
                int p_x = v_y;
285
                int p_y = -v_x;
286
                int dot_product_pe = p_x * e_x + p_y * e_y;
287

    
288
                // if the dot product of p and e is negative, we should turn right. otherwise turn left.
289
                if (dot_product_pe < 0) {
290
                  motor_differential = -motor_differential;
291
                }
292

    
293
                set_motors_with_differential(motor_differential);
294
              }
295
            }
296
          }
297
        }
298
      }
299
      break;
300
      
301
    case RECHARGING:
302
      orb_set_color(PURPLE);
303
      break;
304
        
305
      
306
    }
307
  }
308
}
309

    
310

    
311
/* Private functions */
312

    
313
/** @brief Handles colonet packets.  Should be called by parse_buffer
314
 * when it is determined that a colonet message has been received.
315
 *
316
 * @param robot_id The robot id
317
 * @param pkt_buf The packet buffer (e.g. wl_buf)
318
 *
319
 * @return -1 on error (invalid msgId), 0 on success
320
 */
321
static void colonet_handle_receive(char type, int wl_source, unsigned char* packet, int length) {
322
  length = length;
323
  wl_source = wl_source;
324

    
325
  ColonetRobotServerPacket* pkt = (ColonetRobotServerPacket*)packet;
326
  unsigned char* args; //up to 7 char args
327
  unsigned int_args[3]; //up to 3 int (2-byte) args
328
  char buf[40];
329

    
330

    
331
  /*  
332
  usb_puts("Packet received.\n");
333

334
  sprintf(buf, "length=%d\n", length);
335

336
  int i;
337
  for (i = 0; i < length; i++) {
338
    sprintf(buf, "%d: %d ", i, packet[i]);
339
    usb_puts(buf);
340
 }
341
  usb_puts("\n");
342
  */
343

    
344
  //sprintf(buf, "received message from client_id=%d of length %d\n", pkt->client_id, length);
345
  //usb_puts(buf);
346

    
347
  ///assert(length == sizeof(ColonetRobotServerPacket));
348

    
349
  /*
350
  sprintf(buf, "client_id:%d, msg_code:%d\n", pkt.client_id, pkt.msg_code);
351
  usb_puts(buf);*/
352

    
353
  args = pkt->data;
354

    
355
  int_args[0] = two_bytes_to_int(args[0], args[1]);
356
  int_args[1] = two_bytes_to_int(args[2], args[3]);
357
  int_args[2] = two_bytes_to_int(args[4], args[5]);
358

    
359
  if (type == COLONET_REQUEST) {
360
    //sprintf(buf, "received colonet request: pkt msgcode is %d\n", pkt->msg_code);
361
    //usb_puts(buf);
362

    
363
    switch (pkt->msg_code) {
364
      //Sharp
365
    case READ_DISTANCE:
366
      break;
367

    
368
      //Analog
369
    case WHEEL:
370
      break;
371

    
372
    case BATTERY:
373
      sprintf((char*)pkt->data, "%d", (int)battery8());
374
      // NOTE: wl_send_robot_to_robot_global_packet does not work here!
375
      wl_send_global_packet(COLONET_PACKET_GROUP_ID, 0, (char*)pkt, sizeof(ColonetRobotServerPacket), 0);
376
      break;
377

    
378
      //BOM
379
    case GETMAXBOM:
380
      break;
381

    
382
    case DIGITAL_INPUT:
383
      break;
384
    }
385
  } else if (type == COLONET_COMMAND) {
386
    // sprintf(buf, "colonet command ... pkt->msg_code=%d\n", pkt->msg_code);
387
    // usb_puts(buf);
388

    
389
/* TODO uncomment this stuff */
390
/*     if (pkt.msg_code >= USER_DEFINED_MSG_ID_START && */
391
/*         pkt.msg_code <= USER_DEFINED_MSG_ID_END) { */
392
/*       if (user_handlers[pkt.msg_code - USER_DEFINED_MSG_ID_START].handler) { */
393
/*         /\* Call the user's handler function if it the function's address */
394
/*          * is non-zero (has been set) *\/ */
395
/*         user_handlers[pkt.msg_code - USER_DEFINED_MSG_ID_START].handler(); */
396
/*       } */
397
/*     } */
398

    
399
    switch (pkt->msg_code) {
400
/*     default: */
401
/*       printf("%s: Error - message code %d not implemented\n", __FUNCTION__, */
402
/*              pkt.msg_code); */
403
/*       break; */
404

    
405
    case SERVER_REPORT_ROBOT_LOST:
406
      last_x = robot_x;
407
      last_y = robot_y;
408

    
409
      robot_lost = 1;
410

    
411
      usb_puts("lost\n");
412

    
413
      motors_off();
414
      break;
415

    
416
    case SERVER_CLEAR_VIRTUAL_WALL:
417
      virtual_wall_up_available = 0;
418
      virtual_wall_low_available = 0;
419
      break;
420

    
421
    case SERVER_REPORT_VIRTUAL_WALL_UPPER:
422
      virtual_wall.up_x = int_args[0];
423
      virtual_wall.up_y = int_args[1];
424

    
425
      virtual_wall_up_available = 1;
426

    
427
      sprintf(buf, "%d %d\n", virtual_wall.up_x, virtual_wall.up_y);
428
      usb_puts(buf);
429
      break;
430

    
431
    case SERVER_REPORT_VIRTUAL_WALL_LOWER:
432
      virtual_wall.low_x = int_args[0];
433
      virtual_wall.low_y = int_args[1];
434

    
435
      sprintf(buf, "%d %d\n", virtual_wall.low_x, virtual_wall.low_y);
436
      usb_puts(buf);
437

    
438
      virtual_wall_low_available = 1;
439
      break;
440

    
441
    case SERVER_REPORT_POSITION_TO_ROBOT:
442
      last_x = robot_x;
443
      last_y = robot_y;
444
      robot_x = (unsigned)int_args[0];
445
      robot_y = (unsigned)int_args[1];
446

    
447
      updated_robot_pos_ready = 1;
448
      robot_lost = 0;
449

    
450
      //      sprintf(buf, "pos is: (x:%d, y:%d)\n", robot_x, robot_y);
451
      //usb_puts(buf);
452
      break;
453

    
454
    case MOVE_TO_ABSOLUTE_POSITION:
455
                        sprintf(buf, "move to abs (x:%u, y:%u)\n", (unsigned)int_args[0], (unsigned)int_args[1]);
456
                        usb_puts(buf);
457

    
458
      target_x = (unsigned)int_args[0];
459
      target_y = (unsigned)int_args[1];
460

    
461
      robot_state = MOVING_TO_POSITION;
462
      break;
463

    
464
      //Buzzer
465
    case BUZZER_INIT:
466
      buzzer_init();
467
      break;
468
    case BUZZER_SET_VAL:
469
      buzzer_set_val(args[0]);
470
      break;
471
    case BUZZER_SET_FREQ:
472
      buzzer_set_freq(args[0]);
473
      break;
474
    case BUZZER_CHIRP:
475
      buzzer_chirp(int_args[0], int_args[1]);
476
      break;
477
    case BUZZER_OFF:
478
      buzzer_off();
479
      break;
480

    
481
      //Orb
482
    case ORB_INIT:
483
      orb_init();
484
      break;
485
    case ORB_SET:
486
      orb_set(args[0], args[1], args[2]);
487
      break;
488
    case ORB_SET_COLOR:
489
      orb_set_color(int_args[0]);
490
      break;
491
    case ORB_DISABLE:
492
      orb_disable();
493
      break;
494
    case ORB_ENABLE:
495
      orb_enable();
496
      break;
497

    
498
      //Motors
499
    case MOTORS_INIT:
500
      usb_puts("calling motors_init\n");
501
      motors_init();
502
      break;
503

    
504
    case MOTORS_OFF:
505
      robot_state = NO_COMMAND;
506

    
507
      usb_puts("calling motors_off\n");
508
      motors_off();
509
      break;
510

    
511
    case MOVE:
512
      if (args[1] == 0 && args[3] == 0) {
513
        motors_off();
514
        robot_state = MOVING;
515

    
516
        usb_puts("stopped\n");
517
      } else {
518
        /* format for move: left direction, left velocity, right direction, right velocity */
519
        motor1_set(args[0], args[1]);
520
        motor2_set(args[2], args[3]);
521

    
522
        /* Loop and update position for virtual wall. */
523
        robot_state = MOVING;
524
      }
525
      break;
526
      
527
    case RECHARGE:
528
      robot_state = RECHARGING;
529
      break;
530

    
531
    case PRINTF:
532
      usb_puts((char*)pkt->data);
533
      break;
534

    
535
    case KILL_ROBOT:
536
      robot_state = NO_COMMAND;
537

    
538
      motors_off();
539
      buzzer_off();
540
      exit(0); //kill the robot
541
      break;
542
    //Time
543
    case DELAY_MS:
544
      delay_ms(int_args[0]);
545
      break;
546

    
547
      //Analog
548
    case ANALOG_INIT:
549
      //TODO: how do we really init the analog? this doesn't work:
550
      //analog_init();
551
      break;
552

    
553
      //BOM
554
    case BOM_ON:
555
      bom_on();
556
      break;
557
    case BOM_OFF:
558
      bom_off();
559
      break;
560

    
561
    //Dio
562
    case DIGITAL_OUTPUT:
563
      digital_output(int_args[0], int_args[1]);
564
      break;
565
    }
566
  } else {
567
    sprintf(buf, "%s: Error: Invalid colonet message type\n", __FUNCTION__);
568
    usb_puts(buf);
569
  }
570
}