# Turn your Volumio Pi into a FM radio station

**URL:** <https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432>\
**Category:** Diy Volumio Projects\
**Created:** [April 29, 2018, 3:34pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432 "2018-04-29T15:34:44Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Randy](https://community.volumio.com/letter_avatar_proxy/v4/letter/r/2acd7d/32.png) [@Randy](https://community.volumio.com/u/Randy)\
**Post date:** [April 29, 2018, 3:34pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/1 "2018-04-29T15:34:44Z")

</div>

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](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](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](https://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

---

<div class="post-metadata">

**Author:** ![Randy](https://community.volumio.com/letter_avatar_proxy/v4/letter/r/2acd7d/32.png) [@Randy](https://community.volumio.com/u/Randy)\
**Post date:** [April 30, 2018, 1:12pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/2 "2018-04-30T13:12:16Z")

</div>

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](https://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

---

<div class="post-metadata">

**Author:** ![Josh2000](https://community.volumio.com/user_avatar/community.volumio.com/josh2000/32/347_2.png) [@Josh2000](https://community.volumio.com/u/Josh2000)\
**Post date:** [May 1, 2018, 2:25pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/3 "2018-05-01T14:25:08Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Randy](https://community.volumio.com/letter_avatar_proxy/v4/letter/r/2acd7d/32.png) [@Randy](https://community.volumio.com/u/Randy)\
**Post date:** [May 6, 2018, 9:26pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/4 "2018-05-06T21:26:42Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Josh2000](https://community.volumio.com/user_avatar/community.volumio.com/josh2000/32/347_2.png) [@Josh2000](https://community.volumio.com/u/Josh2000)\
**Post date:** [June 13, 2018, 6:17pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/5 "2018-06-13T18:17:10Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Josh2000](https://community.volumio.com/user_avatar/community.volumio.com/josh2000/32/347_2.png) [@Josh2000](https://community.volumio.com/u/Josh2000)\
**Post date:** [June 22, 2018, 9:31pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/6 "2018-06-22T21:31:25Z")

</div>

Hi Randy,

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

- Josef.

---

<div class="post-metadata">

**Author:** ![buck](https://community.volumio.com/letter_avatar_proxy/v4/letter/b/c5a1d2/32.png) [@buck](https://community.volumio.com/u/buck)\
**Post date:** [September 17, 2023, 3:16pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/7 "2023-09-17T15:16:33Z")

</div>

@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?

---

<div class="post-metadata">

**Author:** ![Josh2000](https://community.volumio.com/user_avatar/community.volumio.com/josh2000/32/347_2.png) [@Josh2000](https://community.volumio.com/u/Josh2000)\
**Post date:** [September 17, 2023, 8:20pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/8 "2023-09-17T20:20:19Z")

</div>

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 😀

Best Regards

Josef

---

<div class="post-metadata">

**Author:** ![buck](https://community.volumio.com/letter_avatar_proxy/v4/letter/b/c5a1d2/32.png) [@buck](https://community.volumio.com/u/buck)\
**Post date:** [September 18, 2023, 9:36pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/9 "2023-09-18T21:36:50Z")

</div>

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](https://github.com/markondej/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?

---

<div class="post-metadata">

**Author:** ![Josh2000](https://community.volumio.com/user_avatar/community.volumio.com/josh2000/32/347_2.png) [@Josh2000](https://community.volumio.com/u/Josh2000)\
**Post date:** [September 21, 2023, 10:35pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/10 "2023-09-21T22:35:25Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![GreyWolf](https://community.volumio.com/letter_avatar_proxy/v4/letter/g/e47c2d/32.png) [@GreyWolf](https://community.volumio.com/u/GreyWolf)\
**Post date:** [October 18, 2023, 1:57pm UTC](https://community.volumio.com/t/turn-your-volumio-pi-into-a-fm-radio-station/8432/11 "2023-10-18T13:57:09Z")

</div>

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.
