summaryrefslogtreecommitdiff
path: root/core.c
blob: d671d0c748a0d5bfc52b248087a5cc8aaa1f978e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#include "core.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define FAKE_SENSOR_VAL 50
#define MOTOR_OFFSET_RIGHT 0
#define MOTOR_OFFSET_LEFT 0

#define POWER_LEVEL_LOW 30
#define POWER_LEVEL_MED 50
#define POWER_LEVEL_HIGH 70
#define SEND_IT 100

#define IR_THRESHOLD_STRAIGHT 50
#define IR_THRESHOLD_HAS_SIGNAL 200
#define MIN_FREE_FORWARD_SPACE 50 // cm

#define MOVE_TIME_SEARCH 1000

#define POWER_LEVEL_APPROACH 20
#define MOVE_TIME_APPROACH 500

enum RobotCorrection_t { MOVE_STRAIGHT = 0, MOVE_RIGHT = 1, MOVE_LEFT = 2 };

// use a global context structure
// to avoid coupling our routines
// to the global state
struct RobotContext_t {
  enum RobotState_t state;
  enum RobotState_t prevState;
};

int randomIntInRange(int upper, int lower) {
  return rand() % (upper + 1 - lower) + lower;
}

void waitMs(int waitTimeMs) { usleep(waitTimeMs * 1000); }

char *stateToString(enum RobotState_t state) {
  switch (state) {
  case IDLE:
    return "IDLE";
  case SEARCHING:
    return "SEARCHING";
  case ALIGNMENT:
    return "ALIGNMENT";
  case DROP_OBJECT:
    return "DROP_OBJECT";
  case COMPLETE:
    return "COMPLETE";
  case COLLISION:
    return "COLLISION";
  default:
    return "STATE OUT OF BOUNDS";
  }
}

void setMotor(int motorNumber, int powerLevel) {
  printf("Setting motor #%d to %d power level\n", motorNumber, powerLevel);
}

int readSensor(char *sensorName) {
  int val = randomIntInRange(500, -500);
  printf("Reading %d from sensor %s\n", val, sensorName);
  // all sensors can return an int in [-500,500]
  return val;
}

int readIrRight() { return readSensor("Photo Transistor Right"); }

int readIrLeft() { return readSensor("Photo Transistor Left"); }

enum RobotCorrection_t readIrDirection() {
  int irRight, irLeft, irDiff;
  bool diffInRange, hasRightSignal, hasLeftSignal;
  irRight = readIrRight();
  irLeft = readIrLeft();
  irDiff = irLeft - irRight;
  diffInRange = irDiff < IR_THRESHOLD_STRAIGHT;
  hasRightSignal = irRight > IR_THRESHOLD_HAS_SIGNAL;
  hasLeftSignal = irLeft > IR_THRESHOLD_HAS_SIGNAL;
  if (diffInRange && hasRightSignal && hasLeftSignal)
    return MOVE_STRAIGHT;
  else if (irDiff < 0) {
    return MOVE_RIGHT;
  } else {
    return MOVE_LEFT;
  }
}

int readForwardDistance() { return readSensor("Sonic Distance Sensor"); }

// TODO
void sonicSensorCorrection() {}

void dropObject() { printf("Dropping object...\n"); }

void controlDriveMotors(int powerLevelLeft, int powerLevelRight) {
  // motor "1" is the left wheel
  setMotor(1, powerLevelLeft + MOTOR_OFFSET_LEFT);
  // motor "2" is the right wheel;
  setMotor(2, powerLevelRight + MOTOR_OFFSET_RIGHT);
}

void driveStraight(int powerLevel, int time) {
  controlDriveMotors(powerLevel, powerLevel);
  waitMs(time);
  controlDriveMotors(0, 0);
}

void turnRight(int powerLevel, int time) {
  controlDriveMotors(powerLevel, -powerLevel);
  waitMs(time);
  controlDriveMotors(0, 0);
}

void turnLeft(int powerLevel, int time) {
  controlDriveMotors(-powerLevel, powerLevel);
  waitMs(time);
  controlDriveMotors(0, 0);
}

// TODO
bool checkForCollisions() { return false; }

void waitForStartButton() {
  char c;
  while (true) {
    c = getchar();
    if (c == 'n') {
      return;
    }
  }
}

