Turn your Volumio Pi into a FM radio station

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.

Ebay - https://www.ebay.com/sch/i.html?_odkw=FM+Radio+Transmitter+Module+V2+0&_osacat=0&_from=R40&_trksid=m570.l1313&_nkw=FM+Radio+Transmitter+Module+V2.0&_sacat=0

Amazon - https://www.amazon.com/Transmitter-Module-Digital-Arduino-Microphone/dp/B076KR12VJ/ref=sr_1_4?ie=UTF8&qid=1525012149&sr=8-4&keywords=FM+Radio+Transmitter+Module+V2.0

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

1 Like

Greetings,
Had to make a couple of fixes. It wasn’t creating the config file on a fresh install, had some permission issues and it required the full path to I2C tools.

See below.

install.sh

[code]#!/bin/bash

echo “Installing fmxmtr Dependencies”
sudo apt-get update

Install the required packages via apt-get

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 kew v-conf fs-extra shelljs --no-install-recommends

echo “Creating default configuration file”
mkdir /data/configuration/miscellanea/fmxmtr
echo ‘{’ > /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “I2C_BUS”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “1”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “I2C_ADDRESS”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “text”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “3e”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “Freq”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “94.5”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “BaseBoost”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “0”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “RFGain”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “15”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “MuteAudio”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “boolean”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: false’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “MonoAudio”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “boolean”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: false’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “PGAMod”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “0”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “PGAmp”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “0”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “PltAdj”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “0”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “PhTCnst”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “number”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: “0”’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “PDPA”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “boolean”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: false’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ },’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “StartUp”: {’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “type”: “boolean”,’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ “value”: false’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ’ }’ >> /data/configuration/miscellanea/fmxmtr/config.json
echo ‘}’ >> /data/configuration/miscellanea/fmxmtr/config.json
chown -R volumio:volumio /data/configuration/miscellanea/fmxmtr

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]

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 = ‘/usr/sbin/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 = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x00 0x’ + Reg0x00Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x01 0x’ + Reg0x01Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x02 0x’ + Reg0x02Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x04 0x’ + Reg0x04Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x0B 0x’ + Reg0x0BWrite.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x10 0x’ + Reg0x10Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/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 = ‘/usr/sbin/i2cset -y ’ + this.config.get(‘I2C_BUS’) + ’ 0x’ + this.config.get(‘I2C_ADDRESS’) + ’ 0x0B 0x00’;
shell.exec(ShellCommand);
}
else
{
ShellCommand = ‘/usr/sbin/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(’/usr/sbin/i2cset -y 1 0x3e 0x0B 0x20’);
self.logger.info(‘Disabled RF output’);

// Create file
ShellCommand = (‘mkdir /data/configuration/miscellanea/fmxmtr’);
shell.exec(ShellCommand);
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(’/usr/sbin/i2cset -y 1 0x3e 0x00 0xb1’);
shell.exec(’/usr/sbin/i2cset -y 1 0x3e 0x01 0xe3’);
shell.exec(’/usr/sbin/i2cset -y 1 0x3e 0x02 0x40’);
shell.exec(’/usr/sbin/i2cset -y 1 0x3e 0x04 0x04’);
shell.exec(’/usr/sbin/i2cset -y 1 0x3e 0x0b 0x00’);
shell.exec(’/usr/sbin/i2cset -y 1 0x3e 0x10 0xa8’);
shell.exec(’/usr/sbin/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(’/usr/sbin/i2cget -y 1 0x3e 0x00’);
Reg0x01 = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x01’);
Reg0x02 = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x02’);
Reg0x04 = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x04’);
Reg0x0B = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x0B’);
Reg0x10 = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x10’);
Reg0x12 = shell.exec(’/usr/sbin/i2cget -y 1 0x3e 0x12’);
Reg0x13 = shell.exec(’/usr/sbin/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(’/usr/sbin/i2cset -y 1 0x3e 0x00 0x00’);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x00 0x’ + Reg0x00Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x01 0x’ + Reg0x01Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x02 0x’ + Reg0x02Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x04 0x’ + Reg0x04Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x0B 0x’ + Reg0x0BWrite.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x10 0x’ + Reg0x10Write.toString(16);
shell.exec(ShellCommand);
ShellCommand = ‘/usr/sbin/i2cset -y ’ + I2CBusSave + ’ 0x’ + I2CAddressSave + ’ 0x13 0x’ + Reg0x13Write.toString(16);
shell.exec(ShellCommand);
};
[/code]

