### I2C Analog TV Modulator controlled by Arduino

Using a RMUP74055AD or TNF0170U722 device to build an analog UHF TV modulator with Arduino and LCD display.

Analog video is getting replaced by digital signals which provide better resolution and picture without noise or interference. But, analog video signal is easy to generate with simple hardware and then it can be FM modulated for broadcasting over a wire. I2C controlled RF modulators are common modules in obsolete VCRs and set top boxes. Most of them cover the entire UHF band and support multistandard sound carrier frequencies. Once taken out of its device, the modulator needs a microcontroller to set up its frequency and other parameters.

Using an Arduino board with LCD and keypad shield a full featured modulator can be built. Arduino can be used to generate video too, but a single board can't use I2C and generate video in the same sketch. You'll need different boards if that's what you want to do.

I used for this project a Samsung RMUP74055AD modulator with MBS74T1AEF controller. Some searching reveals the same IC is also used by Tena TNF0170U722 modulator. Some datasheets will come up too, if you search for them. Anyway, these modulators are 5V devices.

Finding the pinout of these devices is possible even without the datasheet. They have one or two power supply pins, I2C Clock and Data pins and AV pins. Look carefully on the board you get the modulator from and you should be able to identify the pins.

Above is the pinout of the modulator I will use. Both MB+ and LOP are power pins. Ground connects to the metal case. Hooking up this to Arduino becomes very easy now. The parameters of the modulator are changed using the analog keypad provided by the shield.

 RF Modulator connected to Arduino and SDR dongle
I hooked up the modulator output to a SDR dongle to monitor its output using TVSharp application. You may see in the photo, on the breadboard below the wires, there are pull-up resistors on I2C lines. I had a lot of issues with them because it seemed the MCU wouldn't send I2C commands all the time. Removing those resistors fixed the protocol transmission.

LCD shield is placed over Arduino UNO board. The modulator connects directly to A4 and A5 pins of the Arduino (for I2C) and to the power pins. There are the only connections you need.

To make this work, we need some software. The modulator has 4 write registers and 1 read register, each of 1 byte.

 I2C registers of MBS74T1AEF
OSC, SO and ATT bits control sleep mode (OSC=0, SO=1 and ATT=1 turn off the device). TPEN enables test pattern output (two vertical white lines and beep sound). SFD bits control sound carrier frequency and OOR is the read bit that indicates an out of range error.

To set frequency, its value in kHz must be divided by 250 and the result written in the 12 frequency bits. The analog video frequency of an UHF channel is 8 * channel + 303.25. Therefore:

Frequency (kHz) = 8 * channel * 1000 + 303250
Frequency (bits) = Frequency (kHz) / 250
Frequency (bits) = 8 * channel * 4 + 1213
Frequency (bits) = 32 * channel + 1213

Then, upper sixth bits go to FM register: Frequency (bits) >> 6
Lower sixth bits go to left of FL register: [Frequency (bits) & 0x3F] << 2

An I2C transmission may contain, after the device address, either C1-C0 bytes, either FM-FL bytes, or all of them (C1-C0-FM-FL or FM-FL-C1-C0). Here is the code:
#include <LiquidCrystal.h>
#include <Wire.h>
#include <EEPROM.h>

// 4.5MHz, 5.5MHz, 6MHz, 6.5MHz
const byte mod_sound[4] = { 0x00, 0x08, 0x10, 0x18 };
byte eChannel, eSoundIdx, eOn, eTest;

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

unsigned long keyTime = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

if (adc < 50) return btnRIGHT;
if (adc < 250) return btnUP;
if (adc < 450) return btnDOWN;
if (adc < 650) return btnLEFT;
if (adc < 850) return btnSELECT;

return btnNONE;
}

unsigned long currentTime = millis();
if (currentTime - keyTime < 300) return; // less than 100 ms passed; quit

keyTime = currentTime; // store last call time

// perform actions
if (key == btnNONE) return; // no key is pressed; quit
switch (key)
{
case btnRIGHT:
{
if (eChannel < 69) eChannel++;
EEPROM.write(0, eChannel);

if (eOn) mod_changeChannel(eChannel, eTest);
break;
}
case btnLEFT:
{
if (eChannel > 21) eChannel--;
EEPROM.write(0, eChannel);

if (eOn) mod_changeChannel(eChannel, eTest);
break;
}
case btnUP:
{
if (eOn) eOn = 0;
else eOn = 1;
EEPROM.write(2, eOn);

if (eOn) {
mod_start(eChannel, eSoundIdx, eTest);
lcd.setCursor(0, 0);
lcd.print("  << ON AIR >>  ");
}
else {
mod_sleep();
lcd.setCursor(0, 0);
lcd.print("   << OFF >>    ");
}
break;
}
case btnDOWN:
{
if (eTest) eTest = 0;
else eTest = 1;

if (eOn) mod_changeChannel(eChannel, eTest);
break;
}
case btnSELECT:
{
eSoundIdx++;
eSoundIdx &= 3;
EEPROM.write(1, eSoundIdx);

if (eOn) mod_setSoundCarrier(eSoundIdx);
break;
}
}
lcdUpdate(eChannel, eSoundIdx, eTest);