void handleIdle(struct RobotContext_t *ctx) {
  // wait for start button
  waitForStartButton();
  // and then go into our "main"
  // searching state
  ctx->state = SEARCHING;
}

bool shouldLeaveSearching() { return true; }

/**
 * How do we want our search to work?
 *
 * Each time we run this,
 * we'll follow roughly the steps below
 *
 * 1. Check how much space is directly ahead of us.
 *    If there is only a small amount (a few cm or less),
 *    we should turn until we have more "search space".
 *    This makes sure we're never pointed at a wall
 *
 * 2. Look at the difference between the readings
 *    on the two phototransistors.
 *
 *    If the diff ~ 0 and signal levels meet a
 *    certain minimum threshold, then we should proceed
 *    straight for a short distance
 *
 *    If the diff > 0, we should turn until
 *    this difference gets closer. If it strats increasing,
 *    back track and turn the other way.
 */
void handleSearching(struct RobotContext_t *ctx) {
  enum RobotCorrection_t move;
  if (readForwardDistance() < MIN_FREE_FORWARD_SPACE) {
    sonicSensorCorrection();
  }
  move = readIrDirection();
  switch (move) {
  case MOVE_STRAIGHT:
    driveStraight(POWER_LEVEL_MED, MOVE_TIME_SEARCH);
    break;
  case MOVE_RIGHT:
    turnRight(POWER_LEVEL_LOW, MOVE_TIME_SEARCH);
    break;
  case MOVE_LEFT:
    turnLeft(POWER_LEVEL_LOW, MOVE_TIME_SEARCH);
    break;
  }
  if (shouldLeaveSearching()) {
    ctx->state = ALIGNMENT;
  }
};

/**
 * This state is reponsible for the approach,
 * so if things get too inaccurate, you kick back
 * to the SEARCHING state
 *
 * TODO
 */
void handleAlignment(struct RobotContext_t *ctx) {
  enum RobotCorrection_t move = readIrDirection();
  // if we aren't still aligned
  // to move straight, kick back
  // to the searching state,
  // and bail hard on this
  if (move != MOVE_STRAIGHT) {
    ctx->state = SEARCHING;
    return;
  }
}

void handleDrop(struct RobotContext_t *ctx) {
  dropObject();
  waitMs(1000);
  driveStraight(-POWER_LEVEL_MED, 2000);
}

void handleComplete(struct RobotContext_t *ctx) { printf("Done!\n"); }

void handleCollision(struct RobotContext_t *ctx) {
  // we must search again before returning
  // to the alignment state after recovering from
  // a collision
  if (ctx->prevState == ALIGNMENT) {
    ctx->state = SEARCHING;
  } else {
    ctx->state = ctx->prevState;
  }
}

void startStateMachine(struct RobotContext_t *ctx) {
  bool inCollisionState;
  while (true) {
    printf("Current state %s\n", stateToString(ctx->state));
    // we assume one of routines called in the switch
    // statement changes ctx->state, so we make a backup
    // of it before any of those routines run
    ctx->prevState = ctx->state;
    inCollisionState = ctx->state == COLLISION;
    if (checkForCollisions() && !inCollisionState) {
      ctx->state = COLLISION;
    }
    switch (ctx->state) {
    case IDLE:
      handleIdle(ctx);
      break;
    case SEARCHING:
      handleSearching(ctx);
      break;
    case ALIGNMENT:
      handleAlignment(ctx);
      break;
    case DROP_OBJECT:
      handleDrop(ctx);
      break;
    case COMPLETE:
      handleComplete(ctx);
      break;
    case COLLISION:
      handleCollision(ctx);
      break;
    }
  }
}
/**
 * Problem description:
 *
 * 1. Robot must locate infrared beacon
 * 2. Get within a certain distance to the infrared beacon
 * 3. Drop object on beacon
 * 4. Signal completion somehow
 *
 * * must avoid walls as best possible
 */
void core() {
  // RobotContext_t refers to the "state of the world"
  // and "context of our robot" in our current enviornment

  // initial state is idle
  struct RobotContext_t ctx = {.state = IDLE};
  struct RobotContext_t *ctxPtr = &ctx;
  startStateMachine(ctxPtr);
}