blob: 0c413884f8591d2c3f0d97afe7f3cfbdca2e0b21 [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 Morrill7b123992010-10-20 12:22:44 -070026#define MESSAGE_SIZE 128
27#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;
65 uint16_t paddle_width;
66 uint16_t paddle_offset;
67 uint16_t max_paddle_motion;
68 uint8_t unused[MESSAGE_SIZE - 14];
69};
70
71#define OPCODE_PONG_BALL_STATE (OPCODE_RESET + 4)
72struct pong_ball_state {
73 uint16_t opcode;
74 uint16_t id;
75 uint16_t ball_x;
76 uint16_t ball_y;
77 uint8_t unused[MESSAGE_SIZE - 8];
Dan Morrill8f1028f2010-09-12 12:02:59 -070078};
79
80struct wall_time_struct {
81 uint32_t raw; // long == 4-byte on AVR
82 uint8_t initialized;
83 uint8_t hours;
84 uint8_t minutes;
85 uint8_t seconds;
86};
87struct wall_time_struct WALL_TIME;
88
Dan Morrill7b123992010-10-20 12:22:44 -070089struct pong_state_struct {
90 uint16_t playfield_width;
91 uint16_t playfield_height;
92 uint16_t paddle_width;
93 uint16_t paddle_offset;
94 uint16_t max_paddle_motion;
95 uint16_t paddle_x;
96 uint16_t last_ball_x;
97 uint16_t last_ball_y;
Dan Morrill8f1028f2010-09-12 12:02:59 -070098};
Dan Morrill7b123992010-10-20 12:22:44 -070099struct pong_state_struct PONG_STATE;
100
101
102void print_current_time() {
103 if (WALL_TIME.initialized) {
104 Serial.print("current_time=");
105 Serial.print(WALL_TIME.hours, DEC);
106 Serial.print(":");
107 if (WALL_TIME.minutes < 10)
108 Serial.print("0");
109 Serial.print(WALL_TIME.minutes, DEC);
110 Serial.print(":");
111 if (WALL_TIME.seconds < 10)
112 Serial.print("0");
113 Serial.println(WALL_TIME.seconds, DEC);
114 } else {
115 Serial.println("current_time=00:00:00");
116 }
117}
118
Dan Morrill8f1028f2010-09-12 12:02:59 -0700119
120void handle_current_message() {
Dan Morrill7b123992010-10-20 12:22:44 -0700121 static uint16_t last_id;
122 static struct setmode_pong* setmode_pong_msg;
123 static struct pong_ball_state* pong_ball_state_msg;
124 static uint16_t paddle_half_width;
125 static uint16_t paddle_max;
126 static uint16_t danger;
127 static uint8_t invert;
128 static uint16_t delta;
129
130 if (CURRENT_MESSAGE.id == 0 || CURRENT_MESSAGE.id == last_id) {
131 return;
132 }
133 last_id = CURRENT_MESSAGE.id;
134
135 switch (CURRENT_MESSAGE.opcode) {
136
137 case OPCODE_SETMODE_PONG:
138 memset(&PONG_STATE, 0, sizeof(PONG_STATE));
139 setmode_pong_msg = (struct setmode_pong*)(&CURRENT_MESSAGE);
140 PONG_STATE.playfield_width = setmode_pong_msg->playfield_width;
141 PONG_STATE.playfield_height = setmode_pong_msg->playfield_height;
142 PONG_STATE.paddle_width = setmode_pong_msg->paddle_width;
143 PONG_STATE.paddle_offset = setmode_pong_msg->paddle_offset;
144 PONG_STATE.max_paddle_motion = setmode_pong_msg->max_paddle_motion;
145
146 paddle_half_width = PONG_STATE.paddle_width / 2;
147 paddle_max = PONG_STATE.playfield_width - paddle_half_width;
148
149 Serial.println("message_type=setmode_pong_ack");
150 Serial.print("id=");
151 Serial.println(CURRENT_MESSAGE.id);
152 print_current_time();
153 Serial.println("");
154 break;
155
156 case OPCODE_PONG_BALL_STATE:
157 pong_ball_state_msg = (struct pong_ball_state*)(&CURRENT_MESSAGE);
158 danger = pong_ball_state_msg->ball_x - PONG_STATE.paddle_x;
159 invert = (danger < 0);
160 danger *= invert ? -1 : 1;
161 if (danger < paddle_half_width) {
162 delta = 0;
163 } else if (danger < PONG_STATE.playfield_width / 3) {
164 delta = PONG_STATE.max_paddle_motion / 3;
165 } else if (danger < PONG_STATE.playfield_width * 2 / 3) {
166 delta = PONG_STATE.max_paddle_motion * 2 / 3;
167 } else {
168 delta = PONG_STATE.max_paddle_motion;
169 }
170 delta *= invert ? 1 : -1;
171 PONG_STATE.paddle_x += delta;
172 if (PONG_STATE.paddle_x < paddle_half_width) {
173 PONG_STATE.paddle_x = paddle_half_width;
174 } else if (PONG_STATE.paddle_x > paddle_max) {
175 PONG_STATE.paddle_x = paddle_max;
176 }
177
178 Serial.println("message_type=pong_paddle_state");
179 Serial.print("id=");
180 Serial.println(CURRENT_MESSAGE.id);
181 print_current_time();
182 Serial.print("paddle_x=");
183 Serial.println(PONG_STATE.paddle_x);
184 Serial.println("");
185 break;
186
187 default:
188 break;
189 }
Dan Morrill8f1028f2010-09-12 12:02:59 -0700190}
191
192/* This is a temporary buffer used by the message handler */
193struct message_buffer {
Dan Morrill7b123992010-10-20 12:22:44 -0700194 uint16_t count; // number of bytes read into the buffer
195 uint8_t buffer[MESSAGE_SIZE]; // contents of a 'struct message'
Dan Morrill8f1028f2010-09-12 12:02:59 -0700196};
197struct message_buffer MESSAGE_BUFFER;
198
199/*
200 * Clears all stateful values, including the wall clock time, current message
201 * data, and user/app state. Also clears the message handler's buffer. By
202 * "clear" we mean "memset to 0".
203 */
204void reset() {
205 memset(&WALL_TIME, 0, sizeof(WALL_TIME));
206 memset(&CURRENT_MESSAGE, 0, sizeof(CURRENT_MESSAGE));
Dan Morrill8f1028f2010-09-12 12:02:59 -0700207 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
Dan Morrill7b123992010-10-20 12:22:44 -0700208 memset(&PONG_STATE, 0, sizeof(PONG_STATE));
Dan Morrill8f1028f2010-09-12 12:02:59 -0700209}
210
211
212/*
213 * Pumps the message processor. That is, this function is intended to be
214 * called once per loop to read all pending Serial/TTL data, and decode a
215 * message from the peer if one is complete. In cases where data is corrupted
216 * (such as by dropping bytes), this function also attempts to re-sync with
217 * the host by discarding messages until it finds a MESSAGE_DELIMITER, after
218 * which is resyncs its buffer on the first subsequent byte.
219 *
220 * This functional also handles two low-level 'system' messages: a reset
221 * instruction which invokes reset(), and an init_time instruction which
222 * provides the soft clock with the current time so that it can start keeping
223 * time.
224 */
225void pump_message_processor() {
Dan Morrill7b123992010-10-20 12:22:44 -0700226 static uint8_t cur_byte;
Dan Morrill8f1028f2010-09-12 12:02:59 -0700227 static uint16_t* cur_word;
228 static int8_t delimiter_index;
Dan Morrill7b123992010-10-20 12:22:44 -0700229 static char buf[4];
230 while (Serial.available() > 0) { // keep going as long as we might have messages
231 cur_byte = (uint8_t)(Serial.read() & 0x000000ff);
Dan Morrill8f1028f2010-09-12 12:02:59 -0700232 MESSAGE_BUFFER.buffer[(MESSAGE_BUFFER.count)++] = cur_byte;
Dan Morrill7b123992010-10-20 12:22:44 -0700233 Serial.print("booga ");
234 Serial.print(itoa(MESSAGE_BUFFER.count, buf, 10));
235 Serial.print(" ");
236 Serial.print(itoa(Serial.available(), buf, 10));
237 Serial.print(" ");
238 Serial.println(itoa(cur_byte, buf, 10));
239 if (MESSAGE_BUFFER.count >= MESSAGE_SIZE) {
240 if ((*(uint16_t*)(MESSAGE_BUFFER.buffer + MESSAGE_SIZE - 2)) != MESSAGE_DELIMITER) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700241 // whoops, we got out of sync with the transmitter. Scan current
242 // buffer for the delimiter, discard previous message, and shift
243 // partial next message to front of buffer. This loses a message but
244 // gets us back in sync
245 delimiter_index = -2;
Dan Morrill7b123992010-10-20 12:22:44 -0700246 for (int i = MESSAGE_SIZE - 2; i >= 0; --i) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700247 if (*((uint16_t*)(MESSAGE_BUFFER.buffer + i)) == MESSAGE_DELIMITER) {
Dan Morrill7b123992010-10-20 12:22:44 -0700248 if (((i - 1) >= 0) && (MESSAGE_BUFFER.buffer[i - 1] != MESSAGE_ESCAPE)) {
Dan Morrill8f1028f2010-09-12 12:02:59 -0700249 delimiter_index = i;
250 break;
251 }
252 }
253 }
Dan Morrill7b123992010-10-20 12:22:44 -0700254 Serial.print("klaxon ");
255 Serial.println(itoa(delimiter_index, buf, 10));
256 Serial.print("klaxon ");
257 Serial.println(itoa(*((uint16_t*)(MESSAGE_BUFFER.buffer + MESSAGE_SIZE - 2)), buf, 10));
Dan Morrill8f1028f2010-09-12 12:02:59 -0700258 MESSAGE_BUFFER.count = 0;
Dan Morrill7b123992010-10-20 12:22:44 -0700259 if (delimiter_index >= 0) {
260 for (int i = delimiter_index + 2; i < MESSAGE_SIZE; ++i, ++(MESSAGE_BUFFER.count)) {
261 MESSAGE_BUFFER.buffer[MESSAGE_BUFFER.count] = MESSAGE_BUFFER.buffer[i];
262 }
Dan Morrill8f1028f2010-09-12 12:02:59 -0700263 }
Dan Morrill7b123992010-10-20 12:22:44 -0700264 memset(MESSAGE_BUFFER.buffer + MESSAGE_BUFFER.count, 0, MESSAGE_SIZE - MESSAGE_BUFFER.count);
Dan Morrill8f1028f2010-09-12 12:02:59 -0700265 } else {
Dan Morrill7b123992010-10-20 12:22:44 -0700266 memcpy(&CURRENT_MESSAGE, MESSAGE_BUFFER.buffer, MESSAGE_SIZE);
Dan Morrill8f1028f2010-09-12 12:02:59 -0700267 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
268 switch (CURRENT_MESSAGE.opcode) {
269 case OPCODE_RESET:
270 reset();
271 return;
272
273 case OPCODE_INIT_TIME:
274 // cast CURRENT_MESSAGE to our time struct to conveniently fetch
275 // out the current time
276 WALL_TIME.raw = ((struct init_time*)(&CURRENT_MESSAGE))->cur_raw_time;
277 WALL_TIME.initialized = 1;
Dan Morrill7b123992010-10-20 12:22:44 -0700278
279 Serial.println("message_type=init_time_ack");
280 Serial.print("id=");
281 Serial.println(CURRENT_MESSAGE.id);
282 print_current_time();
283 Serial.println("");
284
Dan Morrill8f1028f2010-09-12 12:02:59 -0700285 CURRENT_MESSAGE.id = 0;
286 break;
287
Dan Morrill7b123992010-10-20 12:22:44 -0700288 case OPCODE_CURRENT_TIME:
289 Serial.println("message_type=current_time_ack");
290 Serial.print("id=");
291 Serial.println(CURRENT_MESSAGE.id);
292 print_current_time();
293 Serial.println("");
294
295 CURRENT_MESSAGE.id = 0;
296
Dan Morrill8f1028f2010-09-12 12:02:59 -0700297 default:
298 // no-op -- actually means main loop will handle it
299 break;
300 }
301 }
302 }
303 }
304}
305
Dan Morrill8f1028f2010-09-12 12:02:59 -0700306/*
307 * Pumps the system wall clock. This checks the device's monotonic clock to
308 * determine elapsed time since last invocation, and updates wall clock time
309 * by dead reckoning. Since the device has no battery backup, a power-off will
310 * lose the current time, so timekeeping cannot begin until an INIT_TIME
311 * message is received. (The pump_message_processor() function handles that.)
312 *
313 * Once timekeeping is underway, current time is exposed to user/app code via
314 * the WALL_TIME object, which has 24-hour HH/MM/SS fields.
315 */
316void pump_clock() {
317 static uint32_t prev_millis = 0;
Dan Morrill7b123992010-10-20 12:22:44 -0700318 static uint32_t tmp_prev_millis = 0;
Dan Morrill8f1028f2010-09-12 12:02:59 -0700319 uint32_t tmp = 0;
320
Dan Morrill7b123992010-10-20 12:22:44 -0700321 if (millis() / 1000 != tmp_prev_millis) {
322 tmp_prev_millis = millis() / 1000;
323 print_current_time();
324 }
325
Dan Morrill8f1028f2010-09-12 12:02:59 -0700326 if (WALL_TIME.initialized) {
327 tmp = millis() / 1000;
328 if (tmp != prev_millis) {
329 prev_millis = tmp;
330 WALL_TIME.raw++;
331 }
332 WALL_TIME.seconds = WALL_TIME.raw % 60;
333 WALL_TIME.minutes = (WALL_TIME.raw / 60) % 60;
334 WALL_TIME.hours = (WALL_TIME.raw / (60 * 60)) % 24;
335 }
336}
337
338
339/*
340 * Standard Arduino setup hook.
341 */
342void setup() {
343 Serial.begin(115200);
344}
345
346
347/*
348 * Standard Arduino loop-pump hook.
349 */
350void loop() {
351 static uint16_t last_id = 0;
352
353 // pump the clock and message processor
354 pump_clock();
355 pump_message_processor();
356
357 // ignore any "system" messages (those with ID == 0) but dispatch app messages
358 if ((last_id != CURRENT_MESSAGE.id) && (CURRENT_MESSAGE.id != 0)) {
359 handle_current_message();
360 }
361 last_id = CURRENT_MESSAGE.id;
362}