delay(100);
mod_getStatus();
}

/***** Modulator functions *****/
void mod_setFreqBytes(byte &fm, byte &fl, byte channel, boolean testMode) {
if ((channel < 21) || (channel > 69)) channel = 21;

uint16_t freq = 32 * channel + 1213;
fm = freq >> 6;
if (testMode) fm |= 0x40;
fl = (freq & 0x3F) << 2;
}

void mod_start(byte channel, byte soundIdx, boolean testMode) {
byte fm, fl;

mod_setFreqBytes(fm, fl, channel, testMode);

if (soundIdx > 3) soundIdx = 1;

Wire.write(0x88);
Wire.write(0x40 | mod_sound[soundIdx]);
Wire.write(fm);
Wire.write(fl);
Wire.endTransmission();
}

void mod_sleep() {
Wire.write(0xA8);
Wire.write(0x20);
Wire.endTransmission();
}

void mod_changeChannel(byte channel, boolean testMode) {
byte fm, fl;

mod_setFreqBytes(fm, fl, channel, testMode);

Wire.write(fm);
Wire.write(fl);
Wire.endTransmission();
}

void mod_setSoundCarrier(byte idx) {
if (idx > 3) idx = 1;

Wire.write(0x88);
Wire.write(0x40 | mod_sound[idx]);
Wire.endTransmission();
}

void mod_getStatus() {
if (!eOn) return;
byte s = 1;
if (c == 1) s = Wire.read();

if (s & 0x01) {
lcd.setCursor(0, 0);
lcd.print("  << ERROR !! >>  ");
}
}

void lcdUpdate(byte channel, byte soundIdx, boolean testMode) {
lcd.setCursor(0, 1);
lcd.print("CH");
lcd.print(channel);

lcd.setCursor(6, 1);
if (testMode) lcd.print("TEST");
else lcd.print("    ");

lcd.setCursor(12, 1);
switch (soundIdx) {
case 0: lcd.print("4.5M"); break;
case 1: lcd.print("5.5M"); break;
case 2: lcd.print("6.0M"); break;
case 3: lcd.print("6.5M"); break;
}
}

void setup() {
pinMode(10, INPUT); // for LCD shield

Wire.begin();
lcd.begin(16, 2);

lcd.print("  TV Modulator  ");
delay(1000);
lcd.clear();

// get/set initial EEPROM parameters
if ((eChannel < 21) || (eChannel > 69)) eChannel = 21;
EEPROM.write(0, eChannel);

if (eSoundIdx > 3) eSoundIdx = 1;
EEPROM.write(1, eSoundIdx);

eTest = 0;

if (eOn) {
lcd.setCursor(0, 0);
lcd.print("  << ON AIR >>  ");

mod_start(eChannel, eSoundIdx, eTest);
}
else {
lcd.setCursor(0, 0);
lcd.print("   << OFF >>    ");
}

lcdUpdate(eChannel, eSoundIdx, eTest);
}

void loop() {
}

The software makes use of integrated EEPROM of ATmega MCU to store modulator parameters like on/off state, frequency and sound information. These are restored after reset. Using LEFT/RIGHT keys, output channel can be changed from 21 to 69 UHF. SELECT key cycles through sound carrier frequencies which are displayed on the bottom line, in the right of LCD screen. UP key turns modulator on and off and DOWN key enables/disables test pattern. Enabling the test pattern is not stored in EEPROM, therefor after reset, modulator will start, but not in test mode.

In a future post I will build an analog TV receiver using a tuner with included demodulator, an Arduino board to control it and an USB video capture card to send video to PC.

1. Great project! Thank you man!

Have you some example of demodulator digital TV using Realtek RTL2832U + Arduino?

1. I guess an Arduino doesn't have enough processing power for capturing and performing demodulation on samples from RTL2832U. If the dongle is used for digital TV, a transport stream is sent over USB port. This is a high bitrate stream which can't be decoded by a small microcontroller.

2. Thanks. Also very nice worked with fm-mc016a4f5g modulator with ase74t1aef chip inside.

3. Thanks. But i want to ask something. If we only write this commands (i want to see only test pattern) it doesnt work. Can you help?

#include

void setup() {
Wire.begin();
Serial.begin(9600);

}

void loop() {
Wire.beginTransmission(0xCA);
Wire.write(B10001000);
Wire.write(B01001000);
Wire.write(B01100100);
Wire.write(B11110100);
Wire.endTransmission();

}

1. Your commands are in loop. It is enough to send them to the modulator only once after power up, not continusously, without any delay. You can move them into setup function. If it still doesn't work double check the validity of the bits you sent.

