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