-
Notifications
You must be signed in to change notification settings - Fork 7
/
ArduVidRx.cpp
3251 lines (3139 loc) · 124 KB
/
ArduVidRx.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//ArduVidRx.cpp: Control program for FPV video receiver via Arduino.
//
// 12/17/2016 -- [ET] Version 1.3: Initial version.
// 1/26/2017 -- [ET] Version 1.4: Added 'XR' command.
// 4/22/2017 -- [ET] Version 1.5: Added automatic RSSI calibration and
// 'XA' command; added code-configurable pullups for
// unused input lines.
// 4/25/2017 -- [ET] Version 1.6: Modified 'processAutoRssiCalValue()'
// to detect low-RSSI values on freqs between 5640 and
// 5950 and to prevent min-raw-RSSI scale value from
// getting too low.
// 4/28/2017 -- [ET] Version 1.7: Added RSSI_SEC_PIN (so A6 can be
// used for RSSI if no signal on A7); modified
// 'processAutoRssiCalValue()' to handle lower
// ranges of raw-RSSI values; added optional "defaults"
// parameter to 'XZ'; added EEPROM-integrity check;
// added XT command (with default of 35 ms).
// 4/29/2017 -- [ET] Version 1.71: Added NODISP_PRIVSEL_PIN and
// and NODISP_SECVSEL_PIN options.
// 5/11/2017 -- [ET] Version 1.8: Modified so response of 'L' query
// with empty list and serial-echo off is "0".
// 1/4/2019 -- [ET] Version 1.81: Added IMD6C to frequency-list presets.
//
//Global arrays:
//
//listFreqsMHzArr[]: List of frequencies (in MHz) entered via 'L' command.
//
//scanRssiValuesArr[]: RSSI values (read via scan) for each channel; each
// entry corresponds to a frequency in the Rx5808Fns 'channelFreqTable[]'
// or a frequency in 'listFreqsMHzArr[]' (if entered).
//
//idxSortedByRssiArr[]: List of channel-index values sorted by RSSI values
// (in 'scanRssiValuesArr[]') in descending order. If 'listFreqsMHzArr[]'
// values are entered then the index values are for 'listFreqsMHzArr[]'.
//
//idxSortedSelectedArr: List of channel-index values selected from the
// 'idxSortedByRssiArr[]' array by squelching frequencies adjacent to those
// already loaded. If 'listFreqsMHzArr[]' values are entered then they
// are copied in as-is.
#include <Arduino.h>
#include "Config.h"
#include "ArduVidUtil.h"
#include "Rx5808Fns.h"
#include "Display7Seg.h"
#include "FreqListPresets.h"
#define PROG_NAME_STR "ArduVidRx"
#define PROG_VERSION_STR "1.81"
#define LISTFREQMHZ_ARR_SIZE 80 //size for 'listFreqsMHzArr[]' array
#define EEPROM_ADRW_FREQ 0 //address for freq value in EEPROM (word)
#define EEPROM_ADRB_BTNMODE 2 //address for button mode in EEPROM (byte)
#define EEPROM_ADRB_AUTOCAL 3 //address for auto RSSI calibration (byte)
#define EEPROM_ADRW_RSSIMIN 4 //address for RSSI-scaling min in EEPROM
#define EEPROM_ADRW_RSSIMAX 6 //address for RSSI-scaling max in EEPROM
#define EEPROM_ADRW_CHECKWORD 8 //address for integrity-check value EEPROM
#define EEPROM_ADRB_MINTUNEMS 10 //address for RX5808 min-tune time (byte)
#define EEPROM_ADRS_UNITID 30 //address for unit ID in EEPROM (string)
#define EEPROM_FLEN_UNITID 20 //field length for unit ID in EEPROM
#define EEPROM_ADRA_FREQLIST 64 //address for freq list in EEPROM (array)
#define EEPROM_FLEN_FREQLIST 84 //field length (in bytes) for freq list
//total length (in bytes) of EEPROM used:
#define EEPROM_USED_DATASIZE (EEPROM_ADRA_FREQLIST+EEPROM_FLEN_FREQLIST)
#define EEPROM_CHECK_VALUE 0x5242 //EEPROM integrity-check value
//setup flags for button-interrupt usage depending on pin assignments:
#define BUTTONPINS_UP2DOWN3_FLAG (UP_BUTTON_PIN == 2 && DOWN_BUTTON_PIN == 3)
#define BUTTONPINS_UP3DOWN2_FLAG (UP_BUTTON_PIN == 3 && DOWN_BUTTON_PIN == 2)
#define BUTTONPINS_USEINTERRUPT_FLAG (BUTTONS_ENABLED_FLAG && \
(BUTTONPINS_UP2DOWN3_FLAG || BUTTONPINS_UP3DOWN2_FLAG))
uint16_t currentTunerFreqMhzOrCode = 0;
uint16_t currentTunerFreqInMhz = 0;
boolean contRssiOutFlag = false;
boolean contRssiListFlag = false;
uint16_t contRssiPrevFreqVal = 0;
boolean lastShowCurRssiListFlag = false;
boolean monitorModeNextFlag = false;
boolean displayRssiEnabledFlag = false;
int sessionDefMinRssiLevel = DEF_MIN_RSSI_LEVEL;
char itoaBuff[20];
uint16_t listFreqsMHzArr[LISTFREQMHZ_ARR_SIZE]; //values via 'L' command
uint8_t scanRssiValuesArr[LISTFREQMHZ_ARR_SIZE]; //RSSI vals for all chans
uint8_t idxSortedByRssiArr[LISTFREQMHZ_ARR_SIZE]; //indices sorted by RSSI
uint8_t idxSortedSelectedArr[CHANNEL_MAX_INDEX+1];
int listFreqsMHzArrCount = 0;
int idxSortedSelArrCount = 0;
int nextTuneChannelIndex = -1;
unsigned long lastNextTuneScanTime = 0;
unsigned long monitorModeNextChanTime = 0;
int monitorModeIntervalSecs = DEF_MONITOR_INTERVAL_SECS;
unsigned long rssiOutSamplingAvgrTotal = 0;
byte rssiOutSamplingAvgrCounter = 0;
unsigned long delayedSaveFreqToEepromTime = 0;
boolean delayedSaveFreqToEepromFlag = false;
uint16_t lastEepromFreqInMhzOrCode = 0;
byte buttonsFunctionModeValue = 0;
boolean autoRssiCalibEnabledFlag = true;
byte autoRssiCalibCounterValue = 0;
unsigned long autoRssiCalibMarkedTime = 0;
boolean autoRssiCalibShowOutputFlag = false;
#if DISP7SEG_ENABLED_FLAG
boolean displayConnectedFlag = true;
#else
const boolean displayConnectedFlag = false;
#endif
boolean processExtraCommand(const char *cmdStr);
void showHelpInformation();
void showExtraHelpInformation();
void showListCmdHelpInformation();
void showFrequencyTable();
void showRevisionInfo(boolean dispInfoFlag);
void processTuneCommand(const char *valueStr);
void showCurrentFreqency();
void processFreqsMHzList(const char *listStr);
void showFreqsMHzList();
void processShowRssiCmd(const char *valueStr, boolean contFlag);
uint16_t showCurrentRssi(boolean showListFlag, boolean showChanFlag);
boolean repeatShowCurrentRssi();
void processOneMHzCommand(boolean upFlag, boolean serialOutFlag);
void processIncFreqCodeCommand(boolean bandFlag, boolean upFlag,
boolean buttonFlag);
void processAutoScanAndTuneCommand(const char *valueStr);
void autoScanTuneNextChan(const char *valueStr, boolean scanForwardFlag,
boolean rescanOnSingleFlag);
void processMonitorModeCommand(const char *valueStr);
void monitorAutoTuneNextChan();
boolean scanChannelsAndReport(int minRssiLevel, int fallbackRssiLevel,
boolean inclAllFlag, boolean restoreFreqFlag, boolean showOutputFlag);
boolean processScanChannelsCommand(const char *valueStr, boolean inclAllFlag);
void scanChansGetRssiValues(boolean includeLBandFlag, boolean inclAllFlag,
boolean restoreFreqFlag, boolean showOutputFlag);
void fullScanShowRssiValues();
void processSerialEchoCommand(const char *valueStr);
void processRawRssiMinMaxCommand(const char *valueStr);
void processEnableAutoRssiCalibCmd(const char *valueStr);
void processMinTuneTimeCommand(const char *valueStr);
void processMinRssiCommand(const char *valueStr);
void processMonitorIntervalCmd(const char *valueStr);
void processUnitIdCommand(const char *valueStr);
void processSoftRebootCommand(const char *valueStr);
void processShowFreqPresetListCmd(const char *valueStr);
void processListTranslateInfoCmd(const char *listStr);
void loadIdxSortedByRssiArr(boolean inclAllFlag);
int loadIdxSortedSelectedArr();
void processShowInputsCmd(const char *listStr);
void showDebugInputs();
void checkReportTableValues();
void setCurrentFreqByMhzOrCode(uint16_t freqMhzOrCode);
uint16_t getCurrentFreqInMhz();
uint16_t getCurrentFreqCodeWord();
void setTunerChannelToFreq(uint16_t freqInMhz);
void updateRssiOutput();
void clearRssiOutput();
void updateRssiOutValue(uint16_t rssiVal);
void scheduleDelayedSaveFreqToEeprom(int secs);
void saveCurrentFreqToEeprom();
void setChanToFreqValFromEeprom();
void saveButtonModeToEeprom(byte modeVal);
byte loadButtonModeFromEeprom();
void saveRssiMinMaxValsToEeprom();
void loadRssiMinMaxValsFromEeprom();
void saveAutoRssiCalFlagToEeprom(boolean flagVal);
boolean loadAutoRssiCalFlagFromEeprom();
void saveMinTuneTimeMsToEeprom(byte timeVal);
byte loadMinTuneTimeMsFromEeprom();
void saveUnitIdToEeprom(const char *str);
boolean isUnitIdFromEepromEmpty();
void showUnitIdFromEeprom();
void saveListFreqsMHzArrToEeprom();
void loadListFreqsMHzArrFromEeprom();
void setEepromToDefaultsValues();
void checkEepromIntegrity();
void updateActivityIndicator(boolean activityFlag);
uint16_t readRssiValue();
void processAutoRssiCalValue(uint16_t rawVal);
#if BUTTONS_ENABLED_FLAG
void processButtonModeCommand(const char *valueStr);
byte fetchButtonsTriggerState();
char *processButtonInputs(boolean bEnabledFlag);
#endif
#if DISP7SEG_ENABLED_FLAG
void processWriteDisplayCmd(const char *valueStr);
void showProgramVersionOnDisplay();
void showTunerChannelOnDisplay();
void showNumericValueOnDisplay(uint16_t dispVal, boolean leftDpFlag,
boolean rightDpFlag, int dispTimeMs);
void showRssiValueOnDisplay(uint16_t rssiVal);
#if BUTTONS_ENABLED_FLAG
void showButtonModeOnDisplay(byte bModeVal, int dispTimeMs);
#endif //BUTTONS_ENABLED_FLAG
#endif //DISP7SEG_ENABLED_FLAG
// SETUP ----------------------------------------------------------------------------
void setup()
{
Serial.begin(SERIAL_BAUDRATE);
checkEepromIntegrity(); //check EEPROM; reset to defaults if needed
#if DISP7SEG_ENABLED_FLAG //detect if display is actually wired in:
displayConnectedFlag = disp7SegTestDisplayConnected();
if(displayConnectedFlag)
{ //display is actually wired in
disp7SegSetup(); //do hardware setup for 7-segment displays
showProgramVersionOnDisplay();
}
else //if no display then
pinMode(NODISP_ACTIVITY_PIN,OUTPUT); //enable activity-indicator pin
#else
pinMode(NODISP_ACTIVITY_PIN,OUTPUT);
#endif //DISP7SEG_ENABLED_FLAG
#if BUTTONS_ENABLED_FLAG
buttonsFunctionModeValue = loadButtonModeFromEeprom();
pinMode(UP_BUTTON_PIN,INPUT_PULLUP); //setup button inputs
pinMode(DOWN_BUTTON_PIN,INPUT_PULLUP);
#if BUTTONPINS_USEINTERRUPT_FLAG
installD2InterruptRoutine();
installD3InterruptRoutine();
#endif //BUTTONPINS_USEINTERRUPT_FLAG
#if DISP7SEG_ENABLED_FLAG
if(displayConnectedFlag)
showButtonModeOnDisplay(buttonsFunctionModeValue,1000);
#endif //DISP7SEG_ENABLED_FLAG
#endif //BUTTONS_ENABLED_FLAG
#ifdef PULLUP_1_PIN //if setup then configure
pinMode(PULLUP_1_PIN,INPUT_PULLUP); // unused inputs to have pullups
#endif
#ifdef PULLUP_2_PIN
pinMode(PULLUP_2_PIN,INPUT_PULLUP);
#endif
serialEchoFlag = true;
setRx5808MinTuneTimeMs(loadMinTuneTimeMsFromEeprom());
rx5808setup();
loadRssiMinMaxValsFromEeprom(); //load RSSI-scaling values from EEPROM
//load auto calib flag from EEPROM:
autoRssiCalibEnabledFlag = loadAutoRssiCalFlagFromEeprom();
loadListFreqsMHzArrFromEeprom(); //load array of 'L'-command freqs
//set tuner to freq value from EEPROM (or default if never saved):
setChanToFreqValFromEeprom();
if(!displayConnectedFlag)
{ //7-segment display not connected; set pins to select video output
#ifdef NODISP_PRIVSEL_PIN // (needed for diversity boards)
pinMode(NODISP_PRIVSEL_PIN,OUTPUT);
digitalWrite(NODISP_PRIVSEL_PIN,
isPriRx5808RssiInPinInUse() ? HIGH : LOW);
#endif
#ifdef NODISP_SECVSEL_PIN
pinMode(NODISP_SECVSEL_PIN,OUTPUT);
digitalWrite(NODISP_SECVSEL_PIN,
isPriRx5808RssiInPinInUse() ? LOW : HIGH);
#endif
}
Serial.println();
showRevisionInfo(false);
showCurrentFreqency();
if(listFreqsMHzArrCount > 0)
{ //frequency list (via 'L' command) not empty
Serial.print(F(" Using freq list: "));
showFreqsMHzList();
}
}
//Performs shutdown-cleanup actions (disconnects interrupts).
void doShutdownCleanup()
{
#if BUTTONPINS_USEINTERRUPT_FLAG
uninstallD2InterruptRoutine();
uninstallD3InterruptRoutine();
#endif
#if DISP7SEG_ENABLED_FLAG
if(displayConnectedFlag)
disp7SegShutdown();
#endif
}
// LOOP ----------------------------------------------------------------------------
void loop()
{
if(delayedSaveFreqToEepromFlag && millis() > delayedSaveFreqToEepromTime)
{ //save freq to EEPROM scheduled and time reached
delayedSaveFreqToEepromFlag = false;
saveCurrentFreqToEeprom();
}
//check for next line of serial input:
const char *nextSerialLineStr = getNextSerialLine();
const boolean serialAvailFlag = //serial chars or full line available
(getSerialInputAvailflag() || nextSerialLineStr != NULL);
//if report-RSSI char was received (and not continuous
// RSSI output in progess) then show RSSI/channel:
if(getDoReportRssiFlag() && !contRssiOutFlag)
showCurrentRssi(false,true);
const char *cmdStr;
#if BUTTONS_ENABLED_FLAG
//process button inputs (disabled if serial in or continuous RSSI):
cmdStr = processButtonInputs(!(serialAvailFlag || contRssiOutFlag));
if(cmdStr != NULL)
{ //new command via button action
if(monitorModeNextFlag)
{ //auto-tune-monitor mode is in progress then
monitorModeNextFlag = false; //stop monitor mode
cmdStr = NULL; //discard command
}
else if(serialEchoFlag)
{ //serial echo enabled
Serial.println(cmdStr); //show command
}
setSerialInputPromptFlag(); //setup to show prompt later
//clear any previous command (so can't be invoked via <Enter> key):
clearLastCommandChar();
}
#else
cmdStr = NULL;
#endif
if(contRssiOutFlag)
{ //continuous RSSI output enabled
if(!serialAvailFlag)
{ //no serial port input received
const uint16_t rVal = showCurrentRssi(contRssiListFlag,false);
updateRssiOutValue(rVal); //update analog-RSSI output
if(!displayConnectedFlag) //if no display then
updateActivityIndicator(true); //indicate "extra" activity
return;
}
else
{ //serial port input detected
contRssiOutFlag = false; //stop output
monitorModeNextFlag = false; //make sure both stopped
if(contRssiPrevFreqVal > (uint16_t)0) //if saved then
setTunerChannelToFreq(contRssiPrevFreqVal); //restore tuner freq
clearRssiOutput();
}
}
else if(monitorModeNextFlag)
{ //auto-tune-monitor mode enabled
if(!serialAvailFlag)
{ //no serial port input received
monitorAutoTuneNextChan();
updateRssiOutput(); //update analog-RSSI output
return;
}
else
{ //serial port input detected
monitorModeNextFlag = false; //stop output
contRssiOutFlag = false; //make sure both stopped
clearRssiOutput();
}
}
else //neither output mode enabled
updateRssiOutput(); //update analog-RSSI output
const boolean buttonInFlag = (cmdStr != NULL);
int sLen; //if no input via buttons then check serial input:
if(((buttonInFlag && (sLen=strlen(cmdStr)) > 0)) ||
((cmdStr=nextSerialLineStr) != NULL && ((sLen=strlen(cmdStr)) == 0 ||
(cmdStr[0] != SERIAL_PROMPT_CHAR && cmdStr[0] != SERIAL_LIGNORE_CHAR &&
strncasecmp(cmdStr,PROG_NAME_STR,4) != 0))))
{ //line of input data was received and doesn't begin with prompt,
// space or sign-on string that may be received if slave receiver
//if display not connected then always show "extra" activity:
if(!displayConnectedFlag)
updateActivityIndicator(true);
boolean displayActFlag = false; //set below for activity indicator
int p = 0;
while(p < sLen && cmdStr[p] == ' ')
++p; //ignore any leading spaces
if(p < sLen)
{ //command not empty
const char cmdChar = (char)toupper(cmdStr[p]);
switch(cmdChar)
{
case 'T': //tune receiver to given MHz value
processTuneCommand(&cmdStr[p+1]);
displayActFlag = true; //indicate activity on display
break;
case 'A': //auto-scan and tune to highest-RSSI channel
processAutoScanAndTuneCommand(&cmdStr[p+1]);
break;
case 'N': //auto-scan and tune to next channel
autoScanTuneNextChan(&cmdStr[p+1],true,true);
break;
case 'P': //auto-scan and tune to previous channel
autoScanTuneNextChan(&cmdStr[p+1],false,true);
break;
case 'M': //auto-scan and monitor channels
processMonitorModeCommand(&cmdStr[p+1]);
break;
case 'S': //scan and report channels with highest RSSI
processScanChannelsCommand(&cmdStr[p+1],false);
break;
case 'F': //scan and report RSSI for full set of channels
processScanChannelsCommand(&cmdStr[p+1],true);
break;
case 'L': //list of freq (MHz) values to be scanned
processFreqsMHzList(&cmdStr[p+1]);
displayActFlag = true; //indicate activity on display
break;
case 'R': //read RSSI
processShowRssiCmd(&cmdStr[p+1],false);
displayActFlag = true; //indicate activity on display
break;
case 'O': //continuous RSSI display
processShowRssiCmd(&cmdStr[p+1],true);
break;
case 'U': //increase turned frequency by one MHz
processOneMHzCommand(true,serialEchoFlag);
break;
case 'D': //decrease turned frequency by one MHz
processOneMHzCommand(false,serialEchoFlag);
break;
case 'B': //increment band on tuned-frequency code
processIncFreqCodeCommand(true,true,false);
break;
case 'C': //increment channel on tuned-frequency code
processIncFreqCodeCommand(false,true,false);
break;
case 'G': //show raw debug inputs values
processShowInputsCmd(&cmdStr[p+1]);
displayActFlag = true; //indicate activity on display
break;
#if DISP7SEG_ENABLED_FLAG
case '#': //toggle showing live RSSI on display
if(displayConnectedFlag)
{ //display is actually wired in
if(!displayRssiEnabledFlag)
displayRssiEnabledFlag = true;
else
{ //disable showing
displayRssiEnabledFlag = false;
disp7SegClearOvrDisplay(); //clear RSSI value immediately
}
}
break;
#endif
#if BUTTONS_ENABLED_FLAG
case '=': //set/show button mode value
processButtonModeCommand(&cmdStr[p+1]);
break;
#endif
case 'E': //serial echo on/off or echo text
processSerialEchoCommand(&cmdStr[p+1]);
displayActFlag = true; //indicate activity on display
break;
case 'V': //show program-version information
showRevisionInfo(true);
displayActFlag = true; //indicate activity on display
break;
case 'H': //show help screen
case '?':
showHelpInformation();
displayActFlag = true; //indicate activity on display
break;
case 'I': //show frequency-information screen
showFrequencyTable();
displayActFlag = true; //indicate activity on display
break;
case 'X': //process "extra" command
displayActFlag = processExtraCommand(&cmdStr[p+1]);
break;
default:
Serial.print(F(" Unrecognized command: "));
Serial.print(&cmdStr[p]);
Serial.println(F(" [Enter H for help]"));
displayActFlag = true; //indicate activity on display
}
}
else //received command line is empty,
{ // repeat last command (if one of those below)
const char lastCommandChar = getLastCommandChar();
if(lastCommandChar == 'R')
{
if(!repeatShowCurrentRssi()) //if not 'L' list then
displayActFlag = true; //indicate activity on display
}
else if(lastCommandChar == 'N')
autoScanTuneNextChan("",true,true);
else if(lastCommandChar == 'P')
autoScanTuneNextChan("",false,true);
else if(lastCommandChar == 'B')
processIncFreqCodeCommand(true,true,false);
else if(lastCommandChar == 'C')
processIncFreqCodeCommand(false,true,false);
else if(lastCommandChar == 'G')
{
processShowInputsCmd("");
displayActFlag = true; //indicate activity on display
}
else //empty command line and no repeat command
displayActFlag = true; //indicate activity on display
}
//if display connected and flag was set then show "extra" activity:
if(displayConnectedFlag && displayActFlag && !buttonInFlag)
updateActivityIndicator(true);
}
else //no input-command data received
updateActivityIndicator(false); //indicate normal activity
}
//Processes "extra" (X) command.
// Returns true if activity should be indicated on display; false if not.
boolean processExtraCommand(const char *cmdStr)
{
boolean retFlag = true;
const int sLen = strlen(cmdStr);
int p = 0;
while(cmdStr[p] == ' ' && ++p < sLen); //ignore any leading spaces
if(p >= sLen)
{ //no parameters given
showExtraHelpInformation();
return retFlag;
}
const char cmdChar = (char)toupper(cmdStr[p]);
switch(cmdChar)
{
case 'J': //set or show raw-RSSI-scaling values
processRawRssiMinMaxCommand(&cmdStr[p+1]);
break;
case 'A': //disable/enable/restart auto RSSI calibration
processEnableAutoRssiCalibCmd(&cmdStr[p+1]);
break;
case 'T': //set or show RX5808 min-tune time (ms)
processMinTuneTimeCommand(&cmdStr[p+1]);
break;
case 'M': //set or show minimum-RSSI value for scans
processMinRssiCommand(&cmdStr[p+1]);
break;
case 'I': //set or show monitor-mode interval
processMonitorIntervalCmd(&cmdStr[p+1]);
break;
case 'U': //set or show Unit-ID string
processUnitIdCommand(&cmdStr[p+1]);
break;
case 'R': //read and show RSSI (with channel info)
showCurrentRssi(false,true);
retFlag = !displayConnectedFlag; //indicator if no display
break;
case 'L': //show frequency list for preset name
processShowFreqPresetListCmd(&cmdStr[p+1]);
break;
case 'P': //show all frequency-list presets
freqListPresetShowAllSets();
break;
case 'B': //decrement band on tuned-frequency code
processIncFreqCodeCommand(true,false,false);
break;
case 'C': //decrement channel on tuned-frequency code
processIncFreqCodeCommand(false,false,false);
break;
case 'F': //perform and report full scan of all freqs
fullScanShowRssiValues();
break;
case 'X': //show index values for frequencies (devel)
processListTranslateInfoCmd(&cmdStr[p+1]);
break;
#if DISP7SEG_ENABLED_FLAG
case 'D': //show given chars on 7-segment displays
if(displayConnectedFlag)
{
processWriteDisplayCmd(&cmdStr[p+1]);
retFlag = false; //no indicator (interferes with display)
}
break;
#endif
case 'K': //check/report calc vs table values (debug)
checkReportTableValues();
break;
case 'Z': //soft reboot
processSoftRebootCommand(&cmdStr[p+1]);
break;
case 'H': //show extra help screen
case '?':
showExtraHelpInformation();
break;
default:
Serial.print(F(" Unrecognized 'extra' command: "));
Serial.print(&cmdStr[p]);
Serial.println(F(" [Enter XH for help]"));
}
return retFlag;
}
//Displays help screen.
void showHelpInformation()
{
showRevisionInfo(false);
Serial.println(F(" Commands:"));
Serial.println(F(" T [freq] : Tune receiver to given MHz or XX code"));
Serial.println(F(" A : Auto-scan and tune to highest-RSSI channel"));
Serial.println(F(" N [minRSSI] : Auto-scan and tune to next channel"));
Serial.println(F(" P [minRSSI] : Auto-scan and tune to previous channel"));
Serial.println(F(" M [seconds] : Auto-scan and monitor channels"));
Serial.println(F(" S [minRSSI] : Scan and report channels with highest RSSI"));
Serial.println(F(" F [minRSSI] : Scan and report RSSI for full set of channels"));
Serial.println(F(" L [list] : List of freqs of interest (LH for help)"));
Serial.println(F(" R : Read RSSI for current channel (RL for 'L' freqs)"));
Serial.println(F(" O : Continuous RSSI display (OL for 'L' freqs)"));
Serial.println(F(" U / D : Change tuned frequency up/down by one MHz"));
Serial.println(F(" B / C : Increment band/channel on tuned-frequency code"));
Serial.println(F(" X : Extra commands (XH for help)"));
#if DISP7SEG_ENABLED_FLAG
if(displayConnectedFlag)
Serial.println(F(" # : Toggle showing live RSSI on display"));
#endif
#if BUTTONS_ENABLED_FLAG
Serial.println(F(" = : Set or show button mode value"));
#endif
Serial.println(F(" V : Show program-version information"));
Serial.println(F(" I : Show frequency-table information"));
Serial.println(F(" H or ? : Show help information"));
}
//Displays help screen for 'X' commands.
void showExtraHelpInformation()
{
Serial.println(F(" Extra commands:"));
Serial.println(F(" XJ [min,max] : Set or show RSSI-scaling values"));
Serial.println(F(" XJ default : Set RSSI-scaling values to defaults"));
Serial.println(F(" XA [0|1|R] : Disable/enable/restart auto RSSI calib"));
Serial.println(F(" XT [timeMs] : Set or show RX5808 min-tune time (ms)"));
Serial.println(F(" XM [minRSSI] : Set or show minimum RSSI for scans"));
Serial.println(F(" XI [seconds] : Set or show monitor-mode interval"));
Serial.println(F(" XU [text] : Set or show Unit-ID string"));
Serial.println(F(" XR or ~ : Read and show RSSI (with channel info)"));
Serial.println(F(" XB / XC : Decrement band/channel on tuned-freq code"));
Serial.println(F(" XF : Perform and report full scan of all freqs"));
#if DISP7SEG_ENABLED_FLAG
if(displayConnectedFlag)
Serial.println(F(" XD [chars] : Show given chars on display"));
#endif
Serial.println(F(" XP : Show all frequency-list presets"));
Serial.println(F(" XL [name] : Show frequency list for preset name"));
Serial.println(F(" XX [list] : Show index values for frequencies (devel)"));
Serial.println(F(" XK : Show frequency table values (devel)"));
Serial.println(F(" XZ [defaults] : Perform soft program reboot"));
Serial.println(F(" X, XH or X? : Show extra help information"));
}
//Displays help screen for 'L' command.
void showListCmdHelpInformation()
{
Serial.println(F(" Frequency-list command:"));
Serial.println(F(" L [list] : Set list of freq (MHz) values of interest"));
Serial.println(F(" L : Show list of freq (MHz) values of interest"));
Serial.println(F(" L 0 : Clear list of freq values of interest"));
Serial.println(F(" L +values : Add values to current list"));
Serial.println(F(" L -values : Remove values from current list"));
Serial.println(F(" L S : Load list of freqs via RSSI scan"));
Serial.println(F(" L H : Show help information for 'L' command"));
Serial.println(F(" When a list is entered, the frequencies in the list will be the only ones"));
Serial.println(F(" scanned and selected by the 'A', 'S', 'N', 'P' and 'M' commands. The 'RL'"));
Serial.println(F(" and 'OL' commands will scan and display RSSI values for the frequencies in"));
Serial.println(F(" the list. Entering 'L 0' will clear the list. The '+' and '-' operators"));
Serial.println(F(" may be used to add and remove frequencies, and may be mixed together"));
Serial.println(F(" (i.e., 'L +5740 -5905'). The 'L S' command will load the list with the"));
Serial.println(F(" frequency set returned by the last scan ('S' command), or will perform a"));
Serial.println(F(" scan and load the detected values. Frequency-list-preset names may also"));
Serial.println(F(" be used as parameters to the 'L' command (i.e., 'L IMD5'). Available"));
Serial.println(F(" presets may be displayed via the 'XP' command."));
}
//Displays frequency table.
void showFrequencyTable()
{
Serial.println(F(" Frequency Table 1 2 3 4 5 6 7 8"));
Serial.println(F(" Frequency band A 5865 5845 5825 5805 5785 5765 5745 5725"));
Serial.println(F(" Frequency band B 5733 5752 5771 5790 5809 5828 5847 5866"));
Serial.println(F(" Frequency band E 5705 5685 5665 5645 5885 5905 5925 5945"));
Serial.println(F(" Frequency band F 5740 5760 5780 5800 5820 5840 5860 5880"));
Serial.println(F(" Frequency band R 5658 5695 5732 5769 5806 5843 5880 5917"));
Serial.println(F(" Frequency band L 5362 5399 5436 5473 5510 5547 5584 5621"));
}
//Shows the "Unable to parse value" message via serial.
void showUnableToParseValueMsg()
{
Serial.print(F(" Unable to parse value: "));
}
//Shows program-version information.
// dispInfoFlag: true to include information about 7-segment displays.
void showRevisionInfo(boolean dispInfoFlag)
{
Serial.print(F(PROG_NAME_STR));
Serial.print(F(" Version "));
Serial.print(F(PROG_VERSION_STR));
if(!isUnitIdFromEepromEmpty())
{ //Unit-ID string not empty; show it
Serial.print(F(", Unit ID: "));
showUnitIdFromEeprom();
}
Serial.println();
if(dispInfoFlag)
{ //show display information
Serial.print(F(" Display: "));
#if DISP7SEG_ENABLED_FLAG
if(displayConnectedFlag)
Serial.print(F("Connected"));
else
Serial.print(F("Not connected"));
#else
Serial.print(F("Not supported (disabled via build option)"));
#endif
Serial.println();
}
}
//Sets tuner frequency to given frequency (MHz) or frequency code word,
// and sends status text to the serial port.
// freqInMHz: Frequency value in MHz.
// codeVal: Two-character frequency code packed into 2-byte word
// (high byte is band character), or zero for none.
void doTuneToFreqMHzOrCode(uint16_t freqInMHz, uint16_t codeVal)
{
uint16_t freqMhzOrCode;
if(serialEchoFlag)
{ //more output if echo enabled
Serial.print(F(" Tuning to frequency "));
Serial.print((int)freqInMHz);
Serial.print(F("MHz"));
if(codeVal > (uint16_t)0)
{ //frequency-code value available
freqMhzOrCode = codeVal; //tune to code-word value
Serial.print(" (");
Serial.print((char)(codeVal >> (uint16_t)8));
Serial.print((char)codeVal);
Serial.print(')');
}
else
freqMhzOrCode = freqInMHz; //tune to freq in MHz
Serial.println();
}
else //setup to tune to code word (if available) or freq in MHz:
freqMhzOrCode = (codeVal > (uint16_t)0) ? codeVal : freqInMHz;
setTunerChannelToFreq(freqMhzOrCode);
}
//Processes the 'tune' command with the given frequency (MHz) string.
void processTuneCommand(const char *valueStr)
{
const int sLen = strlen(valueStr);
if(sLen <= 0)
{ //no parameter value; show current
showCurrentFreqency();
return;
}
int iVal;
if(sLen > 2 && convStrToInt(valueStr,&iVal))
{ //more than 2 characters and numeric value parsed OK
if(iVal < MIN_CHANNEL_MHZ || iVal > MAX_CHANNEL_MHZ)
{ //range check failed
Serial.print(F(" Value out of range: "));
Serial.println(iVal);
return;
}
//see if frequency corresponds to a frequency code:
const uint16_t codeVal = freqInMhzToFreqCode((uint16_t)iVal,NULL);
//tune and send status to serial port:
doTuneToFreqMHzOrCode((uint16_t)iVal,codeVal);
//save tuned freq to EEPROM 3 secs after last set:
scheduleDelayedSaveFreqToEeprom(3);
return;
}
//attempt to process as 2-character frequency code:
const uint16_t codeVal = freqCodeStrToCodeWord(valueStr);
//get freq in MHz for code word:
const uint16_t freqInMHz = freqCodeWordToFreqInMhz(codeVal);
if(freqInMHz > (uint16_t)0)
{ //2-character frequency code parsed and converted OK
//tune and send status to serial port:
doTuneToFreqMHzOrCode(freqInMHz,codeVal);
//save tuned freq to EEPROM 3 secs after last set:
scheduleDelayedSaveFreqToEeprom(3);
return;
}
//parsing failed
showUnableToParseValueMsg();
Serial.println(valueStr);
}
//Displays the current frequency.
void showCurrentFreqency()
{
if(serialEchoFlag)
{ //more output if echo enabled
Serial.print(F(" Current frequency is "));
const uint16_t freqVal = getCurrentFreqInMhz();
Serial.print((int)freqVal);
Serial.print("MHz");
const uint16_t codeVal = getCurrentFreqCodeWord();
if(codeVal > (uint16_t)0)
{ //frequency-code value available; show it
Serial.print(" (");
Serial.print((char)(codeVal >> (uint16_t)8));
Serial.print((char)(codeVal & (uint16_t)0x7F));
Serial.print(')');
}
Serial.println();
}
else
{ //serial echo not enabled; show simple output
Serial.print(' ');
Serial.println((int)getCurrentFreqInMhz());
}
}
//Processes the given list of frequency (MHz) values and enters them
// into the 'scanFreqsMHzArr[]' array.
void processFreqsMHzList(const char *listStr)
{
const int sLen = strlen(listStr);
int sPos = 0;
while(listStr[sPos] == ' ' && ++sPos < sLen); //ignore any leading spaces
if(sPos >= sLen)
{ //no parameters given
showFreqsMHzList(); //show current list
return;
}
int numItems;
if(listStr[sPos] == 'H' || listStr[sPos] == 'h' || listStr[sPos] == '?')
{
showListCmdHelpInformation();
return;
}
if((listStr[sPos] == 'S' || listStr[sPos] == 's') && sPos+1 == sLen)
{ //use frequency values from band scan
numItems = listFreqsMHzArrCount; //save current count
listFreqsMHzArrCount = 0; //clear any existing entries
if(idxSortedSelArrCount <= 0 ||
millis() >= lastNextTuneScanTime + NEXT_CHAN_RESCANSECS*1000)
{ //no freqs available from previous scan or too much time elapsed
if(!scanChannelsAndReport(sessionDefMinRssiLevel, //do band scan now
sessionDefMinRssiLevel,false,true,serialEchoFlag))
{ //no channels with high enough RSSI found
listFreqsMHzArrCount = numItems; //keep existing list (if any)
return;
}
}
if(idxSortedSelArrCount <= 0)
return; //abort if no freqs available (shouldn't happen)
for(numItems=0; numItems<idxSortedSelArrCount; ++numItems)
{ //for each index value in array; fetch and copy frequency value
listFreqsMHzArr[numItems] = getChannelFreqTableEntry(
idxSortedSelectedArr[numItems]);
}
idxSortedSelArrCount = 0; //idxSortedSelectedArr[] values no longer valid
}
else
{ //don't use frequency values from band scan
idxSortedSelArrCount = 0; //idxSortedSelectedArr[] values no longer valid
const int listStrLen = strlen(listStr);
boolean plusFlag = false, minusFlag = false, firstFlag = true;
int val, ePos, psetCount;
char ch;
numItems = listFreqsMHzArrCount; //setup to append entries (for now)
while(true)
{ //for each item in list
while(sPos < listStrLen)
{ //scan through any +/-, whitespace or comma characters
ch = listStr[sPos];
if(ch == '+') //handle any leading +/- sign
{
plusFlag = true; //indicate '+' sign (only matters for 1st)
minusFlag = false; //set "adding" mode
}
else if(ch == '-')
minusFlag = true; //set "removing" mode
else if(ch != ' ' && ch != '\t' && ch != ',')
break; //if not whitespace or comma then exit inner loop
++sPos;
}
if(sPos >= listStrLen) //if end of input string then
break; //exit loop
//if first entry & no leading +/- then remove all current entries:
if(firstFlag && !plusFlag && !minusFlag)
numItems = 0;
ePos = sPos; //scan through entry characters
while(ePos < listStrLen && (ch=listStr[++ePos]) != ' ' &&
ch != '\t' && ch != ',' && ch != '+' && ch != '-');
if(convStrToInt(&listStr[sPos],&val))
{ //successfully parsed numeric value
if(val < MIN_CHANNEL_MHZ || val > MAX_CHANNEL_MHZ)
{ //value out of range
if(val != 0 || !firstFlag || plusFlag || minusFlag)
{ //not leading zero value (for list clear)
Serial.print(F(" Entered value out of range: "));
Serial.println(val);
}
break;
}
if(minusFlag)
{ //removing value from list
numItems = removeValueFromArray(
listFreqsMHzArr,numItems,(uint16_t)val);
}
else
{ //adding value to list
numItems = removeValueFromArray( //if already in list then remove
listFreqsMHzArr,numItems,(uint16_t)val);
if(numItems >= LISTFREQMHZ_ARR_SIZE)
{
Serial.println(F(" Too many values specified"));
break;
}
listFreqsMHzArr[numItems++] = (uint16_t)val;
}
}
else if((psetCount=freqListPresetLoadByName(&listStr[sPos],
&listFreqsMHzArr[numItems],LISTFREQMHZ_ARR_SIZE-numItems)) > 0)
{ //successfully parsed entry as frequency-preset name
if(minusFlag)
{ //removing values from list
Serial.println(F(" Removal via freq-preset name not supported"));
break;
}
else
{ //adding values to list
numItems += psetCount; //increase list size by freq values added
if(numItems >= LISTFREQMHZ_ARR_SIZE)
{
Serial.println(F(" Reached maximum list size"));
break;
}
}
}
else
{ //unable to parse entry as numeric or as frequency-preset name
if(firstFlag)
{ //this is the first entry
Serial.print(F(" Error processing input: "));
Serial.print(&listStr[sPos]);
if(serialEchoFlag)
Serial.print(F(" [Enter LH for help]"));
Serial.println();
return; //don't show list contents below
}
//not first entry
Serial.print(F(" Error processing value(s): "));
Serial.print(&listStr[sPos]);
if(serialEchoFlag)
Serial.print(F(" [Enter LH for help]"));
Serial.println();
if(!serialEchoFlag) //if serial echo off then
return; //don't follow error msg with numItems display
break;
}
sPos = ePos;
firstFlag = false;
}
}
listFreqsMHzArrCount = numItems;
saveListFreqsMHzArrToEeprom(); //store new list in EEPROM
Serial.print(' '); //start with leading space (so ignored by slave recvr)
if(serialEchoFlag)
{ //echo on; show list of frequencies entered
if(numItems > 0)
{
Serial.print(numItems);
Serial.print(F(" value"));
if(numItems != 1)
Serial.print('s');
Serial.print(F(": L"));
showUint16ArrayList(listFreqsMHzArr,numItems);
Serial.println();
}
else
Serial.println(F("List is empty"));
}
else
Serial.println(numItems);
}
//Shows the current list of frequency (MHz) values in the
// 'scanFreqsMHzArr[]' array.
void showFreqsMHzList()
{
Serial.print(' '); //start with leading space (so ignored by slave recvr)
if(listFreqsMHzArrCount > 0)
{
showUint16ArrayList(listFreqsMHzArr,listFreqsMHzArrCount);
Serial.println();
}
else
{
if(serialEchoFlag)
Serial.println(F("List is empty"));
else
Serial.println('0');
}
}
//Processes the show-RSSI command. May have 'L' parameter to show
// RSSI values for frequencies entered via 'L' command.
// contFlag: true if 'O' command (continuous output); false if 'R' command.
void processShowRssiCmd(const char *valueStr, boolean contFlag)
{