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