altos: Add config values for tracker start motion limits
[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 #if HAS_EEPROM
48 #ifndef USE_INTERNAL_FLASH
49 #error Please define USE_INTERNAL_FLASH
50 #endif
51 #endif
52 #ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
53 #if USE_INTERNAL_FLASH
54 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ao_storage_config
55 #else
56 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ((uint32_t) 192 * (uint32_t) 1024)
57 #endif
58 #endif
59 #ifndef AO_CONFIG_DEFAULT_RADIO_POWER
60 #define AO_CONFIG_DEFAULT_RADIO_POWER           0x60
61 #endif
62 #define AO_CONFIG_DEFAULT_RADIO_AMP             0
63
64 #if HAS_EEPROM
65 static void
66 _ao_config_put(void)
67 {
68         ao_config_setup();
69         ao_config_erase();
70         ao_config_write(0, &ao_config, sizeof (ao_config));
71 #if HAS_FLIGHT
72         ao_log_write_erase(0);
73 #endif
74         ao_config_flush();
75 }
76
77 void
78 ao_config_put(void)
79 {
80         ao_mutex_get(&ao_config_mutex);
81         _ao_config_put();
82         ao_mutex_put(&ao_config_mutex);
83 }
84 #endif
85
86 #if HAS_RADIO
87 void
88 ao_config_set_radio(void)
89 {
90         ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
91 }
92 #endif /* HAS_RADIO */
93
94 static void
95 _ao_config_get(void)
96 {
97         uint8_t minor;
98
99         if (ao_config_loaded)
100                 return;
101 #if HAS_EEPROM
102         /* Yes, I know ao_storage_read calls ao_storage_setup,
103          * but ao_storage_setup *also* sets ao_storage_config, which we
104          * need before calling ao_storage_read here
105          */
106         ao_config_setup();
107         ao_config_read(0, &ao_config, sizeof (ao_config));
108 #endif
109         if (ao_config.major != AO_CONFIG_MAJOR) {
110                 ao_config.major = AO_CONFIG_MAJOR;
111                 ao_config.minor = 0;
112
113                 /* Version 0 stuff */
114                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
115                 ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
116                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
117                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
118                 ao_config._legacy_radio_channel = 0;
119         }
120         minor = ao_config.minor;
121         if (minor != AO_CONFIG_MINOR) {
122                 /* Fixups for minor version 1 */
123                 if (minor < 1)
124                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
125                 /* Fixups for minor version 2 */
126                 if (minor < 2) {
127                         ao_config.accel_plus_g = 0;
128                         ao_config.accel_minus_g = 0;
129                 }
130                 /* Fixups for minor version 3 */
131 #if HAS_RADIO
132                 if (minor < 3)
133                         ao_config.radio_cal = ao_radio_cal;
134 #endif
135                 /* Fixups for minor version 4 */
136 #if HAS_FLIGHT
137                 if (minor < 4)
138                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
139 #endif
140                 /* Fixupes for minor version 5 */
141                 if (minor < 5)
142                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
143                 if (minor < 6)
144                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
145                 if (minor < 8)
146                         ao_config.radio_enable = AO_RADIO_ENABLE_CORE;
147                 if (minor < 9)
148                         ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
149                 if (minor < 10)
150                         ao_config.frequency = 434550 + ao_config._legacy_radio_channel * 100;
151                 if (minor < 11)
152                         ao_config.apogee_lockout = 0;
153 #if AO_PYRO_NUM
154                 if (minor < 12)
155                         memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro));
156 #endif
157                 if (minor < 13)
158                         ao_config.aprs_interval = AO_CONFIG_DEFAULT_APRS_INTERVAL;
159 #if HAS_RADIO_POWER
160                 if (minor < 14)
161                         ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
162                 #endif
163 #if HAS_RADIO_AMP
164                 if (minor  < 14)
165                         ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
166 #endif
167 #if HAS_GYRO
168                 if (minor < 15) {
169                         ao_config.accel_zero_along = 0;
170                         ao_config.accel_zero_across = 0;
171                         ao_config.accel_zero_through = 0;
172
173                         /* Reset the main accel offsets to force
174                          * re-calibration
175                          */
176                         ao_config.accel_plus_g = 0;
177                         ao_config.accel_minus_g = 0;
178                 }
179 #endif
180 #if HAS_BEEP_CONFIG
181                 if (minor < 16)
182                         ao_config.mid_beep = AO_BEEP_MID_DEFAULT;
183 #endif
184 #if HAS_TRACKER
185                 if (minor < 17) {
186                         ao_config.tracker_start_horiz = AO_CONFIG_DEFAULT_TRACKER_START_HORIZ;
187                         ao_config.tracker_start_vert = AO_CONFIG_DEFAULT_TRACKER_START_VERT;
188                 }
189 #endif
190                 ao_config.minor = AO_CONFIG_MINOR;
191                 ao_config_dirty = 1;
192         }
193 #if HAS_RADIO
194 #if HAS_FORCE_FREQ
195         if (ao_force_freq) {
196                 ao_config.frequency = 434550;
197                 ao_config.radio_cal = ao_radio_cal;
198                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
199                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
200         }
201 #endif
202         ao_config_set_radio();
203 #endif
204         ao_config_loaded = 1;
205 }
206
207 void
208 _ao_config_edit_start(void)
209 {
210         ao_mutex_get(&ao_config_mutex);
211         _ao_config_get();
212 }
213
214 void
215 _ao_config_edit_finish(void)
216 {
217         ao_config_dirty = 1;
218         ao_mutex_put(&ao_config_mutex);
219 }
220
221 void
222 ao_config_get(void)
223 {
224         _ao_config_edit_start();
225         ao_mutex_put(&ao_config_mutex);
226 }
227
228 void
229 ao_config_callsign_show(void)
230 {
231         printf ("Callsign: \"%s\"\n", ao_config.callsign);
232 }
233
234 void
235 ao_config_callsign_set(void) __reentrant
236 {
237         uint8_t c;
238         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
239
240         ao_xmemset(callsign, '\0', sizeof callsign);
241         ao_cmd_white();
242         c = 0;
243         while (ao_cmd_lex_c != '\n') {
244                 if (c < AO_MAX_CALLSIGN)
245                         callsign[c++] = ao_cmd_lex_c;
246                 else
247                         ao_cmd_status = ao_cmd_lex_error;
248                 ao_cmd_lex();
249         }
250         if (ao_cmd_status != ao_cmd_success)
251                 return;
252         _ao_config_edit_start();
253         ao_xmemcpy(&ao_config.callsign, &callsign,
254                AO_MAX_CALLSIGN + 1);
255         _ao_config_edit_finish();
256 }
257
258 #if HAS_RADIO
259
260 void
261 ao_config_frequency_show(void) __reentrant
262 {
263         printf("Frequency: %ld\n",
264                ao_config.frequency);
265 }
266
267 void
268 ao_config_frequency_set(void) __reentrant
269 {
270         ao_cmd_decimal();
271         if (ao_cmd_status != ao_cmd_success)
272                 return;
273         _ao_config_edit_start();
274         ao_config.frequency = ao_cmd_lex_u32;
275         ao_config_set_radio();
276         _ao_config_edit_finish();
277 #if HAS_RADIO_RECV
278         ao_radio_recv_abort();
279 #endif
280 }
281 #endif
282
283 #if HAS_FLIGHT
284
285 void
286 ao_config_main_deploy_show(void) __reentrant
287 {
288         printf("Main deploy: %d meters\n",
289                ao_config.main_deploy);
290 }
291
292 void
293 ao_config_main_deploy_set(void) __reentrant
294 {
295         ao_cmd_decimal();
296         if (ao_cmd_status != ao_cmd_success)
297                 return;
298         _ao_config_edit_start();
299         ao_config.main_deploy = ao_cmd_lex_i;
300         _ao_config_edit_finish();
301 }
302
303 #if HAS_ACCEL
304 void
305 ao_config_accel_calibrate_show(void) __reentrant
306 {
307         printf("Accel cal +1g: %d -1g: %d\n",
308                ao_config.accel_plus_g, ao_config.accel_minus_g);
309 #if HAS_GYRO
310         printf ("IMU cal along %d across %d through %d\n",
311                 ao_config.accel_zero_along,
312                 ao_config.accel_zero_across,
313                 ao_config.accel_zero_through);
314 #endif
315 }
316
317 #define ACCEL_CALIBRATE_SAMPLES 1024
318 #define ACCEL_CALIBRATE_SHIFT   10
319
320 #if HAS_GYRO
321 static int16_t accel_cal_along;
322 static int16_t accel_cal_across;
323 static int16_t accel_cal_through;
324 #endif
325
326 static int16_t
327 ao_config_accel_calibrate_auto(char *orientation) __reentrant
328 {
329         uint16_t        i;
330         int32_t         accel_total;
331         uint8_t         cal_data_ring;
332 #if HAS_GYRO
333         int32_t         accel_along_total = 0;
334         int32_t         accel_across_total = 0;
335         int32_t         accel_through_total = 0;
336 #endif
337
338         printf("Orient antenna %s and press a key...", orientation);
339         flush();
340         (void) getchar();
341         puts("\r\n"); flush();
342         puts("Calibrating..."); flush();
343         i = ACCEL_CALIBRATE_SAMPLES;
344         accel_total = 0;
345         cal_data_ring = ao_sample_data;
346         while (i) {
347                 ao_sleep(DATA_TO_XDATA(&ao_sample_data));
348                 while (i && cal_data_ring != ao_sample_data) {
349                         accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
350 #if HAS_GYRO
351                         accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
352                         accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
353                         accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
354 #endif
355                         cal_data_ring = ao_data_ring_next(cal_data_ring);
356                         i--;
357                 }
358         }
359 #if HAS_GYRO
360         accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
361         accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
362         accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
363 #endif
364         return accel_total >> ACCEL_CALIBRATE_SHIFT;
365 }
366
367 void
368 ao_config_accel_calibrate_set(void) __reentrant
369 {
370         int16_t up, down;
371 #if HAS_GYRO
372         int16_t accel_along_up = 0, accel_along_down = 0;
373         int16_t accel_across_up = 0, accel_across_down = 0;
374         int16_t accel_through_up = 0, accel_through_down = 0;
375 #endif
376
377         ao_cmd_decimal();
378         if (ao_cmd_status != ao_cmd_success)
379                 return;
380         if (ao_cmd_lex_i == 0) {
381                 up = ao_config_accel_calibrate_auto("up");
382 #if HAS_GYRO
383                 accel_along_up = accel_cal_along;
384                 accel_across_up = accel_cal_across;
385                 accel_through_up = accel_cal_through;
386 #endif
387                 down = ao_config_accel_calibrate_auto("down");
388 #if HAS_GYRO
389                 accel_along_down = accel_cal_along;
390                 accel_across_down = accel_cal_across;
391                 accel_through_down = accel_cal_through;
392 #endif
393         } else {
394                 up = ao_cmd_lex_i;
395                 ao_cmd_decimal();
396                 if (ao_cmd_status != ao_cmd_success)
397                         return;
398                 down = ao_cmd_lex_i;
399         }
400         if (up >= down) {
401                 printf("Invalid accel: up (%d) down (%d)\n",
402                        up, down);
403                 return;
404         }
405         _ao_config_edit_start();
406         ao_config.accel_plus_g = up;
407         ao_config.accel_minus_g = down;
408 #if HAS_GYRO
409         if (ao_cmd_lex_i == 0) {
410                 ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
411                 ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
412                 ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
413         }
414 #endif
415         _ao_config_edit_finish();
416 }
417 #endif /* HAS_ACCEL */
418
419 void
420 ao_config_apogee_delay_show(void) __reentrant
421 {
422         printf("Apogee delay: %d seconds\n",
423                ao_config.apogee_delay);
424 }
425
426 void
427 ao_config_apogee_delay_set(void) __reentrant
428 {
429         ao_cmd_decimal();
430         if (ao_cmd_status != ao_cmd_success)
431                 return;
432         _ao_config_edit_start();
433         ao_config.apogee_delay = ao_cmd_lex_i;
434         _ao_config_edit_finish();
435 }
436
437 void
438 ao_config_apogee_lockout_show(void) __reentrant
439 {
440         printf ("Apogee lockout: %d seconds\n",
441                 ao_config.apogee_lockout);
442 }
443
444 void
445 ao_config_apogee_lockout_set(void) __reentrant
446 {
447         ao_cmd_decimal();
448         if (ao_cmd_status != ao_cmd_success)
449                 return;
450         _ao_config_edit_start();
451         ao_config.apogee_lockout = ao_cmd_lex_i;
452         _ao_config_edit_finish();
453 }
454
455 #endif /* HAS_FLIGHT */
456
457 #if HAS_RADIO
458 void
459 ao_config_radio_cal_show(void) __reentrant
460 {
461         printf("Radio cal: %ld\n", ao_config.radio_cal);
462 }
463
464 void
465 ao_config_radio_cal_set(void) __reentrant
466 {
467         ao_cmd_decimal();
468         if (ao_cmd_status != ao_cmd_success)
469                 return;
470         _ao_config_edit_start();
471         ao_config.radio_cal = ao_cmd_lex_u32;
472         ao_config_set_radio();
473         _ao_config_edit_finish();
474 }
475 #endif
476
477 #if HAS_LOG
478 void
479 ao_config_log_show(void) __reentrant
480 {
481         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
482 }
483
484 void
485 ao_config_log_set(void) __reentrant
486 {
487         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
488         uint16_t        log_max = (uint16_t) (ao_storage_log_max >> 10);
489
490         ao_cmd_decimal();
491         if (ao_cmd_status != ao_cmd_success)
492                 return;
493         if (ao_log_present())
494                 printf("Storage must be empty before changing log size\n");
495         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
496                 printf("Flight log size must be multiple of %d kB\n", block);
497         else if (ao_cmd_lex_i > log_max)
498                 printf("Flight log max %d kB\n", log_max);
499         else {
500                 _ao_config_edit_start();
501                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
502                 _ao_config_edit_finish();
503         }
504 }
505 #endif /* HAS_LOG */
506
507 #if HAS_IGNITE
508 void
509 ao_config_ignite_mode_show(void) __reentrant
510 {
511         printf("Ignite mode: %d\n", ao_config.ignite_mode);
512 }
513
514 void
515 ao_config_ignite_mode_set(void) __reentrant
516 {
517         ao_cmd_decimal();
518         if (ao_cmd_status != ao_cmd_success)
519                 return;
520         _ao_config_edit_start();
521         ao_config.ignite_mode = ao_cmd_lex_i;
522         _ao_config_edit_finish();
523 }
524 #endif
525
526 #if HAS_ACCEL
527 void
528 ao_config_pad_orientation_show(void) __reentrant
529 {
530         printf("Pad orientation: %d\n", ao_config.pad_orientation);
531 }
532
533 #ifndef AO_ACCEL_INVERT
534 #define AO_ACCEL_INVERT 0x7fff
535 #endif
536
537 void
538 ao_config_pad_orientation_set(void) __reentrant
539 {
540         ao_cmd_decimal();
541         if (ao_cmd_status != ao_cmd_success)
542                 return;
543         _ao_config_edit_start();
544         ao_cmd_lex_i &= 1;
545         if (ao_config.pad_orientation != ao_cmd_lex_i) {
546                 int16_t t;
547                 t = ao_config.accel_plus_g;
548                 ao_config.accel_plus_g = AO_ACCEL_INVERT - ao_config.accel_minus_g;
549                 ao_config.accel_minus_g = AO_ACCEL_INVERT - t;
550         }
551         ao_config.pad_orientation = ao_cmd_lex_i;
552         _ao_config_edit_finish();
553 }
554 #endif
555
556 #if HAS_RADIO
557 void
558 ao_config_radio_enable_show(void) __reentrant
559 {
560         printf("Radio enable: %d\n", ao_config.radio_enable);
561 }
562
563 void
564 ao_config_radio_enable_set(void) __reentrant
565 {
566         ao_cmd_decimal();
567         if (ao_cmd_status != ao_cmd_success)
568                 return;
569         _ao_config_edit_start();
570         ao_config.radio_enable = ao_cmd_lex_i;
571         _ao_config_edit_finish();
572 }
573 #endif /* HAS_RADIO */
574
575 #if HAS_AES
576
577 __xdata uint8_t ao_config_aes_seq = 1;
578
579 void
580 ao_config_key_show(void) __reentrant
581 {
582         uint8_t i;
583         printf("AES key: ");
584         for (i = 0; i < AO_AES_LEN; i++)
585                 printf ("%02x", ao_config.aes_key[i]);
586         printf("\n");
587 }
588
589 void
590 ao_config_key_set(void) __reentrant
591 {
592         uint8_t i;
593
594         _ao_config_edit_start();
595         for (i = 0; i < AO_AES_LEN; i++) {
596                 ao_cmd_hexbyte();
597                 if (ao_cmd_status != ao_cmd_success)
598                         break;
599                 ao_config.aes_key[i] = ao_cmd_lex_i;
600         }
601         ++ao_config_aes_seq;
602         _ao_config_edit_finish();
603 }
604 #endif
605
606 #if HAS_APRS
607
608 void
609 ao_config_aprs_show(void)
610 {
611         printf ("APRS interval: %d\n", ao_config.aprs_interval);
612 }
613
614 void
615 ao_config_aprs_set(void)
616 {
617         ao_cmd_decimal();
618         if (ao_cmd_status != ao_cmd_success)
619                 return;
620         _ao_config_edit_start();
621         ao_config.aprs_interval = ao_cmd_lex_i;
622         _ao_config_edit_finish();
623 }
624
625 #endif /* HAS_APRS */
626
627 #if HAS_RADIO_AMP
628
629 void
630 ao_config_radio_amp_show(void)
631 {
632         printf ("Radio amp setting: %d\n", ao_config.radio_amp);
633 }
634
635 void
636 ao_config_radio_amp_set(void)
637 {
638         ao_cmd_decimal();
639         if (ao_cmd_status != ao_cmd_success)
640                 return;
641         _ao_config_edit_start();
642         ao_config.radio_amp = ao_cmd_lex_i;
643         _ao_config_edit_finish();
644 }
645
646 #endif
647
648 #if HAS_RADIO_POWER
649
650 void
651 ao_config_radio_power_show(void)
652 {
653         printf ("Radio power setting: %d\n", ao_config.radio_power);
654 }
655
656 void
657 ao_config_radio_power_set(void)
658 {
659         ao_cmd_decimal();
660         if (ao_cmd_status != ao_cmd_success)
661                 return;
662         _ao_config_edit_start();
663         ao_config.radio_power = ao_cmd_lex_i;
664         _ao_config_edit_finish();
665 }
666
667 #endif
668
669 #if HAS_BEEP_CONFIG
670 void
671 ao_config_beep_show(void)
672 {
673         printf ("Beeper setting: %d\n", ao_config.mid_beep);
674 }
675
676 void
677 ao_config_beep_set(void)
678 {
679         ao_cmd_decimal();
680         if (ao_cmd_status != ao_cmd_success)
681                 return;
682         _ao_config_edit_start();
683         ao_config.mid_beep = ao_cmd_lex_i;
684         _ao_config_edit_finish();
685 }
686 #endif
687
688 #if HAS_TRACKER
689 void
690 ao_config_tracker_show(void)
691 {
692         printf ("Tracker setting: %d %d\n",
693                 ao_config.tracker_start_horiz,
694                 ao_config.tracker_start_vert);
695 }
696
697 void
698 ao_config_tracker_set(void)
699 {
700         uint16_t        h, v;
701         ao_cmd_decimal();
702         if (ao_cmd_status != ao_cmd_success)
703                 return;
704         h = ao_cmd_lex_i;
705         ao_cmd_decimal();
706         if (ao_cmd_status != ao_cmd_success)
707                 return;
708         v = ao_cmd_lex_i;
709         _ao_config_edit_start();
710         ao_config.tracker_start_horiz = h;
711         ao_config.tracker_start_vert = v;
712         _ao_config_edit_finish();
713 }
714 #endif /* HAS_TRACKER */
715
716 struct ao_config_var {
717         __code char     *str;
718         void            (*set)(void) __reentrant;
719         void            (*show)(void) __reentrant;
720 };
721
722 static void
723 ao_config_help(void) __reentrant;
724
725 static void
726 ao_config_show(void) __reentrant;
727
728 #if HAS_EEPROM
729 static void
730 ao_config_save(void) __reentrant;
731 #endif
732
733 __code struct ao_config_var ao_config_vars[] = {
734 #if HAS_FLIGHT
735         { "m <meters>\0Main deploy (m)",
736           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
737         { "d <delay>\0Apogee delay (s)",
738           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
739         { "L <seconds>\0Apogee detect lockout (s)",
740           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
741 #endif /* HAS_FLIGHT */
742 #if HAS_RADIO
743         { "F <freq>\0Frequency (kHz)",
744           ao_config_frequency_set, ao_config_frequency_show },
745         { "c <call>\0Callsign (8 char max)",
746           ao_config_callsign_set,       ao_config_callsign_show },
747         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
748           ao_config_radio_enable_set, ao_config_radio_enable_show },
749         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
750           ao_config_radio_cal_set,      ao_config_radio_cal_show },
751 #if HAS_RADIO_POWER
752         { "p <setting>\0Radio power setting (0-255)",
753           ao_config_radio_power_set,    ao_config_radio_power_show },
754 #endif
755 #if HAS_RADIO_AMP
756         { "d <setting>\0Radio amplifier setting (0-3)",
757           ao_config_radio_amp_set,      ao_config_radio_amp_show },
758 #endif
759 #endif /* HAS_RADIO */
760 #if HAS_ACCEL
761         { "a <+g> <-g>\0Accel calib (0 for auto)",
762           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
763         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
764           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
765 #endif /* HAS_ACCEL */
766 #if HAS_LOG
767         { "l <size>\0Flight log size (kB)",
768           ao_config_log_set,            ao_config_log_show },
769 #endif
770 #if HAS_IGNITE
771         { "i <0 dual, 1 apogee, 2 main>\0Igniter mode",
772           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
773 #endif
774 #if HAS_AES
775         { "k <32 hex digits>\0AES encryption key",
776           ao_config_key_set, ao_config_key_show },
777 #endif
778 #if AO_PYRO_NUM
779         { "P <n,?>\0Pyro channels",
780           ao_pyro_set, ao_pyro_show },
781 #endif
782 #if HAS_APRS
783         { "A <secs>\0APRS packet interval (0 disable)",
784           ao_config_aprs_set, ao_config_aprs_show },
785 #endif
786 #if HAS_BEEP_CONFIG
787         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
788           ao_config_beep_set, ao_config_beep_show },
789 #endif
790 #if HAS_TRACKER
791         { "t <horiz> <vert>\0Tracker start trigger distances",
792           ao_config_tracker_set, ao_config_tracker_show },
793 #endif
794         { "s\0Show",
795           ao_config_show,               0 },
796 #if HAS_EEPROM
797         { "w\0Write to eeprom",
798           ao_config_save,               0 },
799 #endif
800         { "?\0Help",
801           ao_config_help,               0 },
802         { 0, 0, 0 }
803 };
804
805 void
806 ao_config_set(void)
807 {
808         char    c;
809         uint8_t cmd;
810
811         ao_cmd_white();
812         c = ao_cmd_lex_c;
813         ao_cmd_lex();
814         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
815                 if (ao_config_vars[cmd].str[0] == c) {
816                         (*ao_config_vars[cmd].set)();
817                         return;
818                 }
819         ao_cmd_status = ao_cmd_syntax_error;
820 }
821
822 static void
823 ao_config_help(void) __reentrant
824 {
825         uint8_t cmd;
826         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
827                 printf("%-20s %s\n",
828                        ao_config_vars[cmd].str,
829                        ao_config_vars[cmd].str+1+
830                        strlen(ao_config_vars[cmd].str));
831 }
832
833 static void
834 ao_config_show(void) __reentrant
835 {
836         uint8_t cmd;
837         ao_config_get();
838         printf("Config version: %d.%d\n",
839                ao_config.major, ao_config.minor);
840         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
841                 if (ao_config_vars[cmd].show)
842                         (*ao_config_vars[cmd].show)();
843 #if HAS_MS5607
844         ao_ms5607_info();
845 #endif
846 }
847
848 #if HAS_EEPROM
849 static void
850 ao_config_save(void) __reentrant
851 {
852         uint8_t saved = 0;
853         ao_mutex_get(&ao_config_mutex);
854         if (ao_config_dirty) {
855                 _ao_config_put();
856                 ao_config_dirty = 0;
857                 saved = 1;
858         }
859         ao_mutex_put(&ao_config_mutex);
860         if (saved)
861                 puts("Saved");
862         else
863                 puts("Nothing to save");
864 }
865 #endif
866
867 __code struct ao_cmds ao_config_cmds[] = {
868         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
869         { 0, NULL },
870 };
871
872 void
873 ao_config_init(void)
874 {
875         ao_cmd_register(&ao_config_cmds[0]);
876 }