From 64f7583d033e28719552fb1e59688f2ffd0465e4 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 4 Sep 2019 17:59:18 +0000 Subject: [PATCH] Support the i2c Si4713 chips --- Makefile | 3 +- plugin_setup.php | 48 ++++-- src/FPPVastFM.cpp | 38 +++-- src/I2CSi4713.cpp | 171 +++++++++++++++++++ src/I2CSi4713.h | 34 ++++ src/Si4713.cpp | 417 +++------------------------------------------- src/Si4713.h | 47 ++---- src/VASTFMT.cpp | 346 ++++++++++++++++++++++++++++++++++++++ src/VASTFMT.h | 39 +++++ 9 files changed, 687 insertions(+), 456 deletions(-) create mode 100755 src/I2CSi4713.cpp create mode 100755 src/I2CSi4713.h create mode 100755 src/VASTFMT.cpp create mode 100755 src/VASTFMT.h diff --git a/Makefile b/Makefile index f450106..1ab7c5a 100755 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ include /opt/fpp/src/makefiles/common/setup.mk +include /opt/fpp/src/makefiles/platform/*.mk all: libfpp-vastfmt.so debug: all CFLAGS+=-I. -I./vastfmt -I/usr/include/libusb-1.0 -OBJECTS_fpp_vastfmt_so += src/FPPVastFM.o src/hid.o src/Si4713.o src/bitstream.o +OBJECTS_fpp_vastfmt_so += src/FPPVastFM.o src/hid.o src/Si4713.o src/bitstream.o src/VASTFMT.o src/I2CSi4713.o LIBS_fpp_vastfmt_so += -L/opt/fpp/src -lfpp -lusb-1.0 CXXFLAGS_src/FPPVastFM.o += -I/opt/fpp/src diff --git a/plugin_setup.php b/plugin_setup.php index 5eaa69e..bbbe71b 100755 --- a/plugin_setup.php +++ b/plugin_setup.php @@ -1,8 +1,9 @@ - +
+
+VAST-FMT/Si4713 Hardware +

Connection: "USB", "I2C"=>"I2C"), "fpp-vastfmt", ""); ?>

+

Reset GPIO:
+I2C connection requires a GPIO pin to reset the Si4713. Can either be a kernal GPIO number or a pin name like "P9-30". Setting is unused for USB connection.

+
+
+ +
+
-VAST-FMT Plugin Settings -

Start VAST-FMT at: "FPPDStart", "Playlist Start"=>"PlaylistStart", "Never"=>"Never"), "fpp-vastfmt", ""); ?>
-At Start, the VAST-FMT is reset, FM settings initialized, will broadcast any audio played, and send static RDS messages (if enabled).

-

Stop VAST-FMT at: "PlaylistStop", "Never (default)"=>"Never"), "fpp-vastfmt", ""); ?>
-At Stop, the VAST-FMT is reset. Listeners will hear static.

+VAST-FMT/Si4713 Plugin Settings +

Start at: "FPPDStart", "Playlist Start"=>"PlaylistStart", "Never"=>"Never"), "fpp-vastfmt", ""); ?>
+At Start, the hardware is reset, FM settings initialized, will broadcast any audio played, and send static RDS messages (if enabled).

+

Stop at: "PlaylistStop", "Never (default)"=>"Never"), "fpp-vastfmt", ""); ?>
+At Stop, the hardware is reset. Listeners will hear static.

@@ -32,12 +44,12 @@ function toggle(id) {
-VAST-FMT FM Settings -

Frequency (76.00-108.00): MHz

-

Power (88-115, 116-120*): dBμV +VAST-FMT/Si4713 FM Settings +

Frequency (76.00-108.00): MHz

+

Power (88-115, 116-120*): dBμV
*Can be set as high as 120dBμV, but voltage accuracy above 115dBμV is not guaranteed.

Preemphasis: "50us", "75μs (USA, default)"=>"75us"), "fpp-vastfmt", ""); ?>

-

Antenna Tuning Capacitor (0=Auto, 1-191): * 0.25pF

+

Antenna Tuning Capacitor (0=Auto, 1-191): * 0.25pF

@@ -45,14 +57,14 @@ function toggle(id) {
-VAST-FMT RDS Settings +VAST-FMT/Si4713 RDS Settings

Enable RDS:

-

RDS Station - Sent 8 characters at a time

-

Station Text: +

RDS Station - Sent 8 characters at a time. Max of 64 characters.
+Station Text:
-

RDS Text: +

RDS Text:

Place {Artist} or {Title} where the media artist/title should be placed. Area's wrapped in brackets ( [] ) will not be output unless media is present. diff --git a/src/FPPVastFM.cpp b/src/FPPVastFM.cpp index 5403de1..de8673b 100755 --- a/src/FPPVastFM.cpp +++ b/src/FPPVastFM.cpp @@ -4,21 +4,16 @@ #include #include -#include #include "mediadetails.h" #include "common.h" #include "settings.h" #include "Plugin.h" #include "log.h" -#include "Si4713.h" +#include "VASTFMT.h" +#include "I2CSi4713.h" -// VASTFM vendor/product -//HID\VID_0451&PID_2100&REV_0101&MI_02 -const uint16_t _usVID=0x0451; /*!< USB vendor ID. */ -const uint16_t _usPID=0x2100; /*!< USB product ID. */ - static std::string padToNearest(std::string s, int l) { if (!s.empty()) { int n = 0; @@ -58,9 +53,13 @@ class FPPVastFMPlugin : public FPPPlugin { void startVast() { if (si4713 == nullptr) { - si4713 = new Si4713(_usVID, _usPID); + if (settings["Connection"] == "I2C") { + si4713 = new I2CSi4713(settings["ResetPin"]); + } else { + si4713 = new VASTFMT(); + } if (si4713->isOk()) { - si4713->powerUp(); + si4713->Init(); std::string rev = si4713->getRev(); LogInfo(VB_PLUGIN, "VAST-FMT: %s\n", rev.c_str()); @@ -86,6 +85,7 @@ class FPPVastFMPlugin : public FPPPlugin { LogInfo(VB_PLUGIN, "VAST-FMT: %s\n", ts.c_str()); if (settings["EnableRDS"] == "True") { + printf("Enabling RDS\n"); si4713->beginRDS(); formatAndSendText(settings["StationText"], "", "", true); formatAndSendText(settings["RDSTextText"], "", "", false); @@ -107,6 +107,9 @@ class FPPVastFMPlugin : public FPPPlugin { void formatAndSendText(const std::string &text, const std::string &artist, const std::string &title, bool station) { std::string output; + + int artistIdx = -1; + int titleIdx = -1; for (int x = 0; x < text.length(); x++) { if (text[x] == '[') { if (artist == "" && title == "") { @@ -121,9 +124,11 @@ class FPPVastFMPlugin : public FPPPlugin { const static std::string TITLE = "{Title}"; std::string subs = text.substr(x); if (subs.rfind(ARTIST) == 0) { + artistIdx = output.length(); x += ARTIST.length() - 1; output += artist; } else if (subs.rfind(TITLE) == 0) { + titleIdx = output.length(); x += TITLE.length() - 1; output += title; } else { @@ -155,16 +160,22 @@ class FPPVastFMPlugin : public FPPPlugin { si4713->setRDSStation(fragments); } else { LogDebug(VB_PLUGIN, "Setting RDS text to %s\n", output.c_str()); - si4713->setRDSBuffer(output); + si4713->setRDSBuffer(output, artistIdx, artist.length(), titleIdx, title.length()); } } + virtual void playlistCallback(const Json::Value &playlist, const std::string &action, const std::string §ion, int item) { + if (action == "stop") { + formatAndSendText(settings["StationText"], "", "", true); + formatAndSendText(settings["RDSTextText"], "", "", false); + } if (settings["Start"] == "PlaylistStart" && action == "start") { startVast(); } else if (settings["Stop"] == "PlaylistStop" && action == "stop") { stopVast(); } + } virtual void mediaCallback(const Json::Value &playlist, const MediaDetails &mediaDetails) { std::string title = mediaDetails.title; @@ -194,6 +205,13 @@ class FPPVastFMPlugin : public FPPPlugin { setIfNotFound("StationText", "Merry Christ- mas", true); setIfNotFound("RDSTextText", "[{Artist} - {Title}]", true); setIfNotFound("Pty", "2"); + + setIfNotFound("Connection", "USB"); +#ifdef PLATFORM_BBB + setIfNotFound("ResetPin", "14"); +#else + setIfNotFound("ResetPin", "4"); +#endif } void setIfNotFound(const std::string &s, const std::string &v, bool emptyAllowed = false) { if (settings.find(s) == settings.end()) { diff --git a/src/I2CSi4713.cpp b/src/I2CSi4713.cpp new file mode 100755 index 0000000..f501d2c --- /dev/null +++ b/src/I2CSi4713.cpp @@ -0,0 +1,171 @@ + +#include +#include +#include + +#include "log.h" + +#include "I2CSi4713.h" + +#include "util/I2CUtils.h" +#include "util/GPIOUtils.h" + +// Commands +#define SI4710_CMD_POWER_UP 0x01 +#define SI4710_CMD_GET_REV 0x10 +#define SI4710_CMD_POWER_DOWN 0x11 +#define SI4710_CMD_SET_PROPERTY 0x12 +#define SI4710_CMD_GET_PROPERTY 0x13 +#define SI4710_CMD_GET_INT_STATUS 0x14 +#define SI4710_CMD_PATCH_ARGS 0x15 +#define SI4710_CMD_PATCH_DATA 0x16 +#define SI4710_CMD_TX_TUNE_FREQ 0x30 +#define SI4710_CMD_TX_TUNE_POWER 0x31 +#define SI4710_CMD_TX_TUNE_MEASURE 0x32 +#define SI4710_CMD_TX_TUNE_STATUS 0x33 +#define SI4710_CMD_TX_ASQ_STATUS 0x34 +#define SI4710_CMD_TX_RDS_BUFF 0x35 +#define SI4710_CMD_TX_RDS_PS 0x36 +#define SI4710_CMD_TX_AGC_OVERRIDE 0x48 +#define SI4710_CMD_GPO_CTL 0x80 +#define SI4710_CMD_GPO_SET 0x81 + + +#define SI4713_PROP_REFCLK_FREQ 0x0201 + +#ifdef PLATFORM_BBB +#define I2CBUS 2 +#else +#define I2CBUS 1 +#endif + +I2CSi4713::I2CSi4713(const std::string &gpioPin) { + resetPin = PinCapabilities::getPinByName(gpioPin).ptr(); + if (resetPin == nullptr) { + resetPin = PinCapabilities::getPinByGPIO(std::stoi(gpioPin)).ptr(); + } + resetPin->configPin("gpio", "out"); + resetPin->setValue(1); + usleep(200000); + resetPin->setValue(0); + usleep(200000); + resetPin->setValue(1); + usleep(300000); + i2c = new I2CUtils(I2CBUS, 0x63); + if (i2c->isOk()) { + sendSi4711Command(SI4710_CMD_POWER_UP, {0x12, 0x50}); + usleep(500000); + setProperty(SI4713_PROP_REFCLK_FREQ, 32768); + } else { + delete i2c; + i2c = nullptr; + } +} +I2CSi4713::~I2CSi4713() { + + if (i2c) { + delete i2c; + } + +} + + +bool I2CSi4713::isOk() { + return i2c != nullptr; +} +void I2CSi4713::powerUp() { +} +void I2CSi4713::powerDown() { +} +void I2CSi4713::reset() { +} + + +std::string I2CSi4713::getRev() { + std::vector response; + response.resize(9); + sendSi4711Command(SI4710_CMD_GET_REV, {0}, response); + + int pn = response[1]; + int fw = response[2] << 8 | response[3]; + int patch = response[4] << 8 | response[5]; + int cmp = response[6] << 8 | response[7]; + int chiprev = response[8]; + + char buf[250]; + sprintf(buf, "Si4713 - pn: %X fw: %X patch: %X cmp: %X chiprev: %X", pn, fw, patch, cmp, chiprev); + return buf; +} + +std::string I2CSi4713::getASQ() { + std::vector out; + out.resize(8); + sendSi4711Command(SI4710_CMD_TX_ASQ_STATUS, {0x1}, out); + std::string r = "ASQ Flags: "; + r += std::to_string(out[1]) + " " + std::to_string(out[2]) + " " + std::to_string(out[3]); + r += " - InLevel:"; + int lev = out[4]; + if (lev > 127) { + lev -= 256; + } + r += std::to_string(lev); + r += " dBfs"; + return r; +} +std::string I2CSi4713::getTuneStatus() { + std::vector out; + out.resize(8); + sendSi4711Command(SI4710_CMD_TX_TUNE_STATUS, {0x1}, out); + int currFreq = out[2] << 8 | out[3]; + int currdBuV = out[5]; + int currAntCap = out[6]; + + float f = currFreq / 100.0f; + + std::string r = "Freq:" + std::to_string(f) + + " MHz - Power:" + std::to_string(currdBuV) + + "dBuV - ANTcap:" + std::to_string(currAntCap); + return r; +} +bool I2CSi4713::sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures) { + //printf("Sending command %X datasize: %d\n", cmd, data.size()); + int i = i2c->writeBlockData(cmd, &data[0], data.size()); + usleep(10000); + if (ignoreFailures || i >= 0) { + return true; + } + return false; +} + +bool I2CSi4713::sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures) { + //printf("Sending command %X datasize: %d toRead: %d\n", cmd, data.size(), out.size()); + int i = i2c->writeBlockData(cmd, &data[0], data.size()); + if (!ignoreFailures && i < 0) { + return false; + } + usleep(10000); + if (out.size()) { + usleep(10000); + i2c->readI2CBlockData(0x00, &out[0], out.size()); + //printf(" read %d\n", i); + } + return false; +} + + +bool I2CSi4713::setProperty(uint16_t prop, uint16_t val) { + //printf("Set property: %X val: %X\n", prop, val); + unsigned char aucBuf[20]; + memset(aucBuf, 0x00, 20); + aucBuf[0] = 0x00; + aucBuf[1] = prop >> 8; + aucBuf[2] = prop; + aucBuf[3] = val >> 8; + aucBuf[4] = val; + int i = i2c->writeBlockData(SI4710_CMD_SET_PROPERTY, aucBuf, 5) >= 0; + if (i == -1) { + printf("Failed property: %X %X\n", prop, val); + } + usleep(1000); + return i; +} diff --git a/src/I2CSi4713.h b/src/I2CSi4713.h new file mode 100755 index 0000000..9bbc6be --- /dev/null +++ b/src/I2CSi4713.h @@ -0,0 +1,34 @@ +#ifndef __I2CSI4713__ +#define __I2CSI4713__ + +#include "Si4713.h" + +class I2CUtils; +class PinCapabilities; + +class I2CSi4713 : public Si4713 { +public: + I2CSi4713(const std::string &gpioPin); + virtual ~I2CSi4713(); + + + virtual bool isOk() override; + virtual std::string getRev() override; + virtual void powerUp() override; + virtual void powerDown() override; + virtual void reset() override; + virtual std::string getASQ() override; + virtual std::string getTuneStatus() override; + + +protected: + virtual bool sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures = false); + virtual bool sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures = false); + virtual bool setProperty(uint16_t prop, uint16_t val); + +private: + I2CUtils *i2c = nullptr; + const PinCapabilities *resetPin = nullptr; +}; + +#endif diff --git a/src/Si4713.cpp b/src/Si4713.cpp index 6c9b8e4..ff3de44 100755 --- a/src/Si4713.cpp +++ b/src/Si4713.cpp @@ -4,23 +4,10 @@ #include "Si4713.h" #include "util/I2CUtils.h" -#include "hidapi.h" #include "log.h" #include "bitstream.h" -#define PCRequestError 0x80 -#define PCTransfer 0x02 -#define RequestDone 0x80 - -//status bits -#define STATUS_BIT_STCINT 0x01 -#define STATUS_BIT_ASQINT 0x02 -#define STATUS_BIT_RDSINT 0x04 -#define STATUS_BIT_RSQINT 0x08 -#define STATUS_BIT_ERR 0x40 -#define STATUS_BIT_CTS 0x80 - // properties #define SI4713_PROP_GPO_IEN 0x0001 #define SI4713_PROP_DIGITAL_INPUT_FORMAT 0x0101 @@ -96,289 +83,12 @@ #define TX_RDS_PS 0x36 -enum { - RequestNone = 0, - RequestCpuId, - RequestSi4711Reset, //reset - RequestSi4711Access, //low level access - RequestSi4711GetProp, //medium level get prop - RequestSi4711SetProp, //medium level set prop - RequestSi4711PowerStatus, - RequestSi4711PowerUp, //high level power up - RequestSi4711PowerDown, //high level power down - RequestSi4711AudioEnable, //this MUST BE!!! called after setting config to enable audio. - RequestSi4711AudioDisable, - RequestEepromSectionRead, - RequestEepromSectionWrite, - RequestSi4711AsqStatus, - RequestSi4711TuneStatus, - RequestUnknown -}; -static const char *si471x_requests[] = { - "NONE", - "CPU ID", - "Frontend Reset", - "Frontend Access", - "Get Property", - "Set Property", - "Frontend Power Status", - "Frontend Power Up", - "Frontend Power Down", - "Frontend Enable Audio", - "Frontend Disable Audio", - "EEPROM Read", - "EEPROM Write", - "unknown" -}; -#define Si471xRequestStr(x) (x < RequestUnknown ? si471x_requests[x] : si471x_requests[RequestUnknown]) - -enum { - SI4711_OK = 0, - SI4711_TIMEOUT, - SI4711_COMM_ERR, - SI4711_BAD_ARG, - SI4711_NOCTS, - SI4711_ERROR_UNKNOWN -}; -static const char *si471x_statuses[] = { - "OK", - "I2C Timeout", - "I2C Communication Error", - "Bad Argument", - "No CTS!", - "unknown" -}; -#define Si471xStatusStr(x) (x < SI4711_ERROR_UNKNOWN ? si471x_statuses[x] : si471x_statuses[SI4711_ERROR_UNKNOWN]) - - - -class Si4713Connector { -public: - Si4713Connector() {} - virtual ~Si4713Connector() {} - - virtual bool isOk() = 0; - - virtual bool sendDeviceCommand(uint8_t cmd, std::vector &dataOut, bool ignoreFailures) = 0; - virtual bool sendSi4711Command(uint8_t cmd, const std::vector &dataIn, std::vector &dataOut, bool ignoreFailures) = 0; - - virtual bool setProperty(uint16_t prop, uint16_t val) = 0; - virtual bool getProperty(uint16_t prop, uint16_t &val) = 0; - -}; - -class USBSi4713Connector : public Si4713Connector { -public: - USBSi4713Connector(uint16_t _usVID, uint16_t _usPID) { - struct hid_device_info *phdi = nullptr; - phdi = hid_enumerate(_usVID,_usPID); - if (phdi == nullptr) { - return; - } - phd = hid_open_path(phdi->path); - hid_free_enumeration(phdi); - phdi=nullptr; - } - virtual ~USBSi4713Connector() { - if (phd) { - hid_close(phd); - } - } - virtual bool isOk() override { return phd != nullptr; } - virtual bool sendDeviceCommand(uint8_t cmd, std::vector &dataOut, bool ignoreFailures) { - unsigned char aucBufIn[43]; - unsigned char aucBufOut[43]; - memset(aucBufOut, 0x00, 43); // Clear out the response buffer - memset(aucBufIn, 0xCC, 43); // Clear out the response buffer - - /* Send a BL Query Command */ - aucBufOut[0] = 0; // Report ID, ignored - aucBufOut[1] = PCTransfer; - aucBufOut[2] = cmd; - - hid_write(phd, aucBufOut, 43); - int r = hid_read(phd, aucBufIn, 43); - if (r < 2) { - LogWarn(VB_PLUGIN, "Si4713/USB: not enough data\n"); - return false; - } - if (!ignoreFailures && aucBufIn[0] & PCRequestError) { - LogWarn(VB_PLUGIN, "Si4713/USB: request error\n"); - return false; - } - if (!ignoreFailures && !(aucBufIn[0] & PCTransfer)) { - LogWarn(VB_PLUGIN, "Si4713/USB: Transfer error.\n"); - return false; - } - if (ignoreFailures || aucBufIn[1] == (cmd|RequestDone)) { - dataOut.resize(r - 2); - memcpy(&dataOut[0], &aucBufIn[2], r - 2); - return true; - } else { - LogWarn(VB_PLUGIN, "Si4713/USB: Request not done.\n"); - } - return false; - } - virtual bool sendSi4711Command(uint8_t cmd, const std::vector &dataIn, std::vector &dataOut, bool ignoreFailures) override { - unsigned char aucBufIn[43]; - unsigned char aucBufOut[43]; - memset(aucBufOut, 0x00, 43); // Clear out the response buffer - memset(aucBufIn, 0xCC, 43); // Clear out the response buffer - - /* Send a BL Query Command */ - aucBufOut[0] = 0; // Report ID, ignored - aucBufOut[1] = PCTransfer; - aucBufOut[2] = RequestSi4711Access; - aucBufOut[3] = dataIn.size() + 1; - aucBufOut[4] = cmd; - memcpy(&aucBufOut[5], &dataIn[0], dataIn.size()); - - hid_write(phd, aucBufOut, 43); - int r = hid_read(phd, aucBufIn, 42); - /* - for (int x = 0; x < 25; x++) { - printf("%x ", aucBufIn[x]); - } - printf("\n"); - */ - if (!ignoreFailures && aucBufIn[2] != 1) { - LogWarn(VB_PLUGIN, "Si4711/USB: command failed (%d): %s, returned (FALSE)\n", aucBufIn[2], Si471xStatusStr(aucBufIn[2])); - //sometimes it timeoutes, but the CTS=0, so we must re-read this byte again. - //return false; - } - - if (!ignoreFailures && aucBufIn[3]!=SI4711_OK) { - LogWarn(VB_PLUGIN, "Si4711/USB: I2C_READ failed (%d): %s\n", aucBufIn[3], Si471xStatusStr(aucBufIn[3])); - return false; - } - - if(!ignoreFailures && aucBufIn[4] > 16) { - LogWarn(VB_PLUGIN, "Si4711/USB: I2C_READ failed, too much bytes received: %d\n", aucBufIn[4]); - return false; - } - - //LogDebug(VB_PLUGIN, "Si4711/USB: Volume: %d.%d (deviation: %d)\n", (int) aucBufIn[21], (int ) aucBufIn[22], (int ) (aucBufIn[23] << 8 | aucBufIn[24])); - //printf("Si4711/USB: Volume: %d.%d (deviation: %d)\n", (int ) aucBufIn[21], (int ) aucBufIn[22], (int ) (aucBufIn[23] << 8 | aucBufIn[24])); - //printf("datasize: %d\n", aucBufIn[4]); - int sz = 16; // aucBufIn[4] - dataOut.resize(sz); - memcpy(&dataOut[0], &aucBufIn[5], sz); - return true; - } - virtual bool setProperty(uint16_t prop, uint16_t val) override { - unsigned char aucBufIn[43]; - unsigned char aucBufOut[43]; - memset(aucBufOut, 0x00, 43); // Clear out the response buffer - memset(aucBufIn, 0xCC, 43); // Clear out the response buffer - aucBufOut[0] = 0x00; //report number, would be unused! - aucBufOut[1] = PCTransfer; // - aucBufOut[2] = RequestSi4711SetProp; - aucBufOut[3] = prop >> 8; - aucBufOut[4] = prop; - aucBufOut[5] = val >> 8; - aucBufOut[6] = val; - hid_write(phd, aucBufOut, 43); - hid_read(phd, aucBufIn, 42); - - if (aucBufIn[0] & PCRequestError) { - LogWarn(VB_PLUGIN, "Si4713/USB: request error for property %X.\n", prop); - return false; - } - - if (!(aucBufIn[1] & RequestDone)) { - LogWarn(VB_PLUGIN, "Si4713/USB: request is not done!\n"); - return false; - } - - if (aucBufIn[8]!=SI4711_OK) { - LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%d): %s\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[2], Si471xStatusStr(aucBufIn[2])); - return false; - } - - if (aucBufIn[7] & STATUS_BIT_ERR) { - LogWarn(VB_PLUGIN, "Si4713/USB: Answers: Error setting property 0x%04x to \"0x%04x\".\n", prop, val); - return false; - } - - if (!(aucBufIn[7] & STATUS_BIT_CTS)) { - LogWarn(VB_PLUGIN, "Si4713/USB: Answers: NO CTS! When setting property 0x%04x.\n", prop); - return false; - } - - if (aucBufIn[6] != 1) { - LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%02x): %s (false!)\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[6]); - return false; - } - return true; - } - virtual bool getProperty(uint16_t prop, uint16_t &val) override { - unsigned char aucBufIn[43]; - unsigned char aucBufOut[43]; - memset(aucBufOut, 0x00, 43); // Clear out the response buffer - memset(aucBufIn, 0xCC, 43); // Clear out the response buffer - aucBufOut[0] = 0x00; //report number, would be unused! - aucBufOut[1] = PCTransfer; // - aucBufOut[2] = RequestSi4711GetProp; - aucBufOut[3] = prop >> 8; - aucBufOut[4] = prop; - hid_write(phd, aucBufOut, 43); - hid_read(phd, aucBufIn, 42); - - if (aucBufIn[0] & PCRequestError) { - LogWarn(VB_PLUGIN, "Si4713/USB: request error for property %X.\n", prop); - return false; - } - - if (!(aucBufIn[1] & RequestDone)) { - LogWarn(VB_PLUGIN, "Si4713/USB: request is not done!\n"); - return false; - } - - if (aucBufIn[8]!=SI4711_OK) { - LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%d): %s\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[2], Si471xStatusStr(aucBufIn[2])); - return false; - } - - if (aucBufIn[7] & STATUS_BIT_ERR) { - LogWarn(VB_PLUGIN, "Si4713/USB: Answers: Error setting property 0x%04x to \"0x%04x\".\n", prop, val); - return false; - } - - if (!(aucBufIn[7] & STATUS_BIT_CTS)) { - LogWarn(VB_PLUGIN, "Si4713/USB: Answers: NO CTS! When setting property 0x%04x.\n", prop); - return false; - } - - if (aucBufIn[6] != 1) { - LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%02x): %s (false!)\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[6]); - return false; - } - val = (int16_t ) ((aucBufIn[4] & 0x00FF) << 8) | aucBufIn[5]; - return true; - } - - - hid_device *phd = nullptr; -}; - - -Si4713::Si4713(uint16_t _usVID, uint16_t _usPID) { - connector = new USBSi4713Connector(_usVID, _usPID); - if (isOk()) { - Init(); - } +Si4713::Si4713() { } Si4713::~Si4713() { - if (connector) { - delete connector; - } -} - -bool Si4713::isOk() { - return connector && connector->isOk(); } @@ -392,52 +102,8 @@ void Si4713::Init() { setProperty(SI4713_PROP_TX_ATTACK_TIME, 0); // 0.5 ms setProperty(SI4713_PROP_TX_RELEASE_TIME, 4); // 1000 ms setProperty(SI4713_PROP_TX_ACOMP_GAIN, 5); // dB - - powerUp(); -} -bool Si4713::sendDeviceCommand(uint8_t cmd, bool ignoreFailures) { - std::vector out; - return connector->sendDeviceCommand(cmd, out, ignoreFailures); -} -bool Si4713::sendDeviceCommand(uint8_t cmd, std::vector &out, bool ignoreFailures) { - return connector->sendDeviceCommand(cmd, out, ignoreFailures); -} -bool Si4713::setProperty(uint16_t prop, uint16_t val) { - return connector->setProperty(prop, val); -} -bool Si4713::getProperty(uint16_t prop, uint16_t &val) { - return connector->getProperty(prop, val); -} - -bool Si4713::sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures) { - std::vector out; - return connector->sendSi4711Command(cmd, data, out, ignoreFailures); -} -bool Si4713::sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures) { - return connector->sendSi4711Command(cmd, data, out, ignoreFailures); -} - -void Si4713::powerUp() { - sendDeviceCommand(RequestSi4711PowerUp, true); -} -void Si4713::powerDown() { - sendDeviceCommand(RequestSi4711PowerDown, true); -} -void Si4713::reset() { - sendDeviceCommand(RequestSi4711Reset, true); } -std::string Si4713::getRev() { - std::vector out; - sendSi4711Command(0x10, {0}, out); - - if (sendDeviceCommand(RequestCpuId, out)) { - std::string rev = (char*)(&out[3]); - std::string board = (char*)(&out[5 + rev.size()]); - return board + " - " + rev; - } - return ""; -} void Si4713::setFrequency(int frequency) { uint8_t ft = frequency>>8; @@ -450,49 +116,11 @@ void Si4713::setTXPower(int power, double antCap) { sendSi4711Command(TX_TUNE_POWER, {0x00, 0x00, p, rfcap0}); } - -std::string Si4713::getASQ() { +bool Si4713::sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures) { std::vector out; - if (sendDeviceCommand(RequestSi4711AsqStatus, out)) { - std::string r = "ASQ Flags: "; - r += std::to_string(out[1]) + " " + std::to_string(out[2]) + " " + std::to_string(out[3]); - r += " - InLevel:"; - int lev = out[4]; - if (lev > 127) { - lev -= 256; - } - r += std::to_string(lev); - r += " dBfs"; - return r; - } - return ""; -} -std::string Si4713::getTuneStatus() { - std::vector out; - if (sendDeviceCommand(RequestSi4711TuneStatus, out)) { - int currFreq = out[1] << 8 | out[2]; - int currdBuV = out[3]; - int currAntCap = out[4]; - - float f = currFreq / 100.0f; - float cap = ((float)currAntCap) * 0.25f; - - std::string r = "Freq:" + std::to_string(f) - + " MHz - Power:" + std::to_string(currdBuV) - + "dBuV - ANTcap:" + std::to_string(cap); - return r; - } - return ""; -} - -void Si4713::enableAudio() { - sendDeviceCommand(RequestSi4711AudioEnable); -} -void Si4713::disableAudio() { - sendDeviceCommand(RequestSi4711AudioDisable); + return sendSi4711Command(cmd, data, out, ignoreFailures); } - void Si4713::beginRDS() { //66.25KHz (default is 68.25) setProperty(SI4713_PROP_TX_AUDIO_DEVIATION, 6625); @@ -506,13 +134,15 @@ void Si4713::beginRDS() { // 50% mix (default) setProperty(SI4713_PROP_TX_RDS_PS_MIX, 0x03); // RDSD0 & RDSMS (default) - setProperty(SI4713_PROP_TX_RDS_PS_MISC, 0x1008 | (pty << 5)); + int i = (0x1848 & 0xFB1F) | (pty << 5); + uint16_t i2 = i; + setProperty(SI4713_PROP_TX_RDS_PS_MISC, i); // 3 repeats (default) setProperty(SI4713_PROP_TX_RDS_PS_REPEAT_COUNT, 3); setProperty(SI4713_PROP_TX_RDS_MESSAGE_COUNT, 1); setProperty(SI4713_PROP_TX_RDS_PS_AF, 0xE0E0); // no AF - setProperty(SI4713_PROP_TX_RDS_FIFO_SIZE, 7); + setProperty(SI4713_PROP_TX_RDS_FIFO_SIZE, 0); setProperty(SI4713_PROP_TX_COMPONENT_ENABLE, 0x0007); } @@ -536,12 +166,14 @@ void Si4713::setRDSStation(const std::vector &station) { buf[x] = a[x]; } for (uint8_t i = 0; i < 2; i++) { - sendSi4711Command(TX_RDS_PS, {idx, buf[i*4], buf[(i*4)+1], buf[(i*4)+2], buf[(i*4)+3]}); + sendSi4711Command(TX_RDS_PS, {idx, buf[i*4], buf[(i*4)+1], buf[(i*4)+2], buf[(i*4)+3], 0}); idx++; } } } -void Si4713::setRDSBuffer(const std::string &station) { +void Si4713::setRDSBuffer(const std::string &station, + int artistPos, int artistLen, + int titlePos, int titleLen) { if (lastRDS == station) { return; } @@ -565,8 +197,6 @@ void Si4713::setRDSBuffer(const std::string &station) { } } - std::vector out; - sendSi4711Command(TX_RDS_BUFF, {TX_RDS_BUFF_IN_MTBUFF, 0, 0, 0, 0, 0, 0}, out); if (station.size() != 0) { int count = (sl + 3) / 4; //printf("%d, %s\n", count, station.c_str()); @@ -575,22 +205,17 @@ void Si4713::setRDSBuffer(const std::string &station) { if (i == 0) { sb |= TX_RDS_BUFF_IN_MTBUFF; } - sendSi4711Command(TX_RDS_BUFF, {sb, 0x20, i, buf[i*4], buf[(i*4)+1], buf[(i*4)+2], buf[(i*4)+3]}, out); - - if (out.size() < 8) { - out.resize(8); - } - //printf("bu out %d: %2X %2X %2X %2X %2X %2X %2X %2X\n", i, out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7]); + sendSi4711Command(TX_RDS_BUFF, {sb, 0x20, i, buf[i*4], buf[(i*4)+1], buf[(i*4)+2], buf[(i*4)+3], 0}); } - //sendRtPlusInfo(1, 0, 10, 4, 11, sl - 11); + sendRtPlusInfo(1, titlePos, titleLen, 4, artistPos, artistLen); + } else { + sendSi4711Command(TX_RDS_BUFF, {TX_RDS_BUFF_IN_MTBUFF, 0, 0, 0, 0, 0, 0}); } sendTimestamp(); setProperty(SI4713_PROP_TX_COMPONENT_ENABLE, 0x0007); - sendSi4711Command(0x14, {}, out); - //printf("0x14 out: %2X %2X %2X %2X %2X %2X %2X %2X\n", out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7]); - sendSi4711Command(TX_RDS_BUFF, {TX_RDS_BUFF_IN_INTACK, 0, 0, 0, 0, 0, 0}, out); - //printf("int ack out: %2X %2X %2X %2X %2X %2X %2X %2X\n", out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7]); + sendSi4711Command(0x14, {}); + sendSi4711Command(TX_RDS_BUFF, {TX_RDS_BUFF_IN_INTACK, 0, 0, 0, 0, 0, 0}); } @@ -603,7 +228,6 @@ void Si4713::sendRtPlusInfo(int content1, int content1_pos, int content1_len, if (content1_len || content2_len) { - memset (msg, 0x00, 6); bitstream_t *bs = nullptr; bs_create(&bs); @@ -626,11 +250,9 @@ void Si4713::sendRtPlusInfo(int content1, int content1_pos, int content1_len, bs_put(bs, content2_len, 5); //length marker 2 (5 bits!) } - std::vector out; sendSi4711Command(TX_RDS_BUFF, {TX_RDS_BUFF_IN_LDBUFF, RTPLUS_GROUP_ID << 4, - msg[0], msg[1], msg[2], msg[3], msg[4]}, out); - printf("RTPLUS Settings: %2X %2X %2X %2X %2X %2X %2X %2X\n", out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7]); + msg[0], msg[1], msg[2], msg[3], msg[4]}); //send RT+ announces // FmRadioController::HandleRDSData @@ -648,8 +270,7 @@ void Si4713::sendRtPlusInfo(int content1, int content1_pos, int content1_len, //zzzz - for rds server needs. 0, //template id=0 0x4B, 0xD7 //it's RT+ - }, out); - printf("RTPLUS Group: %2X %2X %2X %2X %2X %2X %2X %2X\n", out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7]); + }); } } diff --git a/src/Si4713.h b/src/Si4713.h index 1d5c30a..ebe9422 100755 --- a/src/Si4713.h +++ b/src/Si4713.h @@ -5,55 +5,44 @@ #include #include -class Si4713Connector; - class Si4713 { public: - Si4713(uint16_t _usVID, uint16_t _usPID); //usb - + Si4713(); virtual ~Si4713(); - - bool isOk(); - bool init(); - - std::string getRev(); - void powerUp(); - void powerDown(); - void reset(); + + void Init(); + + virtual bool isOk() = 0; + virtual std::string getRev() = 0; + virtual void powerUp() = 0; + virtual void powerDown() = 0; + virtual void reset() = 0; + virtual std::string getASQ() = 0; + virtual std::string getTuneStatus() = 0; void setEUPreemphasis() {isEUPremphasis = true;} void setFrequency(int frequency); // freq * 100, so 8790 for 87.9 void setTXPower(int power, double antCap); - std::string getASQ(); - std::string getTuneStatus(); //RDS stuff void setPTY(int i) { pty = i;} void beginRDS(); void setRDSStation(const std::vector &station); - void setRDSBuffer(const std::string &rds); + void setRDSBuffer(const std::string &rds, + int artistPos, int artistLen, + int titlePos, int titleLen); void sendTimestamp(); - - //USB audio - void enableAudio(); - void disableAudio(); + private: - void Init(); - void sendRtPlusInfo(int content1, int content1_pos, int content1_len, int content2, int content2_pos, int content2_len); - bool sendDeviceCommand(uint8_t cmd, bool ignoreFailures = false); - bool sendDeviceCommand(uint8_t cmd, std::vector &out, bool ignoreFailures = false); - bool sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures = false); - bool sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures = false); - bool setProperty(uint16_t prop, uint16_t val); - bool getProperty(uint16_t prop, uint16_t &val); + virtual bool sendSi4711Command(uint8_t cmd, const std::vector &data, bool ignoreFailures = false); + virtual bool sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures = false) = 0; + virtual bool setProperty(uint16_t prop, uint16_t val) = 0; - Si4713Connector * connector = nullptr; - bool isEUPremphasis = false; int pty = 2; std::vector lastStation; diff --git a/src/VASTFMT.cpp b/src/VASTFMT.cpp new file mode 100755 index 0000000..630d154 --- /dev/null +++ b/src/VASTFMT.cpp @@ -0,0 +1,346 @@ + +#include +#include + +#include "log.h" + + +#include "VASTFMT.h" + +#include "hidapi.h" + +// VASTFM vendor/product +//HID\VID_0451&PID_2100&REV_0101&MI_02 +const uint16_t _usVID=0x0451; /*!< USB vendor ID. */ +const uint16_t _usPID=0x2100; /*!< USB product ID. */ + + +enum { + RequestNone = 0, + RequestCpuId, + RequestSi4711Reset, //reset + RequestSi4711Access, //low level access + RequestSi4711GetProp, //medium level get prop + RequestSi4711SetProp, //medium level set prop + RequestSi4711PowerStatus, + RequestSi4711PowerUp, //high level power up + RequestSi4711PowerDown, //high level power down + RequestSi4711AudioEnable, //this MUST BE!!! called after setting config to enable audio. + RequestSi4711AudioDisable, + RequestEepromSectionRead, + RequestEepromSectionWrite, + RequestSi4711AsqStatus, + RequestSi4711TuneStatus, + RequestUnknown +}; +static const char *si471x_requests[] = { + "NONE", + "CPU ID", + "Frontend Reset", + "Frontend Access", + "Get Property", + "Set Property", + "Frontend Power Status", + "Frontend Power Up", + "Frontend Power Down", + "Frontend Enable Audio", + "Frontend Disable Audio", + "EEPROM Read", + "EEPROM Write", + "unknown" +}; +#define Si471xRequestStr(x) (x < RequestUnknown ? si471x_requests[x] : si471x_requests[RequestUnknown]) + +enum { + SI4711_OK = 0, + SI4711_TIMEOUT, + SI4711_COMM_ERR, + SI4711_BAD_ARG, + SI4711_NOCTS, + SI4711_ERROR_UNKNOWN +}; +static const char *si471x_statuses[] = { + "OK", + "I2C Timeout", + "I2C Communication Error", + "Bad Argument", + "No CTS!", + "unknown" +}; +#define Si471xStatusStr(x) (x < SI4711_ERROR_UNKNOWN ? si471x_statuses[x] : si471x_statuses[SI4711_ERROR_UNKNOWN]) + + +#define PCRequestError 0x80 +#define PCTransfer 0x02 +#define RequestDone 0x80 + +//status bits +#define STATUS_BIT_STCINT 0x01 +#define STATUS_BIT_ASQINT 0x02 +#define STATUS_BIT_RDSINT 0x04 +#define STATUS_BIT_RSQINT 0x08 +#define STATUS_BIT_ERR 0x40 +#define STATUS_BIT_CTS 0x80 + + +VASTFMT::VASTFMT() : Si4713() { + struct hid_device_info *phdi = nullptr; + phdi = hid_enumerate(_usVID,_usPID); + if (phdi == nullptr) { + return; + } + phd = hid_open_path(phdi->path); + hid_free_enumeration(phdi); + phdi=nullptr; + + + if (phd) { + powerUp(); + } +} +VASTFMT::~VASTFMT() { + if (phd) { + hid_close(phd); + } +} +bool VASTFMT::isOk() { + return phd != nullptr; + +} +bool VASTFMT::sendDeviceCommand(uint8_t cmd, bool ignoreFailures) { + std::vector out; + return sendDeviceCommand(cmd, out, ignoreFailures); +} +bool VASTFMT::sendDeviceCommand(uint8_t cmd, std::vector &dataOut, bool ignoreFailures) { + unsigned char aucBufIn[43]; + unsigned char aucBufOut[43]; + memset(aucBufOut, 0x00, 43); // Clear out the response buffer + memset(aucBufIn, 0xCC, 43); // Clear out the response buffer + + /* Send a BL Query Command */ + aucBufOut[0] = 0; // Report ID, ignored + aucBufOut[1] = PCTransfer; + aucBufOut[2] = cmd; + + hid_write(phd, aucBufOut, 43); + int r = hid_read(phd, aucBufIn, 43); + + if (r < 2) { + LogWarn(VB_PLUGIN, "Si4713/USB: not enough data\n"); + return false; + } + if (!ignoreFailures && aucBufIn[0] & PCRequestError) { + LogWarn(VB_PLUGIN, "Si4713/USB: request error\n"); + return false; + } + if (!ignoreFailures && !(aucBufIn[0] & PCTransfer)) { + LogWarn(VB_PLUGIN, "Si4713/USB: Transfer error.\n"); + return false; + } + if (ignoreFailures || aucBufIn[1] == (cmd|RequestDone)) { + dataOut.resize(r - 2); + memcpy(&dataOut[0], &aucBufIn[2], r - 2); + return true; + } else { + LogWarn(VB_PLUGIN, "Si4713/USB: Request not done.\n"); + } + return false; +} +bool VASTFMT::sendSi4711Command(uint8_t cmd, const std::vector &dataIn, std::vector &dataOut, bool ignoreFailures) { + unsigned char aucBufIn[43]; + unsigned char aucBufOut[43]; + memset(aucBufOut, 0x00, 43); // Clear out the response buffer + memset(aucBufIn, 0xCC, 43); // Clear out the response buffer + + /* Send a BL Query Command */ + aucBufOut[0] = 0; // Report ID, ignored + aucBufOut[1] = PCTransfer; + aucBufOut[2] = RequestSi4711Access; + aucBufOut[3] = dataIn.size() + 1; + aucBufOut[4] = cmd; + memcpy(&aucBufOut[5], &dataIn[0], dataIn.size()); + + hid_write(phd, aucBufOut, 43); + int r = hid_read(phd, aucBufIn, 42); + /* + printf("CMD: %2x Read: %d\n", cmd, r); + for (int x = 0; x < 25; x++) { + printf("%x ", aucBufIn[x]); + } + printf("\n"); + */ + if (!ignoreFailures && aucBufIn[2] != 1) { + LogWarn(VB_PLUGIN, "Si4711/USB: command failed (%d): %s, returned (FALSE)\n", aucBufIn[2], Si471xStatusStr(aucBufIn[2])); + //sometimes it timeoutes, but the CTS=0, so we must re-read this byte again. + //return false; + } + + if (!ignoreFailures && aucBufIn[3]!=SI4711_OK) { + LogWarn(VB_PLUGIN, "Si4711/USB: I2C_READ failed (%d): %s\n", aucBufIn[3], Si471xStatusStr(aucBufIn[3])); + return false; + } + + if(!ignoreFailures && aucBufIn[4] > 16) { + LogWarn(VB_PLUGIN, "Si4711/USB: I2C_READ failed, too much bytes received: %d\n", aucBufIn[4]); + return false; + } + + //LogDebug(VB_PLUGIN, "Si4711/USB: Volume: %d.%d (deviation: %d)\n", (int) aucBufIn[21], (int ) aucBufIn[22], (int ) (aucBufIn[23] << 8 | aucBufIn[24])); + //printf("Si4711/USB: Volume: %d.%d (deviation: %d)\n", (int ) aucBufIn[21], (int ) aucBufIn[22], (int ) (aucBufIn[23] << 8 | aucBufIn[24])); + //printf("datasize: %d\n", aucBufIn[4]); + int sz = 16; // aucBufIn[4] + dataOut.resize(sz); + memcpy(&dataOut[0], &aucBufIn[5], sz); + return true; +} +bool VASTFMT::setProperty(uint16_t prop, uint16_t val) { + unsigned char aucBufIn[43]; + unsigned char aucBufOut[43]; + memset(aucBufOut, 0x00, 43); // Clear out the response buffer + memset(aucBufIn, 0xCC, 43); // Clear out the response buffer + aucBufOut[0] = 0x00; //report number, would be unused! + aucBufOut[1] = PCTransfer; // + aucBufOut[2] = RequestSi4711SetProp; + aucBufOut[3] = prop >> 8; + aucBufOut[4] = prop; + aucBufOut[5] = val >> 8; + aucBufOut[6] = val; + hid_write(phd, aucBufOut, 43); + hid_read(phd, aucBufIn, 42); + + if (aucBufIn[0] & PCRequestError) { + LogWarn(VB_PLUGIN, "Si4713/USB: request error for property %X.\n", prop); + return false; + } + + if (!(aucBufIn[1] & RequestDone)) { + LogWarn(VB_PLUGIN, "Si4713/USB: request is not done!\n"); + return false; + } + + if (aucBufIn[8]!=SI4711_OK) { + LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%d): %s\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[2], Si471xStatusStr(aucBufIn[2])); + return false; + } + + if (aucBufIn[7] & STATUS_BIT_ERR) { + LogWarn(VB_PLUGIN, "Si4713/USB: Answers: Error setting property 0x%04x to \"0x%04x\".\n", prop, val); + return false; + } + + if (!(aucBufIn[7] & STATUS_BIT_CTS)) { + LogWarn(VB_PLUGIN, "Si4713/USB: Answers: NO CTS! When setting property 0x%04x.\n", prop); + return false; + } + + if (aucBufIn[6] != 1) { + LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%02x): %s (false!)\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[6]); + return false; + } + return true; +} +bool VASTFMT::getProperty(uint16_t prop, uint16_t &val) { + unsigned char aucBufIn[43]; + unsigned char aucBufOut[43]; + memset(aucBufOut, 0x00, 43); // Clear out the response buffer + memset(aucBufIn, 0xCC, 43); // Clear out the response buffer + aucBufOut[0] = 0x00; //report number, would be unused! + aucBufOut[1] = PCTransfer; // + aucBufOut[2] = RequestSi4711GetProp; + aucBufOut[3] = prop >> 8; + aucBufOut[4] = prop; + hid_write(phd, aucBufOut, 43); + hid_read(phd, aucBufIn, 42); + + if (aucBufIn[0] & PCRequestError) { + LogWarn(VB_PLUGIN, "Si4713/USB: request error for property %X.\n", prop); + return false; + } + + if (!(aucBufIn[1] & RequestDone)) { + LogWarn(VB_PLUGIN, "Si4713/USB: request is not done!\n"); + return false; + } + + if (aucBufIn[8]!=SI4711_OK) { + LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%d): %s\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[2], Si471xStatusStr(aucBufIn[2])); + return false; + } + + if (aucBufIn[7] & STATUS_BIT_ERR) { + LogWarn(VB_PLUGIN, "Si4713/USB: Answers: Error setting property 0x%04x to \"0x%04x\".\n", prop, val); + return false; + } + + if (!(aucBufIn[7] & STATUS_BIT_CTS)) { + LogWarn(VB_PLUGIN, "Si4713/USB: Answers: NO CTS! When setting property 0x%04x.\n", prop); + return false; + } + + if (aucBufIn[6] != 1) { + LogWarn(VB_PLUGIN, "Si4713/USB: Device request \"%s\" failed (%02x): %s (false!)\n", Si471xRequestStr(RequestSi4711SetProp), aucBufIn[6]); + return false; + } + val = (int16_t ) ((aucBufIn[4] & 0x00FF) << 8) | aucBufIn[5]; + return true; +} +void VASTFMT::powerUp() { + sendDeviceCommand(RequestSi4711PowerUp, true); +} +void VASTFMT::powerDown() { + sendDeviceCommand(RequestSi4711PowerDown, true); +} +void VASTFMT::reset() { + sendDeviceCommand(RequestSi4711Reset, true); +} +std::string VASTFMT::getRev() { + std::vector out; + sendSi4711Command(0x10, {0}, out); + + if (sendDeviceCommand(RequestCpuId, out)) { + std::string rev = (char*)(&out[3]); + std::string board = (char*)(&out[5 + rev.size()]); + return board + " - " + rev; + } + return ""; +} + +std::string VASTFMT::getASQ() { + std::vector out; + if (sendDeviceCommand(RequestSi4711AsqStatus, out)) { + std::string r = "ASQ Flags: "; + r += std::to_string(out[1]) + " " + std::to_string(out[2]) + " " + std::to_string(out[3]); + r += " - InLevel:"; + int lev = out[4]; + if (lev > 127) { + lev -= 256; + } + r += std::to_string(lev); + r += " dBfs"; + return r; + } + return ""; +} +std::string VASTFMT::getTuneStatus() { + std::vector out; + if (sendDeviceCommand(RequestSi4711TuneStatus, out)) { + int currFreq = out[1] << 8 | out[2]; + int currdBuV = out[3]; + int currAntCap = out[4]; + + float f = currFreq / 100.0f; + float cap = ((float)currAntCap) * 0.25f; + + std::string r = "Freq:" + std::to_string(f) + + " MHz - Power:" + std::to_string(currdBuV) + + "dBuV - ANTcap:" + std::to_string(cap); + return r; + } + return ""; +} +void VASTFMT::enableAudio() { + sendDeviceCommand(RequestSi4711AudioEnable); +} +void VASTFMT::disableAudio() { + sendDeviceCommand(RequestSi4711AudioDisable); +} diff --git a/src/VASTFMT.h b/src/VASTFMT.h new file mode 100755 index 0000000..e42ebe4 --- /dev/null +++ b/src/VASTFMT.h @@ -0,0 +1,39 @@ +#ifndef __VASTFMT__ +#define __VASTFMT__ + +#include "Si4713.h" + +struct hid_device_; + +class VASTFMT : public Si4713 { +public: + VASTFMT(); + virtual ~VASTFMT(); + + + virtual bool isOk() override; + virtual std::string getRev() override; + virtual void powerUp() override; + virtual void powerDown() override; + virtual void reset() override; + virtual std::string getASQ() override; + virtual std::string getTuneStatus() override; + + + void enableAudio(); + void disableAudio(); +protected: + virtual bool sendSi4711Command(uint8_t cmd, const std::vector &data, std::vector &out, bool ignoreFailures = false); + virtual bool setProperty(uint16_t prop, uint16_t val); + + bool getProperty(uint16_t prop, uint16_t &val); + bool sendDeviceCommand(uint8_t cmd, bool ignoreFailures = false); + bool sendDeviceCommand(uint8_t cmd, std::vector &dataOut, bool ignoreFailures = false); + + +private: + hid_device_ *phd = nullptr; +}; + + +#endif