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