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