lyc - Linux Yamaha receiver control (over serial / RS232 interface)

Yamaha RX-V3800 and some older models like DSP-AX1 are great AV-Receiver, but unfortunately they have no control / web interface to manage it from outside. With a USB-Serial adapter connected to the reciver RS-232 interface, a computer or router and lyc you can control the reciver. In addition, a web interface can be created with wlyc to provide a web access to all network devices like PC, smartphone or tablet.

GitHub repository
https://github.com/panticz/lyc

Download binary
http://dl.panticz.de/lyc/i386/lyc - i386 compiled
http://dl.panticz.de/lyc/ar9132/lyc - Atheros AR9132 (TP-LINK TL-WR1043ND / TL-WDR4300 router)

HowTo compile and run
1. download lyc.c and Makefile
2. compile with: make
3. set up USB Serial adapter http://www.panticz.de/USB-Serial-adapter
4. run command:
./lyc input dvd
./lyc volume up

Run on OpenWrt router
1. download sources
2. comile with
make mips
4. copy lyc to OpenWRT router:
scp lyc root@OPENWRT_IP:/root/lyc
5. login to root@YOUR_OPENWRT_IP
6. download wlyc (OpenWrt wget can not access https, use http redirection)
wget -q http://dl.panticz.de/wlyc/wlyc -O /www/cgi-bin/wlyc
chmod 755 /www/cgi-bin/wlyc
7. Open im broser:
http://OPENWRT_IP/cgi-bin/wlyc
Example: http://ROUTER_IP/cgi-bin/wlyc?p1=input&p2=dvd

cros-compile script for OpenWRT
http://dl.panticz.de/lyc/compile_openwrt_ar9132.sh

http://raw.githubusercontent.com/panticz/lyc/master/lyc.c - lyc source code

#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include "rxvx700.h" /* Yamaha RX-Vx700 protocol codes */

#define STX 0x02
#define ETX 0x03
#define DC1 0x11

int fd;

//Initialize serial port
int init_port()
{
    int portstatus = 0;

    struct termios options;
    // Get the current options for the port...
    tcgetattr(fd, &options);
    // Set the baud rates to 9600...'textB' un
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    // Enable the receiver and setSTX local mode...
    options.c_cflag |= (CLOCAL | CREAD);

    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    //options.c_cflag |= SerialDataBitsInterp(8);           /* CS8 - Selects 8 data bits */
    options.c_cflag &= ~CRTSCTS;                            // disable hardware flow control
    options.c_iflag &= ~(IXON | IXOFF | IXANY);           // disable XON XOFF (for transmit and receive)
    //options.c_cflag |= CRTSCTS;                     /* enable hardware flow control */


    options.c_cc[VMIN] = 0;     //min carachters to be read
    options.c_cc[VTIME] = 0;    //Time to wait for data (tenths of seconds)


    // Set the new options for the port...
    //tcsetattr(fd, TCSANOW, &options);


    //Set the new options for the port...
    tcflush(fd, TCIFLUSH);
    if (tcsetattr(fd, TCSANOW, &options)==-1)
    {
        perror("On tcsetattr:");
        portstatus = -1;
    }
    else
        portstatus = 1;

    return portstatus;
}


/*
* send command to reciver
*/
void send_cmd(const char *n) {
    int len = strlen(n);

    // create command
    char cmd[len + 2];
    cmd[0]=STX;
    cmd[len + 1]=ETX;

    // copy string to char array
    int i;
    for (i = 0; i < strlen(n); i++) {
        cmd[i + 1] = n[i];
    }

/*
    // debug
    printf("CMD:");
    for (i = 0; i < sizeof(cmd); i++) {
        printf("%c|", cmd[i]);
    }
    printf("\n");
*/

    write(fd, cmd, sizeof(cmd));
}

/*
* send init command to reciver
*/
void init_cmd() {
    char b[5];
    b[0]=DC1;
    b[1]=0;
    b[2]=0;
    b[3]=1;
    b[4]=ETX;

    int n = write(fd, b, sizeof(b));
}

/*
* open serial port
*/
int open_port(void)
{
    int fd; /* File descriptor for the port */
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);

    if (fd == -1) {
        /*
        * Could not open the port.
        */
        perror("open_port: Unable to open /dev/ttyUSB0 --- \n");
    } else {
        fcntl(fd, F_SETFL, 0);
    }

    return (fd);
}

/*
* decode command
*/
int run_cmd(char *cmd1, char *cmd2) {
    int l = 0;

    // check parameter
    if(cmd1 != NULL) {
        l += strlen(cmd1);
    }

    if(cmd2 != NULL) {
        l += strlen(cmd2);
    }

    // put parameter together
    char c[l + 2];
    strcpy(c, cmd1);
    if(cmd2 != NULL) {
        strcat(c, " ");
        strcat(c, cmd2);
    }

    int i;
    int size = (int)(sizeof(cmd) / sizeof(*cmd));
    for(i = 0; i < size; i++) {
        // search command
        if(strcmp(c, cmd[i][0]) == 0) {
            // todo calculate volume
            // int i = (int)strtol(cmd[i][1], (char **)NULL, 10);

            char code[strlen(cmd[i][1]) + 1];
            strcpy(code, cmd[i][1]);

            char *pch;
            pch = strtok(code, " ");
            while (pch != NULL) {
                send_cmd(pch);

                pch = strtok(NULL, " ");
            }

            return 0;
        }
    }

    return 1;
}

