// Copyright 2015-2021 Mobilinkd LLC // All rights reserved. #pragma once #include "Log.h" #include "HdlcFrame.hpp" #include "AFSKTestTone.hpp" #include #include #include extern "C" void updatePtt(void); namespace mobilinkd { namespace tnc { namespace kiss { extern const char FIRMWARE_VERSION[]; extern const char HARDWARE_VERSION[]; AFSKTestTone& getAFSKTestTone(); namespace hardware { /** * This indicates the API version of the device. API versions are used to * indicate to the user that the config app may need to be upgraded because * the device is using a newer configuration API. * * The minor version should be updated whenever the API is extended (new * GET/SET or CAP types added. * * The major version should be updated whenever non-backwards compatible * changes to the API are made. */ constexpr const uint16_t KISS_API_VERSION = 0x0202; constexpr const uint16_t CAP_DCD = 0x0100; constexpr const uint16_t CAP_SQUELCH = 0x0200; constexpr const uint16_t CAP_INPUT_ATTEN = 0x0400; constexpr const uint16_t CAP_FIRMWARE_VERSION = 0x0800; constexpr const uint16_t CAP_BATTERY_LEVEL = 0x1000; constexpr const uint16_t CAP_BT_CONN_TRACK = 0x2000; constexpr const uint16_t CAP_BT_NAME_CHANGE = 0x4000; constexpr const uint16_t CAP_BT_PIN_CHANGE = 0x8000; constexpr const uint16_t CAP_VERBOSE_ERROR = 0x0001; constexpr const uint16_t CAP_EEPROM_SAVE = 0x0002; constexpr const uint16_t CAP_ADJUST_INPUT = 0x0004; // Auto-adjust input levels. constexpr const uint16_t CAP_DFU_FIRMWARE = 0x0008; // DFU firmware style. constexpr const uint8_t SAVE = 0; // Save settings to EEPROM. constexpr const uint8_t SET_OUTPUT_GAIN = 1; constexpr const uint8_t SET_INPUT_GAIN = 2; constexpr const uint8_t SET_SQUELCH_LEVEL = 3; // deprecated. constexpr const uint8_t POLL_INPUT_LEVEL = 4; constexpr const uint8_t STREAM_INPUT_LEVEL = 5; constexpr const uint8_t GET_BATTERY_LEVEL = 6; constexpr const uint8_t SEND_MARK = 7; constexpr const uint8_t SEND_SPACE = 8; constexpr const uint8_t SEND_BOTH = 9; constexpr const uint8_t STOP_TX = 10; constexpr const uint8_t RESET = 11; constexpr const uint8_t GET_OUTPUT_GAIN = 12; constexpr const uint8_t GET_INPUT_GAIN = 13; constexpr const uint8_t GET_SQUELCH_LEVEL = 14; constexpr const uint8_t STREAM_DCD_VALUE = 15; constexpr const uint8_t SET_VERBOSITY = 16; constexpr const uint8_t GET_VERBOSITY = 17; constexpr const uint8_t SET_INPUT_OFFSET = 18; constexpr const uint8_t GET_INPUT_OFFSET = 19; constexpr const uint8_t SET_OUTPUT_OFFSET = 20; constexpr const uint8_t GET_OUTPUT_OFFSET = 21; constexpr const uint8_t SET_LOWPASS_FREQ = 22; constexpr const uint8_t GET_LOWPASS_FREQ = 23; constexpr const uint8_t SET_INPUT_TWIST = 24; constexpr const uint8_t GET_INPUT_TWIST = 25; constexpr const uint8_t SET_OUTPUT_TWIST = 26; constexpr const uint8_t GET_OUTPUT_TWIST = 27; constexpr const uint8_t STREAM_RAW_INPUT = 28; constexpr const uint8_t STREAM_AMPLIFIED_INPUT = 29; constexpr const uint8_t STREAM_FILTERED_INPUT = 30; constexpr const uint8_t STREAM_OUTPUT = 31; constexpr const uint8_t OK = 32; // Acknowledge SET commands. constexpr const uint8_t GET_TXDELAY = 33; constexpr const uint8_t GET_PERSIST = 34; constexpr const uint8_t GET_TIMESLOT = 35; constexpr const uint8_t GET_TXTAIL = 36; constexpr const uint8_t GET_DUPLEX = 37; constexpr const uint8_t GET_FIRMWARE_VERSION = 40; constexpr const uint8_t GET_HARDWARE_VERSION = 41; constexpr const uint8_t SAVE_EEPROM_SETTINGS = 42; constexpr const uint8_t ADJUST_INPUT_LEVELS = 43; // Auto-adjust levels. constexpr const uint8_t POLL_INPUT_TWIST = 44; constexpr const uint8_t STREAM_AVG_INPUT_TWIST = 45; constexpr const uint8_t STREAM_INPUT_TWIST = 46; constexpr const uint8_t GET_SERIAL_NUMBER = 47; constexpr const uint8_t GET_MAC_ADDRESS = 48; constexpr const uint8_t GET_DATETIME = 49; constexpr const uint8_t SET_DATETIME = 50; constexpr const uint8_t GET_ERROR_MSG = 51; constexpr const uint8_t GET_SNR = 52; constexpr const uint8_t GET_BER = 53; constexpr const uint8_t SET_BLUETOOTH_NAME = 65; constexpr const uint8_t GET_BLUETOOTH_NAME = 66; constexpr const uint8_t SET_BLUETOOTH_PIN = 67; // Danger Will Robinson. constexpr const uint8_t GET_BLUETOOTH_PIN = 68; constexpr const uint8_t SET_BT_CONN_TRACK = 69; // Bluetooth connection tracking constexpr const uint8_t GET_BT_CONN_TRACK = 70; // Bluetooth connection tracking constexpr const uint8_t SET_BT_MAJOR_CLASS = 71; // Bluetooth Major Class constexpr const uint8_t GET_BT_MAJOR_CLASS = 72; // Bluetooth Major Class constexpr const uint8_t SET_USB_POWER_ON = 73; // Power on when USB power available constexpr const uint8_t GET_USB_POWER_ON = 74; constexpr const uint8_t SET_USB_POWER_OFF = 75; // Power off when USB power unavailable constexpr const uint8_t GET_USB_POWER_OFF = 76; constexpr const uint8_t SET_BT_POWER_OFF = 77; // Power off after n seconds w/o BT conn constexpr const uint8_t GET_BT_POWER_OFF = 78; constexpr const uint8_t SET_PTT_CHANNEL = 79; // Which PTT line to use (currently 0 or 1, constexpr const uint8_t GET_PTT_CHANNEL = 80; // multiplex or simplex) constexpr const uint8_t SET_PASSALL = 81; // Allow invalid CRC through when constexpr const uint8_t GET_PASSALL = 82; // true (1). constexpr const uint8_t SET_RX_REV_POLARITY = 83; // Reverse RX polarity for constexpr const uint8_t GET_RX_REV_POLARITY = 84; // 4-FSK modes when true (1). constexpr const uint8_t SET_TX_REV_POLARITY = 85; // Reverse TX polarity for constexpr const uint8_t GET_TX_REV_POLARITY = 86; // 4-FSK modes when true (1). constexpr const uint8_t GET_MIN_OUTPUT_TWIST = 119; ///< int8_t (may be negative). constexpr const uint8_t GET_MAX_OUTPUT_TWIST = 120; ///< int8_t (may be negative). constexpr const uint8_t GET_MIN_INPUT_TWIST = 121; ///< int8_t (may be negative). constexpr const uint8_t GET_MAX_INPUT_TWIST = 122; ///< int8_t (may be negative). constexpr const uint8_t GET_API_VERSION = 123; ///< uint16_t (major/minor) constexpr const uint8_t GET_MIN_INPUT_GAIN = 124; ///< int8_t (may be negative/attenuated). constexpr const uint8_t GET_MAX_INPUT_GAIN = 125; ///< int8_t (may be negative/attenuated). constexpr const uint8_t GET_CAPABILITIES = 126; ///< Send all capabilities. constexpr const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions. /** * Extended commands are two+ bytes in length. They follow the model used * by UTF-8 to extend the command set. Extended commands start at C1:80 * and go through DF:BF (11 significant bits), then proceed to E0:80:80 * through EF:BF:BF (16 significant bits). * * To avoid special KISS characters, we skip the following byte values in * the first byte: * * - 0xC0 / FEND * - 0xDB / FESC * - 0xDC / TFEND * - 0xDD / TFESC */ constexpr const uint8_t EXTENDED_CMD = 0xC1; constexpr const uint8_t EXT_OK = 0x80; constexpr std::array EXT_GET_MODEM_TYPE = {0xC1, 0x81}; constexpr std::array EXT_SET_MODEM_TYPE = {0xC1, 0x82}; constexpr std::array EXT_GET_MODEM_TYPES = {0xC1, 0x83}; ///< Return a list of supported modem types constexpr std::array EXT_GET_ALIASES = {0xC1, 0x88}; ///< Number of aliases supported constexpr std::array EXT_GET_ALIAS = {0xC1, 0x89}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) constexpr std::array EXT_SET_ALIAS = {0xC1, 0x8A}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) constexpr std::array EXT_GET_BEACON_SLOTS = {0xC1, 0x8C}; ///< Number of beacons supported constexpr std::array EXT_GET_BEACON = {0xC1, 0x8D}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) constexpr std::array EXT_SET_BEACON = {0xC1, 0x8E}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) /* * Modem type values 0x00 - 0x7F are single-byte types. Modem type values * starting at 0xC0 are multi-byte values, and should follow the model used * by UTF-8 to extend the modem types, should that ever be necessary. */ constexpr uint8_t MODEM_TYPE_1200 = 1; constexpr uint8_t MODEM_TYPE_300 = 2; constexpr uint8_t MODEM_TYPE_9600 = 3; constexpr uint8_t MODEM_TYPE_PSK31 = 4; constexpr uint8_t MODEM_TYPE_M17 = 5; // Boolean options. #define KISS_OPTION_CONN_TRACK 0x01 #define KISS_OPTION_VERBOSE 0x02 #define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged in to USB #define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when unplugged from USB #define KISS_OPTION_PTT_SIMPLEX 0x10 // Simplex PTT (the default) #define KISS_OPTION_PASSALL 0x20 // Ignore invalid CRC. #define KISS_OPTION_RX_REV_POLARITY 0x40 // Reverse Polarity on RX when set. #define KISS_OPTION_TX_REV_POLARITY 0x80 // Reverse Polarity on TX when set. #ifndef NUCLEOTNC const char TOCALL[] = "APML30"; // Update for every feature change. #else const char TOCALL[] = "APML00"; // Update for every feature change. #endif } // hardware const size_t CALLSIGN_LEN = 8; using call_t = std::array; struct Alias { call_t call; ///< Callsign. Pad unused with NUL. bool set; ///< Alias is configured. bool use; ///< Use this alias. bool insert_id; ///< Tracing. bool preempt; ///< Allow out of order pathing. uint8_t hops; }; // size = 10 const size_t BEACON_PATH_LEN = 30; const size_t BEACON_TEXT_LEN = 128; struct Beacon { call_t dest; ///< callsign. Pad unused with NUL. uint8_t path[BEACON_PATH_LEN + 1]; ///< NUL terminated string. uint8_t text[BEACON_TEXT_LEN + 1]; ///< NUL terminated string. uint16_t seconds; ///< Number of seconds between beacons. }; // size = 170 const size_t NUMBER_OF_ALIASES = 8; // 80 bytes const size_t NUMBER_OF_BEACONS = 4; // 680 bytes /** * Values from the KISS settings (including hardware settings) which are * stored in EEPROM. */ struct Hardware { static constexpr std::array modem_type_lookup = { "NOT SET", "AFSK1200", "AFSK300", "FSK9600", "PSK31", "M17" }; // This must match the constants defined above. enum ModemType { AFSK1200 = hardware::MODEM_TYPE_1200, AFSK300 = hardware::MODEM_TYPE_300, FSK9600 = hardware::MODEM_TYPE_9600, PSK31 = hardware::MODEM_TYPE_PSK31, M17 = hardware::MODEM_TYPE_M17 }; static constexpr std::array supported_modem_types = { hardware::MODEM_TYPE_1200, hardware::MODEM_TYPE_9600, hardware::MODEM_TYPE_M17 }; uint8_t txdelay; ///< How long in 10mS units to wait for TX to settle before starting data uint8_t ppersist; ///< Likelihood of taking the channel when its not busy uint8_t slot; ///< How long in 10mS units to wait between sampling the channel to see if free uint8_t txtail; ///< How long in 10mS units to wait after the data before keying off the transmitter uint8_t duplex; ///< Ignore current channel activity - just key up uint8_t modem_type; ///< Modem type. uint16_t output_gain; ///< output volume (0-256). uint16_t input_gain; ///< input volume (0-256). int8_t tx_twist; ///< 0 to 100 (50 = even). int8_t rx_twist; ///< 0, 3, 6 dB uint8_t log_level; ///< Log level (0 - 4 : debug - severe). uint16_t options; ///< boolean options /// Callsign. Pad unused with NUL. call_t mycall; uint8_t dedupe_seconds; ///< number of seconds to dedupe packets. Alias aliases[NUMBER_OF_ALIASES]; ///< Digipeater aliases Beacon beacons[NUMBER_OF_BEACONS]; ///< Beacons uint16_t checksum; ///< Validity check of param data (CRC16) uint16_t crc() const { uint32_t crc = HAL_CRC_Calculate( &hcrc, (uint32_t*) this, sizeof(Hardware) - 2); return crc & 0xFFFF; } void update_crc() { checksum = crc(); INFO("EEPROM checksum = %04hx", checksum); } bool crc_ok() const { auto result = (crc() == checksum); if (!result) { WARN("CRC mismatch %04hx != %04hx", checksum, crc()); } return result; } /** * Configure hardware settings. Load up the defaults. Call load() to * load values from EEPROM and save() to store the settings in EEPROM. * */ void init() { txdelay = 30; ppersist = 64; slot = 10; txtail = 1; duplex = 0; modem_type = ModemType::AFSK1200; output_gain = 63; input_gain = 0; // 0-4 on TNC3 tx_twist = 50; rx_twist = 0; log_level = Log::Level::debug; options = KISS_OPTION_PTT_SIMPLEX; /// Callsign. Pad unused with NUL. strcpy(mycall.data(), "NOCALL"); dedupe_seconds = 30; memset(aliases, 0, sizeof(aliases)); memset(beacons, 0, sizeof(beacons)); update_crc(); DEBUG("Settings initialized"); } void debug() { #ifdef KISS_LOGGING DEBUG("Hardware Settings (size=%d):", sizeof(Hardware)); DEBUG("TX Delay: %d", (int)txdelay); DEBUG("P* Persistence: %d", (int)ppersist); DEBUG("Slot Time: %d", (int)slot); DEBUG("TX Tail: %d", (int)txtail); DEBUG("Duplex: %d", (int)duplex); DEBUG("Modem Type: %s", modem_type_lookup[modem_type]); DEBUG("TX Gain: %d", (int)output_gain); DEBUG("RX Gain: %d", (int)input_gain); DEBUG("TX Twist: %d", (int)tx_twist); DEBUG("RX Twist: %d", (int)rx_twist); DEBUG("Log Level: %d", (int)log_level); DEBUG("Options: %04hx", options); DEBUG("MYCALL: %s", mycall.data()); DEBUG("Dedupe time (secs): %d", (int)dedupe_seconds); DEBUG("Aliases:"); for (auto& a : aliases) { if (!a.set) continue; DEBUG(" call: %s", a.call.data()); DEBUG(" use: %d", (int)a.use); DEBUG(" insert: %d", (int)a.insert_id); DEBUG(" preempt: %d", (int)a.preempt); DEBUG(" hops: %d", (int)a.hops); } DEBUG("Beacons:"); for (auto& b : this->beacons) { if (b.seconds == 0) continue; DEBUG(" dest: %s", b.dest.data()); DEBUG(" path: %s", (char*)b.path); DEBUG(" text: %s", (char*)b.text); DEBUG(" frequency (secs): %d", (int)b.seconds); } DEBUG("Checksum: %04hx", checksum); #endif } bool load(); bool store() const; void set_txdelay(uint8_t value); void set_ppersist(uint8_t value); void set_slottime(uint8_t value); void set_txtail(uint8_t value); void set_duplex(uint8_t value); void handle_request(hdlc::IoFrame* frame) __attribute__((optimize("-O2"))); void handle_ext_request(hdlc::IoFrame* frame); void get_aliases(); void get_alias(uint8_t alias); void set_alias(const hdlc::IoFrame* frame); bool rx_rev_polarity() const { return (options & KISS_OPTION_RX_REV_POLARITY) != 0; } bool tx_rev_polarity() const { return (options & KISS_OPTION_TX_REV_POLARITY) != 0; } void announce_input_settings(); }; // 812 bytes extern Hardware& settings(); struct I2C_Storage { constexpr static const uint16_t i2c_address{EEPROM_ADDRESS}; constexpr static const uint16_t capacity{EEPROM_CAPACITY}; constexpr static const uint16_t page_size{EEPROM_PAGE_SIZE}; constexpr static const uint32_t write_time{EEPROM_WRITE_TIME}; static bool load(void* ptr, size_t len); template static bool load(T& t) { return load(&t, sizeof(T)); } static bool store(const void* ptr, size_t len); template static bool store(const T& t) { return store(&t, sizeof(T)); } }; void reply8(uint8_t cmd, uint8_t result) __attribute__((noinline)); void reply16(uint8_t cmd, uint16_t result) __attribute__((noinline)); void reply(uint8_t cmd, const uint8_t* data, uint16_t len) __attribute__((noinline)); }}} // mobilinkd::tnc::kiss