Enjoy,
Randy

1 Like

Hi Randy,

Great idea.

Could you please provide a zip file containing the content on github ?
I’d like to try but i’m not aware of how to copy all the stuff.

Thank you.

Josef

Josh,
Sorry to take so long to respond. Been busy at work.

I can email the zip file to you. Just need an address.

Is there a way to attach it to a message here?

You could also try opening a console on the Volumio Pi or SSH into it and enter the commands below.

volumio plugin init
cd volumio-plugins/plugins/miscellanea/fmxmtr
volumio plugin install

That should do it. It will tell you to do “Volumio plugin init” again, but it is not required. The first time should download all the available plugins into the volumio home folder under volumio-plugins. Under that are the different categories. This one will be under miscellanea in the folder fmxmtr. Do “volumio plugin install” from within this directory to install it.

Good luck,
Randy

1 Like

Hi Randy,

Sorry for also took so long to answer.

After i got it managed to get the 1.02 version downloaded everythink worked perfect.

The only thing is the sound get’s distorted. I decreased the line level to about 200mV - it gets more quiet but still distorts the signal.
It’s a little bit better if i switch to mono.
I ordered another sender module now in hope the one i got is damaged.

Anyway this has nothing to do with your plugin which is working perfect.
Thank you a lot.

Josef

Hi Randy,

I just want to let you know that the distortion is gone with the new module i got last week. :smiley: :smiley: :smiley:

  • Josef.

@Randy Very pleased to have found this, as it seems to be exactly what I need for my own setup. However, it has been five years since this thread was active, and I can’t seem to find the mentioned files anywhere? Is the plugin still available and working with the latest version of Volumio, and are your instructions here up to date?

Hi Buck,

unfortunately i can tell you it is not working with version 3 and i also don‘t know how to fix.

Let‘s hope @Randy will have time to get it back to life :grinning:

Best Regards

Josef

Let’s hope so! We don’t have FM radio broadcasts in my country anymore, which means all my FM radios are basically unusable. Right now I’m running a separate Pi with Volumio for three separate radios, but I find it quite redundant when I could be just transmitting an FM signal to all of them. I’ve been looking into the possibility of using ALSA loopback to transmit audio through fm_transmitter, but with varied results (Volumio seems intent on removing any custom asound.conf entries on boot, for example). Could a new Volumio plugin be made from this code?

Hey Buck,

I love your idea.

Why don’t you setup Volumio 2 with the working plugin to just program the transceiver - and then plug it to a superduper stuffed Volumio V3 device ( FM Frequency etc. fixed for sure…).

I guess you are sure - sound quality will not be hyperfidelity with this kind of “multiroom” - but i know what is going with pretty FM receivers…

br
Josef

p.s. As soon as someone of the coders do know how that “fu****” ALSA Loop thing is working - and in addition someone gets a glue on how the Volumio Github Voodoo is working - there should be a possibility
@volumio

My working solution for the past 3 years has Been to use an external FM transmitter. I got tired of walking around my house with the Bluetooth speaker and my phone. Once I found Volumio, I had my solution. I had an old FM transmitter that plugged into my car’s cigarette lighter port. I loaded up Volumio on an old Pi I had laying around, attached a Usb2audio and plugged in the FM xmitter. Easy Peasy. After a month of use, I broke the transmitter apart and put everything in an old net switch case. Even used the PSU from the old switch. The thing covers both stories of my house and part of my yard. Is it CD quality? No, It is FM stereo so no worries using the USB2Audio solution. Now the caveats. I am using a good quality FM transmitter. In fact it may be a bit hot as it was during the time in the mid early 2000s the manufactures creatively interpreted the F.C.C.s rules (US). It is digitally tuned but it has not moved off of where I have set it for the time period. Don’t use the cheapo units you find that only covers a small range of frequencies and are quite weak in output. I duplicated my setup for a friend and ended up buying a better transmitter module because went the cheap unit route. So now I can walk any where in my house where a radio is always some where near by. Access Volumio on my phone/tablet or Pc handy and choose the music I want. Don’t even need Volulio premium for room sync.