I recently purchased an Onkyo TX-NR515 A/V receiver & amplifier. One of my main reasons for choosing this amp was that it supports network remote control via its ethernet port. This enables me to shut the amp away out of sight in an A/V cabinet and control it using the Onkyo Remote Android app.
Being a geek though it wasn’t sufficient for me to be able to control it via app. I wanted to make the amp “magically” turn itself on & select the correct input source when I turned on my HTPC or Squeezebox Touch (more on this soon).
Thanks to Tom Gutwin‘s excellent work of finding Onkyo’s protocol specification & documenting his efforts to produce a Java eISCP client it was pretty easy to produce a little Linux command-line utility which sends remote commands to an Onkyo amp.
The utility is written in C and should compile cleanly with GCC on Linux (it may work on other platforms, I haven’t tried it). Usage is as follows:
./onkyo-iscp <amp hostname or ip> <ISCP command> <command parameter>
For example:
./onkyo-iscp onkyo.home.lan PWR 01
will send a “power on” message to the amp. There’s currently no error checking on the command or parameters, it assumes you know what you’re sending to the amp (read the protocol spec for a list of commands & parameters). It also doesn’t read any data from the amp, I might get around to implementing this eventually (if it turns out that I need it).
Source tarball is here: onkyo-iscp.tar.gz
Hi, nice work.
Looks like you are on the same course as me. Trying to make my AV system all automagic!
I found the Android Onkyo app a bit rough around the edges.
When you get to implementing/processing responses from your NR515, beware, I found that it sends multiple responses back; some have nothing to do with the request you send.
I ended up encapsulating that into
public Vector readQueryResponses()
which buffers ALL response packets into a vector that can be looped through to pick out the ones associated with your request.
The other thing to watch for is the end of packet bytes. (also describerd in the readQueryResponses()_ method.
// NOTE: the end of packet char (0x1A) for a response message is DIFFERENT that the sent message
// NOTE: ITs also different than what is in the Onkyo eISCP docs
// NR-5008 sends 3 chars to terminate the packet – 0x1a 0x0d 0x0a
Have fun, I did.
I want to build a single Android app that wraps
my Onkyo,
My Panasonic TV,
My Home Lighting system (Universal Devices ISY-994),
MythTV,
Lirc controlling my cable box,
tom
Hi,
nice work.
Can you help me with the syntax of the NPU command?
The description of the ISCP Protocol specifikation from the Net Popup Command is nor clear for me.
I think it cant send with your solution, but i have a VB Solution to send.
In my opinion it must be
“NPU” + Chr(0) + “T” + Chr(0) + “Toptitelname” + Chr(0) + “Poptitelname” + Chr(0) + “Message” + Chr(0) + “y” + Chr(0) + “1” + Chr(0) + “B1name” + Chr(0) + “B2name”
but it doesnt work. Have you a good idea?
Nice greetings,
André
Hi Simon,
thank you very very much for your
“Network remote for Onkyo A/V Receivers”
I was looking around for a solution to that and found your page. That helped a lot, it works now. i am now able to send commands from my htpc (based on linux) to my ONKYO 509 just fine.
Have a great day.
Kindest regards
Holger
So what ends up being sent? I’m not too familiar with C, so I’m having a little trouble understanding the code.
I understand that “!1PWR01\r\n” is sent at some point. But I’m not understanding what else is sent. Is anything else sent..?
Hello,
First thank you for this source code. I tried tonight to compile an .exe for Windows with WinGW from it. Maybe it will help somebody. Here my code changes in network.c:
#include
#include
#include
#pragma comment(lib,”ws2_32.lib”) // Winsock Library
int networkTCPConnect(char *hostname, short unsigned int port)
{
WSADATA wsa;
SOCKET s;
struct sockaddr_in server;
printf(“\nInitialising Winsock…”);
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf(“Failed. Error Code : %d”,WSAGetLastError());
return 1;
}
printf(“Initialised.\n”);
// Create a socket
if ((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
printf(“Could not create socket : %d”,WSAGetLastError());
}
printf(“Socket created.\n”);
server.sin_addr.s_addr = inet_addr(hostname);
server.sin_family = AF_INET;
server.sin_port = htons(port);
// Connect to remote server
if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("connect error\n");
return 1;
}
printf("Connected\n");
return s;
}
And in iscp.c file:
#include
#include
#include
#include
#include
#include “network.h”
#include “iscp.h”
struct eISCPMsg {
struct {
char id[4]; // “ISCP”
uint32_t headerSize; // Big-Endian header size
uint32_t dataSize; // Big-Endian data size
uint8_t version; // 1
uint8_t reserved[3];
} header;
struct {
uint8_t start; // ‘!’
uint8_t dest; // ‘1’ == Receiver
char cmd[3]; // Three character command string
char param[]; // Variable length parameter, terminated with \r\n
} data;
};
struct iscpHost
{
char *hostname;
int port;
int socket;
};
struct iscpHost *host_p = NULL;
void iscp_free(struct iscpHost *h_p)
{
if (h_p)
{
if (h_p->hostname)
{
free(h_p->hostname);
}
if (h_p->socket != -1)
{
close(h_p->socket);
}
free(h_p);
}
}
void iscpDisconnect()
{
if (host_p)
{
iscp_free(host_p);
host_p = NULL;
}
}
int iscpConnect(char *hostname, unsigned short int port)
{
int result = -1;
struct iscpHost *connect_p = NULL;
connect_p = calloc(1, sizeof(*connect_p));
if (connect_p != NULL)
{
if (port > 0)
{
connect_p->port = port;
}
else
{
connect_p->port = ISCP_PORT;
}
connect_p->hostname = strdup(hostname);
if (connect_p->hostname)
{
connect_p->socket = networkTCPConnect(connect_p->hostname,
connect_p->port);
if (connect_p->socket != -1)
{
result = 0;
}
}
}
if (result != 0)
{
iscp_free(connect_p);
}
else
{
host_p = connect_p;
}
return result;
}
int iscpSendCommand(char *cmd, char* param)
{
int result = 0;
char *buffer_p = NULL;
unsigned int bufferSize = 0;
struct eISCPMsg *msg_p;
if ((!host_p) ||
(!cmd) ||
(!param))
{
return -1;
}
bufferSize = sizeof(*msg_p) + strlen(param) + 2;
buffer_p = calloc(1, bufferSize);
if (!buffer_p)
{
return -2;
}
msg_p = (struct eISCPMsg *) buffer_p;
memcpy(msg_p->header.id, “ISCP”, 4);
msg_p->header.headerSize = htonl(sizeof(msg_p->header));
msg_p->header.dataSize = htonl(sizeof(msg_p->data) +
strlen(param) +
1);
msg_p->header.version = 1;
msg_p->data.start = (uint8_t) ‘!’;
msg_p->data.dest = (uint8_t) ‘1’;
memcpy(msg_p->data.cmd, cmd, sizeof(msg_p->data.cmd));
sprintf(msg_p->data.param,
“%s\r\n”,
param);
// Send some data
if ( send(host_p->socket, buffer_p, bufferSize, 0) < 0)
{
printf("Send failed");
return 1;
}
printf("Data Send\n");
free(buffer_p);
return result;
}
The Makefile is a little bit tricky. The compile option -lwsock32 must be at the end:
onkyo-iscp:
gcc network.c iscp.c onkyo-iscp.c -o onkyo-iscp -lwsock32
clean:
rm -f *.o
rm -f onkyo-iscp.exe
I'm no programmer. It's a quick and dirty midnight rework, but works.
Regards
Sry, the includes gone lost and are windows.h and winsock2.h.
Sorry, forgot to close the socket in iscp.c. Has to be:
void iscp_free(struct iscpHost *h_p)
{
if (h_p)
{
if (h_p->hostname)
{
free(h_p->hostname);
}
if (h_p->socket != -1)
{
closesocket(h_p->socket);
WSACleanup();
}
free(h_p);
}
}