Greetings,
Using a FM transmitter module and the FMXmtr plug-in I created to control it you can broadcast your favorite music from a Raspberry Pi with Volumio installed.
The transmitter module can be found on Ebay and Amazon. See links below. Ebay is cheaper, but takes longer to get because they are coming on the slow boat from China.
These are I2C modules based on the KT-0803K FM transmitter on an IC. Any I2C module based on the KT-0803 series should work.
Connecting to a Pi requires 4 wires. Connect VCC (3.3vdc), ground, SDA and SCL to the respective pins on the 40-pin GPIO header.
Pin 1 = 3.3vdc
Pin 3 = GPIO2 / SDA
Pin 5 = GPIO3 / SCL
Pin 9 = ground
Note: If pins 3 and 5 (GPIO2 and GPIO3) have been configured for in or out signal I@C will note work.
Audio is a 3.5mm audio jack. You can connect directly to the audio out on the PI, but the sound quality is not that great. Recommend a USB sound card or a DAC.
Final connection is the antenna. A piece of wire soldered to the antenna connection. Optimal wire length is 5 foot. Shorter lengths still work. I used a 10 inch wire for testing and was able to pick up the signal about 75 yards away with RF gain set at full power.
I created a plug-in to control the transmitter from within Volumio. Pushed it to git hub. This is my first time writing a plug-in. First time using GitHub. According to documentation it should go to the Volumio maintainers for review and then get put in to the official plug-ins if they agree with it, provided I did everything correctly. I’ll post the required files below in case I didn’t publish it correctly. This way anybody who want to use it can create a blank plug-in and paste these into it.
The required files are “install.sh”, “package.json”, “config.json”, “index.js”, “UIConfig.json” and “strings_en.json”. The plug-in folder is labeled “fmxmtr”. Except for “strings_en.json”, all files go in “fmxmtr”. “strings_en.json” goes in “fmxmtr/i18n”.
install.sh
[code]#!/bin/bash
echo “Installing fmxmtr Dependencies”
sudo apt-get update
Install the required packages via apt-get
echo “Installing build-essentials”
sudo apt-get -y install build-essential --no-install-recommends
echo “Installing I2C-tools”
sudo apt-get -y install i2c-tools --no-install-recommends
echo "Installing base functionality for working with a Raspberry Pi from Node.js "
npm install raspi --no-install-recommends
If you need to differentiate install for armhf and i386 you can get the variable like this
#DPKG_ARCH=dpkg --print-architecture
Then use it to differentiate your install
#requred to end the plugin install
echo “plugininstallend”
[/code]
package.json
{
"name": "fmxmtr",
"version": "1.0.1",
"description": "Set up and control I2C FM transmitter module by Electhouse based on KT-0803K fm transmitter chip",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Randy Bancroft",
"license": "ISC",
"volumio_info": {
"prettyName": "FM Transmitter",
"icon": "fa-wifi",
"plugin_type": "miscellanea"
},
"dependencies": {
"fs-extra": "^0.28.0",
"kew": "^0.7.0",
"v-conf": "^1.4.0"
}
}
config.json
{
"I2C_BUS": {
"type": "number",
"value": "1"
},
"I2C_ADDRESS": {
"type": "text",
"value": "3e"
},
"Freq": {
"type": "number",
"value": "94"
},
"BaseBoost": {
"type": "number",
"value": "2"
},
"RFGain": {
"type": "number",
"value": "15"
},
"MuteAudio": {
"type": "boolean",
"value": false
},
"MonoAudio": {
"type": "boolean",
"value": false
},
"PGAMod": {
"type": "number",
"value": "0"
},
"PGAmp": {
"type": "number",
"value": "12"
},
"PltAdj": {
"type": "number",
"value": "0"
},
"PhTCnst": {
"type": "number",
"value": "0"
},
"PDPA": {
"type": "boolean",
"value": false
},
"StartUp": {
"type": "boolean",
"value": false
}
}
index.js
[code]‘use strict’;
var libQ = require(‘kew’);
var fs=require(‘fs-extra’);
var config = new (require(‘v-conf’))();
var exec = require(‘child_process’).exec;
var execSync = require(‘child_process’).execSync;
var shell = require(‘shelljs’);
// Create ShellCommand variable. It is used in several functions
var ShellCommand = ‘’;
// Define variables to hold register values read from transmitter module
var Reg0x00 = 0;
var Reg0x01 = 0;
var Reg0x02 = 0;
var Reg0x04 = 0;
var Reg0x0B = 0;
var Reg0x10 = 0;
var Reg0x12 = 0;
var Reg0x13 = 0;
// Define variables for decoded register values
var FreqRead = 0;
var BaseBoostRead = 0;
var RFGainRead = 0;
var MuteAudioRead = 0;
var MonoAudioRead = 0;
var PGAModRead = 0;
var PGAmpRead = 0;
var PltAdjRead = 0;
var PhTCnStRead = 0;
var PDPARead = false;
// Define variables for data read from conf.json
var FreqStored=0;
var BaseBoostStored=0;
var RFGainStored=0;
var MuteAudioStored=false;
var MonoAudioStored=false;
var PGAModStored=0;
var PGAmpStored=0;
var PltAdjStored=0;
var PhTCnstStored=0;
var PDPAStored=false;
var StartUpStored=false;
module.exports = Controllerfmxmtr;
function Controllerfmxmtr(context) {
var self = this;
this.context = context;
this.commandRouter = this.context.coreCommand;
this.logger = this.context.logger;
this.configManager = this.context.configManager;
}
Controllerfmxmtr.prototype.onVolumioStart = function()
{
var self = this;
var configFile=this.commandRouter.pluginManager.getConfigurationFile(this.context,‘config.json’);
this.config = new (require(‘v-conf’))();
this.config.loadFile(configFile);
// check to see if config.json exists
var checkfile = ("/bin/cp " + configFile + " /tmp/config.txt");
var exists = false;
try {
var cp3 = execSync(checkfile);
exists = true;
} catch (err) {
self.logger.info(‘fmxmtr config.json does not exist’);
exists = false;
}
if(exists){
// Disable broadcasting. a.k.a. Airplane mode
ShellCommand = ‘i2cset -y ’ + this.config.get(‘I2C_BUS’) + ’ 0x’ + this.config.get(‘I2C_ADDRESS’) + ’ 0x0B 0x20’;
shell.exec(ShellCommand);
self.logger.info(‘Disabled RF output’);
// Config.json exists, read user configuration from config.json and write to transmitter module
// I2C_BUS
var I2CBusSave = this.config.get(‘I2C_BUS’);
// I2C_ADDRESS
var I2CAddressSave = this.config.get(‘I2C_ADDRESS’);
// Freq
var FreqSave = this.config.get(‘Freq’);
FreqSave = (FreqSave * 1000)/50;
// Convert config.json values to register values
// Frequency is 12 bits divided over three registers
// Register 0x02 bit 0x07 = Freq bit 0x01
// Register 0x00 bits 0x07 to 0x00 = Freq bits 0x08 to 0x01
// Register 0x01 bits 0x02 to 0x00 = Freq bits 0x11 to 0x09
// Split Freq into three registers
var Reg0x02Freq = (FreqSave & 0x0001) *8;
var Reg0x00Freq = (FreqSave / 2) & 0x00ff;
var Reg0x01Freq = ((FreqSave & 0x0600) / 512);
// BaseBoost
var BaseBoostSave = this.config.get(‘BaseBoost’);
// BaseBoost is two bits in one register
// Register 0x04 bits 0x01 to 0x00 = BaseBoost bits 0x01 to 0x00
var Reg0x04BaseBoost = (BaseBoostSave & 0x03);
// RFGain
var RFGainSave = this.config.get(‘RFGain’);
// RFGain is 4 bits divided over tthree registers
// Register 0x02 bit 0x06 = RFGain bit 0x03
// Register 0x13 bit 0x07 = RFGain bit 0x02
// Register 0x01 bits 0x07 to 0x06 - RFGain bits 0x01 to 0x00
var Reg0x02FRGain = (RFGainSave & 0x08) * 8;
var Reg0x13FRGain = (RFGainSave & 0x04) * 32;
var Reg0x01FRGain = (RFGainSave & 0x03) * 64;
// MuteAudio
var MuteAudioSave = this.config.get(‘MuteAudio’);
// MuteAudio is one bit in one register
// Register 0x02 bit 0x03 = MuteAudio logic state
var Reg0x02MuteAudio = 0x00;
if (MuteAudioSave){
Reg0x02MuteAudio = 0x08;
} else {
Reg0x02MuteAudio = 0x00;
};
// MonoAudio
var MonoAudioSave = this.config.get(‘MonoAudio’);
// MonoAudio is one bit in one register
// Register 0x04 bit 0x06 = MonoAudio logic state
var Reg0x04MonoAudio = 0x00;
if (MonoAudioSave){
Reg0x04MonoAudio = 0x40;
} else {
Reg0x04MonoAudio = 0x00;
};
// PGAMod
var PGAModSave = this.config.get(‘PGAMod’);
// PGAMod is three bits in one register
// Register 0x10 bit 0x00 = PGAMod bit 0x00
var Reg0x10PGAMod = PGAModSave
// PGAmp
var PGAmpSave = this.config.get(‘PGAmp’);
// PGAmp is 5 bits divided over two registers
// Register 0x01 bits 0x05 to 0x03 = PGAmp bits 0x04 to 0x02
// Register 0x04 bits 0x05 to 0x04 - PGAmp bits 0x01 to 0x00
if (PGAmpSave < 0){
PGAmpSave = PGAmpSave * -1;
}
else{
PGAmpSave = (PGAmpSave *1) + 16;
};
var Reg0x01PGAmp = (PGAmpSave & 0x1c) * 2;
var Reg0x04PGAmp = (PGAmpSave & 0x03) * 16;
// PltAdj
var PltAdjSave = this.config.get(‘PltAdj’);
// PltAdj is one bit in one register
// Register 0x02 bit 0x02 = PltAdj bit 0x00
var Reg0x02PltAdj = (PltAdjSave & 0x01) * 4;
// PhTCnst
var PhTCnstSave = this.config.get(‘PhTCnst’);
// PhTCnst is one bit in one register
// Register 0x02 bit 0x00 = PhTCnst bit 0x00
var Reg0x02PhTCnst = (PhTCnstSave & 0x01);
// PDPA
var PDPASave = this.config.get(‘PDPA’);
// PDPA is one bit in one register
// Register 0x0B bit 0x05 = PDPA logic state
var Reg0x0BPDPA = 0x00;
if (PDPASave){
Reg0x0BPDPA = 0x20;
} else {
Reg0x0BPDPA = 0x00;
};
// combine all bit per register
// Register 0x00 is made up of Reg0x00Freq only
var Reg0x00Write = Reg0x00Freq;
// Register 0x01 is made up of Reg0x01FRGain, Reg0x01PGAmp and Reg0x01Freq
var Reg0x01Write = (Reg0x01FRGain + Reg0x01PGAmp + Reg0x01Freq);
//Register 0x02 is made up of Reg0x02Freq, Reg0x02FRGain, Reg0x02MuteAudio, Reg0x02PltAdj and Reg0x02PhTCnst
var Reg0x02Write = (Reg0x02 & 0x32) + (Reg0x02Freq + Reg0x02FRGain + Reg0x02MuteAudio + Reg0x02PltAdj + Reg0x02PhTCnst);
// Register 0x04 is made up of Reg0x04MonoAudio, Reg0x04PGAmp and Reg0x04BaseBoost
var Reg0x04Write = (Reg0x04 & 0x8c) + (Reg0x04MonoAudio + Reg0x04PGAmp + Reg0x04BaseBoost);
// Register 0x0b is made up of Reg0x0BPDPA only
var Reg0x0BWrite = (Reg0x0B & 0xdf) + Reg0x0BPDPA;
// Register 0x10 is made up of Reg0x10PGAMod only
var Reg0x10Write = (Reg0x10 & 0xfe) + Reg0x10PGAMod;
// Register 0x13 is made up of only Reg0x13FRGain
var Reg0x13Write = (Reg0x13 & 0x7f) + Reg0x13FRGain;
// Write registers
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x00 0x’ + Reg0x00Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x01 0x’ + Reg0x01Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x02 0x’ + Reg0x02Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x04 0x’ + Reg0x04Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x0B 0x’ + Reg0x0BWrite.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x10 0x’ + Reg0x10Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x13 0x’ + Reg0x13Write.toString(16);
shell.exec(ShellCommand);
// Check to see if user desires to start transmitting on Volumio Start
if (this.config.get(‘StartUp’)){
ShellCommand = ‘i2cset -y ’ + this.config.get(‘I2C_BUS’) + ’ 0x’ + this.config.get(‘I2C_ADDRESS’) + ’ 0x0B 0x00’;
shell.exec(ShellCommand);
}
else
{
ShellCommand = ‘i2cset -y ’ + this.config.get(‘I2C_BUS’) + ’ 0x’ + this.config.get(‘I2C_ADDRESS’) + ’ 0x0B 0x20’;
shell.exec(ShellCommand);
};
}
else {
// config.json does not exist, create it, fill with defaults and write defaults to registers on transmitter module
// Disable broadcasting using default values. a.k.a. Airplane mode
shell.exec(‘i2cset -y 1 0x3e 0x0B 0x20’);
self.logger.info(‘Disabled RF output’);
// Create file
ShellCommand = ('echo {} > ’ + configFile);
shell.exec(ShellCommand);
self.logger.info(‘Created fmxmtr config.json’);
// Fill it with default values
self.config.set(‘I2C_BUS’,1);
self.config.set(‘I2C_ADDRESS’,“3e”);
self.config.set(‘Freq’,“94.5”);
self.config.set(‘BaseBoost’,0);
self.config.set(‘RFGain’,15);
self.config.set(‘MuteAudio’,false);
self.config.set(‘MonoAudio’,false);
self.config.set(‘PGAMod’,0);
self.config.set(‘PGAmp’,0);
self.config.set(‘PltAdj’,0);
self.config.set(‘PhTCnst’,0);
self.config.set(‘PDPA’,false);
self.config.set(‘StartUp’,true);
// Set default values on transmiter module
// Write default registers
shell.exec(‘i2cset -y 1 0x3e 0x00 0xb1’);
shell.exec(‘i2cset -y 1 0x3e 0x01 0xe3’);
shell.exec(‘i2cset -y 1 0x3e 0x02 0x40’);
shell.exec(‘i2cset -y 1 0x3e 0x04 0x04’);
shell.exec(‘i2cset -y 1 0x3e 0x0b 0x00’);
shell.exec(‘i2cset -y 1 0x3e 0x10 0xa8’);
shell.exec(‘i2cset -y 1 0x3e 0x13 0x80’);
};
return libQ.resolve();
}
Controllerfmxmtr.prototype.onStart = function() {
var self = this;
var defer=libQ.defer();
// Read registers starting values from module
Reg0x00 = shell.exec(‘i2cget -y 1 0x3e 0x00’);
Reg0x01 = shell.exec(‘i2cget -y 1 0x3e 0x01’);
Reg0x02 = shell.exec(‘i2cget -y 1 0x3e 0x02’);
Reg0x04 = shell.exec(‘i2cget -y 1 0x3e 0x04’);
Reg0x0B = shell.exec(‘i2cget -y 1 0x3e 0x0B’);
Reg0x10 = shell.exec(‘i2cget -y 1 0x3e 0x10’);
Reg0x12 = shell.exec(‘i2cget -y 1 0x3e 0x12’);
Reg0x13 = shell.exec(‘i2cget -y 1 0x3e 0x13’);
// Decode registers into working values
FreqRead = ((Reg0x01 & 0x07) * 512) + (Reg0x00 * 2) + ((Reg0x02 & 0x80)/128);
BaseBoostRead = (Reg0x04 & 0x03);
RFGainRead = ((Reg0x02 & 0x40)/8) + ((Reg0x13 & 0x80)/32) + ((Reg0x01 & 0xc0)/64);
MuteAudioRead = ((Reg0x02 & 0x08)/8);
MonoAudioRead = ((Reg0x04 & 0x40)/64);
PGAModRead = (Reg0x10 & 1);
PGAmpRead = ((Reg0x01 & 0x38)/2) + ((Reg0x04 & 0x30)/16);
PltAdjRead = ((Reg0x02 & 0x04)/4);
PhTCnStRead = (Reg0x02 & 0x01);
PDPARead = ((Reg0x0B & 0x20)/32);
// Once the Plugin has successfull started resolve the promise
defer.resolve();
return defer.promise;
};
Controllerfmxmtr.prototype.onStop = function() {
var self = this;
var defer=libQ.defer();
// Once the Plugin has successfull stopped resolve the promise
defer.resolve();
return libQ.resolve();
};
Controllerfmxmtr.prototype.onRestart = function() {
var self = this;
// Optional, use if you need it
};
// Configuration Methods -----------------------------------------------------------------------------
Controllerfmxmtr.prototype.getUIConfig = function() {
var defer = libQ.defer();
var self = this;
var lang_code = this.commandRouter.sharedVars.get('language_code');
self.commandRouter.i18nJson(__dirname+'/i18n/strings_'+lang_code+'.json',
__dirname+'/i18n/strings_en.json',
__dirname + '/UIConfig.json')
.then(function(uiconf)
{
uiconf.sections[0].content[0].value.value = self.config.get('I2C_BUS');
uiconf.sections[0].content[0].value.label = self.config.get('I2C_BUS');
uiconf.sections[0].content[1].value = self.config.get('I2C_ADDRESS');
uiconf.sections[0].content[2].config.bars[0].value = self.config.get('Freq');
uiconf.sections[0].content[3].value.value = self.config.get('BaseBoost');
uiconf.sections[0].content[3].value.label = self.config.get('BaseBoost');
uiconf.sections[0].content[4].value.value = self.config.get('RFGain');
uiconf.sections[0].content[4].value.label = self.config.get('RFGain');
uiconf.sections[0].content[5].value = self.config.get('MuteAudio');
uiconf.sections[0].content[6].value = self.config.get('MonoAudio');
uiconf.sections[0].content[7].value.value = self.config.get('PGAMod');
uiconf.sections[0].content[7].value.label = self.config.get('PGAMod');
if (self.config.get('PGAMod') == 0) {
uiconf.sections[0].content[8].config.bars[0].value = (self.config.get('PGAmp') - (self.config.get('PGAmp') % 5));
uiconf.sections[0].content[8].config.bars[0].step = 5;
} else {
uiconf.sections[0].content[8].config.bars[0].step = 1;
uiconf.sections[0].content[8].config.bars[0].value = self.config.get('PGAmp');
};
uiconf.sections[0].content[9].value.value = self.config.get('PltAdj');
uiconf.sections[0].content[9].value.label = self.config.get('PltAdj');
uiconf.sections[0].content[10].value.value = self.config.get('PhTCnst');
uiconf.sections[0].content[10].value.label = self.config.get('PhTCnst');
uiconf.sections[0].content[11].value = self.config.get('PDPA');
uiconf.sections[0].content[12].value = self.config.get('StartUp');
defer.resolve(uiconf);
}
)
.fail(function()
{
defer.reject(new Error());
}
);
return defer.promise;
};
Controllerfmxmtr.prototype.setUIConfig = function(data) {
var self = this;
//Perform your installation tasks here
};
Controllerfmxmtr.prototype.getConf = function(varName) {
var self = this;
//Perform your installation tasks here
};
Controllerfmxmtr.prototype.setConf = function(varName, varValue) {
var self = this;
//Perform your installation tasks here
};
// define what happens when the user clicks the ‘save’ button on the settings page
// save new values and write them to the transmitter module
Controllerfmxmtr.prototype.SaveFMXmtrOptions = function(data) {
var self = this;
var successful = true;
// Save I2C_BUS
var I2CBusSave = data.I2C_BUS.value;
self.config.set(‘I2C_BUS’,I2CBusSave);
// Save I2C_ADDRESS
var I2CAddressSave = data.I2C_ADDRESS;
self.config.set(‘I2C_ADDRESS’,I2CAddressSave);
// Save Freq
var FreqSave = data.Freq;
self.config.set(‘Freq’,FreqSave);
FreqSave = (FreqSave * 1000)/50;
// Convert save values to register values
// Frequency is 12 bits divided over three registers
// Register 0x02 bit 0x07 = Freq bit 0x01
// Register 0x00 bits 0x07 to 0x00 = Freq bits 0x08 to 0x01
// Register 0x01 bits 0x02 to 0x00 = Freq bits 0x11 to 0x09
// Split Freq into three registers
var Reg0x02Freq = (FreqSave & 0x0001) *8;
var Reg0x00Freq = (FreqSave / 2) & 0x00ff;
var Reg0x01Freq = ((FreqSave & 0x0600) / 512);
// Save BaseBoost
var BaseBoostSave = data.BaseBoost.value;
self.config.set(‘BaseBoost’,BaseBoostSave);
// Convert save values to register values
// BaseBoost is two bits in one register
// Register 0x04 bits 0x01 to 0x00 = BaseBoost bits 0x01 to 0x00
var Reg0x04BaseBoost = (BaseBoostSave & 0x03);
// Save RFGain
var RFGainSave = data.RFGain.value;
self.config.set(‘RFGain’,RFGainSave);
// Convert save values to register values
// RFGain is 4 bits divided over tthree registers
// Register 0x02 bit 0x06 = RFGain bit 0x03
// Register 0x13 bit 0x07 = RFGain bit 0x02
// Register 0x01 bits 0x07 to 0x06 - RFGain bits 0x01 to 0x00
var Reg0x02FRGain = (RFGainSave & 0x08) * 8;
var Reg0x13FRGain = (RFGainSave & 0x04) * 32;
var Reg0x01FRGain = (RFGainSave & 0x03) * 64;
// Save MuteAudio
var MuteAudioSave = data.MuteAudio;
self.config.set(‘MuteAudio’,MuteAudioSave);
// Convert save values to register values
// MuteAudio is one bit in one register
// Register 0x02 bit 0x03 = MuteAudio logic state
var Reg0x02MuteAudio = 0x00;
if (MuteAudioSave){
Reg0x02MuteAudio = 0x08;
} else {
Reg0x02MuteAudio = 0x00;
};
// Save MonoAudio
var MonoAudioSave = data.MonoAudio;
self.config.set(‘MonoAudio’,MonoAudioSave);
// Convert save values to register values
// MonoAudio is one bit in one register
// Register 0x04 bit 0x06 = MonoAudio logic state
var Reg0x04MonoAudio = 0x00;
if (MonoAudioSave){
Reg0x04MonoAudio = 0x40;
} else {
Reg0x04MonoAudio = 0x00;
};
// Save PGAMod
var PGAModSave = data.PGAMod.value;
self.config.set(‘PGAMod’,PGAModSave);
// Convert save values to register values
// PGAMod is three bits in one register
// Register 0x10 bit 0x00 = PGAMod bit 0x00
var Reg0x10PGAMod = PGAModSave
// Save PGAmp
var PGAmpSave = data.PGAmp;
self.config.set(‘PGAmp’,PGAmpSave);
// Convert save values to register values
// PGAmp is 5 bits divided over two registers
// Register 0x01 bits 0x05 to 0x03 = PGAmp bits 0x04 to 0x02
// Register 0x04 bits 0x05 to 0x04 - PGAmp bits 0x01 to 0x00
if (PGAmpSave < 0){
PGAmpSave = PGAmpSave * -1;
}
else{
PGAmpSave = (PGAmpSave *1) + 16;
};
var Reg0x01PGAmp = (PGAmpSave & 0x1c) * 2;
var Reg0x04PGAmp = (PGAmpSave & 0x03) * 16;
// Save PltAdj
var PltAdjSave = data.PltAdj.value;
self.config.set(‘PltAdj’,PltAdjSave);
// Convert save values to register values
// PltAdj is one bit in one register
// Register 0x02 bit 0x02 = PltAdj bit 0x00
var Reg0x02PltAdj = (PltAdjSave & 0x01) * 4;
// Save PhTCnst
var PhTCnstSave = data.PhTCnst.value;
self.config.set(‘PhTCnst’,PhTCnstSave);
// Convert save values to register values
// PhTCnst is one bit in one register
// Register 0x02 bit 0x00 = PhTCnst bit 0x00
var Reg0x02PhTCnst = (PhTCnstSave & 0x01);
// Save PDPA
var PDPASave = data.PDPA;
self.config.set(‘PDPA’,PDPASave);
// Save StartUp
var StartUpSave = data.StartUp;
self.config.set(‘StartUp’,StartUpSave);
// Convert save values to register values
// PDPA is one bit in one register
// Register 0x0B bit 0x05 = PDPA logic state
var Reg0x0BPDPA = 0x00;
if (PDPASave){
Reg0x0BPDPA = 0x20;
} else {
Reg0x0BPDPA = 0x00;
};
// combine all bit per register
// Register 0x00 is made up of Reg0x00Freq only
var Reg0x00Write = Reg0x00Freq;
// Register 0x01 is made up of Reg0x01FRGain, Reg0x01PGAmp and Reg0x01Freq
var Reg0x01Write = (Reg0x01FRGain + Reg0x01PGAmp + Reg0x01Freq);
//Register 0x02 is made up of Reg0x02Freq, Reg0x02FRGain, Reg0x02MuteAudio, Reg0x02PltAdj and Reg0x02PhTCnst
var Reg0x02Write = (Reg0x02 & 0x32) + (Reg0x02Freq + Reg0x02FRGain + Reg0x02MuteAudio + Reg0x02PltAdj + Reg0x02PhTCnst);
// Register 0x04 is made up of Reg0x04MonoAudio, Reg0x04PGAmp and Reg0x04BaseBoost
var Reg0x04Write = (Reg0x04 & 0x8c) + (Reg0x04MonoAudio + Reg0x04PGAmp + Reg0x04BaseBoost);
// Register 0x0b is made up of Reg0x0BPDPA only
var Reg0x0BWrite = (Reg0x0B & 0xdf) + Reg0x0BPDPA;
// Register 0x10 is made up of Reg0x10PGAMod only
var Reg0x10Write = (Reg0x10 & 0xfe) + Reg0x10PGAMod;
// Register 0x13 is made up of only Reg0x13FRGain
var Reg0x13Write = (Reg0x13 & 0x7f) + Reg0x13FRGain;
// Write registers
//shell.exec(‘i2cset -y 1 0x3e 0x00 0x00’);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x00 0x’ + Reg0x00Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x01 0x’ + Reg0x01Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x02 0x’ + Reg0x02Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x04 0x’ + Reg0x04Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x0B 0x’ + Reg0x0BWrite.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x10 0x’ + Reg0x10Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x13 0x’ + Reg0x13Write.toString(16);
shell.exec(ShellCommand);
};
[/code]
UIConfig.json
[code]{
“page”: {
“label”: “FMXmtr CONFIGURATION”
},
“sections”: [
{
“id”: “fmxmtr”,
“element”: “switch”,
“label”: “TRANSLATE.FMXMTR.LABEL”,
“icon”: “volume-off”,
“description”: “TRANSLATE.FMXMTR.DESC”,
“value”: true,
“onSave”: {
“type”:“controller”,
“endpoint”:“miscellanea/fmxmtr”,
“method”:“SaveFMXmtrOptions”
},
“saveButton”: {
“label”: “TRANSLATE.SAVE”,
“data”: [
“I2C_BUS”,
“I2C_ADDRESS”,
“Freq”,
“BaseBoost”,
“RFGain”,
“MuteAudio”,
“MonoAudio”,
“PGAMod”,
“PGAmp”,
“PltAdj”,
“PhTCnst”,
“PDPA”,
“StartUp”
]
},
“content”: [
{
“id”: “I2C_BUS”,
“element”: “select”,
“label”: “TRANSLATE.I2CBUS.LABEL”,
“description”: “TRANSLATE.I2CBUS.DESC”,
“doc”: “TRANSLATE.I2CBUS.DOC”,
“value”: {“value”: 1 ,“label”: “1 - Default”},
“options”: [
{
“value”: 1,
“label”: “1 - Default”
},
{
“value”: 0,
“label”: “0 - Alternate bus”
}
]
},
{
“id”: “I2C_ADDRESS”,
“element”: “input”,
“type”: “text”,
“label”: “TRANSLATE.I2CADDRESS.LABEL”,
“description”: “TRANSLATE.I2CADDRESS.DESC”,
“doc”: “TRANSLATE.I2CADDRESS.DOC”,
“attributes”: [
{“placeholder”: “3E”}, {“maxlength”: 2}
],
“value”: “3E”
},
{
“id”: “Freq”,
“element”: “equalizer”,
“label”: “TRANSLATE.FREQ.LABEL”,
“description”: “TRANSLATE.FREQ.DESC”,
“doc”: “TRANSLATE.FREQ.DOC”,
“config”: {
“orientation”: “horizontal”,
“bars”: [
{
“min”: 70.1,
“max”: 107.9,
“step”: “.2”,
“value”: “94.5”,
“tooltip”: “always”
}
]
}
},
{
“id”: “BaseBoost”,
“element”: “select”,
“label”: “TRANSLATE.BASEBOOST.LABEL”,
“description”: “TRANSLATE.BASEBOOST.DESC”,
“doc”: “TRANSLATE.BASEBOOST.DOC”,
“value”: {“value”: 0 ,“label”: “0 = 0db - Default”},
“options”: [
{
“value”: 0,
“label”: “0 = 0db - Default”
},
{
“value”: 1,
“label”: “1 = 5db”
},
{
“value”: 2,
“label”: “2 = 11db”
},
{
“value”: 3,
“label”: “3 = 17db”
}
]
},
{
“id”: “RFGain”,
“element”: “select”,
“label”: “TRANSLATE.RFGAIN.LABEL”,
“description”: “TRANSLATE.RFGAIN.DESC”,
“doc”: “TRANSLATE.RFGAIN.DOC”,
“value”: {“value”: 15 ,“label”: “15 = 108 dBuV - Default”},
“options”: [
{
“value”: 0,
“label”: “0 = 95.5 dBuV”
},
{
“value”: 1,
“label”: “1 = 96.5 dBuV”
},
{
“value”: 2,
“label”: “2 = 97.5 dBuV”
},
{
“value”: 3,
“label”: “3 = 98.2 dBuV”
},
{
“value”: 4,
“label”: “4 = 98.9 dBuV”
},
{
“value”: 5,
“label”: “5 = 100 dBuV”
},
{
“value”: 6,
“label”: “6 = 101.5 dBuV”
},
{
“value”: 7,
“label”: “7 = 102.8 dBuV”
},
{
“value”: 8,
“label”: “8 = 105.1 dBuV”
},
{
“value”: 9,
“label”: “9 = 105.6 dBuV”
},
{
“value”: 10,
“label”: “10 = 106.2 dBuV”
},
{
“value”: 11,
“label”: “11 = 106.5 dBuV”
},
{
“value”: 12,
“label”: “12 = 107 dBuV”
},
{
“value”: 13,
“label”: “13 = 107.4 dBuV”
},
{
“value”: 14,
“label”: “14 = 107.7 dBuV”
},
{
“value”: 15,
“label”: “15 = 108 dBuV - Default”
}
]
},
{
“id”: “MuteAudio”,
“element”: “switch”,
“label”: “TRANSLATE.MUTE.LABEL”,
“description”: “TRANSLATE.MUTE.DESC”,
“doc”: “TRANSLATE.MUTE.DOC”,
“value”: false
},
{
“id”: “MonoAudio”,
“element”: “switch”,
“label”: “TRANSLATE.MONO.LABEL”,
“description”: “TRANSLATE.MONO.DESC”,
“doc”: “TRANSLATE.MONO.DOC”,
“value”: false
},
{
“id”: “PGAMod”,
“element”: “select”,
“label”: “TRANSLATE.PGAMOD.LABEL”,
“description”: “TRANSLATE.PGAMOD.DESC”,
“doc”: “TRANSLATE.PGAMOD.DOC”,
“value”: {“value”: 0 ,“label”: “0 = 4db - Default”},
“options”: [
{
“value”: 0,
“label”: “0 = 4db - Default”
},
{
“value”: 1,
“label”: “1 = 1db”
}
]
},
{
“id”: “PGAmp”,
“element”: “equalizer”,
“label”: “TRANSLATE.PGAMP.LABEL”,
“description”: “TRANSLATE.PGAMP.DESC”,
“doc”: “TRANSLATE.PGAMP.DOC”,
“config”: {
“orientation”: “horizontal”,
“bars”: [
{
“min”: -15,
“max”: 15,
“step”: “1”,
“value”: “0”,
“tooltip”: “always”
}
]
}
},
{
"id": "PltAdj",
"element": "select",
"label": "TRANSLATE.PLTADJ.LABEL",
"description": "TRANSLATE.PLTADJ.DESC",
"doc": "TRANSLATE.PLTADJ.DOC",
"value": {"value": 0 ,"label": "0 (Low) - Default"},
"options": [
{
"value": 0,
"label": "0 = Low - Default"
},
{
"value": 1,
"label": "1 = High"
}
]
},
{
"id": "PhTCnst",
"element": "select",
"label": "TRANSLATE.PHTCNST.LABEL",
"description": "TRANSLATE.PHTCNST.DESC",
"doc": "TRANSLATE.PHTCNST.DOC",
"value": {"value": 0 ,"label": "0 = 75 µs US - Default"},
"options": [
{
"value": 0,
"label": "0 = 75 µs US - Default"
},
{
"value": 1,
"label": "1 = 50 µs"
}
]
},
{
"id": "PDPA",
"element": "switch",
"label": "TRANSLATE.PDPA.LABEL",
"description": "TRANSLATE.PDPA.DESC",
"doc": "TRANSLATE.PDPA.DOC",
"value": false
},
{
"id": "StartUp",
"element": "switch",
"label": "TRANSLATE.STARTUP.LABEL",
"description": "TRANSLATE.STARTUP.DESC",
"doc": "TRANSLATE.STARTUP.DOC",
"value": false
}
]
}
]
}
[/code]
strings_en.json
[code]{
“PLUGIN_CONFIGURATION”:“Example Plugin Configuration”,
“SAVE”:“Save”,
“FMXMTR”:{
“LABEL”:“Configure I2C FM Transmitter module by Elechouse”,
“DESC”:“Settings”
},
“I2CBUS”:{
“LABEL”:“I2C BUS”,
“DESC”:“Select I2C bus. Options 0 or 1. Default = 1”,
“DOC”:“Selects which I2C bus Transmitter Module is attach to”
},
“I2CADDRESS”:{
“LABEL”:“I2C Address 0x”,
“DESC”:“Select I2C address of Module. Enter in Hex. Default = 3E”,
“DOC”:“Selects I2C address on bus selected above for module. Default = 3E”
},
“FREQ”:{
“LABEL”:“FM Channel”,
“DESC”:“Enter the desired FM channel. Default = 94.5”,
“DOC”:“Selects transmission frequency. Range of transmitter is 70.1Mhz to 107.9.0Mhz in .2Mhz steps. US FM band range is 88.0Mhz to 108.0Mhz. Default = 94.5”
},
“BASEBOOST”:{
“LABEL”:“Base Boost”,
“DESC”:“Amplify audio base frequencies. Default = 0 (0db)”,
“DOC”:“Amplify audio base frequencies. Options 0, 5, 11 and 17db. Default = 0db”
},
“RFGAIN”:{
“LABEL”:“RF Gain”,
“DESC”:“Select transmitter output power level. Default = 15 (108 dBuV)”,
“DOC”:“Select transmitter output power level. Options 0 - 95.5 dBuV (.071 milliwatts) to 15 - 108 dBuV (1.3 milliwatts) in 15 increments. Default = 15 (108 dBuV)”
},
“MUTE”:{
“LABEL”:“Mute Audio”,
“DESC”:“Mute audio output”,
“DOC”:“Silences audio output. Still transmitting, but channel is silent.”
},
“MONO”:{
“LABEL”:“Mono Audio”,
“DESC”:“Selects Mono or Stereo audio mode. Default = Off (Stereo mode)”,
“DOC”:“Selects Mono or Stereo audio mode. On = Mono, Off = Stereo. Default = Off (Stereo mode)”
},
“PGAMOD”:{
“LABEL”:“PGA Mode”,
“DESC”:“Select Programmable Amplifier mode. Default = 0 (4db-increments). Must save and exit config screen to change mode.”,
“DOC”:“Select Programmable Amplifier mode. 1db or 4db increments. Default = 0 (4db-increments). Must save and exit config screen to change mode.”
},
“PGAMP”:{
“LABEL”:“Programmable Amplifier”,
“DESC”:“Programmable Amplifier acts as a volume control for the transmitter. Default = 0 (no change to audio volume)”,
“DOC”:“Programmable Amplifier acts as a volume control for the transmitter. PGAMod = 0 (Range is -15 to +15 in increments of 5). PGAMod = 1 (Range is -15 to +15 in increments of 1). Default = 0 (no change to audio volume)”
},
“PLTADJ”:{
“LABEL”:“Pilot Tone Adjust”,
“DESC”:“Select Pilot Tone signal strength. Default = 0 (Low)”,
“DOC”:“In FM stereo broadcasting, a pilot tone of 19 kHz indicates that there is stereophonic information at 38 kHz (19×2, the second harmonic of the pilot). The receiver doubles the frequency of the pilot tone and uses it as a frequency and phase reference to demodulate the stereo information. Range is 1 (high) or 0 (low). Default = 0 (Low)”
},
“PHTCNST”:{
“LABEL”:“Pre-emphasis Time-Constant Set.”,
“DESC”:“Select Pre-emphasis Time-Constant Set timing. Default = 0 (75 µs US)”,
“DOC”:“In most of the world a 50 µs time constant is used. In the Americas, Japan and South Korea, 75 µs is used. Range is 0 (75 µs) or 1 (50 µs) . Default = 0 (75 µs US).”
},
“PDPA”:{
“LABEL”:“Power Amplifier Power Down”,
“DESC”:“Power Amplifier Power Down, disable RF output. Default = Off”,
“DOC”:“Power Amplifier Power Down, disable RF output. On = shuts down transmitter. Off = turns on transmitter, Default = Off.”
},
“STARTUP”:{
“LABEL”:“Transmit on Volumio start up”,
“DESC”:“Begin transmitting immediately after Volumio starts. Default = Off”,
“DOC”:“Begin transmitting immediately after Volumio starts. If off you must save settings from here to start broadcasting. Off = Do not transmit. On = Transmit. Default = Off.”
}
}
[/code]
Enjoy,
Randy