blob: 5649972d33fb710cff390c2eae9ef73f9d650e04 [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.
19 * Messages are 512 bytes, with a 2-byte opcode, a 2-byte
20 * unique ID defined by the sender, and 506 bytes of payload.
21 * 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 */
26#define MESSAGE_DELIMITER 0xFEEDFACE // required to be
27#define MESSAGE_ESCAPE 0x2a
28struct message {
29 uint16_t opcode;
30 uint16_t id;
31 uint8_t data[506];
32 uint16_t tail;
33};
34struct message CURRENT_MESSAGE;
35
36#define OPCODE_RESET 0x00
37struct reset {
38 uint16_t opcode;
39 uint16_t id;
40 uint8_t unused[506];
41 uint16_t tail;
42};
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;
49 uint8_t unused[502];
50 uint16_t tail;
51};
52
53struct wall_time_struct {
54 uint32_t raw; // long == 4-byte on AVR
55 uint8_t initialized;
56 uint8_t hours;
57 uint8_t minutes;
58 uint8_t seconds;
59};
60struct wall_time_struct WALL_TIME;
61
62
63/*
64 * An object used to store app-specific state data.
65 */
66struct struct_state {
67};
68struct struct_state STATE;
69
70void handle_current_message() {
71}
72
73/* This is a temporary buffer used by the message handler */
74struct message_buffer {
75 uint8_t count; // number of bytes read into the buffer
76 uint8_t buffer[512]; // contents of a 'struct message'
77};
78struct message_buffer MESSAGE_BUFFER;
79
80/*
81 * Clears all stateful values, including the wall clock time, current message
82 * data, and user/app state. Also clears the message handler's buffer. By
83 * "clear" we mean "memset to 0".
84 */
85void reset() {
86 memset(&WALL_TIME, 0, sizeof(WALL_TIME));
87 memset(&CURRENT_MESSAGE, 0, sizeof(CURRENT_MESSAGE));
88 memset(&STATE, 0, sizeof(STATE));
89 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
90}
91
92
93/*
94 * Pumps the message processor. That is, this function is intended to be
95 * called once per loop to read all pending Serial/TTL data, and decode a
96 * message from the peer if one is complete. In cases where data is corrupted
97 * (such as by dropping bytes), this function also attempts to re-sync with
98 * the host by discarding messages until it finds a MESSAGE_DELIMITER, after
99 * which is resyncs its buffer on the first subsequent byte.
100 *
101 * This functional also handles two low-level 'system' messages: a reset
102 * instruction which invokes reset(), and an init_time instruction which
103 * provides the soft clock with the current time so that it can start keeping
104 * time.
105 */
106void pump_message_processor() {
107 static uint16_t cur_byte;
108 static uint16_t* cur_word;
109 static int8_t delimiter_index;
110 while (Serial.available() > 0) { // keep going as long as it we might have messages
111 cur_byte = ((uint16_t)Serial.read()) & 0x00ff;
112 MESSAGE_BUFFER.buffer[(MESSAGE_BUFFER.count)++] = cur_byte;
113 if (MESSAGE_BUFFER.count >= 512) {
114 if ((uint16_t)(*(MESSAGE_BUFFER.buffer + 510)) != MESSAGE_DELIMITER) {
115 // whoops, we got out of sync with the transmitter. Scan current
116 // buffer for the delimiter, discard previous message, and shift
117 // partial next message to front of buffer. This loses a message but
118 // gets us back in sync
119 delimiter_index = -2;
120 for (int i = 510; i >= 0; --i) {
121 if (*((uint16_t*)(MESSAGE_BUFFER.buffer + i)) == MESSAGE_DELIMITER) {
122 if (((i - 1) < 0) || (MESSAGE_BUFFER.buffer[i - 1] != MESSAGE_ESCAPE)) {
123 delimiter_index = i;
124 break;
125 }
126 }
127 }
128 MESSAGE_BUFFER.count = 0;
129 for (int i = delimiter_index + 2; i < 512; ++i, ++(MESSAGE_BUFFER.count)) {
130 MESSAGE_BUFFER.buffer[MESSAGE_BUFFER.count] = MESSAGE_BUFFER.buffer[i];
131 }
132 memset(MESSAGE_BUFFER.buffer + MESSAGE_BUFFER.count, 0, 512 - MESSAGE_BUFFER.count);
133 } else {
134 memcpy(&CURRENT_MESSAGE, MESSAGE_BUFFER.buffer, 512);
135 memset(&MESSAGE_BUFFER, 0, sizeof(MESSAGE_BUFFER));
136 switch (CURRENT_MESSAGE.opcode) {
137 case OPCODE_RESET:
138 reset();
139 return;
140
141 case OPCODE_INIT_TIME:
142 // cast CURRENT_MESSAGE to our time struct to conveniently fetch
143 // out the current time
144 WALL_TIME.raw = ((struct init_time*)(&CURRENT_MESSAGE))->cur_raw_time;
145 WALL_TIME.initialized = 1;
146 CURRENT_MESSAGE.id = 0;
147 break;
148
149 default:
150 // no-op -- actually means main loop will handle it
151 break;
152 }
153 }
154 }
155 }
156}
157
158
159/* Dumps the full state of the system for the other side to peruse. Because we dump our state
160 * periodically, we don't need to worry about responding to commands -- the other side can
161 * just monitor for changes in state.
162 */
163void dump_state() {
164 Serial.print("current_time=");
165 Serial.print(WALL_TIME.hours, DEC);
166 Serial.print(":");
167 if (WALL_TIME.minutes < 10)
168 Serial.print("0");
169 Serial.print(WALL_TIME.minutes, DEC);
170 Serial.print(":");
171 if (WALL_TIME.seconds < 10)
172 Serial.print("0");
173 Serial.println(WALL_TIME.seconds, DEC);
174
175 // TODO
176
177 Serial.println("");
178}
179
180
181/*
182 * Pumps the system wall clock. This checks the device's monotonic clock to
183 * determine elapsed time since last invocation, and updates wall clock time
184 * by dead reckoning. Since the device has no battery backup, a power-off will
185 * lose the current time, so timekeeping cannot begin until an INIT_TIME
186 * message is received. (The pump_message_processor() function handles that.)
187 *
188 * Once timekeeping is underway, current time is exposed to user/app code via
189 * the WALL_TIME object, which has 24-hour HH/MM/SS fields.
190 */
191void pump_clock() {
192 static uint32_t prev_millis = 0;
193 uint32_t tmp = 0;
194
195 if (WALL_TIME.initialized) {
196 tmp = millis() / 1000;
197 if (tmp != prev_millis) {
198 prev_millis = tmp;
199 WALL_TIME.raw++;
200 }
201 WALL_TIME.seconds = WALL_TIME.raw % 60;
202 WALL_TIME.minutes = (WALL_TIME.raw / 60) % 60;
203 WALL_TIME.hours = (WALL_TIME.raw / (60 * 60)) % 24;
204 }
205}
206
207
208/*
209 * Standard Arduino setup hook.
210 */
211void setup() {
212 Serial.begin(115200);
213}
214
215
216/*
217 * Standard Arduino loop-pump hook.
218 */
219void loop() {
220 static uint16_t last_id = 0;
221
222 // pump the clock and message processor
223 pump_clock();
224 pump_message_processor();
225
226 // ignore any "system" messages (those with ID == 0) but dispatch app messages
227 if ((last_id != CURRENT_MESSAGE.id) && (CURRENT_MESSAGE.id != 0)) {
228 handle_current_message();
229 }
230 last_id = CURRENT_MESSAGE.id;
231}