Skip to content

Commit

Permalink
Support the i2c Si4713 chips
Browse files Browse the repository at this point in the history
  • Loading branch information
dkulp committed Sep 4, 2019
1 parent 950981b commit 64f7583
Show file tree
Hide file tree
Showing 9 changed files with 687 additions and 456 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -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

Expand Down
48 changes: 30 additions & 18 deletions plugin_setup.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php $outputGPIOReset = "";
if (isset($_POST["GPIOResetButton"]))
{
$outputGPIOReset = shell_exec(escapeshellcmd("sudo ".$pluginDirectory."/".$_GET['plugin']."/callbacks.py --reset"));
}
<?php

$defaultGPIO = "4";
if ($settings['Platform'] == "BeagleBone Black") {
$defaultGPIO = "14";
}
?>

<script type="text/javascript">
Expand All @@ -18,41 +19,52 @@ function toggle(id) {
</script>


<div id="VASTFMTPluginhw" class="settings">
<fieldset>
<legend>VAST-FMT/Si4713 Hardware</legend>
<p>Connection: <?php PrintSettingSelect("Connection", "Connection", 1, 0, "USB", Array("USB"=>"USB", "I2C"=>"I2C"), "fpp-vastfmt", ""); ?></p>
<p>Reset GPIO: <?php PrintSettingTextSaved("ResetPin", 1, 0, 6, 6, "fpp-vastfmt", $defaultGPIO); ?><br />
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. </p>
</fieldset>
</div>

<br />

<div id="VASTFMTPluginsettings" class="settings">
<fieldset>
<legend>VAST-FMT Plugin Settings</legend>
<p>Start VAST-FMT at: <?php PrintSettingSelect("Start", "Start", 1, 0, "FPPDStart", Array("FPPD Start (default)"=>"FPPDStart", "Playlist Start"=>"PlaylistStart", "Never"=>"Never"), "fpp-vastfmt", ""); ?><br />
At Start, the VAST-FMT is reset, FM settings initialized, will broadcast any audio played, and send static RDS messages (if enabled).</p>
<p>Stop VAST-FMT at: <?php PrintSettingSelect("Stop", "Stop", 1, 0, "Never", Array("Playlist Stop"=>"PlaylistStop", "Never (default)"=>"Never"), "fpp-vastfmt", ""); ?><br />
At Stop, the VAST-FMT is reset. Listeners will hear static.</p>
<legend>VAST-FMT/Si4713 Plugin Settings</legend>
<p>Start at: <?php PrintSettingSelect("Start", "Start", 1, 0, "FPPDStart", Array("FPPD Start (default)"=>"FPPDStart", "Playlist Start"=>"PlaylistStart", "Never"=>"Never"), "fpp-vastfmt", ""); ?><br />
At Start, the hardware is reset, FM settings initialized, will broadcast any audio played, and send static RDS messages (if enabled).</p>
<p>Stop at: <?php PrintSettingSelect("Stop", "Stop", 1, 0, "Never", Array("Playlist Stop"=>"PlaylistStop", "Never (default)"=>"Never"), "fpp-vastfmt", ""); ?><br />
At Stop, the hardware is reset. Listeners will hear static.</p>
</fieldset>
</div>

<br />

<div id="VASTFMTsettings" class="settings">
<fieldset>
<legend>VAST-FMT FM Settings</legend>
<p>Frequency (76.00-108.00): <?php PrintSettingText("Frequency", 1, 0, 6, 6, "fpp-vastfmt", "100.10"); ?>MHz <?php PrintSettingSave("Frequency", "Frequency", 1, 0, "fpp-vastfmt"); ?></p>
<p>Power (88-115, 116-120<sup>*</sup>): <?php PrintSettingText("Power", 1, 0, 3, 3, "fpp-vastfmt", "110"); ?>dB&mu;V <?php PrintSettingSave("Power", "Power", 1, 0, "fpp-vastfmt"); ?>
<legend>VAST-FMT/Si4713 FM Settings</legend>
<p>Frequency (76.00-108.00): <?php PrintSettingTextSaved("Frequency", 1, 0, 6, 6, "fpp-vastfmt", "100.10"); ?>MHz</p>
<p>Power (88-115, 116-120<sup>*</sup>): <?php PrintSettingTextSaved("Power", 1, 0, 3, 3, "fpp-vastfmt", "110"); ?>dB&mu;V
<br /><sup>*</sup>Can be set as high as 120dB&mu;V, but voltage accuracy above 115dB&mu;V is not guaranteed.</p>
<p>Preemphasis: <?php PrintSettingSelect("Preemphasis", "Preemphasis", 1, 0, "75us", Array("50&mu;s (Europe, Australia, Japan)"=>"50us", "75&mu;s (USA, default)"=>"75us"), "fpp-vastfmt", ""); ?></p>
<p>Antenna Tuning Capacitor (0=Auto, 1-191): <?php PrintSettingText("AntCap", 1, 0, 3, 3, "fpp-vastfmt", "0"); ?> * 0.25pF <?php PrintSettingSave("AntCap", "AntCap", 1, 0, "fpp-vastfmt"); ?></p>
<p>Antenna Tuning Capacitor (0=Auto, 1-191): <?php PrintSettingText("AntCap", 1, 0, 3, 3, "fpp-vastfmt", "0"); ?> * 0.25pF </p>
</fieldset>
</div>

<br />

<div id="VASTFMTRDSsettings" class="settings">
<fieldset>
<legend>VAST-FMT RDS Settings</legend>
<legend>VAST-FMT/Si4713 RDS Settings</legend>
<p>Enable RDS: <?php PrintSettingCheckbox("EnableRDS", "EnableRDS", 1, 0, "True", "False", "fpp-vastfmt", ""); ?></p>
<p>RDS Station - Sent 8 characters at a time</p>
<p>Station Text: <?php PrintSettingText("StationText", 1, 0, 64, 32, "fpp-vastfmt", "Merry Christ- mas"); ?>
<p>RDS Station - Sent 8 characters at a time. Max of 64 characters.<br />
Station Text: <?php PrintSettingTextSaved("StationText", 1, 0, 64, 32, "fpp-vastfmt", "Merry Christ- mas"); ?>

<br />

<p>RDS Text: <?php PrintSettingText("RDSTextText", 1, 0, 64, 32, "fpp-vastfmt", "[{Artist} - {Title}]"); ?>
<p>RDS Text: <?php PrintSettingTextSaved("RDSTextText", 1, 0, 64, 32, "fpp-vastfmt", "[{Artist} - {Title}]"); ?>
<p>
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.

Expand Down
38 changes: 28 additions & 10 deletions src/FPPVastFM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@
#include <unistd.h>
#include <termios.h>

#include <httpserver.hpp>
#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;
Expand Down Expand Up @@ -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());
Expand All @@ -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);
Expand All @@ -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 == "") {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 &section, 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;
Expand Down Expand Up @@ -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()) {
Expand Down
171 changes: 171 additions & 0 deletions src/I2CSi4713.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@

#include <unistd.h>
#include <cstring>
#include <sys/time.h>

#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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> &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<uint8_t> &data, std::vector<uint8_t> &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;
}
Loading

0 comments on commit 64f7583

Please sign in to comment.