altos: Remove old AO_SEND_ALL_BARO bits
[fw/altos] / src / kernel / ao_config.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include "ao_log.h"
20 #include <ao_config.h>
21 #if HAS_FLIGHT
22 #include <ao_sample.h>
23 #include <ao_data.h>
24 #endif
25 #if HAS_BEEP
26 #include <ao_beep.h>
27 #endif
28 #if HAS_TRACKER
29 #include <ao_tracker.h>
30 #endif
31
32 __xdata struct ao_config ao_config;
33 __pdata uint8_t ao_config_loaded;
34 __pdata uint8_t ao_config_dirty;
35 __xdata uint8_t ao_config_mutex;
36
37 #ifndef AO_CONFIG_DEFAULT_APRS_INTERVAL
38 #define AO_CONFIG_DEFAULT_APRS_INTERVAL 0
39 #endif
40 #define AO_CONFIG_DEFAULT_MAIN_DEPLOY   250
41 #define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
42 #define AO_CONFIG_DEFAULT_CALLSIGN      "N0CALL"
43 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G  16000
44 #define AO_CONFIG_DEFAULT_APOGEE_DELAY  0
45 #define AO_CONFIG_DEFAULT_IGNITE_MODE   AO_IGNITE_MODE_DUAL
46 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION       AO_PAD_ORIENTATION_ANTENNA_UP
47 #define AO_CONFIG_DEFAULT_PYRO_TIME     AO_MS_TO_TICKS(50)
48 #if HAS_EEPROM
49 #ifndef USE_INTERNAL_FLASH
50 #error Please define USE_INTERNAL_FLASH
51 #endif
52 #endif
53
54 #ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
55 # if FLIGHT_LOG_APPEND
56 #  define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX      ao_storage_log_max
57 # else
58 #  if USE_INTERNAL_FLASH
59 #   define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX     ao_storage_config
60 #  else
61 #   define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX     ((uint32_t) 192 * (uint32_t) 1024)
62 #  endif
63 # endif
64 #endif
65
66 #ifndef AO_CONFIG_DEFAULT_RADIO_POWER
67 #define AO_CONFIG_DEFAULT_RADIO_POWER           0x60
68 #endif
69 #define AO_CONFIG_DEFAULT_RADIO_AMP             0
70 #define AO_CONFIG_DEFAULT_APRS_SSID             (ao_serial_number % 10)
71 #define AO_CONFIG_DEFAULT_RADIO_RATE            AO_RADIO_RATE_38400
72
73 #if HAS_EEPROM
74 static void
75 _ao_config_put(void)
76 {
77         ao_config_setup();
78         ao_config_erase();
79         ao_config_write(0, &ao_config, sizeof (ao_config));
80 #if HAS_FLIGHT
81         ao_log_write_erase(0);
82 #endif
83         ao_config_flush();
84 }
85
86 void
87 ao_config_put(void)
88 {
89         ao_mutex_get(&ao_config_mutex);
90         _ao_config_put();
91         ao_mutex_put(&ao_config_mutex);
92 }
93 #endif
94
95 #if HAS_RADIO
96
97 #if HAS_RADIO_FORWARD
98 __xdata uint32_t        ao_send_radio_setting;
99 #endif
100
101 void
102 ao_config_set_radio(void)
103 {
104         ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
105 #if HAS_RADIO_FORWARD
106         ao_send_radio_setting = ao_freq_to_set(ao_config.send_frequency, ao_config.radio_cal);
107 #endif
108 }
109 #endif /* HAS_RADIO */
110
111 static void
112 _ao_config_get(void)
113 {
114         uint8_t minor;
115
116         if (ao_config_loaded)
117                 return;
118 #if HAS_EEPROM
119         /* Yes, I know ao_storage_read calls ao_storage_setup,
120          * but ao_storage_setup *also* sets ao_storage_config, which we
121          * need before calling ao_storage_read here
122          */
123         ao_config_setup();
124         ao_config_read(0, &ao_config, sizeof (ao_config));
125 #endif
126         if (ao_config.major != AO_CONFIG_MAJOR) {
127                 ao_config.major = AO_CONFIG_MAJOR;
128                 ao_config.minor = 0;
129
130                 /* Version 0 stuff */
131                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
132                 ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
133                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
134                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
135                 ao_config._legacy_radio_channel = 0;
136         }
137         minor = ao_config.minor;
138         if (minor != AO_CONFIG_MINOR) {
139                 /* Fixups for minor version 1 */
140                 if (minor < 1)
141                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
142                 /* Fixups for minor version 2 */
143                 if (minor < 2) {
144                         ao_config.accel_plus_g = 0;
145                         ao_config.accel_minus_g = 0;
146                 }
147                 /* Fixups for minor version 3 */
148 #if HAS_RADIO
149                 if (minor < 3)
150                         ao_config.radio_cal = ao_radio_cal;
151 #endif
152                 /* Fixups for minor version 4 */
153 #if HAS_LOG
154                 if (minor < 4)
155                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
156 #endif
157                 /* Fixupes for minor version 5 */
158                 if (minor < 5)
159                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
160                 if (minor < 6)
161                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
162                 if (minor < 8)
163                         ao_config.radio_enable = AO_RADIO_ENABLE_CORE;
164                 if (minor < 9)
165                         ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
166                 if (minor < 10)
167                         ao_config.frequency = 434550 + ao_config._legacy_radio_channel * 100;
168                 if (minor < 11)
169                         ao_config.apogee_lockout = 0;
170 #if AO_PYRO_NUM
171                 if (minor < 12)
172                         memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro));
173 #endif
174                 if (minor < 13)
175                         ao_config.aprs_interval = AO_CONFIG_DEFAULT_APRS_INTERVAL;
176 #if HAS_RADIO_POWER
177                 if (minor < 14)
178                         ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
179                 #endif
180 #if HAS_RADIO_AMP
181                 if (minor  < 14)
182                         ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
183 #endif
184 #if HAS_GYRO
185                 if (minor < 15) {
186                         ao_config.accel_zero_along = 0;
187                         ao_config.accel_zero_across = 0;
188                         ao_config.accel_zero_through = 0;
189
190                         /* Reset the main accel offsets to force
191                          * re-calibration
192                          */
193                         ao_config.accel_plus_g = 0;
194                         ao_config.accel_minus_g = 0;
195                 }
196 #endif
197 #if HAS_BEEP_CONFIG
198                 if (minor < 16)
199                         ao_config.mid_beep = AO_BEEP_MID_DEFAULT;
200 #endif
201 #if HAS_TRACKER
202                 if (minor < 17) {
203                         ao_config.tracker_motion = AO_TRACKER_MOTION_DEFAULT;
204                         ao_config.tracker_interval = AO_TRACKER_INTERVAL_DEFAULT;
205                 }
206 #endif
207 #if AO_PYRO_NUM
208                 if (minor < 18)
209                         ao_config.pyro_time = AO_CONFIG_DEFAULT_PYRO_TIME;
210 #endif
211 #if HAS_APRS
212                 if (minor < 19)
213                         ao_config.aprs_ssid = AO_CONFIG_DEFAULT_APRS_SSID;
214 #endif
215 #if HAS_RADIO_RATE
216                 if (minor < 20)
217                         ao_config.radio_rate = AO_CONFIG_DEFAULT_RADIO_RATE;
218 #endif
219 #if HAS_RADIO_FORWARD
220                 if (minor < 21)
221                         ao_config.send_frequency = 434550;
222 #endif
223                 ao_config.minor = AO_CONFIG_MINOR;
224                 ao_config_dirty = 1;
225         }
226 #if HAS_RADIO
227 #if HAS_FORCE_FREQ
228         if (ao_force_freq) {
229                 ao_config.frequency = 434550;
230                 ao_config.radio_cal = ao_radio_cal;
231                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
232                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
233         }
234 #endif
235         ao_config_set_radio();
236 #endif
237         ao_config_loaded = 1;
238 }
239
240 void
241 _ao_config_edit_start(void)
242 {
243         ao_mutex_get(&ao_config_mutex);
244         _ao_config_get();
245 }
246
247 void
248 _ao_config_edit_finish(void)
249 {
250         ao_config_dirty = 1;
251         ao_mutex_put(&ao_config_mutex);
252 }
253
254 void
255 ao_config_get(void)
256 {
257         _ao_config_edit_start();
258         ao_mutex_put(&ao_config_mutex);
259 }
260
261 void
262 ao_config_callsign_show(void)
263 {
264         printf ("Callsign: \"%s\"\n", ao_config.callsign);
265 }
266
267 void
268 ao_config_callsign_set(void) __reentrant
269 {
270         uint8_t c;
271         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
272
273         ao_xmemset(callsign, '\0', sizeof callsign);
274         ao_cmd_white();
275         c = 0;
276         while (ao_cmd_lex_c != '\n') {
277                 if (c < AO_MAX_CALLSIGN)
278                         callsign[c++] = ao_cmd_lex_c;
279                 else
280                         ao_cmd_status = ao_cmd_lex_error;
281                 ao_cmd_lex();
282         }
283         if (ao_cmd_status != ao_cmd_success)
284                 return;
285         _ao_config_edit_start();
286         ao_xmemcpy(&ao_config.callsign, &callsign,
287                AO_MAX_CALLSIGN + 1);
288         _ao_config_edit_finish();
289 }
290
291 #if HAS_RADIO
292
293 void
294 ao_config_frequency_show(void) __reentrant
295 {
296         printf("Frequency: %ld\n",
297                ao_config.frequency);
298 }
299
300 void
301 ao_config_frequency_set(void) __reentrant
302 {
303         ao_cmd_decimal();
304         if (ao_cmd_status != ao_cmd_success)
305                 return;
306         _ao_config_edit_start();
307         ao_config.frequency = ao_cmd_lex_u32;
308         ao_config_set_radio();
309         _ao_config_edit_finish();
310 #if HAS_RADIO_RECV
311         ao_radio_recv_abort();
312 #endif
313 }
314
315 #endif
316
317 #if HAS_RADIO_FORWARD
318 void
319 ao_config_send_frequency_show(void) __reentrant
320 {
321         printf("Send frequency: %ld\n",
322                ao_config.send_frequency);
323 }
324
325 void
326 ao_config_send_frequency_set(void) __reentrant
327 {
328         ao_cmd_decimal();
329         if (ao_cmd_status != ao_cmd_success)
330                 return;
331         _ao_config_edit_start();
332         ao_config.send_frequency = ao_cmd_lex_u32;
333         ao_config_set_radio();
334         _ao_config_edit_finish();
335 #if HAS_RADIO_RECV
336         ao_radio_recv_abort();
337 #endif
338 }
339
340 #endif
341
342 #if HAS_FLIGHT
343
344 void
345 ao_config_main_deploy_show(void) __reentrant
346 {
347         printf("Main deploy: %d meters\n",
348                ao_config.main_deploy);
349 }
350
351 void
352 ao_config_main_deploy_set(void) __reentrant
353 {
354         ao_cmd_decimal();
355         if (ao_cmd_status != ao_cmd_success)
356                 return;
357         _ao_config_edit_start();
358         ao_config.main_deploy = ao_cmd_lex_i;
359         _ao_config_edit_finish();
360 }
361
362 #if HAS_ACCEL
363 void
364 ao_config_accel_calibrate_show(void) __reentrant
365 {
366         printf("Accel cal +1g: %d -1g: %d\n",
367                ao_config.accel_plus_g, ao_config.accel_minus_g);
368 #if HAS_GYRO
369         printf ("IMU cal along %d across %d through %d\n",
370                 ao_config.accel_zero_along,
371                 ao_config.accel_zero_across,
372                 ao_config.accel_zero_through);
373 #endif
374 }
375
376 #define ACCEL_CALIBRATE_SAMPLES 1024
377 #define ACCEL_CALIBRATE_SHIFT   10
378
379 #if HAS_GYRO
380 static int16_t accel_cal_along;
381 static int16_t accel_cal_across;
382 static int16_t accel_cal_through;
383 #endif
384
385 static int16_t
386 ao_config_accel_calibrate_auto(char *orientation) __reentrant
387 {
388         uint16_t        i;
389         int32_t         accel_total;
390         uint8_t         cal_data_ring;
391 #if HAS_GYRO
392         int32_t         accel_along_total = 0;
393         int32_t         accel_across_total = 0;
394         int32_t         accel_through_total = 0;
395 #endif
396
397         printf("Orient antenna %s and press a key...", orientation);
398         flush();
399         (void) getchar();
400         puts("\r\n"); flush();
401         puts("Calibrating..."); flush();
402         i = ACCEL_CALIBRATE_SAMPLES;
403         accel_total = 0;
404         cal_data_ring = ao_sample_data;
405         while (i) {
406                 ao_sleep(DATA_TO_XDATA(&ao_sample_data));
407                 while (i && cal_data_ring != ao_sample_data) {
408                         accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
409 #if HAS_GYRO
410                         accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
411                         accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
412                         accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
413 #endif
414                         cal_data_ring = ao_data_ring_next(cal_data_ring);
415                         i--;
416                 }
417         }
418 #if HAS_GYRO
419         accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
420         accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
421         accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
422 #endif
423         return accel_total >> ACCEL_CALIBRATE_SHIFT;
424 }
425
426 void
427 ao_config_accel_calibrate_set(void) __reentrant
428 {
429         int16_t up, down;
430 #if HAS_GYRO
431         int16_t accel_along_up = 0, accel_along_down = 0;
432         int16_t accel_across_up = 0, accel_across_down = 0;
433         int16_t accel_through_up = 0, accel_through_down = 0;
434 #endif
435
436         ao_cmd_decimal();
437         if (ao_cmd_status != ao_cmd_success)
438                 return;
439         if (ao_cmd_lex_i == 0) {
440                 up = ao_config_accel_calibrate_auto("up");
441 #if HAS_GYRO
442                 accel_along_up = accel_cal_along;
443                 accel_across_up = accel_cal_across;
444                 accel_through_up = accel_cal_through;
445 #endif
446                 down = ao_config_accel_calibrate_auto("down");
447 #if HAS_GYRO
448                 accel_along_down = accel_cal_along;
449                 accel_across_down = accel_cal_across;
450                 accel_through_down = accel_cal_through;
451 #endif
452         } else {
453                 up = ao_cmd_lex_i;
454                 ao_cmd_decimal();
455                 if (ao_cmd_status != ao_cmd_success)
456                         return;
457                 down = ao_cmd_lex_i;
458         }
459         if (up >= down) {
460                 printf("Invalid accel: up (%d) down (%d)\n",
461                        up, down);
462                 return;
463         }
464         _ao_config_edit_start();
465         ao_config.accel_plus_g = up;
466         ao_config.accel_minus_g = down;
467 #if HAS_GYRO
468         if (ao_cmd_lex_i == 0) {
469                 ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
470                 ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
471                 ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
472         }
473 #endif
474         _ao_config_edit_finish();
475 }
476 #endif /* HAS_ACCEL */
477
478 void
479 ao_config_apogee_delay_show(void) __reentrant
480 {
481         printf("Apogee delay: %d seconds\n",
482                ao_config.apogee_delay);
483 }
484
485 void
486 ao_config_apogee_delay_set(void) __reentrant
487 {
488         ao_cmd_decimal();
489         if (ao_cmd_status != ao_cmd_success)
490                 return;
491         _ao_config_edit_start();
492         ao_config.apogee_delay = ao_cmd_lex_i;
493         _ao_config_edit_finish();
494 }
495
496 void
497 ao_config_apogee_lockout_show(void) __reentrant
498 {
499         printf ("Apogee lockout: %d seconds\n",
500                 ao_config.apogee_lockout);
501 }
502
503 void
504 ao_config_apogee_lockout_set(void) __reentrant
505 {
506         ao_cmd_decimal();
507         if (ao_cmd_status != ao_cmd_success)
508                 return;
509         _ao_config_edit_start();
510         ao_config.apogee_lockout = ao_cmd_lex_i;
511         _ao_config_edit_finish();
512 }
513
514 #endif /* HAS_FLIGHT */
515
516 #if HAS_RADIO
517 void
518 ao_config_radio_cal_show(void) __reentrant
519 {
520         printf("Radio cal: %ld\n", ao_config.radio_cal);
521 }
522
523 void
524 ao_config_radio_cal_set(void) __reentrant
525 {
526         ao_cmd_decimal();
527         if (ao_cmd_status != ao_cmd_success)
528                 return;
529         _ao_config_edit_start();
530         ao_config.radio_cal = ao_cmd_lex_u32;
531         ao_config_set_radio();
532         _ao_config_edit_finish();
533 }
534
535 #endif
536
537 #if HAS_RADIO_RATE
538 #ifndef HAS_TELEMETRY
539 #error Please define HAS_TELEMETRY
540 #endif
541
542 void
543 ao_config_radio_rate_show(void) __reentrant
544 {
545         printf("Telemetry rate: %d\n", ao_config.radio_rate);
546 }
547
548 void
549 ao_config_radio_rate_set(void) __reentrant
550 {
551         ao_cmd_decimal();
552         if (ao_cmd_status != ao_cmd_success)
553                 return;
554         if (AO_RADIO_RATE_MAX < ao_cmd_lex_i) {
555                 ao_cmd_status = ao_cmd_lex_error;
556                 return;
557         }
558         _ao_config_edit_start();
559         ao_config.radio_rate = ao_cmd_lex_i;
560 #if HAS_TELEMETRY
561         ao_telemetry_reset_interval();
562 #endif
563         _ao_config_edit_finish();
564 #if HAS_RADIO_RECV
565         ao_radio_recv_abort();
566 #endif
567 }
568 #endif
569
570 #if HAS_LOG
571
572 void
573 ao_config_log_show(void) __reentrant
574 {
575         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
576 #if FLIGHT_LOG_APPEND
577         printf("Log fixed: 1\n");
578 #endif
579 }
580
581 #if FLIGHT_LOG_APPEND
582 void
583 ao_config_log_fix_append(void)
584 {
585         _ao_config_edit_start();
586         ao_config.flight_log_max = ao_storage_log_max;
587         _ao_config_edit_finish();
588         ao_mutex_get(&ao_config_mutex);
589         _ao_config_put();
590         ao_config_dirty = 0;
591         ao_mutex_put(&ao_config_mutex);
592 }
593 #endif
594
595 void
596 ao_config_log_set(void) __reentrant
597 {
598 #if FLIGHT_LOG_APPEND
599         printf("Flight log fixed size %d kB\n", ao_storage_log_max >> 10);
600 #else
601         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
602         uint16_t        log_max = (uint16_t) (ao_storage_log_max >> 10);
603
604         ao_cmd_decimal();
605         if (ao_cmd_status != ao_cmd_success)
606                 return;
607         if (ao_log_present())
608                 printf("Storage must be empty before changing log size\n");
609         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
610                 printf("Flight log size must be multiple of %d kB\n", block);
611         else if (ao_cmd_lex_i > log_max)
612                 printf("Flight log max %d kB\n", log_max);
613         else {
614                 _ao_config_edit_start();
615                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
616                 _ao_config_edit_finish();
617         }
618 #endif
619 }
620 #endif /* HAS_LOG */
621
622 #if HAS_IGNITE
623 void
624 ao_config_ignite_mode_show(void) __reentrant
625 {
626         printf("Ignite mode: %d\n", ao_config.ignite_mode);
627 }
628
629 void
630 ao_config_ignite_mode_set(void) __reentrant
631 {
632         ao_cmd_decimal();
633         if (ao_cmd_status != ao_cmd_success)
634                 return;
635         _ao_config_edit_start();
636         ao_config.ignite_mode = ao_cmd_lex_i;
637         _ao_config_edit_finish();
638 }
639 #endif
640
641 #if HAS_ACCEL
642 void
643 ao_config_pad_orientation_show(void) __reentrant
644 {
645         printf("Pad orientation: %d\n", ao_config.pad_orientation);
646 }
647
648 #ifndef AO_ACCEL_INVERT
649 #define AO_ACCEL_INVERT 0x7fff
650 #endif
651
652 void
653 ao_config_pad_orientation_set(void) __reentrant
654 {
655         ao_cmd_decimal();
656         if (ao_cmd_status != ao_cmd_success)
657                 return;
658         _ao_config_edit_start();
659         ao_cmd_lex_i &= 1;
660         if (ao_config.pad_orientation != ao_cmd_lex_i) {
661                 int16_t t;
662                 t = ao_config.accel_plus_g;
663                 ao_config.accel_plus_g = AO_ACCEL_INVERT - ao_config.accel_minus_g;
664                 ao_config.accel_minus_g = AO_ACCEL_INVERT - t;
665         }
666         ao_config.pad_orientation = ao_cmd_lex_i;
667         _ao_config_edit_finish();
668 }
669 #endif
670
671 #if HAS_RADIO
672 void
673 ao_config_radio_enable_show(void) __reentrant
674 {
675         printf("Radio enable: %d\n", ao_config.radio_enable);
676 }
677
678 void
679 ao_config_radio_enable_set(void) __reentrant
680 {
681         ao_cmd_decimal();
682         if (ao_cmd_status != ao_cmd_success)
683                 return;
684         _ao_config_edit_start();
685         ao_config.radio_enable = ao_cmd_lex_i;
686         _ao_config_edit_finish();
687 }
688 #endif /* HAS_RADIO */
689
690 #if HAS_AES
691
692 __xdata uint8_t ao_config_aes_seq = 1;
693
694 void
695 ao_config_key_show(void) __reentrant
696 {
697         uint8_t i;
698         printf("AES key: ");
699         for (i = 0; i < AO_AES_LEN; i++)
700                 printf ("%02x", ao_config.aes_key[i]);
701         printf("\n");
702 }
703
704 void
705 ao_config_key_set(void) __reentrant
706 {
707         uint8_t i;
708
709         _ao_config_edit_start();
710         for (i = 0; i < AO_AES_LEN; i++) {
711                 ao_cmd_hexbyte();
712                 if (ao_cmd_status != ao_cmd_success)
713                         break;
714                 ao_config.aes_key[i] = ao_cmd_lex_i;
715         }
716         ++ao_config_aes_seq;
717         _ao_config_edit_finish();
718 }
719 #endif
720
721 #if HAS_APRS
722
723 void
724 ao_config_aprs_show(void)
725 {
726         printf ("APRS interval: %d\n", ao_config.aprs_interval);
727 }
728
729 void
730 ao_config_aprs_set(void)
731 {
732         ao_cmd_decimal();
733         if (ao_cmd_status != ao_cmd_success)
734                 return;
735         _ao_config_edit_start();
736         ao_config.aprs_interval = ao_cmd_lex_i;
737         _ao_config_edit_finish();
738 }
739
740 #endif /* HAS_APRS */
741
742 #if HAS_RADIO_AMP
743
744 void
745 ao_config_radio_amp_show(void)
746 {
747         printf ("Radio amp setting: %d\n", ao_config.radio_amp);
748 }
749
750 void
751 ao_config_radio_amp_set(void)
752 {
753         ao_cmd_decimal();
754         if (ao_cmd_status != ao_cmd_success)
755                 return;
756         _ao_config_edit_start();
757         ao_config.radio_amp = ao_cmd_lex_i;
758         _ao_config_edit_finish();
759 }
760
761 #endif
762
763 #if HAS_RADIO_POWER
764
765 void
766 ao_config_radio_power_show(void)
767 {
768         printf ("Radio power setting: %d\n", ao_config.radio_power);
769 }
770
771 void
772 ao_config_radio_power_set(void)
773 {
774         ao_cmd_decimal();
775         if (ao_cmd_status != ao_cmd_success)
776                 return;
777         _ao_config_edit_start();
778         ao_config.radio_power = ao_cmd_lex_i;
779         _ao_config_edit_finish();
780 }
781
782 #endif
783
784 #if HAS_BEEP_CONFIG
785 void
786 ao_config_beep_show(void)
787 {
788         printf ("Beeper setting: %d\n", ao_config.mid_beep);
789 }
790
791 void
792 ao_config_beep_set(void)
793 {
794         ao_cmd_decimal();
795         if (ao_cmd_status != ao_cmd_success)
796                 return;
797         _ao_config_edit_start();
798         ao_config.mid_beep = ao_cmd_lex_i;
799         _ao_config_edit_finish();
800 }
801 #endif
802
803 #if HAS_TRACKER
804 void
805 ao_config_tracker_show(void)
806 {
807         printf ("Tracker setting: %d %d\n",
808                 ao_config.tracker_motion,
809                 ao_config.tracker_interval);
810 }
811
812 void
813 ao_config_tracker_set(void)
814 {
815         uint16_t        m, i;
816         ao_cmd_decimal();
817         if (ao_cmd_status != ao_cmd_success)
818                 return;
819         m = ao_cmd_lex_i;
820         ao_cmd_decimal();
821         if (ao_cmd_status != ao_cmd_success)
822                 return;
823         i = ao_cmd_lex_i;
824         _ao_config_edit_start();
825         ao_config.tracker_motion = m;
826         ao_config.tracker_interval = i;
827         _ao_config_edit_finish();
828 }
829 #endif /* HAS_TRACKER */
830
831 #if AO_PYRO_NUM
832 void
833 ao_config_pyro_time_show(void)
834 {
835         printf ("Pyro time: %d\n", ao_config.pyro_time);
836 }
837
838 void
839 ao_config_pyro_time_set(void)
840 {
841         ao_cmd_decimal();
842         if (ao_cmd_status != ao_cmd_success)
843                 return;
844         _ao_config_edit_start();
845         ao_config.pyro_time = ao_cmd_lex_i;
846         _ao_config_edit_finish();
847 }
848 #endif
849
850 #if HAS_APRS
851 void
852 ao_config_aprs_ssid_show(void)
853 {
854         printf ("APRS SSID: %d\n",
855                 ao_config.aprs_ssid);
856 }
857
858 void
859 ao_config_aprs_ssid_set(void)
860 {
861         ao_cmd_decimal();
862         if (ao_cmd_status != ao_cmd_success)
863                 return;
864         if (15 < ao_cmd_lex_i) {
865                 ao_cmd_status = ao_cmd_lex_error;
866                 return;
867         }
868         _ao_config_edit_start();
869         ao_config.aprs_ssid = ao_cmd_lex_i;
870         _ao_config_edit_finish();
871 }
872 #endif /* HAS_APRS */
873
874 struct ao_config_var {
875         __code char     *str;
876         void            (*set)(void) __reentrant;
877         void            (*show)(void) __reentrant;
878 };
879
880 static void
881 ao_config_help(void) __reentrant;
882
883 static void
884 ao_config_show(void) __reentrant;
885
886 #if HAS_EEPROM
887 static void
888 ao_config_save(void) __reentrant;
889 #endif
890
891 __code struct ao_config_var ao_config_vars[] = {
892 #if HAS_FLIGHT
893         { "m <meters>\0Main deploy (m)",
894           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
895         { "d <delay>\0Apogee delay (s)",
896           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
897         { "L <seconds>\0Apogee detect lockout (s)",
898           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
899 #endif /* HAS_FLIGHT */
900 #if HAS_RADIO
901         { "F <freq>\0Frequency (kHz)",
902           ao_config_frequency_set, ao_config_frequency_show },
903 #if HAS_RADIO_FORWARD
904         { "R <freq>\0Repeater output frequency (kHz)",
905           ao_config_send_frequency_set, ao_config_send_frequency_show },
906 #endif
907         { "c <call>\0Callsign (8 char max)",
908           ao_config_callsign_set,       ao_config_callsign_show },
909         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
910           ao_config_radio_enable_set, ao_config_radio_enable_show },
911         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
912           ao_config_radio_cal_set,      ao_config_radio_cal_show },
913 #if HAS_RADIO_RATE
914         { "T <rate>\0Telemetry rate (0=38.4, 1=9.6, 2=2.4)",
915           ao_config_radio_rate_set,     ao_config_radio_rate_show },
916 #endif
917 #if HAS_RADIO_POWER
918         { "p <setting>\0Radio power setting (0-255)",
919           ao_config_radio_power_set,    ao_config_radio_power_show },
920 #endif
921 #if HAS_RADIO_AMP
922         { "d <setting>\0Radio amplifier setting (0-3)",
923           ao_config_radio_amp_set,      ao_config_radio_amp_show },
924 #endif
925 #endif /* HAS_RADIO */
926 #if HAS_ACCEL
927         { "a <+g> <-g>\0Accel calib (0 for auto)",
928           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
929         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
930           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
931 #endif /* HAS_ACCEL */
932 #if HAS_LOG
933         { "l <size>\0Flight log size (kB)",
934           ao_config_log_set,            ao_config_log_show },
935 #endif
936 #if HAS_IGNITE
937         { "i <0 dual, 1 apogee, 2 main>\0Igniter mode",
938           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
939 #endif
940 #if HAS_AES
941         { "k <32 hex digits>\0AES encryption key",
942           ao_config_key_set, ao_config_key_show },
943 #endif
944 #if AO_PYRO_NUM
945         { "P <n,?>\0Pyro channels",
946           ao_pyro_set, ao_pyro_show },
947         { "I <ticks>\0Pyro firing time",
948           ao_config_pyro_time_set, ao_config_pyro_time_show },
949 #endif
950 #if HAS_APRS
951         { "A <secs>\0APRS packet interval (0 disable)",
952           ao_config_aprs_set, ao_config_aprs_show },
953 #endif
954 #if HAS_BEEP_CONFIG
955         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
956           ao_config_beep_set, ao_config_beep_show },
957 #endif
958 #if HAS_TRACKER
959         { "t <motion> <interval>\0Tracker configuration",
960           ao_config_tracker_set, ao_config_tracker_show },
961 #endif
962 #if HAS_APRS
963         { "S <ssid>\0Set APRS SSID (0-15)",
964           ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
965 #endif
966         { "s\0Show",
967           ao_config_show,               0 },
968 #if HAS_EEPROM
969         { "w\0Write to eeprom",
970           ao_config_save,               0 },
971 #endif
972         { "?\0Help",
973           ao_config_help,               0 },
974         { 0, 0, 0 }
975 };
976
977 void
978 ao_config_set(void)
979 {
980         char    c;
981         uint8_t cmd;
982
983         ao_cmd_white();
984         c = ao_cmd_lex_c;
985         ao_cmd_lex();
986         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
987                 if (ao_config_vars[cmd].str[0] == c) {
988                         (*ao_config_vars[cmd].set)();
989                         return;
990                 }
991         ao_cmd_status = ao_cmd_syntax_error;
992 }
993
994 static void
995 ao_config_help(void) __reentrant
996 {
997         uint8_t cmd;
998         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
999                 printf("%-20s %s\n",
1000                        ao_config_vars[cmd].str,
1001                        ao_config_vars[cmd].str+1+
1002                        strlen(ao_config_vars[cmd].str));
1003 }
1004
1005 static void
1006 ao_config_show(void) __reentrant
1007 {
1008         uint8_t cmd;
1009         ao_config_get();
1010         printf("Config version: %d.%d\n",
1011                ao_config.major, ao_config.minor);
1012         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1013                 if (ao_config_vars[cmd].show)
1014                         (*ao_config_vars[cmd].show)();
1015 #if HAS_MS5607
1016         ao_ms5607_info();
1017 #endif
1018 }
1019
1020 #if HAS_EEPROM
1021 static void
1022 ao_config_save(void) __reentrant
1023 {
1024         uint8_t saved = 0;
1025         ao_mutex_get(&ao_config_mutex);
1026         if (ao_config_dirty) {
1027                 _ao_config_put();
1028                 ao_config_dirty = 0;
1029                 saved = 1;
1030         }
1031         ao_mutex_put(&ao_config_mutex);
1032         if (saved)
1033                 puts("Saved");
1034         else
1035                 puts("Nothing to save");
1036 }
1037 #endif
1038
1039 __code struct ao_cmds ao_config_cmds[] = {
1040         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
1041         { 0, NULL },
1042 };
1043
1044 void
1045 ao_config_init(void)
1046 {
1047         ao_cmd_register(&ao_config_cmds[0]);
1048 }