AVR Control+

Home Up AVR Control+ Auto Power Plant Life Space Cadet PantryPod IR Repeater Watt Watcher FlashmasterŪ AutoAwning Mailbox Monitor

These are the preliminary details on AVR Control+, which is a web interface to an Audio Visual Receiver, with a few bonus features. It hasn't been built yet, but I need to capture the design ideas somewhere. Here's what it might do -

This is quite a combination, and it starts with a moderately high end Yamaha RX-V2400 which has an RS-232 port on the back allowing full control and the ability to send some arbitrary, but short, text messages to the screen of the attached monitor. This started the original concept of the onscreen Caller ID. Then, since we can also fully control the AVR via the RS-232 port, I think I can make remote control for other zones more user friendly than with an infrared remote control, hence the web interface. And, lastly, once the EtherNut is on the back of the AVR, there is a simple design to tune in Internet Radio based MP3 streams, or accept MP3 from your home PC, and feed the audio into the AVR.

Here's the System Architecture:

As you can see, the basic interfaces are:

Really quite a simple set of connections, but the combined functionality goes further.

Here's the catch -

I haven't built this yet. I'm prowling for another EtherNut to put this project into action. When I built this house, I pre-wired Ethernet to many locations, including the backside of the AV center. Until now, I haven't had a need to make that circuit hot. This may be it.

Web Based AVR Control

This link will take you to a few pages put together as a sample for how it might be constructed. Since EtherNut is a small device, it is necessary to have a minimalist memory footprint. So, we may forgo some more exotic interface techniques.

Here's the Caller ID process:

  1. Caller ID information is extracted from a caller ID modem with the YAC software running on the home PC,
  2. The YAC program serves it to designated network nodes, of which EtherNut is one,
  3. EtherNut receives and converts this message to its RS-232 port for transmission to the Yamaha,
  4. The Yamaha RX-V2400 receives it and presents it on the output to the video monitor.

The Zone Control Process:

  1. EtherNut is a web server, with a number of web pages for control of the Yamaha AVR.
  2. Web Browsers can select the zone and functionality by interaction with the EtherNut,
  3. EtherNut translates the browser commands to AVR commands.

The MP3 Player / Radio Process:

  1. EtherNut with an add-on MP3 decoder, receives streams from either the host PC or Web Radio stations,
  2. The MP3 decoder translates this into audio signals,
  3. The audio is plugged into the Yamaha AVR.

Reference Information:

EtherNut - A great programmable embedded appliance

YAC - This program was a great discovery, and a copy of v0.16 of the Windows program is hosted here for posterity. A copy of v0.15 of the TiVo listener was used to create the EtherNut version you see below.

Yamaha AVR - The Audio Visual Receiver.

MediaNut - Hardware and Software to add to EtherNut.

YAC Translation

Take note that this compiles with NutOS 3.3.2.1, but hasn't yet been run in hardware. If you get a chance to try it before me, please let me know what changes you made to make it work.

/*!
 * Copyright (C) 2001-2003 by Smartware Computing.
 *
 * Yac-Nut
 *
 *	Yet Another Caller ID Listener...
 *
 *	Based on 
 *
 *		RS232D - Application Sample with EtherNut
 *		YAC - www.sunflowerhead.com/software/yac
 *
 */

#ifdef ETHERNUT2
#include <dev/lanc111.h>
#else
#include <dev/nicrtl.h>
#endif
#include <dev/uartavr.h>

#include <sys/heap.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/print.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>

#include <netinet/sostream.h>
#include <arpa/inet.h>

#include <pro/dhcp.h>

#define BUFFERSIZE  300
#define TCPPORT     10629
#define CALL_TAG    "@CALL"

// AutoIncrementBuildNumber = 1
#define BUILDNUMBER 2

enum {false, true };
typedef u_char bool;

typedef struct 
    {
    FILE *cd_rs232;
    FILE *cd_tcpip;
    char cd_connected;
    } CHANNEL;

bool IsCallerIDInfoAndStripTag(char *szText, int cbLen);
bool DecodeCallerIDInfo(char *szInput, char *szName, char *szNumber);
void StreamCopy(FILE * istream, FILE * ostream, char *cop);


bool DecodeCallerIDInfo(char *szInput, char *szName, char *szNumber)
{
    int ch, i;
    bool fCopyToNumber = false, fIsCallerID = false;

    ch = 0;
    szName[0] = '\0';
    szNumber[0] = '\0';
    fIsCallerID = IsCallerIDInfoAndStripTag(szInput, strlen(szInput));
    if (fIsCallerID)
    {
        // Parse szInput, if applicable, into name and number
        for (i = 0; i < strlen(szInput); i++)
        {
            if ((szInput[i] == '~') && !fCopyToNumber)
            {
                fCopyToNumber = true;
                szName[ch] = '\0';
                ch = -1;
            }
            else if (!fCopyToNumber)
            {
                szName[ch] = szInput[i];
            }
            else
            {
                szNumber[ch] = szInput[i];
            }
            ch++;
        }
        if (fCopyToNumber)
            szNumber[ch] = '\0';
        return true;
    }
    else
        return false;
}

