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