blob: 7d560963f5550b9553a6adddae18ab839ba662bf [file] [log] [blame]
Dan Morrill8f1028f2010-09-12 12:02:59 -07001/*
2 * Copyright 2010 The Android Open-Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17/*
18 * Define the basic structure for messages from the host.
Dan Morrill7b123992010-10-20 12:22:44 -070019 * Messages are MESSAGE_SIZE bytes, with a 2-byte opcode, a 2-byte
20 * unique ID defined by the sender, and the remainder payload.
Dan Morrill8f1028f2010-09-12 12:02:59 -070021 * The remaining 2 bytes must be 0xFEEDFACE. This is used by
22 * the message handler as a tail sentinel to resync with the
23 * sender in case data is lost and the fixed-byte messages
24 * get out of sync.
25 */
Dan Morrill6bfbb182011-01-21 10:04:37 -080026#define MESSAGE_SIZE 128 // serial buffer size on TTL is 128 bytes
Dan Morrill7b123992010-10-20 12:22:44 -070027#define MESSAGE_DELIMITER 0xBEEF
Dan Morrill8f1028f2010-09-12 12:02:59 -070028#define MESSAGE_ESCAPE 0x2a
29struct message {
30 uint16_t opcode;
31 uint16_t id;
Dan Morrill7b123992010-10-20 12:22:44 -070032 uint8_t data[MESSAGE_SIZE - 6];
Dan Morrill8f1028f2010-09-12 12:02:59 -070033 uint16_t tail;
34};
35struct message CURRENT_MESSAGE;
36
37#define OPCODE_RESET 0x00
38struct reset {
39 uint16_t opcode;
40 uint16_t id;
Dan Morrill7b123992010-10-20 12:22:44 -070041 uint8_t unused[MESSAGE_SIZE - 4];
Dan Morrill8f1028f2010-09-12 12:02:59 -070042};
43
44#define OPCODE_INIT_TIME (OPCODE_RESET + 1)
45struct init_time {
46 uint16_t opcode;
47 uint16_t id;
48 uint32_t cur_raw_time;
Dan Morrill7b123992010-10-20 12:22:44 -070049 uint8_t unused[MESSAGE_SIZE - 6];
50};
51
52#define OPCODE_CURRENT_TIME (OPCODE_RESET + 2)
53struct current_time { // we never actually use this, but here for consistency
54 uint16_t opcode;
55 uint16_t id;
56 uint8_t unused[MESSAGE_SIZE - 4];
57};
58
59#define OPCODE_SETMODE_PONG (OPCODE_RESET + 3)
60struct setmode_pong {
61 uint16_t opcode;
62 uint16_t id;
63 uint16_t playfield_width;
64 uint16_t playfield_height;
Dan Morrill6bfbb182011-01-21 10:04:37 -080065 uint16_t ball_x;
66 uint16_t ball_y;
67 uint16_t ball_radius;
68 uint16_t ball_velocity_x;
69 uint16_t ball_velocity_y;
70 uint8_t unused[MESSAGE_SIZE - 18];
Dan Morrill7b123992010-10-20 12:22:44 -070071};
72
73#define OPCODE_PONG_BALL_STATE (OPCODE_RESET + 4)
74struct pong_ball_state {
75 uint16_t opcode;
76 uint16_t id;
Dan Morrill6bfbb182011-01-21 10:04:37 -080077 uint8_t unused[MESSAGE_SIZE - 4];
Dan Morrill8f1028f2010-09-12 12:02:59 -070078};
79
80struct wall_time_struct {
Dan Morrill6bfbb182011-01-21 10:04:37 -080081 uint32_t raw;
Dan Morrill8f1028f2010-09-12 12:02:59 -070082 uint8_t initialized;
83 uint8_t hours;
84 uint8_t minutes;
85 uint8_t seconds;
86};
87struct wall_time_struct WALL_TIME;
88
Dan Morrill6bfbb182011-01-21 10:04:37 -080089/*
90 * Main ball-playing state record.
91 */
Dan Morrill7b123992010-10-20 12:22:44 -070092struct pong_state_struct {
93 uint16_t playfield_width;
94 uint16_t playfield_height;
Dan Morrill6bfbb182011-01-21 10:04:37 -080095 int16_t ball_x;
96 int16_t ball_y;
97 int16_t ball_radius;
98 int16_t velocity_x;
99 int16_t velocity_y;
Dan Morrill8f1028f2010-09-12 12:02:59 -0700100};
Dan Morrill7b123992010-10-20 12:22:44 -0700101struct pong_state_struct PONG_STATE;
102
103
Dan Morrill6bfbb182011-01-21 10:04:37 -0800104/* This is a temporary buffer used by the message handler */
105struct message_buffer {
106 uint16_t count; // number of bytes read into the buffer
107 uint8_t buffer[MESSAGE_SIZE]; // contents of a 'struct message'
108};
109struct message_buffer MESSAGE_BUFFER;
110
111
112/*
113 * Clears all stateful values, including the wall clock time, current message
114 * data, and user/app state. Also clears the message handler's buffer. By
115 * "clear" we mean "memset to 0".
116 */
117void reset() {
118 memset(&WALL_TIME, 0, sizeof(WALL_TIME));
119 memset(&CURRENT_MESSAGE, 0, sizeof(CURRENT_MESSAGE));
120 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
121 memset(&PONG_STATE, 0, sizeof(PONG_STATE));
122}
123
124
125/*
126 * Closes out a serial response, which involves sending a blank line in
127 * between messages. Also prints the current time, for funsies.
128 */
129void close_response() {
Dan Morrill7b123992010-10-20 12:22:44 -0700130 if (WALL_TIME.initialized) {
131 Serial.print("current_time=");
132 Serial.print(WALL_TIME.hours, DEC);
133 Serial.print(":");
134 if (WALL_TIME.minutes < 10)
135 Serial.print("0");
136 Serial.print(WALL_TIME.minutes, DEC);
137 Serial.print(":");
138 if (WALL_TIME.seconds < 10)
139 Serial.print("0");
140 Serial.println(WALL_TIME.seconds, DEC);
141 } else {
142 Serial.println("current_time=00:00:00");
143 }
Dan Morrill6bfbb182011-01-21 10:04:37 -0800144 Serial.print("\r\n");
Dan Morrill7b123992010-10-20 12:22:44 -0700145}
146
Dan Morrill8f1028f2010-09-12 12:02:59 -0700147
Dan Morrill6bfbb182011-01-21 10:04:37 -0800148/*
149 * Opcode processor/handler.
150 */
Dan Morrill8f1028f2010-09-12 12:02:59 -0700151void handle_current_message() {
Dan Morrill6bfbb182011-01-21 10:04:37 -0800152 static uint16_t last_id = 999999;
Dan Morrill7b123992010-10-20 12:22:44 -0700153 static struct setmode_pong* setmode_pong_msg;
Dan Morrill7b123992010-10-20 12:22:44 -0700154
Dan Morrill6bfbb182011-01-21 10:04:37 -0800155 if (CURRENT_MESSAGE.id == last_id) {
Dan Morrill7b123992010-10-20 12:22:44 -0700156 return;
157 }
158 last_id = CURRENT_MESSAGE.id;
159
160 switch (CURRENT_MESSAGE.opcode) {
161
Dan Morrill6bfbb182011-01-21 10:04:37 -0800162 case OPCODE_SETMODE_PONG: // initialize ball animation state
Dan Morrill7b123992010-10-20 12:22:44 -0700163 memset(&PONG_STATE, 0, sizeof(PONG_STATE));
164 setmode_pong_msg = (struct setmode_pong*)(&CURRENT_MESSAGE);
165 PONG_STATE.playfield_width = setmode_pong_msg->playfield_width;
166 PONG_STATE.playfield_height = setmode_pong_msg->playfield_height;
Dan Morrill6bfbb182011-01-21 10:04:37 -0800167 PONG_STATE.ball_x = setmode_pong_msg->ball_x;
168 PONG_STATE.ball_y = setmode_pong_msg->ball_y;
169 PONG_STATE.ball_radius = setmode_pong_msg->ball_radius;
170 PONG_STATE.velocity_x = setmode_pong_msg->ball_velocity_x;
171 PONG_STATE.velocity_y = setmode_pong_msg->ball_velocity_y;
Dan Morrill7b123992010-10-20 12:22:44 -0700172
173 Serial.println("message_type=setmode_pong_ack");
174 Serial.print("id=");
175 Serial.println(CURRENT_MESSAGE.id);
Dan Morrill6bfbb182011-01-21 10:04:37 -0800176 Serial.print("ball_x=");
177 Serial.println(PONG_STATE.ball_x, DEC);
178 Serial.print("ball_y=");
179 Serial.println(PONG_STATE.ball_y, DEC);
180 Serial.print("ball_velocity_x=");
181 Serial.println(PONG_STATE.velocity_x, DEC);
182 Serial.print("ball_velocity_y=");
183 Serial.println(PONG_STATE.velocity_y, DEC);
184 Serial.print("playfield_width=");
185 Serial.println(PONG_STATE.playfield_width, DEC);
186 Serial.print("playfield_height=");
187 Serial.println(PONG_STATE.playfield_height, DEC);
188 Serial.print("ball_radius=");
189 Serial.println(PONG_STATE.ball_radius, DEC);
190 close_response();
Dan Morrill7b123992010-10-20 12:22:44 -0700191 break;
192
Dan Morrill6bfbb182011-01-21 10:04:37 -0800193 case OPCODE_PONG_BALL_STATE: // update a frame
194 /* This gets called once per update/refresh request from host. From the
195 * perspective of the AVR, we are running this animation in arbitrary
196 * time units. If host calls this at 10 FPS, we return data at 10 FPS.
197 * If it calls us at 100FPS, we return data at 100FPS. */
198 PONG_STATE.ball_x += PONG_STATE.velocity_x;
199 PONG_STATE.ball_y += PONG_STATE.velocity_y;
200
201 // all we do is bounce around the inside of the box we were given
202 if ((PONG_STATE.ball_x - PONG_STATE.ball_radius) < 0) {
203 PONG_STATE.velocity_x *= -1;
204 PONG_STATE.ball_x = PONG_STATE.ball_radius;
205 } else if ((PONG_STATE.ball_x + PONG_STATE.ball_radius) > PONG_STATE.playfield_width) {
206 PONG_STATE.velocity_x *= -1;
207 PONG_STATE.ball_x = PONG_STATE.playfield_width - PONG_STATE.ball_radius;
Dan Morrill7b123992010-10-20 12:22:44 -0700208 }
Dan Morrill6bfbb182011-01-21 10:04:37 -0800209
210 if ((PONG_STATE.ball_y - PONG_STATE.ball_radius) < 0) {
211 PONG_STATE.velocity_y *= -1;
212 PONG_STATE.ball_y = PONG_STATE.ball_radius;
213 } else if ((PONG_STATE.ball_y + PONG_STATE.ball_radius) > PONG_STATE.playfield_height) {
214 PONG_STATE.velocity_y *= -1;
215 PONG_STATE.ball_y = PONG_STATE.playfield_height - PONG_STATE.ball_radius;
Dan Morrill7b123992010-10-20 12:22:44 -0700216 }
217
218 Serial.println("message_type=pong_paddle_state");
219 Serial.print("id=");
Dan Morrill6bfbb182011-01-21 10:04:37 -0800220 Serial.println(CURRENT_MESSAGE.id, DEC);
221 Serial.print("ball_x=");
222 Serial.println(PONG_STATE.ball_x, DEC);
223 Serial.print("ball_y=");
224 Serial.println(PONG_STATE.ball_y, DEC);
225 close_response();
226 break;
227
228 case OPCODE_RESET:
229 reset();
230 Serial.println("message_type=reset_ack");
231 Serial.print("id=");
Dan Morrill7b123992010-10-20 12:22:44 -0700232 Serial.println(CURRENT_MESSAGE.id);
Dan Morrill6bfbb182011-01-21 10:04:37 -0800233 close_response();
234 break;
235
236 case OPCODE_INIT_TIME:
237 // cast CURRENT_MESSAGE to our time struct to conveniently fetch
238 // out the current time
239 WALL_TIME.raw = ((struct init_time*)(&CURRENT_MESSAGE))->cur_raw_time;
240 WALL_TIME.initialized = 1;
241
242 Serial.println("message_type=init_time_ack");
243 Serial.print("id=");
244 Serial.println(CURRENT_MESSAGE.id);
245 close_response();
246 break;
247
248 case OPCODE_CURRENT_TIME:
249 Serial.println("message_type=current_time_ack");
250 Serial.print("id=");
251 Serial.println(CURRENT_MESSAGE.id);
252 close_response();
Dan Morrill7b123992010-10-20 12:22:44 -0700253 break;
254
255 default:
Dan Morrill6bfbb182011-01-21 10:04:37 -0800256 Serial.println("message_type=unknown_command_ack");
257 Serial.print("id=");
258 Serial.println(CURRENT_MESSAGE.id);
259 close_response();
Dan Morrill7b123992010-10-20 12:22:44 -0700260 break;
261 }
Dan Morrill8f1028f2010-09-12 12:02:59 -0700262}
263
Dan Morrill8f1028f2010-09-12 12:02:59 -0700264
265/*
266 * Pumps the message processor. That is, this function is intended to be
267 * called once per loop to read all pending Serial/TTL data, and decode a
268 * message from the peer if one is complete. In cases where data is corrupted
269 * (such as by dropping bytes), this function also attempts to re-sync with
270 * the host by discarding messages until it finds a MESSAGE_DELIMITER, after
271 * which is resyncs its buffer on the first subsequent byte.
272 *
273 * This functional also handles two low-level 'system' messages: a reset
274 * instruction which invokes reset(), and an init_time instruction which
275 * provides the soft clock with the current time so that it can start keeping
276 * time.
277 */
278void pump_message_processor() {
Dan Morrill7b123992010-10-20 12:22:44 -0700279 static uint8_t cur_byte;
Dan Morrill8f1028f2010-09-12 12:02:59 -0700280 static uint16_t* cur_word;
281 static int8_t delimiter_index;
Dan Morrill6bfbb182011-01-21 10:04:37 -0800282 static char buf[6];
Dan Morrill7b123992010-10-20 12:22:44 -0700283 while (Serial.available() > 0) { // keep going as long as we might have messages
284 cur_byte = (uint8_t)(Serial.read() & 0x000000ff);
Dan Morrill8f1028f2010-09-12 12:02:59 -0700285 MESSAGE_BUFFER.buffer[(MESSAGE_BUFFER.count)++] = cur_byte;
Dan Morrill7b123992010-10-20 12:22:44 -0700286 if (MESSAGE_BUFFER.count >= MESSAGE_SIZE) {
287 if ((*(uint16_t*)(MESSAGE_BUFFER.buffer + MESSAGE_SIZE - 2)) != MESSAGE_DELIMITER) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700288 // whoops, we got out of sync with the transmitter. Scan current
289 // buffer for the delimiter, discard previous message, and shift
290 // partial next message to front of buffer. This loses a message but
291 // gets us back in sync
292 delimiter_index = -2;
Dan Morrill7b123992010-10-20 12:22:44 -0700293 for (int i = MESSAGE_SIZE - 2; i >= 0; --i) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700294 if (*((uint16_t*)(MESSAGE_BUFFER.buffer + i)) == MESSAGE_DELIMITER) {
Dan Morrill7b123992010-10-20 12:22:44 -0700295 if (((i - 1) >= 0) && (MESSAGE_BUFFER.buffer[i - 1] != MESSAGE_ESCAPE)) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700296 delimiter_index = i;
297 break;
298 }
299 }
300 }
301 MESSAGE_BUFFER.count = 0;
Dan Morrill7b123992010-10-20 12:22:44 -0700302 if (delimiter_index >= 0) {
303 for (int i = delimiter_index + 2; i < MESSAGE_SIZE; ++i, ++(MESSAGE_BUFFER.count)) {
304 MESSAGE_BUFFER.buffer[MESSAGE_BUFFER.count] = MESSAGE_BUFFER.buffer[i];
305 }
Dan Morrill8f1028f2010-09-12 12:02:59 -0700306 }
Dan Morrill7b123992010-10-20 12:22:44 -0700307 memset(MESSAGE_BUFFER.buffer + MESSAGE_BUFFER.count, 0, MESSAGE_SIZE - MESSAGE_BUFFER.count);
Dan Morrill6bfbb182011-01-21 10:04:37 -0800308 close_response();
Dan Morrill8f1028f2010-09-12 12:02:59 -0700309 } else {
Dan Morrill7b123992010-10-20 12:22:44 -0700310 memcpy(&CURRENT_MESSAGE, MESSAGE_BUFFER.buffer, MESSAGE_SIZE);
Dan Morrill8f1028f2010-09-12 12:02:59 -0700311 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
Dan Morrill8f1028f2010-09-12 12:02:59 -0700312 }
313 }
314 }
315}
316
Dan Morrill6bfbb182011-01-21 10:04:37 -0800317
Dan Morrill8f1028f2010-09-12 12:02:59 -0700318/*
319 * Pumps the system wall clock. This checks the device's monotonic clock to
320 * determine elapsed time since last invocation, and updates wall clock time
321 * by dead reckoning. Since the device has no battery backup, a power-off will
322 * lose the current time, so timekeeping cannot begin until an INIT_TIME
323 * message is received. (The pump_message_processor() function handles that.)
324 *
325 * Once timekeeping is underway, current time is exposed to user/app code via
326 * the WALL_TIME object, which has 24-hour HH/MM/SS fields.
327 */
328void pump_clock() {
329 static uint32_t prev_millis = 0;
Dan Morrill7b123992010-10-20 12:22:44 -0700330 static uint32_t tmp_prev_millis = 0;
Dan Morrill8f1028f2010-09-12 12:02:59 -0700331 uint32_t tmp = 0;
332
Dan Morrill7b123992010-10-20 12:22:44 -0700333 if (millis() / 1000 != tmp_prev_millis) {
334 tmp_prev_millis = millis() / 1000;
Dan Morrill7b123992010-10-20 12:22:44 -0700335 }
336
Dan Morrill8f1028f2010-09-12 12:02:59 -0700337 if (WALL_TIME.initialized) {
338 tmp = millis() / 1000;
339 if (tmp != prev_millis) {
340 prev_millis = tmp;
341 WALL_TIME.raw++;
342 }
343 WALL_TIME.seconds = WALL_TIME.raw % 60;
344 WALL_TIME.minutes = (WALL_TIME.raw / 60) % 60;
345 WALL_TIME.hours = (WALL_TIME.raw / (60 * 60)) % 24;
346 }
347}
348
349
350/*
351 * Standard Arduino setup hook.
352 */
353void setup() {
354 Serial.begin(115200);
355}
356
357
358/*
359 * Standard Arduino loop-pump hook.
360 */
361void loop() {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700362 pump_clock();
363 pump_message_processor();
Dan Morrill6bfbb182011-01-21 10:04:37 -0800364 handle_current_message();
Dan Morrill8f1028f2010-09-12 12:02:59 -0700365}