bool IsCallerIDInfoAndStripTag(char *szText, int cbLen)
{
    char szTemp[6], szOutText[BUFFERSIZE];
    bool fFoundMarker;
    int i;

    strncpy(szTemp, szText, 5);
    szTemp[5] = '\0';

    if (!strcmp(CALL_TAG, szTemp))
    {
        // This is probably Caller ID info, check for the '~' marker
        fFoundMarker = false;

        for (i = 0; i < strlen(szText); i++)
        {
            if (szText[i] == '~')
            {
                fFoundMarker = true;
                break;
            }
        }
        if (fFoundMarker)
        {
            // Yep, it's definitely Caller ID info, strip tag
            strncpy(szOutText, &szText[5], BUFFERSIZE);
            strncpy(szText, szOutText, cbLen);
            return true;
        }
    }
    return false;
}


/*
 * Transfer data from input stream to output stream.
 */
void StreamCopy(FILE * istream, FILE * ostream, char *cop)
{
    int cnt;
    char *buff;
    char szName[BUFFERSIZE], szNumber[BUFFERSIZE];
    bool fCallerID;

    buff = malloc(BUFFERSIZE);
    while (*cop) 
    {
        if ((cnt = fread(buff, 1, BUFFERSIZE, istream)) <= 0)
            break;
        {
        // Need to parse the data to see if it's Caller ID or not.
        fCallerID = DecodeCallerIDInfo(buff, szName, szNumber);

        if (fCallerID)
        {
            printf("The received data was Caller ID information.\n");
            sprintf(buff, "%s  %s", szName, szNumber);
        }

        // This is where it should be communicated to the Yamaha RX-2400
        printf("buf: %s\n", buff);
        }
        if (*cop && (cnt = fwrite(buff, 1, cnt, ostream)) <= 0)
            break;
        if (*cop && fflush(ostream))
            break;
    }
    *cop = 0;
    free(buff);
}

/*
 * From RS232 to socket.
 */
THREAD(Receiver, arg)
{
    CHANNEL *cdp = arg;

    for (;;)
    {
        if (cdp->cd_connected) 
        {
            NutThreadSetPriority(64);
            /*
             * We are reading from the UART without any timeout. So we
             * won't return immediately when disconnected.
             */
            StreamCopy(cdp->cd_rs232, cdp->cd_tcpip, &cdp->cd_connected);
            NutThreadSetPriority(128);
        }
        NutThreadYield();
    }
}

/*
 * Main application routine. 
 *
 * Nut/OS automatically calls this entry after initialization.
 */
int main(void)
{
    TCPSOCKET *sock;
    CHANNEL cd;
    u_long baud = 9600;

    /*
     * Register our devices.
     */
    NutRegisterDevice(&devUart0, 0, 0);
    NutRegisterDevice(&DEV_ETHER, 0x8300, 5);

    /*
     * Setup the uart device.
     */
    cd.cd_rs232 = fopen("uart0", "r+b");
    _ioctl(_fileno(cd.cd_rs232), UART_SETSPEED, &baud);

    /*
     * Setup the ethernet device. Try DHCP first. If this is
     * the first time boot with empty EEPROM and no DHCP server
     * was found, use hardcoded values.
     */
    if (NutDhcpIfConfig("eth0", 0, 60000)) 
    {
        /* No valid EEPROM contents, use hard coded MAC. */
        u_char my_mac[] = { 0x00, 0x06, 0x98, 0x20, 0x00, 0x00 };

        if (NutDhcpIfConfig("eth0", my_mac, 60000)) 
        {
            /* No DHCP server found, use hard coded IP address. */
            u_long ip_addr = inet_addr("192.168.192.100");
            u_long ip_mask = inet_addr("255.255.255.0");

            NutNetIfConfig("eth0", my_mac, ip_addr, ip_mask);
            /* If not in a local network, we must also call 
               NutIpRouteAdd() to configure the routing. */
        }
    }

    /*
     * Start a RS232 receiver thread.
     */
    NutThreadCreate("xmit", Receiver, &cd, 512);

    /*
     * Now loop endless for connections.
     */
    cd.cd_connected = 0;
    for (;;) 
    {
        /*
         * Create a socket and listen for a client.
         */
        sock = NutTcpCreateSocket();
        NutTcpAccept(sock, TCPPORT);

        /*
         * Open a stdio stream assigned to the connected socket.
         */
        cd.cd_tcpip = _fdopen((int) sock, "r+b");
        cd.cd_connected = 1;

        /*
         * Call RS232 transmit routine. On return we will be
         * disconnected again.
         */
        StreamCopy(cd.cd_tcpip, cd.cd_rs232, &cd.cd_connected);

        /*
         * Close the stream.
         */
        fclose(cd.cd_tcpip);

        /*
         * Close our socket.
         */
        NutTcpCloseSocket(sock);
    }
}