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