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