/*
* main
*/
int main(int argc, char **argv) {
    if(argc == 1) {
        return 1;
    }
        
    fd = open_port();

    if(fd == -1) {
        printf("Error opening serial port\n");
    } else {
        if(init_port() == -1) {  
            sleep(.5);
            printf("Error initializing serial port\n");

            close(fd);
            return 0;
        }

        sleep(.5);
        init_cmd();

/*
        // todo: recive response from reciver
        if (n < 0) {
            fputs("write() of X bytes failed!\n", stderr);
        } else {
            printf("Successfully wrote 8 bytes\n");

            sleep(.5);
            char buffer[200];
            int n = read(fd, buffer, sizeof(buffer));
            sleep(.5);
            if (n < 0) {
                fputs("read failed!\n", stderr);
            } else {
                printf("Successfully read from serial port -- %s\n", buffer);
            }
        }
*/

        sleep(.5);
        if(run_cmd(argv[1], argv[2]) == 1) {
            printf("Command not found\n");
        }

        sleep(.5);
        close(fd);
    }

    return 0;
}

https://raw.githubusercontent.com/panticz/lyc/master/rxvx700.h - Yamaha RS232 codes

// todo: add Zone2 and Zone3
const char* cmd[][2] = {
    {"power on", "07E7E"},
    {"power off", "07E7F"},

    {"input cd", "07A15"},
    {"input dvd", "07AC1"},
    {"input bd", "07AC8"},
    {"input netusb", "0F7F013FC0"},
    {"input usb",   "0F7F013FC0 0F7F0138C7"},
    {"input net",   "0F7F013FC0 0F7F0137C8"},
    {"input pc",    "0F7F013FC0 0F7F0136C9"}, 

    {"volume up", "07A1A"},
    {"volume down", "07A1B"}, 
    {"volume -30", "2308B"},
    {"volume -35", "23081"},
    {"volume -40", "23077"},
    {"volume -45", "2306D"},
    {"volume -50", "23063"},
    {"volume -55", "23059"},
    {"volume -60", "2304F"},
    {"volume -65", "23045"},
    {"volume -70", "2303B"},
    {"volume -75", "23031"},
    {"volume -80", "23027"},

    {"mute on", "07EA2"},
    {"mute off", "07EA3"},

    {"sleep off", "07EB3"},
    {"sleep 120", "07EB4"},
    {"sleep 90", "07EB5"},
    {"sleep 60", "07EB6"},
    {"sleep 30", "07EB7"},

    {"dsp 7ch", "07EFF"},
    {"dsp 2ch", "07EC0"},
    {"dsp normal", "07EFD"},
    {"dsp straight", "07EE0"},
    {"dsp general", "07EFC"},

    {"memory 1", "07E35"},
    {"memory 2", "07E36"},
    {"memory 3", "07E37"},
    {"memory 4", "07E38"},
    {"memory 5", "07E39"},
    {"memory 6", "07E3A"},

    {"osd menu", "0F7F012FD0"},
    {"osd enter", "0F7F0131CE"},
    {"osd display", "0F7F0135CA"},
    {"osd up", "0F7F012ED1"},
    {"osd down", "0F7F0134CB"},
    {"osd left", "0F7F0132CD"},
    {"osd right", "0F7F0130CF"},

    {"enhancer on", "07ED8"},
    {"enhancer off", "07ED9"},

    {"stop", "0F7F013DC2"},
    {"play", "0F7F013EC1"},
    {"next", "0F7F013CC3"},
    {"prev", "0F7F013BC4"},

};

https://raw.githubusercontent.com/panticz/lyc/master/Makefile - Makefile

STAGING_DIR=../toolchain-mips_34kc_gcc-4.6-linaro_uClibc-0.9.33.2/bin
export STAGING_DIR

all:
	gcc $(FLAGS) $(INCLUDEDIR) lyc.c -o lyc

arm:
	arm-linux-gnueabihf-gcc lyc.c -o lyc

mips:
	$(STAGING_DIR)/mips-openwrt-linux-uclibc-gcc lyc.c -o lyc
        
clean:
	rm -f *~ *.o *.cgi lyc

Links
http://mark.jerde.org:13780/v1/comp/rxv2400/
http://linuxmce.org/
http://forum.linuxmce.org/index.php?topic=5759.0
http://www.remotecentral.com/cgi-bin/mboard/rc-touch/thread.cgi?849
http://www.omei.de/