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