4ed111099c65e74bd9452a7919e83c1bfecadaf4
[uccvend-snackrom.git] / ROM2 / main_basic.c
1 /*
2  * main_basic.c - a simplified main procedure that relies upon a ersver to do
3  * anything smart. Just be a dumb interface to a display, keypad, coin mech
4  * and snacks.
5  */
6
7 #define VERSION_STRING "R 20040622"
8
9 #include "display_basic.h"
10 #include "keypad.h"
11 #include "chime.h"
12 #include "coinmech.h"
13 #include "motors.h"
14 #include "sci.h"
15 #include "vend.h"
16 #include "xmodem.h"
17 //#include "sha1.h"
18
19 u8 last_standalone;
20 u8 last_switch_input;
21 u8 last_misc_input;
22 u16 last_coin_value;
23 bool last_door_open;
24 char display_buf[11];
25 /* cur_motor[0] is 0 for nothing, or 1..10, or 0xff to say redraw.
26  * cur_motor[1] is 0..9 and only meaningful is cur_motor[0] != 0. */
27 u8 cur_motor[2];
28
29 bool check_standalone() {
30         if (is_standalone()) {
31                 send_string("011 In standalone mode. Function disabled." CRLF);
32                 return 1;
33         }
34         return 0;
35 }
36
37 void unknown_command() {
38         send_string("012 Unknown command. Type HELP for help." CRLF);
39 }
40
41 void motor_reply(u8 code) {
42         /* returns a message of the form MXYY - X is return code, YY is motor */
43         switch (code) {
44                 case MOTOR_SUCCESS:
45                         send_string("100 Vend successful." CRLF);
46                         break;
47                 case MOTOR_NOSLOT:
48                         send_string("151 No motor there." CRLF);
49                         break;
50                 case MOTOR_CURRENT_FAIL:
51                         send_string("152 Over current." CRLF);
52                         break;
53                 case MOTOR_HOME_FAIL:
54                         send_string("153 Home sensors failing." CRLF);
55                         break;
56                 default:
57                         send_string("159 Unknown motor error." CRLF);
58         }
59 }
60
61 void dispense_something() {
62         /* process a message VXX in sci_rx_buf where XX is motor number */
63         u8 slot;
64
65         if (check_standalone()) return;
66
67         if (my_strncmp("ALL", (char*)sci_rx_buf+1, 3)) {
68                 char motor[3];
69                 motor[2] = '\0';
70                 send_string("102 Vend all motors starting." CRLF);
71                 for (motor[0] = '0'; motor[0] <= '9'; motor[0]++) {
72                         for (motor[1] = '0'; motor[1] <= '9'; motor[1]++) {
73                                 send_string("101 Vending ");
74                                 send_string(motor);
75                                 send_string(CRLF);
76                                 motor_reply(dispense_motor(motor[0]*10+motor[1]));
77                         }
78                 }
79                 send_string("102 Vend all motors complete." CRLF);
80                 return;
81         }
82
83         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
84                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9')) {
85                 sci_rx_buf[1] = sci_rx_buf[2] = '0';
86                 motor_reply(MOTOR_NOSLOT);
87                 return;
88         }
89
90         slot = (sci_rx_buf[1] - '0') * 10;
91         slot += sci_rx_buf[2] - '0';
92
93         motor_reply(dispense_motor(slot));
94 }
95
96 void write_to_display() {
97         /* process a message in the form DXXXXXXXXXX to send to display */
98         u8 i;
99         char buf[11];
100
101         if (check_standalone()) return;
102
103         for (i = 0; i < 10; i++)
104                 if (sci_rx_buf[i+1])
105                         buf[i] = sci_rx_buf[i+1];
106                 else
107                         break;
108
109         for (; i < 10; i++) /* pad the rest out with spaces */
110                 buf[i] = ' ';
111         buf[i] = '\0';
112
113         set_msg(buf);
114         send_string("300 Written." CRLF);
115 }
116
117 void send_balance() {
118         sci_tx_buf[0] = 'C';
119         sci_tx_buf[1] = have_change?'0':'1';
120         sci_tx_buf[2] = (coin_value/10000)%10;
121         sci_tx_buf[3] = (coin_value/1000)%10;
122         sci_tx_buf[4] = (coin_value/100)%10;
123         sci_tx_buf[5] = (coin_value/10)%10;
124         sci_tx_buf[6] = coin_value%10;
125         sci_tx_buf[8] = 0;
126         send_buffer(1);
127 }
128
129 void give_change() {
130         u16 cost;
131
132         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
133                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9') ||
134                 (sci_rx_buf[3] < '0') || (sci_rx_buf[3] > '9') ||
135                 (sci_rx_buf[4] < '0') || (sci_rx_buf[4] > '9') ||
136                 (sci_rx_buf[5] < '0') || (sci_rx_buf[5] > '9')) {
137                 send_nack();
138         }
139         cost = sci_rx_buf[1] - '0';
140         cost *= 10; cost = sci_rx_buf[2] - '0';
141         cost *= 10; cost = sci_rx_buf[3] - '0';
142         cost *= 10; cost = sci_rx_buf[4] - '0';
143         cost *= 10; cost = sci_rx_buf[5] - '0';
144
145         coin_cost(cost);
146         send_ack();
147
148
149 void send_keypress(u8 key) {
150         /* send a packet of the form KX with X being the key, or R for reset */
151         if (is_standalone()) return;
152
153         sci_tx_buf[0] = '2';
154         if (key == KEY_RESET) {
155                 sci_tx_buf[1] = '1';
156                 sci_tx_buf[2] = '1';
157         } else {
158                 sci_tx_buf[1] = '0';
159                 sci_tx_buf[2] = (key%10)+'0';
160         }
161         sci_tx_buf[3] = '\0';
162         send_buffer(0);
163         send_string(" key." CRLF);
164 }
165
166 void send_door_msg(bool open) {
167         if (is_standalone()) return;
168         sci_tx_buf[0] = '4';
169         sci_tx_buf[1] = '0';
170         sci_tx_buf[2] = open?'1':'0';
171         send_buffer(0);
172         if (open)
173                 send_string(" door open." CRLF);
174         else
175                 send_string(" door closed." CRLF);
176 }
177
178 u8 hexchar2u8(char b) {
179         if (b >= '0' && b <= '9') return b-'0';
180         if (b >= 'a' && b <= 'f') return b-'a'+0x0a;
181         if (b >= 'A' && b <= 'F') return b-'A'+0x0a;
182         return 0;
183 }
184
185 char nibble2hexchar(u8 b) {
186         if (b <= 9) return b+'0';
187         if (b >= 10 && b <= 15) return b+'A'-10;
188         return 'X';
189 }
190
191 u8 hex2u8(char msb, char lsb) {
192         return (hexchar2u8(msb) << 4) + hexchar2u8(lsb);
193 }
194
195 static char hexconv_buf[3];
196 char* u82hex(u8 a) {
197         hexconv_buf[0] = nibble2hexchar((a&0xf0) >> 4);
198         hexconv_buf[1] = nibble2hexchar(a&0x0f);
199         hexconv_buf[2] = '\0';
200         return hexconv_buf;
201 }
202
203 void do_chime() {
204         if (check_standalone()) return;
205         if (sci_rx_buf[1] == '\0')
206                 chime_start();
207         else if (sci_rx_buf[2] != '\0' && sci_rx_buf[3] == '\0')
208                 chime_for(hex2u8(sci_rx_buf[1], sci_rx_buf[2]));
209         else {
210                 send_string("510 Unknown chime duration." CRLF);
211                 return;
212         }
213         send_string("500 Chimed." CRLF);
214 }
215
216 void print_switches(u8 prompted) {
217         if (prompted)
218                 send_string("600 ");
219         else
220                 send_string("610 ");
221         send_string(u82hex(misc_input));
222         send_string(" ");
223         send_string(u82hex(switch_input));
224         send_string(CRLF);
225 }
226
227 void ping_pong() {
228         /* make sure it's really a ping */
229         if (!my_strncmp("ING", (char*)sci_rx_buf+1, 3)) {
230                 unknown_command();
231                 return;
232         }
233         /* respond with ack & pong */
234         send_string("000 PONG!" CRLF);
235 }
236
237 void send_prompt() {
238         send_string(is_standalone()?"$ ":"# ");
239 }
240
241 void about() {
242         if (!my_strncmp("BOUT", (char*)sci_rx_buf+1, 4)) {
243                 unknown_command();
244                 return;
245         }
246         send_string(
247                 "-----------------------------------------------------------------" CRLF
248                 "    ROM2 (C) 2004 Bernard Blackham <[email protected]>" CRLF
249                 "-----------------------------------------------------------------" CRLF
250                 "                                        Revision " VERSION_STRING CRLF
251                 "" CRLF
252                 "   This snack machine was brought to you by " CRLF
253                 "    Bernard Blackham" CRLF
254                 "    Mark Tearle" CRLF
255                 "    Harry McNally" CRLF
256                 "    Michal Gornisiewicz" CRLF
257                 "    and others." CRLF
258                 "" CRLF
259                 " Another UCC project in action.         http://www.ucc.asn.au/" CRLF
260         );
261 }
262
263 void set_echo() {
264         if (my_strncmp("CHO ON", (char*)sci_rx_buf+1, 6))
265                 sci_echo = 1;
266         else if (my_strncmp("CHO OFF", (char*)sci_rx_buf+1, 7))
267                 sci_echo = 0;
268         else
269                 unknown_command();
270 }
271
272 void moo() {
273         if (!my_strncmp("OO", (char*)sci_rx_buf+1, 2)) {
274                 unknown_command();
275                 return;
276         }
277         send_string(
278 "       ____________" CRLF
279 "       |__________|" CRLF
280 "      /           /\\" CRLF
281 "     /  U C C    /  \\" CRLF
282 "    /___________/___/|" CRLF
283 "    |          |     |" CRLF
284 "    |  ==\\ /== |     |" CRLF
285 "    |   O   O  | \\ \\ |" CRLF
286 "    |     <    |  \\ \\|" CRLF
287 "   /|          |   \\ \\" CRLF
288 "  / |  \\_____/ |   / /" CRLF
289 " / /|          |  / /|" CRLF
290 "/||\\|          | /||\\/" CRLF
291 "    -------------|   " CRLF
292 "        | |    | | " CRLF
293 "       <__/    \\__>" CRLF
294 "" CRLF
295 "  ... Where's the cheese?" CRLF
296         );
297 }
298
299 void help() {
300         send_string(
301                 "Valid commands are:" CRLF
302                 " ABOUT         ROM information" CRLF
303                 " PING          pongs" CRLF
304                 " ECHO {ON|OFF} turn echo on or off" CRLF
305                 " Vnn           vend an item" CRLF
306                 " VALL          vend all items" CRLF
307                 " DXXXXXXXXXX   show a message on the display" CRLF
308                 " B[nn]         beep for a duration nn (optional)" CRLF
309                 " S[...]        query all internal switch states" CRLF
310                 " H[...]        this help screen" CRLF
311                 " GETROM        download the ROM source code using xmodem" CRLF
312                 "Comments start with a #" CRLF
313         );
314 }
315
316 extern const char _rom_src_data[];
317 extern const u16 _rom_src_len;
318 void getrom() {
319         if (!my_strncmp("ETROM", (char*)sci_rx_buf+1, 5)) {
320                 unknown_command();
321                 return;
322         }
323         char s[4];
324         send_string("Writing to serial port (maybe). Size is 0x");
325         send_string(u82hex(_rom_src_len >> 8));
326         send_string(u82hex(_rom_src_len & 0xff));
327         send_string("@0x");
328         send_string(u82hex((u16)(&_rom_src_data) >> 8));
329         send_string(u82hex((u16)(&_rom_src_data) & 0xff));
330         send_string(" with signature ");
331         s[0] = _rom_src_data[0];
332         s[1] = _rom_src_data[1];
333         s[2] = _rom_src_data[2];
334         s[3] = '\0';
335         send_string(s);
336         send_string(CRLF " Type YES to download rom.tar.bz2 via XMODEM: ");
337         msg_clr();
338         while (!sci_have_packet); /* spin */
339         if (!my_strncmp("YES", (char*)sci_rx_buf, 3)) {
340                 send_string(CRLF "Transfer cancelled." CRLF);
341                 return;
342         }
343
344         sci_init();
345         sci_doing_xmodem = 1;
346         if (!xmodem_init_xfer()) {
347                 sci_doing_xmodem = 0;
348                 send_string("XMODEM init failed. Nobody's listening :(" CRLF);
349                 return;
350         }
351         char *p = (char*)_rom_src_data;
352         char *end = (char*)_rom_src_data+_rom_src_len;
353         bool aborted = 0;
354         while (1) {
355                 if (p + 128 > end) {
356                         /* send partial packet */
357                         if (!xmodem_send_packet((char*)p, end-p)) aborted = 1;
358                         break;
359                 } if ((u16)p == 0xb600) {
360                         /* we have an eeprom here. skip it. */
361                         p += 0x0200;
362                         continue;
363                 } else if (!xmodem_send_packet((char*)p, 128)) {
364                         aborted = 1;
365                         break;
366                 }
367                 p += 128;
368         }
369
370         xmodem_finish_xfer();
371         sci_doing_xmodem = 0;
372         if (aborted)
373                 send_string(CRLF "Transfer aborted." CRLF);
374         else
375                 send_string(CRLF "Transfer complete." CRLF);
376 }
377
378 void quit() {
379         if (my_strncmp("UIT", (char*)sci_rx_buf+1, 3))
380                 send_string("013 You can't quit you doofus." CRLF);
381         else
382                 unknown_command();
383 }
384
385 //SHA1_CTX ctx;
386 //u8 sha1_digest[SHA1_SIGNATURE_SIZE];
387
388 int main() {
389         u8 i;
390         for (i = 0; i < 11; i++)
391                 display_buf[i] = ' ';
392         display_buf[10] = '\0';
393
394         changer_output = 0x7f;
395         _io_ports[M6811_PORTA] = 0xc0; /* display on. talking to serial port */
396         _io_ports[M6811_DDRA] = 0xfc;
397         _io_ports[M6811_DDRD] = 0x3e;
398         _io_ports[M6811_SPCR] = M6811_MSTR | M6811_SPR1;
399         set_misc_output(0x00);
400
401         display_init();
402         set_msg(" HELLO    ");
403         delay(1000);
404
405         unlock(); /* enable interrupts */
406
407         set_msg("  CRUEL   ");
408
409         //coinmech_init();
410         sci_init();
411         keypad_init();
412         last_coin_value = 0;
413         last_door_open = 0;
414
415         delay(1000);
416
417         set_msg("   WORLD  ");
418         delay(1000);
419
420         chime_start();
421
422         send_string("5N4X0RZ R US" CRLF);
423         
424         last_standalone = is_standalone();
425         if (last_standalone)
426                 cur_motor[0] = 0xff;
427         else
428                 cur_motor[0] = 0;
429         send_prompt();
430
431         last_switch_input = switch_input;
432         last_misc_input = misc_input;
433
434         while(1) {
435                 if (cur_motor[0] == 0xff) { /* signal to say redraw screen */
436                         set_msg("*5N4X0RZ* ");
437                         cur_motor[0] = 0;
438                 }
439
440                 if (door_open() != last_door_open) {
441                         last_door_open = door_open();
442                         send_door_msg(last_door_open);
443                         chime_start();
444                         if (is_standalone())
445                                 set_msg(last_door_open?"DOOR OPEN ":"DOOR CLOSE");
446                 }
447
448                 if (last_standalone != is_standalone()) {
449                         /* somebody flicked the standalone switch */
450                         msg_clr();
451                         send_string(CRLF);
452                         send_prompt();
453                         last_standalone = is_standalone();
454                 }
455
456                 if (last_misc_input != misc_input) {
457                         print_switches(0);
458                         last_misc_input = misc_input;
459                 }
460
461                 if (last_switch_input != switch_input) {
462                         print_switches(0);
463                         last_switch_input = switch_input;
464                 }
465
466                 if (sci_have_packet) {
467                         if (must_verify()) {
468                                 //SHA1_Init(&ctx);
469                                 //SHA1_Update(&ctx, sci_rx_buf, my_strlen(sci_rx_buf));
470                                 //SHA1_Final(sha1_digest, &ctx);
471                         }
472                         switch (sci_rx_buf[0]) {
473                                 case '\0':
474                                 case '#':
475                                         send_string(CRLF);
476                                         break;
477                                 case 'E':
478                                         set_echo();
479                                         break;
480                                 case 'H':
481                                         help();
482                                         break;
483                                 case 'V':
484                                         dispense_something();
485                                         break;
486                                 case 'D':
487                                         write_to_display();
488                                         break;
489                                 case 'B': 
490                                         do_chime();
491                                         break;
492                                 case 'P':
493                                         ping_pong();
494                                         break;
495                                 case 'A':
496                                         about();
497                                         break;
498                                 case 'S':
499                                         print_switches(1);
500                                         break;
501                                 case 'M':
502                                         moo();
503                                         break;
504                                 case 'Q':
505                                         quit();
506                                         break;
507                                 case 'G':
508                                         getrom();
509                                         break;
510                                 default:
511                                         // shurg
512                                         unknown_command();
513                                         break;
514                         }
515                         msg_clr();
516                         send_prompt();
517                 }
518
519                 keypad_read();
520                 if (keypad_pressed()) {
521                         if (is_standalone()) {
522                                 if (last_key == KEY_RESET) {
523                                         cur_motor[0] = 0xff;
524                                 } else {
525                                         if (cur_motor[0]) {
526                                                 u8 motor_num;
527                                                 cur_motor[1] = last_key%10;
528                                                 display_buf[1] = cur_motor[1]+'0';
529                                                 set_msg(display_buf);
530
531                                                 motor_num = cur_motor[0]%10;
532                                                 motor_num *= 10;
533                                                 motor_num += cur_motor[1];
534                                                 switch (dispense_motor(motor_num)) {
535                                                         case MOTOR_HOME_FAIL:
536                                                                 set_msg(" HOME FAIL ");
537                                                                 break;
538                                                         case MOTOR_CURRENT_FAIL:
539                                                                 set_msg(" OVER CRNT ");
540                                                                 break;
541                                                         case MOTOR_SUCCESS:
542                                                                 set_msg("THANK  YOU");
543                                                                 break;
544                                                         case MOTOR_NOSLOT:
545                                                                 set_msg(" NO MOTOR ");
546                                                                 break;
547                                                         default:
548                                                                 set_msg("ERRRRRRRR?");
549                                                                 break;
550                                                 }
551
552                                                 display_buf[0] = ' ';
553                                                 display_buf[1] = ' ';
554                                                 cur_motor[0] = 0xff;
555                                                 delay(500);
556                                         } else {
557                                                 cur_motor[0] = last_key;
558                                                 display_buf[0] = (last_key%10)+'0';
559                                                 set_msg(display_buf);
560                                         }
561                                 }
562                         } else
563                                 send_keypress(last_key);
564                 }
565
566                 /*
567                 if (coin_value != last_coin_value) {
568                         send_balance();
569                         last_coin_value = coin_value;
570                 }
571                 */
572         }
573 }

UCC git Repository :: git.ucc.asn.au