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