3. #include

void setup() {
Wire.begin();
Wire.beginTransmission(0xCA);
Wire.write(0x84);
Wire.write(0x48);
Wire.write(0x64);
Wire.write(0xF4);
Wire.endTransmission();

}
void loop() {
while(1);
}

I changed my codes as you said. But it still not worked. I try a new modulator one. Its same too. Where is my mistakes? I dont know :(

ATT=0 , OSC=1, SFD1=0, SFD0=1, PS=0, SO=0, PWC=0, TPEN=1,
Frequency bits are, N11 ==> 100100111101 CH36 = 591,25Mhz

I checked Arduino's sda, scl pins a basic code. It's not damaged. It still work.

At hardware,

MB+ and B+ are 5 volts

4. Hi, again. A little time ago. I tried Nick Gammon's "i2c scanner" program.

I2C scanner. Scanning ...
Done.
Found 1 device(s).

This program said that my chip adress is 0x65, but our chip adress is 0xCA isnt it?

4. Yes, finally I finished. I see two vertical lines on tv. But how was it?

1. 0xCA is full chip address, with R/W bit included. 0x65 is actually 0xCA >> 1. This is the 7-bit address without R/W bit and this is what you need to specify to Wire.beginTransmission. See the definition #define MOD_ADDRESS (0xCA >> 1) which means MOD_ADDRESS is 0x65.

5. Thanks a lot..

6. Great work. Can you do something similar with Satellite / DVB-S or DVB-S2 tuner. There is very little information about it.

1. I controlled satellite tuners too: https://www.onetransistor.eu/2016/05/control-s7vz6306-tuner-ix2470.html. This kind of tuners can be used to build ADS-B receivers.

7. Hi, Could You provide me any assistance? I have almost the same RF Modulator (RMUP74055DD) but I cannot accomplish what You did. I'm unable to see the address of the modulator in I2C Scanner and while using Your code it is unable to go "on air". It freezes ... and when I disconnect modulator then I see error instead. I'm unable to make I2C connection with the rf modulator What can I check ? I've tried with pullup resistors but no change.

1. Make sure the modulator is powered (there are two power pins for +5V) and try again with I2C scanner sketch. Do not connect pull up resistors. Make sure ground is connected to metal case. Try to switch SDA and SCL, maybe you got them wrong. If it still does not work, maybe there is something wrong with your modulator.

2. I did all of that. I'm also powering the modulator from external 5V source of course connecting all grounds. While powering from Arduino 5v pin voltage was to much below 5V. Modulator is ok for sure. It was working fine just a few hours ago in VCR :) Moreover this is the second one I'm trying. I'm powering module as You showed . I have no idea what to check more.

3. It could be different pin configuration between 74055AD and 74055DD. If it draws so much current that Arduino can't supply 5V, then something seems to be wrong.

4. When it was still in VCR I checked all pins. +5V were in the same place as in AD

5. I have one more (and last) question. Are You familiar with python or perl? RF modulator will be used with Raspberry Pi and I don't want to add any extra devices (I'm trying to make it as small as possible) so I'd like to turn the modulator on while booting raspberry. That is why I need to transform arduino code to perl/python. I just need to send a command to switch on, on one of the channels and then switch off. For test purpose I would also need to run test signal on modulator. Can You help me with that? I just need to know what to sent? Thanks in advance

6. I'm not familiar with specific Raspberry Pi programming. I don't know whether you can send I2C data while Linux kernel is booting. I guess you can use bash scripting too and make a script that will be launched after startup. Have a look at mod_start function that sends all bytes required to configure and start modulator. You'll have to write your code based on it.

7. Please explain me a bit "i2c bit map" in modulator.
Wire.write(0x88);
Wire.write(0x40 | mod_sound[soundIdx]);
Wire.write(fm);
Wire.write(fl);
Wire.endTransmission();
Are there any registers and every time I'm sending data I need to set register or offset or You just send bytes one by one to 0x65 address?

8. Have a look at the registers table above. After the device address you need to send 2 or 4 bytes of data. These bytes are either (C1, C0) or (FM, FL) or (C1, C0, FM, FL) or (FM, FL, C1, C0).

8. Found the proper pinout for RMUP74055DD
pin1 -> 5V
pin2 -> Audio in
pin3 -> SDA
pin4 -> 5V
pin5 -> SCL
pin6 -> Video in

Now everything works great!

1. That's great! Only voltage supply pins are the same. A/V and I2C are totally different.

9. Hi
Is it possible to run the modulator without the micro-controller on fixed settings for example?
Regards.

1. Unfortunately this type of modulator needs a microcontroller to set it up after power on.

10. Nice. It works. Thanks

11. This comment has been removed by a blog administrator.

12. RMUP74055AD is it the same as with V5151MUP????

1. Open the metal case and see if it contains MBS74T1AEF IC. If yes, then they are the same.