altos: Support pad/lco boxes with fixed box numbers
[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 #if HAS_APRS
224                 if (minor < 22)
225                         ao_config.aprs_format = AO_CONFIG_DEFAULT_APRS_FORMAT;
226 #endif
227 #if HAS_FIXED_PAD_BOX
228                 if (minor < 22)
229                         ao_config.pad_box = 1;
230 #endif
231                 ao_config.minor = AO_CONFIG_MINOR;
232                 ao_config_dirty = 1;
233         }
234 #if HAS_RADIO
235 #if HAS_FORCE_FREQ
236         if (ao_force_freq) {
237                 ao_config.frequency = 434550;
238                 ao_config.radio_cal = ao_radio_cal;
239                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
240                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
241         }
242 #endif
243         ao_config_set_radio();
244 #endif
245         ao_config_loaded = 1;
246 }
247
248 void
249 _ao_config_edit_start(void)
250 {
251         ao_mutex_get(&ao_config_mutex);
252         _ao_config_get();
253 }
254
255 void
256 _ao_config_edit_finish(void)
257 {
258         ao_config_dirty = 1;
259         ao_mutex_put(&ao_config_mutex);
260 }
261
262 void
263 ao_config_get(void)
264 {
265         _ao_config_edit_start();
266         ao_mutex_put(&ao_config_mutex);
267 }
268
269 void
270 ao_config_callsign_show(void)
271 {
272         printf ("Callsign: \"%s\"\n", ao_config.callsign);
273 }
274
275 void
276 ao_config_callsign_set(void) __reentrant
277 {
278         uint8_t c;
279         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
280
281         ao_xmemset(callsign, '\0', sizeof callsign);
282         ao_cmd_white();
283         c = 0;
284         while (ao_cmd_lex_c != '\n') {
285                 if (c < AO_MAX_CALLSIGN)
286                         callsign[c++] = ao_cmd_lex_c;
287                 else
288                         ao_cmd_status = ao_cmd_lex_error;
289                 ao_cmd_lex();
290         }
291         if (ao_cmd_status != ao_cmd_success)
292                 return;
293         _ao_config_edit_start();
294         ao_xmemcpy(&ao_config.callsign, &callsign,
295                AO_MAX_CALLSIGN + 1);
296         _ao_config_edit_finish();
297 }
298
299 #if HAS_RADIO
300
301 void
302 ao_config_frequency_show(void) __reentrant
303 {
304         printf("Frequency: %ld\n",
305                ao_config.frequency);
306 }
307
308 void
309 ao_config_frequency_set(void) __reentrant
310 {
311         ao_cmd_decimal();
312         if (ao_cmd_status != ao_cmd_success)
313                 return;
314         _ao_config_edit_start();
315         ao_config.frequency = ao_cmd_lex_u32;
316         ao_config_set_radio();
317         _ao_config_edit_finish();
318 #if HAS_RADIO_RECV
319         ao_radio_recv_abort();
320 #endif
321 }
322
323 #endif
324
325 #if HAS_RADIO_FORWARD
326 void
327 ao_config_send_frequency_show(void) __reentrant
328 {
329         printf("Send frequency: %ld\n",
330                ao_config.send_frequency);
331 }
332
333 void
334 ao_config_send_frequency_set(void) __reentrant
335 {
336         ao_cmd_decimal();
337         if (ao_cmd_status != ao_cmd_success)
338                 return;
339         _ao_config_edit_start();
340         ao_config.send_frequency = ao_cmd_lex_u32;
341         ao_config_set_radio();
342         _ao_config_edit_finish();
343 #if HAS_RADIO_RECV
344         ao_radio_recv_abort();
345 #endif
346 }
347
348 #endif
349
350 #if HAS_FLIGHT
351
352 void
353 ao_config_main_deploy_show(void) __reentrant
354 {
355         printf("Main deploy: %d meters\n",
356                ao_config.main_deploy);
357 }
358
359 void
360 ao_config_main_deploy_set(void) __reentrant
361 {
362         ao_cmd_decimal();
363         if (ao_cmd_status != ao_cmd_success)
364                 return;
365         _ao_config_edit_start();
366         ao_config.main_deploy = ao_cmd_lex_i;
367         _ao_config_edit_finish();
368 }
369
370 #if HAS_ACCEL
371 void
372 ao_config_accel_calibrate_show(void) __reentrant
373 {
374         printf("Accel cal +1g: %d -1g: %d\n",
375                ao_config.accel_plus_g, ao_config.accel_minus_g);
376 #if HAS_GYRO
377         printf ("IMU cal along %d across %d through %d\n",
378                 ao_config.accel_zero_along,
379                 ao_config.accel_zero_across,
380                 ao_config.accel_zero_through);
381 #endif
382 }
383
384 #define ACCEL_CALIBRATE_SAMPLES 1024
385 #define ACCEL_CALIBRATE_SHIFT   10
386
387 #if HAS_GYRO
388 static int16_t accel_cal_along;
389 static int16_t accel_cal_across;
390 static int16_t accel_cal_through;
391 #endif
392
393 static int16_t
394 ao_config_accel_calibrate_auto(char *orientation) __reentrant
395 {
396         uint16_t        i;
397         int32_t         accel_total;
398         uint8_t         cal_data_ring;
399 #if HAS_GYRO
400         int32_t         accel_along_total = 0;
401         int32_t         accel_across_total = 0;
402         int32_t         accel_through_total = 0;
403 #endif
404
405         printf("Orient antenna %s and press a key...", orientation);
406         flush();
407         (void) getchar();
408         puts("\r\n"); flush();
409         puts("Calibrating..."); flush();
410         i = ACCEL_CALIBRATE_SAMPLES;
411         accel_total = 0;
412         cal_data_ring = ao_sample_data;
413         while (i) {
414                 ao_sleep(DATA_TO_XDATA(&ao_sample_data));
415                 while (i && cal_data_ring != ao_sample_data) {
416                         accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
417 #if HAS_GYRO
418                         accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
419                         accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
420                         accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
421 #endif
422                         cal_data_ring = ao_data_ring_next(cal_data_ring);
423                         i--;
424                 }
425         }
426 #if HAS_GYRO
427         accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
428         accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
429         accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
430 #endif
431         return accel_total >> ACCEL_CALIBRATE_SHIFT;
432 }
433
434 void
435 ao_config_accel_calibrate_set(void) __reentrant
436 {
437         int16_t up, down;
438 #if HAS_GYRO
439         int16_t accel_along_up = 0, accel_along_down = 0;
440         int16_t accel_across_up = 0, accel_across_down = 0;
441         int16_t accel_through_up = 0, accel_through_down = 0;
442 #endif
443
444         ao_cmd_decimal();
445         if (ao_cmd_status != ao_cmd_success)
446                 return;
447         if (ao_cmd_lex_i == 0) {
448                 up = ao_config_accel_calibrate_auto("up");
449 #if HAS_GYRO
450                 accel_along_up = accel_cal_along;
451                 accel_across_up = accel_cal_across;
452                 accel_through_up = accel_cal_through;
453 #endif
454                 down = ao_config_accel_calibrate_auto("down");
455 #if HAS_GYRO
456                 accel_along_down = accel_cal_along;
457                 accel_across_down = accel_cal_across;
458                 accel_through_down = accel_cal_through;
459 #endif
460         } else {
461                 up = ao_cmd_lex_i;
462                 ao_cmd_decimal();
463                 if (ao_cmd_status != ao_cmd_success)
464                         return;
465                 down = ao_cmd_lex_i;
466         }
467         if (up >= down) {
468                 printf("Invalid accel: up (%d) down (%d)\n",
469                        up, down);
470                 return;
471         }
472         _ao_config_edit_start();
473         ao_config.accel_plus_g = up;
474         ao_config.accel_minus_g = down;
475 #if HAS_GYRO
476         if (ao_cmd_lex_i == 0) {
477                 ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
478                 ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
479                 ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
480         }
481 #endif
482         _ao_config_edit_finish();
483 }
484 #endif /* HAS_ACCEL */
485
486 void
487 ao_config_apogee_delay_show(void) __reentrant
488 {
489         printf("Apogee delay: %d seconds\n",
490                ao_config.apogee_delay);
491 }
492
493 void
494 ao_config_apogee_delay_set(void) __reentrant
495 {
496         ao_cmd_decimal();
497         if (ao_cmd_status != ao_cmd_success)
498                 return;
499         _ao_config_edit_start();
500         ao_config.apogee_delay = ao_cmd_lex_i;
501         _ao_config_edit_finish();
502 }
503
504 void
505 ao_config_apogee_lockout_show(void) __reentrant
506 {
507         printf ("Apogee lockout: %d seconds\n",
508                 ao_config.apogee_lockout);
509 }
510
511 void
512 ao_config_apogee_lockout_set(void) __reentrant
513 {
514         ao_cmd_decimal();
515         if (ao_cmd_status != ao_cmd_success)
516                 return;
517         _ao_config_edit_start();
518         ao_config.apogee_lockout = ao_cmd_lex_i;
519         _ao_config_edit_finish();
520 }
521
522 #endif /* HAS_FLIGHT */
523
524 #if HAS_RADIO
525 void
526 ao_config_radio_cal_show(void) __reentrant
527 {
528         printf("Radio cal: %ld\n", ao_config.radio_cal);
529 }
530
531 void
532 ao_config_radio_cal_set(void) __reentrant
533 {
534         ao_cmd_decimal();
535         if (ao_cmd_status != ao_cmd_success)
536                 return;
537         _ao_config_edit_start();
538         ao_config.radio_cal = ao_cmd_lex_u32;
539         ao_config_set_radio();
540         _ao_config_edit_finish();
541 }
542
543 #endif
544
545 #if HAS_RADIO_RATE
546 #ifndef HAS_TELEMETRY
547 #error Please define HAS_TELEMETRY
548 #endif
549
550 void
551 ao_config_radio_rate_show(void) __reentrant
552 {
553         printf("Telemetry rate: %d\n", ao_config.radio_rate);
554 }
555
556 void
557 ao_config_radio_rate_set(void) __reentrant
558 {
559         ao_cmd_decimal();
560         if (ao_cmd_status != ao_cmd_success)
561                 return;
562         if (AO_RADIO_RATE_MAX < ao_cmd_lex_i) {
563                 ao_cmd_status = ao_cmd_lex_error;
564                 return;
565         }
566         _ao_config_edit_start();
567         ao_config.radio_rate = ao_cmd_lex_i;
568         _ao_config_edit_finish();
569 #if HAS_TELEMETRY
570         ao_telemetry_reset_interval();
571 #endif
572 #if HAS_RADIO_RECV
573         ao_radio_recv_abort();
574 #endif
575 }
576 #endif
577
578 #if HAS_LOG
579
580 void
581 ao_config_log_show(void) __reentrant
582 {
583         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
584 #if FLIGHT_LOG_APPEND
585         printf("Log fixed: 1\n");
586 #endif
587 }
588
589 #if FLIGHT_LOG_APPEND
590 void
591 ao_config_log_fix_append(void)
592 {
593         _ao_config_edit_start();
594         ao_config.flight_log_max = ao_storage_log_max;
595         _ao_config_edit_finish();
596         ao_mutex_get(&ao_config_mutex);
597         _ao_config_put();
598         ao_config_dirty = 0;
599         ao_mutex_put(&ao_config_mutex);
600 }
601 #endif
602
603 void
604 ao_config_log_set(void) __reentrant
605 {
606 #if FLIGHT_LOG_APPEND
607         printf("Flight log fixed size %d kB\n", ao_storage_log_max >> 10);
608 #else
609         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
610         uint16_t        log_max = (uint16_t) (ao_storage_log_max >> 10);
611
612         ao_cmd_decimal();
613         if (ao_cmd_status != ao_cmd_success)
614                 return;
615         if (ao_log_present())
616                 printf("Storage must be empty before changing log size\n");
617         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
618                 printf("Flight log size must be multiple of %d kB\n", block);
619         else if (ao_cmd_lex_i > log_max)
620                 printf("Flight log max %d kB\n", log_max);
621         else {
622                 _ao_config_edit_start();
623                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
624                 _ao_config_edit_finish();
625         }
626 #endif
627 }
628 #endif /* HAS_LOG */
629
630 #if HAS_IGNITE
631 void
632 ao_config_ignite_mode_show(void) __reentrant
633 {
634         printf("Ignite mode: %d\n", ao_config.ignite_mode);
635 }
636
637 void
638 ao_config_ignite_mode_set(void) __reentrant
639 {
640         ao_cmd_decimal();
641         if (ao_cmd_status != ao_cmd_success)
642                 return;
643         _ao_config_edit_start();
644         ao_config.ignite_mode = ao_cmd_lex_i;
645         _ao_config_edit_finish();
646 }
647 #endif
648
649 #if HAS_ACCEL
650 void
651 ao_config_pad_orientation_show(void) __reentrant
652 {
653         printf("Pad orientation: %d\n", ao_config.pad_orientation);
654 }
655
656 #ifndef AO_ACCEL_INVERT
657 #define AO_ACCEL_INVERT 0x7fff
658 #endif
659
660 void
661 ao_config_pad_orientation_set(void) __reentrant
662 {
663         ao_cmd_decimal();
664         if (ao_cmd_status != ao_cmd_success)
665                 return;
666         _ao_config_edit_start();
667         ao_cmd_lex_i &= 1;
668         if (ao_config.pad_orientation != ao_cmd_lex_i) {
669                 int16_t t;
670                 t = ao_config.accel_plus_g;
671                 ao_config.accel_plus_g = AO_ACCEL_INVERT - ao_config.accel_minus_g;
672                 ao_config.accel_minus_g = AO_ACCEL_INVERT - t;
673         }
674         ao_config.pad_orientation = ao_cmd_lex_i;
675         _ao_config_edit_finish();
676 }
677 #endif
678
679 #if HAS_RADIO
680 void
681 ao_config_radio_enable_show(void) __reentrant
682 {
683         printf("Radio enable: %d\n", ao_config.radio_enable);
684 }
685
686 void
687 ao_config_radio_enable_set(void) __reentrant
688 {
689         ao_cmd_decimal();
690         if (ao_cmd_status != ao_cmd_success)
691                 return;
692         _ao_config_edit_start();
693         ao_config.radio_enable = ao_cmd_lex_i;
694         _ao_config_edit_finish();
695 #if HAS_TELEMETRY && HAS_RADIO_RATE
696         ao_telemetry_reset_interval();
697 #endif
698 }
699 #endif /* HAS_RADIO */
700
701 #if HAS_AES
702
703 __xdata uint8_t ao_config_aes_seq = 1;
704
705 void
706 ao_config_key_show(void) __reentrant
707 {
708         uint8_t i;
709         printf("AES key: ");
710         for (i = 0; i < AO_AES_LEN; i++)
711                 printf ("%02x", ao_config.aes_key[i]);
712         printf("\n");
713 }
714
715 void
716 ao_config_key_set(void) __reentrant
717 {
718         uint8_t i;
719
720         _ao_config_edit_start();
721         for (i = 0; i < AO_AES_LEN; i++) {
722                 ao_cmd_hexbyte();
723                 if (ao_cmd_status != ao_cmd_success)
724                         break;
725                 ao_config.aes_key[i] = ao_cmd_lex_i;
726         }
727         ++ao_config_aes_seq;
728         _ao_config_edit_finish();
729 }
730 #endif
731
732 #if HAS_APRS
733
734 void
735 ao_config_aprs_show(void)
736 {
737         printf ("APRS interval: %d\n", ao_config.aprs_interval);
738 }
739
740 void
741 ao_config_aprs_set(void)
742 {
743         ao_cmd_decimal();
744         if (ao_cmd_status != ao_cmd_success)
745                 return;
746         _ao_config_edit_start();
747         ao_config.aprs_interval = ao_cmd_lex_i;
748         _ao_config_edit_finish();
749         ao_telemetry_reset_interval();
750 }
751
752 #endif /* HAS_APRS */
753
754 #if HAS_RADIO_AMP
755
756 void
757 ao_config_radio_amp_show(void)
758 {
759         printf ("Radio amp setting: %d\n", ao_config.radio_amp);
760 }
761
762 void
763 ao_config_radio_amp_set(void)
764 {
765         ao_cmd_decimal();
766         if (ao_cmd_status != ao_cmd_success)
767                 return;
768         _ao_config_edit_start();
769         ao_config.radio_amp = ao_cmd_lex_i;
770         _ao_config_edit_finish();
771 }
772
773 #endif
774
775 #if HAS_RADIO_POWER
776
777 void
778 ao_config_radio_power_show(void)
779 {
780         printf ("Radio power setting: %d\n", ao_config.radio_power);
781 }
782
783 void
784 ao_config_radio_power_set(void)
785 {
786         ao_cmd_decimal();
787         if (ao_cmd_status != ao_cmd_success)
788                 return;
789         _ao_config_edit_start();
790         ao_config.radio_power = ao_cmd_lex_i;
791         _ao_config_edit_finish();
792 }
793
794 #endif
795
796 #if HAS_BEEP_CONFIG
797 void
798 ao_config_beep_show(void)
799 {
800         printf ("Beeper setting: %d\n", ao_config.mid_beep);
801 }
802
803 void
804 ao_config_beep_set(void)
805 {
806         ao_cmd_decimal();
807         if (ao_cmd_status != ao_cmd_success)
808                 return;
809         _ao_config_edit_start();
810         ao_config.mid_beep = ao_cmd_lex_i;
811         _ao_config_edit_finish();
812 }
813 #endif
814
815 #if HAS_TRACKER
816 void
817 ao_config_tracker_show(void)
818 {
819         printf ("Tracker setting: %d %d\n",
820                 ao_config.tracker_motion,
821                 ao_config.tracker_interval);
822 }
823
824 void
825 ao_config_tracker_set(void)
826 {
827         uint16_t        m, i;
828         ao_cmd_decimal();
829         if (ao_cmd_status != ao_cmd_success)
830                 return;
831         m = ao_cmd_lex_i;
832         ao_cmd_decimal();
833         if (ao_cmd_status != ao_cmd_success)
834                 return;
835         i = ao_cmd_lex_i;
836         _ao_config_edit_start();
837         ao_config.tracker_motion = m;
838         ao_config.tracker_interval = i;
839         _ao_config_edit_finish();
840 #if HAS_TELEMETRY
841         ao_telemetry_reset_interval();
842 #endif
843 }
844 #endif /* HAS_TRACKER */
845
846 #if AO_PYRO_NUM
847 void
848 ao_config_pyro_time_show(void)
849 {
850         printf ("Pyro time: %d\n", ao_config.pyro_time);
851 }
852
853 void
854 ao_config_pyro_time_set(void)
855 {
856         ao_cmd_decimal();
857         if (ao_cmd_status != ao_cmd_success)
858                 return;
859         _ao_config_edit_start();
860         ao_config.pyro_time = ao_cmd_lex_i;
861         _ao_config_edit_finish();
862 }
863 #endif
864
865 #if HAS_APRS
866 void
867 ao_config_aprs_ssid_show(void)
868 {
869         printf ("APRS SSID: %d\n",
870                 ao_config.aprs_ssid);
871 }
872
873 void
874 ao_config_aprs_ssid_set(void)
875 {
876         ao_cmd_decimal();
877         if (ao_cmd_status != ao_cmd_success)
878                 return;
879         if (15 < ao_cmd_lex_i) {
880                 ao_cmd_status = ao_cmd_lex_error;
881                 return;
882         }
883         _ao_config_edit_start();
884         ao_config.aprs_ssid = ao_cmd_lex_i;
885         _ao_config_edit_finish();
886 }
887
888 void
889 ao_config_aprs_format_set(void)
890 {
891         ao_cmd_decimal();
892         if (ao_cmd_status != ao_cmd_success)
893                 return;
894         _ao_config_edit_start();
895         ao_config.aprs_format = ao_cmd_lex_i != 0;
896         _ao_config_edit_finish();
897 }
898
899 void
900 ao_config_aprs_format_show(void)
901 {
902         printf ("APRS format: %d\n", ao_config.aprs_format);
903 }
904 #endif /* HAS_APRS */
905
906 #if HAS_FIXED_PAD_BOX
907 void
908 ao_config_pad_box_show(void)
909 {
910         printf ("Pad box: %d\n", ao_config.pad_box);
911 }
912
913 void
914 ao_config_pad_box_set(void)
915 {
916         ao_cmd_decimal();
917         if (ao_cmd_status != ao_cmd_success)
918                 return;
919         _ao_config_edit_start();
920         ao_config.pad_box = ao_cmd_lex_i;
921         _ao_config_edit_finish();
922 }
923 #endif
924
925 struct ao_config_var {
926         __code char     *str;
927         void            (*set)(void) __reentrant;
928         void            (*show)(void) __reentrant;
929 };
930
931 static void
932 ao_config_help(void) __reentrant;
933
934 static void
935 ao_config_show(void) __reentrant;
936
937 #if HAS_EEPROM
938 static void
939 ao_config_save(void) __reentrant;
940 #endif
941
942 __code struct ao_config_var ao_config_vars[] = {
943 #if HAS_FLIGHT
944         { "m <meters>\0Main deploy (m)",
945           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
946         { "d <delay>\0Apogee delay (s)",
947           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
948         { "L <seconds>\0Apogee detect lockout (s)",
949           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
950 #endif /* HAS_FLIGHT */
951 #if HAS_RADIO
952         { "F <freq>\0Frequency (kHz)",
953           ao_config_frequency_set, ao_config_frequency_show },
954 #if HAS_RADIO_FORWARD
955         { "R <freq>\0Repeater output frequency (kHz)",
956           ao_config_send_frequency_set, ao_config_send_frequency_show },
957 #endif
958         { "c <call>\0Callsign (8 char max)",
959           ao_config_callsign_set,       ao_config_callsign_show },
960         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
961           ao_config_radio_enable_set, ao_config_radio_enable_show },
962         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
963           ao_config_radio_cal_set,      ao_config_radio_cal_show },
964 #if HAS_RADIO_RATE
965         { "T <rate>\0Telemetry rate (0=38.4, 1=9.6, 2=2.4)",
966           ao_config_radio_rate_set,     ao_config_radio_rate_show },
967 #endif
968 #if HAS_RADIO_POWER
969         { "p <setting>\0Radio power setting (0-255)",
970           ao_config_radio_power_set,    ao_config_radio_power_show },
971 #endif
972 #if HAS_RADIO_AMP
973         { "d <setting>\0Radio amplifier setting (0-3)",
974           ao_config_radio_amp_set,      ao_config_radio_amp_show },
975 #endif
976 #endif /* HAS_RADIO */
977 #if HAS_ACCEL
978         { "a <+g> <-g>\0Accel calib (0 for auto)",
979           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
980         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
981           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
982 #endif /* HAS_ACCEL */
983 #if HAS_LOG
984         { "l <size>\0Flight log size (kB)",
985           ao_config_log_set,            ao_config_log_show },
986 #endif
987 #if HAS_IGNITE
988         { "i <0 dual, 1 apogee, 2 main>\0Igniter mode",
989           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
990 #endif
991 #if HAS_AES
992         { "k <32 hex digits>\0AES encryption key",
993           ao_config_key_set, ao_config_key_show },
994 #endif
995 #if AO_PYRO_NUM
996         { "P <n,?>\0Pyro channels",
997           ao_pyro_set, ao_pyro_show },
998         { "I <ticks>\0Pyro firing time",
999           ao_config_pyro_time_set, ao_config_pyro_time_show },
1000 #endif
1001 #if HAS_APRS
1002         { "A <secs>\0APRS packet interval (0 disable)",
1003           ao_config_aprs_set, ao_config_aprs_show },
1004 #endif
1005 #if HAS_BEEP_CONFIG
1006         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
1007           ao_config_beep_set, ao_config_beep_show },
1008 #endif
1009 #if HAS_TRACKER
1010         { "t <motion> <interval>\0Tracker configuration",
1011           ao_config_tracker_set, ao_config_tracker_show },
1012 #endif
1013 #if HAS_APRS
1014         { "S <ssid>\0Set APRS SSID (0-15)",
1015           ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
1016         { "C <0 compressed, 1 uncompressed>\0APRS format",
1017           ao_config_aprs_format_set, ao_config_aprs_format_show },
1018 #endif
1019 #if HAS_FIXED_PAD_BOX
1020         { "B <box>\0Set pad box (1-99)",
1021           ao_config_pad_box_set, ao_config_pad_box_show },
1022 #endif
1023         { "s\0Show",
1024           ao_config_show,               0 },
1025 #if HAS_EEPROM
1026         { "w\0Write to eeprom",
1027           ao_config_save,               0 },
1028 #endif
1029         { "?\0Help",
1030           ao_config_help,               0 },
1031         { 0, 0, 0 }
1032 };
1033
1034 void
1035 ao_config_set(void)
1036 {
1037         char    c;
1038         uint8_t cmd;
1039
1040         ao_cmd_white();
1041         c = ao_cmd_lex_c;
1042         ao_cmd_lex();
1043         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1044                 if (ao_config_vars[cmd].str[0] == c) {
1045                         (*ao_config_vars[cmd].set)();
1046                         return;
1047                 }
1048         ao_cmd_status = ao_cmd_syntax_error;
1049 }
1050
1051 static void
1052 ao_config_help(void) __reentrant
1053 {
1054         uint8_t cmd;
1055         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1056                 printf("%-20s %s\n",
1057                        ao_config_vars[cmd].str,
1058                        ao_config_vars[cmd].str+1+
1059                        strlen(ao_config_vars[cmd].str));
1060 }
1061
1062 static void
1063 ao_config_show(void) __reentrant
1064 {
1065         uint8_t cmd;
1066         ao_config_get();
1067         printf("Config version: %d.%d\n",
1068                ao_config.major, ao_config.minor);
1069         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1070                 if (ao_config_vars[cmd].show)
1071                         (*ao_config_vars[cmd].show)();
1072 #if HAS_MS5607
1073         ao_ms5607_info();
1074 #endif
1075 }
1076
1077 #if HAS_EEPROM
1078 static void
1079 ao_config_save(void) __reentrant
1080 {
1081         uint8_t saved = 0;
1082         ao_mutex_get(&ao_config_mutex);
1083         if (ao_config_dirty) {
1084                 _ao_config_put();
1085                 ao_config_dirty = 0;
1086                 saved = 1;
1087         }
1088         ao_mutex_put(&ao_config_mutex);
1089         if (saved)
1090                 puts("Saved");
1091         else
1092                 puts("Nothing to save");
1093 }
1094 #endif
1095
1096 __code struct ao_cmds ao_config_cmds[] = {
1097         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
1098         { 0, NULL },
1099 };
1100
1101 void
1102 ao_config_init(void)
1103 {
1104         ao_cmd_register(&ao_config_cmds[0]);
1105 }