adding nRF52840 (feather) example
This commit is contained in:
		
							parent
							
								
									70bd364473
								
							
						
					
					
						commit
						1500d07213
					
				| 
						 | 
					@ -0,0 +1,704 @@
 | 
				
			||||||
 | 
					/* Hamshield
 | 
				
			||||||
 | 
					 * Example: AppSerialController_nRF52840
 | 
				
			||||||
 | 
					 * This is a simple example to demonstrate the HamShield working 
 | 
				
			||||||
 | 
					 * with an Adafruit Feather nRF52840 Express
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * HamShield to Feather Connections:
 | 
				
			||||||
 | 
					 *  SPKR - Feather A0
 | 
				
			||||||
 | 
					 *  MIC - Feather D11
 | 
				
			||||||
 | 
					 *  CLK - Feather D5
 | 
				
			||||||
 | 
					 *  nCS - Feather D6
 | 
				
			||||||
 | 
					 *  DAT - Feather D9
 | 
				
			||||||
 | 
					 *  GND - Feather GND
 | 
				
			||||||
 | 
					 *  VCC - Feather 3.3V
 | 
				
			||||||
 | 
					 *  
 | 
				
			||||||
 | 
					 * Connect the HamShield to your Feather as above. 
 | 
				
			||||||
 | 
					 * Screw the antenna into the HamShield RF jack. Plug a pair 
 | 
				
			||||||
 | 
					 * of headphones into the HamShield.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Connect the Feather nRF52840 Express to your computer via 
 | 
				
			||||||
 | 
					 * a USB Micro B cable. After uploading this program to 
 | 
				
			||||||
 | 
					 * your Feather, open the Serial Monitor. You should see some
 | 
				
			||||||
 | 
					 * text displayed that documents the setup process.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Once the Feather is set up and talking to the HamShield,
 | 
				
			||||||
 | 
					 * you can control it over USB-Serial or BLE-Serial(UART).
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Try using Adafruit's Bluefruit app to connect to the Feather.
 | 
				
			||||||
 | 
					 * Once you're connected, you can control the HamShield using
 | 
				
			||||||
 | 
					 * the same commands you'd use over USB-Serial. The response to
 | 
				
			||||||
 | 
					 * all commands will be echoed to both USB-Serial and BLE-Serial(UART).
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Commands:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Mode           ASCII       Description                                                                                                                                  
 | 
				
			||||||
 | 
					-------------- ----------- -------------------------------------------------------------------------------------------------------------------------------------------- 
 | 
				
			||||||
 | 
					Transmit       space       Space must be received at least every 500 mS                                                                                                 
 | 
				
			||||||
 | 
					Receive        not space   If space is not received and/or 500 mS timeout of space occurs, unit will go into receive mode                                               
 | 
				
			||||||
 | 
					Frequency      F<freq>;    Set the receive frequency in KHz, if offset is disabled, this is the transmit frequency                                                      
 | 
				
			||||||
 | 
					Morse Out      M<text>;    A small buffer for morse code (32 chars)
 | 
				
			||||||
 | 
					Morse In       N;          Sets mode to Morse In, listening for Morse
 | 
				
			||||||
 | 
					Power level    P<level>;   Set the power amp level, 0 = lowest, 15 = highest
 | 
				
			||||||
 | 
					Enable Offset  R<state>;   1 turns on repeater offset mode, 0 turns off repeater offset mode
 | 
				
			||||||
 | 
					Squelch        S<level>;   Set the squelch level
 | 
				
			||||||
 | 
					TX Offset      T<freq>;    The absolute frequency of the repeater offset to transmit on in KHz
 | 
				
			||||||
 | 
					RSSI           ?           Respond with the current receive level in - dBm (no sign provided on numerical response)
 | 
				
			||||||
 | 
					Voice Level    ^           Respond with the current voice level (VSSI), only valid when transmitting
 | 
				
			||||||
 | 
					DTMF Out       D<vals>;    A small buffer for DTMF out (only 0-9,A,B,C,D,*,# accepted)
 | 
				
			||||||
 | 
					DTMF In        B;          Sets mode to DTMF In, listening for DTMF
 | 
				
			||||||
 | 
					PL Tone Tx     A<val>;     Sets PL tone for TX, value is tone frequency in Hz (float), set to 0 to disable
 | 
				
			||||||
 | 
					PL Tone Rx     C<val>;     Sets PL tone for RX, value is tone frequency in Hz (float), set to 0 to disable
 | 
				
			||||||
 | 
					Volume 1       V1<val>;    Set volume 1 (value between 0 and 15)
 | 
				
			||||||
 | 
					Volume 2       V2<val>;    Set volume 2 (value between 0 and 15)
 | 
				
			||||||
 | 
					KISS TNC       K;          Move to KISS TNC mode (send ^; to move back to normal mode). NOT IMPELEMENTED YET
 | 
				
			||||||
 | 
					Normal Mode    _           Move to Normal mode from any other mode (except TX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Responses:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Condition    ASCII      Description
 | 
				
			||||||
 | 
					------------ ---------- -----------------------------------------------------------------
 | 
				
			||||||
 | 
					Startup      *<code>;   Startup and shield connection status
 | 
				
			||||||
 | 
					Success      !;         Generic success message for command that returns no value
 | 
				
			||||||
 | 
					Error        X<code>;   Indicates an error code. The numerical value is the type of error
 | 
				
			||||||
 | 
					Value        :<value>;  In response to a query
 | 
				
			||||||
 | 
					Status       #<value>;  Unsolicited status message
 | 
				
			||||||
 | 
					Debug Msg    @<text>;   32 character debug message
 | 
				
			||||||
 | 
					Rx Msg       R<text>;   up to 32 characters of received message, only if device is in DTMF or Morse Rx modes
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Note that the following are not yet implemented
 | 
				
			||||||
 | 
					// TODO: change get_value so it's intuitive
 | 
				
			||||||
 | 
					// TODO: Squelch open and squelch shut independently controllable
 | 
				
			||||||
 | 
					// TODO: pre/de emph filter
 | 
				
			||||||
 | 
					// TODO: walkie-talkie
 | 
				
			||||||
 | 
					// TODO: KISS TNC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <bluefruit.h>
 | 
				
			||||||
 | 
					#include <stdarg.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <HamShield.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BLE Service
 | 
				
			||||||
 | 
					BLEDis  bledis;  // device information
 | 
				
			||||||
 | 
					BLEUart bleuart; // uart over ble
 | 
				
			||||||
 | 
					BLEBas  blebas;  // battery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// create object for radio
 | 
				
			||||||
 | 
					HamShield radio(6,5,9);
 | 
				
			||||||
 | 
					// To use non-standard pins, use the following initialization
 | 
				
			||||||
 | 
					//HamShield radio(ncs_pin, clk_pin, dat_pin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LED_PIN 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MIC_PIN A1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {TX, NORMAL, DTMF, MORSE, KISS};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int state = NORMAL;
 | 
				
			||||||
 | 
					bool rx_ctcss = false;
 | 
				
			||||||
 | 
					bool muted = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int txcount = 0;
 | 
				
			||||||
 | 
					long timer = 0; // Transmit timer to track timeout (send space to reset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long freq = 432100; // 70cm calling frequency, receive frequency and default transmit frequency
 | 
				
			||||||
 | 
					long tx_freq = 0; // transmit frequency if repeater is on
 | 
				
			||||||
 | 
					int pwr = 0; // tx power
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char cmdbuff[32] = "";
 | 
				
			||||||
 | 
					int temp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool repeater = false; // true if transmit and receive operate on different frequencies
 | 
				
			||||||
 | 
					char pl_rx_buffer[32]; // pl tone rx buffer
 | 
				
			||||||
 | 
					char pl_tx_buffer[32]; // pl tone tx buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float ctcssin = 0;
 | 
				
			||||||
 | 
					float ctcssout = 0;
 | 
				
			||||||
 | 
					int cdcssin = 0;
 | 
				
			||||||
 | 
					int cdcssout = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void setup() {
 | 
				
			||||||
 | 
					  // NOTE: if not using PWM out (MIC pin), it should be held low to avoid tx noise
 | 
				
			||||||
 | 
					  pinMode(MIC_PIN, OUTPUT);
 | 
				
			||||||
 | 
					  digitalWrite(MIC_PIN, LOW);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // initialize serial communication
 | 
				
			||||||
 | 
					  Serial.begin(115200);
 | 
				
			||||||
 | 
					  while (!Serial) delay(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Setup the BLE LED to be enabled on CONNECT
 | 
				
			||||||
 | 
					  // Note: This is actually the default behaviour, but provided
 | 
				
			||||||
 | 
					  // here in case you want to control this LED manually via PIN 19
 | 
				
			||||||
 | 
					  Bluefruit.autoConnLed(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Config the peripheral connection with maximum bandwidth 
 | 
				
			||||||
 | 
					  // more SRAM required by SoftDevice
 | 
				
			||||||
 | 
					  // Note: All config***() function must be called before begin()
 | 
				
			||||||
 | 
					  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Bluefruit.begin();
 | 
				
			||||||
 | 
					  // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
 | 
				
			||||||
 | 
					  Bluefruit.setTxPower(4);
 | 
				
			||||||
 | 
					  Bluefruit.setName("MyBlueHam");
 | 
				
			||||||
 | 
					  //Bluefruit.setName(getMcuUniqueID()); // useful testing with multiple central connections
 | 
				
			||||||
 | 
					  Bluefruit.setConnectCallback(connect_callback);
 | 
				
			||||||
 | 
					  Bluefruit.setDisconnectCallback(disconnect_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Configure and Start Device Information Service
 | 
				
			||||||
 | 
					  bledis.setManufacturer("Enhanced Radio Devices");
 | 
				
			||||||
 | 
					  bledis.setModel("BlueHam");
 | 
				
			||||||
 | 
					  bledis.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Configure and Start BLE Uart Service
 | 
				
			||||||
 | 
					  bleuart.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Start BLE Battery Service
 | 
				
			||||||
 | 
					  blebas.begin();
 | 
				
			||||||
 | 
					  blebas.write(100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set up and start advertising
 | 
				
			||||||
 | 
					  startAdv();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  delay(100);  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SerialWrite(";;;;;;;;;;;;;;;;;;;;;;;;;;");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int result = radio.testConnection();
 | 
				
			||||||
 | 
					  SerialWrite("*%d;\n", result);
 | 
				
			||||||
 | 
					  radio.initialize(); // initializes automatically for UHF 12.5kHz channel
 | 
				
			||||||
 | 
					  radio.frequency(freq);
 | 
				
			||||||
 | 
					  radio.setVolume1(0xF);
 | 
				
			||||||
 | 
					  radio.setVolume2(0xF);
 | 
				
			||||||
 | 
					  radio.setModeReceive();
 | 
				
			||||||
 | 
					  radio.setTxSourceMic();
 | 
				
			||||||
 | 
					  radio.setRfPower(pwr);
 | 
				
			||||||
 | 
					  radio.setSQLoThresh(-80);
 | 
				
			||||||
 | 
					  radio.setSQHiThresh(-70);
 | 
				
			||||||
 | 
					  radio.setSQOn();
 | 
				
			||||||
 | 
					  SerialWrite("*START;\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void startAdv(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Advertising packet
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.addTxPower();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Include bleuart 128-bit uuid
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.addService(bleuart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Secondary Scan Response packet (optional)
 | 
				
			||||||
 | 
					  // Since there is no room for 'Name' in Advertising packet
 | 
				
			||||||
 | 
					  Bluefruit.ScanResponse.addName();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /* Start Advertising
 | 
				
			||||||
 | 
					   * - Enable auto advertising if disconnected
 | 
				
			||||||
 | 
					   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
 | 
				
			||||||
 | 
					   * - Timeout for fast mode is 30 seconds
 | 
				
			||||||
 | 
					   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * For recommended advertising interval
 | 
				
			||||||
 | 
					   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.restartOnDisconnect(true);
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
 | 
				
			||||||
 | 
					  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void loop() {  
 | 
				
			||||||
 | 
					  // TODO: loop fixing based on serialtransciever!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  char c = 0;
 | 
				
			||||||
 | 
					  bool ble_serial = false;
 | 
				
			||||||
 | 
					  if (Serial.available()) {
 | 
				
			||||||
 | 
					    Serial.readBytes(&c, 1);
 | 
				
			||||||
 | 
					  } else if (bleuart.available()) {
 | 
				
			||||||
 | 
					    c = (char) bleuart.read();
 | 
				
			||||||
 | 
					    ble_serial = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO: BLE
 | 
				
			||||||
 | 
					  if(c != 0) { 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int text = c; // get the first char to see what the upcoming command is
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (state) {
 | 
				
			||||||
 | 
					      // we handle commands differently based on what state we're in
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      case TX:
 | 
				
			||||||
 | 
					         // we're currently transmitting
 | 
				
			||||||
 | 
					         // if we got a space, reset our transmit timeout
 | 
				
			||||||
 | 
					         if(text == ' ') { timer = millis();}
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case NORMAL:
 | 
				
			||||||
 | 
					        switch(text) {
 | 
				
			||||||
 | 
					          case ' ':  // space - transmit
 | 
				
			||||||
 | 
					            if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); }
 | 
				
			||||||
 | 
					            muted = false; // can't mute (for PL tones) during tx
 | 
				
			||||||
 | 
					            radio.setUnmute(); 
 | 
				
			||||||
 | 
					            radio.setModeTransmit();
 | 
				
			||||||
 | 
					            state = TX;
 | 
				
			||||||
 | 
					            SerialWrite("#TX,ON;\n");
 | 
				
			||||||
 | 
					            timer = millis();
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case '?': // ? - RSSI
 | 
				
			||||||
 | 
					            SerialWrite(":%d;\n", radio.readRSSI());
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					               
 | 
				
			||||||
 | 
					          case '^': // ^ - VSSI (voice) level
 | 
				
			||||||
 | 
					            SerialWrite(":%d;\n", radio.readVSSI());
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					               
 | 
				
			||||||
 | 
					          case 'F': // F - frequency
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            freq = atol(cmdbuff);
 | 
				
			||||||
 | 
					            if(radio.frequency(freq) == true) { 
 | 
				
			||||||
 | 
					              SerialWrite("@%d;!;\n", freq); 
 | 
				
			||||||
 | 
					            } else { 
 | 
				
			||||||
 | 
					              SerialWrite("X1;\n"); 
 | 
				
			||||||
 | 
					            } 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					               
 | 
				
			||||||
 | 
					          case 'P': // P - power level
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            temp = atol(cmdbuff);
 | 
				
			||||||
 | 
					            radio.setRfPower(temp);
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case 'S': // S - squelch
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            temp = atol(cmdbuff);
 | 
				
			||||||
 | 
					            if (temp < -2 && temp > -130) {
 | 
				
			||||||
 | 
					              radio.setSQLoThresh(temp);
 | 
				
			||||||
 | 
					              radio.setSQHiThresh(temp+2);
 | 
				
			||||||
 | 
					              radio.setSQOn();
 | 
				
			||||||
 | 
					              SerialWrite("%d!;\n", temp);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              SerialWrite("X!;\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case 'R': // R - repeater offset mode
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            temp = atol(cmdbuff);
 | 
				
			||||||
 | 
					            if(temp == 0) { repeater = 0; }
 | 
				
			||||||
 | 
					            if(temp == 1) { repeater = 1; }
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case 'T': // T - transmit offset 
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            tx_freq = atol(cmdbuff);
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case 'M': // M - Morse
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); }
 | 
				
			||||||
 | 
					            muted = false; // can't mute (for PL tones) during tx
 | 
				
			||||||
 | 
					            radio.setUnmute(); 
 | 
				
			||||||
 | 
					            radio.setModeTransmit();
 | 
				
			||||||
 | 
					            delay(300);
 | 
				
			||||||
 | 
					            radio.morseOut(cmdbuff);
 | 
				
			||||||
 | 
					            if(repeater == true) { radio.frequency(freq); } 
 | 
				
			||||||
 | 
					            radio.setModeReceive();
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break; 
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					          case 'N': // N - set to Morse in Mode
 | 
				
			||||||
 | 
					            morse_rx_setup();
 | 
				
			||||||
 | 
					            state = MORSE;
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					               
 | 
				
			||||||
 | 
					          case 'D': // D - DTMF Out
 | 
				
			||||||
 | 
					            dtmfSetup();
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            dtmf_out(cmdbuff);
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'B': // B - set to DTMF in Mode
 | 
				
			||||||
 | 
					            dtmfSetup();
 | 
				
			||||||
 | 
					            radio.enableDTMFReceive();
 | 
				
			||||||
 | 
					            state = DTMF;
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'A': // A - TX PL Tone configuration command
 | 
				
			||||||
 | 
					            pl_tone_tx();
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          case 'C': // C - RX PL Tone configuration command
 | 
				
			||||||
 | 
					            pl_tone_rx();
 | 
				
			||||||
 | 
					            SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'V': // V - set volume
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            temp = cmdbuff[0];
 | 
				
			||||||
 | 
					            if (temp == 0x31) {
 | 
				
			||||||
 | 
					              temp = atol(cmdbuff + 1);
 | 
				
			||||||
 | 
					              radio.setVolume1(temp);
 | 
				
			||||||
 | 
					              SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            } else if (temp == 0x32) {
 | 
				
			||||||
 | 
					              temp = atol(cmdbuff + 1);
 | 
				
			||||||
 | 
					              radio.setVolume2(temp);
 | 
				
			||||||
 | 
					              SerialWrite("!;\n"); 
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              // not a valid volume command, flush buffers
 | 
				
			||||||
 | 
					              SerialFlush(ble_serial);
 | 
				
			||||||
 | 
					              SerialWrite("X!;\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'K': // K - switch to KISS TNC mode
 | 
				
			||||||
 | 
					            //state = KISS;
 | 
				
			||||||
 | 
					            //TODO: set up KISS
 | 
				
			||||||
 | 
					            SerialWrite("X1;\n"); 
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            // unknown command, flush the input buffer and wait for next one
 | 
				
			||||||
 | 
					            SerialWrite("X1;\n");
 | 
				
			||||||
 | 
					            SerialFlush(ble_serial);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       case KISS:
 | 
				
			||||||
 | 
					         if ((ble_serial && bleuart.peek() == '_') || (!ble_serial && Serial.peek() == '_')) {
 | 
				
			||||||
 | 
					           state = NORMAL;
 | 
				
			||||||
 | 
					           if (rx_ctcss) {
 | 
				
			||||||
 | 
					             radio.enableCtcss();
 | 
				
			||||||
 | 
					             muted = true; // can't mute (for PL tones) during tx
 | 
				
			||||||
 | 
					             radio.setMute();
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					         // TODO: handle KISS TNC
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       case MORSE:
 | 
				
			||||||
 | 
					         if (text == '_') { state = NORMAL; }
 | 
				
			||||||
 | 
					         if (text == 'M') { // tx message
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); } 
 | 
				
			||||||
 | 
					            muted = false; // can't mute (for PL tones) during tx
 | 
				
			||||||
 | 
					            radio.setUnmute();
 | 
				
			||||||
 | 
					            radio.setModeTransmit();
 | 
				
			||||||
 | 
					            delay(300);
 | 
				
			||||||
 | 
					            radio.morseOut(cmdbuff);
 | 
				
			||||||
 | 
					            if(repeater == true) { radio.frequency(freq); } 
 | 
				
			||||||
 | 
					            radio.setModeReceive();
 | 
				
			||||||
 | 
					         } else {
 | 
				
			||||||
 | 
					           // not a valid cmd
 | 
				
			||||||
 | 
					           SerialFlush(ble_serial);
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       case DTMF:
 | 
				
			||||||
 | 
					         if (text == '_') { state = NORMAL; }
 | 
				
			||||||
 | 
					         if (text == 'D') { // tx message
 | 
				
			||||||
 | 
					            getValue(ble_serial);
 | 
				
			||||||
 | 
					            dtmf_out(cmdbuff);
 | 
				
			||||||
 | 
					         } else {
 | 
				
			||||||
 | 
					           // not a valid cmd
 | 
				
			||||||
 | 
					           SerialFlush(ble_serial);
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       default:
 | 
				
			||||||
 | 
					         // we're in an invalid state, reset to safe settings
 | 
				
			||||||
 | 
					         SerialFlush(ble_serial);
 | 
				
			||||||
 | 
					         radio.frequency(freq);
 | 
				
			||||||
 | 
					         radio.setModeReceive();
 | 
				
			||||||
 | 
					         state = NORMAL;
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // now handle any state related functions
 | 
				
			||||||
 | 
					  switch (state) {
 | 
				
			||||||
 | 
					    case TX:
 | 
				
			||||||
 | 
					      if(millis() > (timer + 500)) { 
 | 
				
			||||||
 | 
					        SerialWrite("#TX,OFF;\n");
 | 
				
			||||||
 | 
					        radio.setModeReceive(); 
 | 
				
			||||||
 | 
					        if(repeater == true) { radio.frequency(freq); }
 | 
				
			||||||
 | 
					        if (rx_ctcss) {
 | 
				
			||||||
 | 
					          radio.setMute();
 | 
				
			||||||
 | 
					          muted = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        txcount = 0;
 | 
				
			||||||
 | 
					        state = NORMAL;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case NORMAL:
 | 
				
			||||||
 | 
					      // deal with rx ctccs if necessary
 | 
				
			||||||
 | 
					      if (rx_ctcss) {
 | 
				
			||||||
 | 
					        if (radio.getCtcssToneDetected()) {
 | 
				
			||||||
 | 
					          if (muted) {
 | 
				
			||||||
 | 
					            muted = false;
 | 
				
			||||||
 | 
					            radio.setUnmute();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          if (!muted) {
 | 
				
			||||||
 | 
					            muted = true;
 | 
				
			||||||
 | 
					            radio.setMute();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case DTMF:
 | 
				
			||||||
 | 
					      dtmf_rx(); // wait for DTMF reception
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MORSE:
 | 
				
			||||||
 | 
					      morse_rx(); // wait for Morse reception
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // get rid of any trailing whitespace in the serial buffer
 | 
				
			||||||
 | 
					  SerialFlushWhitespace(ble_serial);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// callback invoked when central connects
 | 
				
			||||||
 | 
					void connect_callback(uint16_t conn_handle)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  char central_name[32] = { 0 };
 | 
				
			||||||
 | 
					  Bluefruit.Gap.getPeerName(conn_handle, central_name, sizeof(central_name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Serial.print("Connected to ");
 | 
				
			||||||
 | 
					  Serial.println(central_name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Callback invoked when a connection is dropped
 | 
				
			||||||
 | 
					 * @param conn_handle connection where this event happens
 | 
				
			||||||
 | 
					 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 | 
				
			||||||
 | 
					 * https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/cores/nRF5/nordic/softdevice/s140_nrf52_6.1.1_API/include/ble_hci.h
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void disconnect_callback(uint16_t conn_handle, uint8_t reason)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  (void) conn_handle;
 | 
				
			||||||
 | 
					  (void) reason;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Serial.println();
 | 
				
			||||||
 | 
					  Serial.println("Disconnected");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void getValue(bool ble_serial) {  
 | 
				
			||||||
 | 
					  int p = 0;
 | 
				
			||||||
 | 
					  char temp;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  for(;;) {
 | 
				
			||||||
 | 
					    if((!ble_serial && Serial.available()) || (ble_serial && bleuart.available())) { 
 | 
				
			||||||
 | 
					      if (ble_serial) {
 | 
				
			||||||
 | 
					        temp = Serial.read();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        temp = bleuart.read();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if(temp == 59) { 
 | 
				
			||||||
 | 
					        cmdbuff[p] = 0; 
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      cmdbuff[p] = temp;
 | 
				
			||||||
 | 
					      p++;
 | 
				
			||||||
 | 
					      if(p == 32) { 
 | 
				
			||||||
 | 
					        cmdbuff[0] = 0; 
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dtmfSetup() {
 | 
				
			||||||
 | 
					  radio.setVolume1(6);
 | 
				
			||||||
 | 
					  radio.setVolume2(0);
 | 
				
			||||||
 | 
					  radio.setDTMFDetectTime(24); // time to detect a DTMF code, units are 2.5ms
 | 
				
			||||||
 | 
					  radio.setDTMFIdleTime(50); // time between transmitted DTMF codes, units are 2.5ms
 | 
				
			||||||
 | 
					  radio.setDTMFTxTime(60); // duration of transmitted DTMF codes, units are 2.5ms
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dtmf_out(char * out_buf) {
 | 
				
			||||||
 | 
					  if (out_buf[0] == ';' || out_buf[0] == 0) return; // empty message
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  uint8_t i = 0;
 | 
				
			||||||
 | 
					  uint8_t code = radio.DTMFchar2code(out_buf[i]);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  // start transmitting
 | 
				
			||||||
 | 
					  radio.setDTMFCode(code); // set first
 | 
				
			||||||
 | 
					  radio.setTxSourceTones();
 | 
				
			||||||
 | 
					  if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); }
 | 
				
			||||||
 | 
					  muted = false; // can't mute during transmit
 | 
				
			||||||
 | 
					  radio.setUnmute();
 | 
				
			||||||
 | 
					  radio.setModeTransmit();
 | 
				
			||||||
 | 
					  delay(300); // wait for TX to come to full power
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool dtmf_to_tx = true;
 | 
				
			||||||
 | 
					  while (dtmf_to_tx) {
 | 
				
			||||||
 | 
					    // wait until ready
 | 
				
			||||||
 | 
					    while (radio.getDTMFTxActive() != 1) {
 | 
				
			||||||
 | 
					      // wait until we're ready for a new code
 | 
				
			||||||
 | 
					      delay(10);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (i < 32 && out_buf[i] != ';' && out_buf[i] != 0) {
 | 
				
			||||||
 | 
					      code = radio.DTMFchar2code(out_buf[i]);
 | 
				
			||||||
 | 
					      if (code == 255) code = 0xE; // throw a * in there so we don't break things with an invalid code
 | 
				
			||||||
 | 
					      radio.setDTMFCode(code); // set first
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dtmf_to_tx = false;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    i++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (radio.getDTMFTxActive() != 0) {
 | 
				
			||||||
 | 
					      // wait until this code is done
 | 
				
			||||||
 | 
					      delay(10);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // done with tone
 | 
				
			||||||
 | 
					  radio.setModeReceive();
 | 
				
			||||||
 | 
					  if (repeater == true) {radio.frequency(freq);}
 | 
				
			||||||
 | 
					  radio.setTxSourceMic();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dtmf_rx() {
 | 
				
			||||||
 | 
					  char m = radio.DTMFRxLoop();
 | 
				
			||||||
 | 
					  if (m != 0) {
 | 
				
			||||||
 | 
					    // Note: not doing buffering of messages, 
 | 
				
			||||||
 | 
					    // we just send a single morse character
 | 
				
			||||||
 | 
					    // whenever we get it
 | 
				
			||||||
 | 
					    SerialWrite("R%d;\n", m);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: morse config info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void morse_rx_setup() {
 | 
				
			||||||
 | 
					  // Set the morse code characteristics
 | 
				
			||||||
 | 
					  radio.setMorseFreq(MORSE_FREQ);
 | 
				
			||||||
 | 
					  radio.setMorseDotMillis(MORSE_DOT);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  radio.lookForTone(MORSE_FREQ);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  radio.setupMorseRx();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void morse_rx() {
 | 
				
			||||||
 | 
					  char m = radio.morseRxLoop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (m != 0) {
 | 
				
			||||||
 | 
					     // Note: not doing buffering of messages, 
 | 
				
			||||||
 | 
					     // we just send a single morse character
 | 
				
			||||||
 | 
					     // whenever we get it
 | 
				
			||||||
 | 
					     SerialWrite("R%c;\n",m);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pl_tone_tx() { 
 | 
				
			||||||
 | 
					  memset(pl_tx_buffer,0,32);
 | 
				
			||||||
 | 
					  uint8_t ptr = 0;
 | 
				
			||||||
 | 
					  while(1) { 
 | 
				
			||||||
 | 
					    if(Serial.available()) { 
 | 
				
			||||||
 | 
					      uint8_t buf = Serial.read();
 | 
				
			||||||
 | 
					      if(buf == 'X') { return; }
 | 
				
			||||||
 | 
					      if(buf == ';') { pl_tx_buffer[ptr] = 0; program_pl_tx(); return; }
 | 
				
			||||||
 | 
					      if(ptr == 31) { return; }
 | 
				
			||||||
 | 
					      pl_tx_buffer[ptr] = buf; ptr++; 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void program_pl_tx() { 
 | 
				
			||||||
 | 
					  float pl_tx = atof(pl_tx_buffer);
 | 
				
			||||||
 | 
					  radio.setCtcss(pl_tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (pl_tx == 0) {
 | 
				
			||||||
 | 
					    radio.disableCtcssTx();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    radio.enableCtcssTx();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pl_tone_rx() { 
 | 
				
			||||||
 | 
					  memset(pl_rx_buffer,0,32);
 | 
				
			||||||
 | 
					  uint8_t ptr = 0;
 | 
				
			||||||
 | 
					  while(1) { 
 | 
				
			||||||
 | 
					    if(Serial.available()) { 
 | 
				
			||||||
 | 
					    uint8_t buf = Serial.read();
 | 
				
			||||||
 | 
					    if(buf == 'X') { return; }
 | 
				
			||||||
 | 
					    if(buf == ';') { pl_rx_buffer[ptr] = 0; program_pl_rx(); return; }
 | 
				
			||||||
 | 
					    if(ptr == 31) { return; }
 | 
				
			||||||
 | 
					    pl_rx_buffer[ptr] = buf; ptr++; 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void program_pl_rx() {
 | 
				
			||||||
 | 
					  float pl_rx = atof(pl_rx_buffer);
 | 
				
			||||||
 | 
					  radio.setCtcss(pl_rx);
 | 
				
			||||||
 | 
					  if (pl_rx == 0) {
 | 
				
			||||||
 | 
					    rx_ctcss = false;
 | 
				
			||||||
 | 
					    radio.setUnmute();
 | 
				
			||||||
 | 
					    muted = false;
 | 
				
			||||||
 | 
					    radio.disableCtcssRx();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    rx_ctcss = true;
 | 
				
			||||||
 | 
					    radio.setMute();
 | 
				
			||||||
 | 
					    muted = true;
 | 
				
			||||||
 | 
					    radio.enableCtcssRx();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TEXT_BUF_LEN 64
 | 
				
			||||||
 | 
					char text_buf[TEXT_BUF_LEN];
 | 
				
			||||||
 | 
					void SerialWrite(const char *fmt, ...) {
 | 
				
			||||||
 | 
					    va_list args;
 | 
				
			||||||
 | 
					    va_start(args, fmt);    
 | 
				
			||||||
 | 
					    int str_len = snprintf(text_buf, TEXT_BUF_LEN, fmt, args);
 | 
				
			||||||
 | 
					    va_end(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bleuart.write(text_buf, str_len);
 | 
				
			||||||
 | 
					    Serial.write(text_buf, str_len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SerialFlush(bool ble_serial) {
 | 
				
			||||||
 | 
					  if (ble_serial) {
 | 
				
			||||||
 | 
					    while (bleuart.available()) { bleuart.read(); }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    while (Serial.available()) { Serial.read(); }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SerialFlushWhitespace(bool ble_serial) {
 | 
				
			||||||
 | 
					  if (!ble_serial && Serial.available()) {
 | 
				
			||||||
 | 
					    char cpeek = Serial.peek();
 | 
				
			||||||
 | 
					    while (cpeek == ' ' || cpeek == '\r' || cpeek == '\n')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Serial.read();
 | 
				
			||||||
 | 
					      cpeek = Serial.peek();
 | 
				
			||||||
 | 
					    }    
 | 
				
			||||||
 | 
					  } else if (ble_serial && bleuart.available()) {
 | 
				
			||||||
 | 
					    char cpeek = bleuart.peek();
 | 
				
			||||||
 | 
					    while (cpeek == ' ' || cpeek == '\r' || cpeek == '\n')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      bleuart.read();
 | 
				
			||||||
 | 
					      cpeek = bleuart.peek();
 | 
				
			||||||
 | 
					    }    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue