pj talk - npinguin - 24.04.2016
Hi
I am trying to create a library for PJ talk communication with a Sony VPL-HW65ES Projector
I am experiencing some difficulties
protocol description is very limited, so far I found the following info
http://forum.coolux.de/viewtopic.php?t=2004&p=6623
http://ec1.images-amazon.com/media/i3d/01/A/man-migrate/MANUAL000015642.pdf
From what I figure out I need to send the following hex values
projector on
h01 h00 hA9 h17 h2F h00 h00 h00 h3F h9A
project off
h01 h00 hA9 h17 h2E h00 h00 h00 h3F h9A
My LUA knowledge is still limited
How can I setup a socket and communicate the above hex values ?
- how to setup the socket ?
- how to send hex values ?
Thanks
Nicky
RE: pj talk - admin - 24.04.2016
Try this example, just change ip variable to your projector IP address:
Code: require('socket')
ip = '192.168.1.23'
port = 53484
cmd = string.char(0x01, 0x00, 0xA9, 0x17, 0x2F, 0x00, 0x00, 0x00, 0x3F, 0x9A)
sock = socket.tcp()
sock:settimeout(3)
res, err = sock:connect(ip, port)
if res then
res, err = sock:send(cmd)
if res then
alert('send OK')
else
alert('send failed: ' .. tostring(err))
end
else
alert('connect failed: ' .. tostring(err))
end
sock:close()
RE: pj talk - npinguin - 24.04.2016
Super
thanks for the Lightning fast reply !
RE: pj talk - npinguin - 24.04.2016
I am one step further, but not there yet
- communication works, but command is probably not correct because I get a weird response
my user script
Code: --------------------------------------
-- Author: Nicky Wessels
-- Last Modified: 24/04/2016
--
-- Change Log:
-- 24/04/2016 Intial Version
--------------------------------------
-- IP of the projector
PROJECTOR_IP = '192.168.0.20'
PROJECTOR_PORT = '53484'
-- Power ON
-- 'h01 h00 hA9 h17 h2F h00 h00 h00 h3F h9A'
SONYPJ_CMD_ON = string.char(0x01, 0x00, 0xA9, 0x17, 0x2F, 0x00, 0x00, 0x00, 0x3F, 0x9A)
-- Power Off
-- 'h01 h00 hA9 h17 h2E h00 h00 h00 h3F h9A'
SONYPJ_CMD_OFF = string.char(0x01, 0x00, 0xA9, 0x17, 0x2E, 0x00, 0x00, 0x00, 0x3F, 0x9A)
-- change input s-video
-- 'h01 h00 ha9 h00 h01 h00 h00 h01 h01 h9a'
SONYPJ_SET_SVIDEO = string.char(0x01, 0x00, 0xA9, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x9A)
-- funtion to send basic commands to the receiver
function sonypj_send_cmd(cmd, ip, port)
require 'socket.http'
response_tbl = { }
-- fill up optional parameters
if (ip == nil) then
ip = PROJECTOR_IP
end
if (port == nil) then
port = PROJECTOR_PORT
end
response_tbl['ip']=ip
response_tbl['port']=port
response_tbl['cmd']=cmd
-- Send command to projector
sock = socket.tcp()
sock:settimeout(3)
result, err = sock:connect(ip, port)
response_tbl['result']=result
if result then
result, err = sock:send(cmd)
response_tbl['result']=result
response_tbl['error']=error
-- Check response from projector
if result then
alert('send OK')
else
alert('send failed: ' .. tostring(err), response_tbl)
end
else
alert('connect failed: ' .. tostring(err), response_tbl)
end
sock:close()
return response_tbl
end
my trigger script
Code: require 'user.sony_pjtalk'
-- object mapped to this event must have its data type set
bStatus = event.getvalue()
-- get the status of the projector
if (bStatus) then
status_tbl = sonypj_send_cmd(SONYPJ_CMD_ON)
else
status_tbl = sonypj_send_cmd(SONYPJ_CMD_OFF)
end
log('status',status_tbl)
the response that I get for all commands
Code: * arg: 2
* table:
[error]
* function: builtin#19
[ip]
* string: 192.168.0.20
[result]
* number: 10
[cmd]
* string:
RE: pj talk - Erwin van der Zwart - 24.04.2016
Hi,
Try this to see the HEX values from cmd after string.char(0x01, 0x00, 0xA9, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x9A):
Code: response_tbl['cmd']=lmcore.strtohex(cmd)
Are your commands processed? Sometimes you need to end a command like this:
Code: res, err = sock:send(cmd .. '\r')
If you expect an answer from the socket after sending a command you need to switch to receive mode like this:
Code: s, status, partial = sock:receive(1024) -- 1024 is expected response length and be sure to set socket timeout like this sock:settimeout(2) before sending a command
Good luck!
BR,
Erwin
RE: pj talk - npinguin - 25.04.2016
Thanks Eric
Will try,
I have been searching on this topic and it seems nobody got this protocol working over ip...
Hmmm
RE: pj talk - npinguin - 30.11.2017
I got it working, but I am unable to convert a string into a hex array to compute the code
These codes work
Code: -- Power ON
-- HEX: 02 0A 53 4F 4E 59 00 01 30 02 00 01
SONYSDCP_CMD_ON = string.char(0x02, 0x0A, 0x53, 0x4F, 0x4E, 0x59, 0x00, 0x01, 0x30, 0x02, 0x00, 0x01)
-- Power Off
-- HEX: 02 0A 53 4F 4E 59 00 01 30 02 00 00
SONYSDCP_CMD_OFF =string.char(0x02, 0x0A, 0x53, 0x4F, 0x4E, 0x59, 0x00, 0x01, 0x30, 0x02, 0x00, 0x00)
I use the following strings after computation
Code: -- Power ON
-- HEX: 02 0A 53 4F 4E 59 00 01 30 02 00 01
SONYSDCP_CMD_ON = "020A534F4E59000130020001"
-- Power Off
-- HEX: 02 0A 53 4F 4E 59 00 01 30 02 00 00
SONYSDCP_CMD_OFF = "020A534F4E59000130020000"
The following helpers do not convert correctly, but I am unable to determine why
Code: -- convert hex char array to string
function string.fromhex(str)
return (str:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end))
end
-- convert string to hex char array
function string.tohex(str)
return (str:gsub('.', function (c)
return string.format('%02X', string.byte(c))
end))
end
RE: pj talk - admin - 30.11.2017
You can convert hex to binary string like this (second argument is needed, otherwise NULL bytes will be removed). You can also use loghex function which logs binary strings in readable hex format.
Code: SONYSDCP_CMD_ON = lmcore.hextostr('020A534F4E59000130020001', true)
loghex(SONYSDCP_CMD_ON)
RE: pj talk - npinguin - 30.11.2017
Thanks
I found an alternative in the mean time
Hereby the full script for anyone looking for a PJ Talk solution
Code: --------------------------------------
-- Author: Nicky Wessels
-- Last Modified: 30/11/2017
--
-- Change Log:
-- 24/04/2016 Intial Version
-- 30/11/2017 Update Based on SDCP-Com - By Vokkim on JSDelivr
-- 30/11/2017 Javascript code https://github.com/vokkim/sony-sdcp-com
-- 30/11/2017 Protocol https://www.digis.ru/upload/iblock/f5a/VPL-VW320,%20VW520_ProtocolManual.pdf
--------------------------------------
-------------------------
-- CONFIG
-------------------------
-- IP of the projector
BASEMENT_PROJECTOR_IP = '192.168.0.21'
BASEMENT_PROJECTOR_PORT = '53484'
-------------------------
-- CONSTANTS
-------------------------
-- CONSTANTS
VERSION = '02'
CATEGORY = '0A'
-- Default to 'SONY'
COMMUNITY = '534F4E59'
actions = {
GET= '01',
SET= '00'
}
commands = {
SET_POWER= '0130',
CALIBRATION_PRESET= '0002',
ASPECT_RATIO= '0020',
INPUT= '0001',
GET_STATUS_ERROR= '0101',
GET_STATUS_POWER= '0102',
GET_STATUS_LAMP_TIMER= '0113'
}
input = {
HDMI1 = '0002',
HDMI2 = '0003',
}
aspectRatio = {
NORMAL= '0001',
V_STRETCH= '000B',
ZOOM_1_85= '000C',
ZOOM_2_35= '000D',
STRETCH= '000E',
SQUEEZE= '000F'
}
powerStatus = {
STANDBY= '0000',
START_UP= '0001',
START_UP_LAMP= '0002',
POWER_ON= '0003',
COOLING= '0004',
COOLING2= '0005'
}
-- set projector input
function setInput(input, ip, port, debug)
if (debug) then
log (string.format("setInput:%s projector:%s:%s", input, ip, port))
end
frame = createMessageAsHex(actions.SET, commands.INPUT, input, debug)
result = sony_sdcp_send_frame(frame, ip, port, debug)
return result
end
-- get projector input status
function getInputStatus(ip, port, debug)
if (debug) then
log (string.format("getInputStatus projector:%s:%s", ip, port))
end
frame = createMessageAsHex(actions.GET, commands.INPUT, nil, debug)
result = sony_sdcp_send_frame(frame, ip, port, debug)
inputStatus = nil
if (result and result.reply and result.reply.data) then
inputStatus = result.reply.data
end
return inputStatus
end
-- power projector on
function setPower(powerOn, ip, port, debug)
if (debug) then
log (string.format("powerOn:%s projector:%s:%s", tostring(powerOn), ip, port))
end
if (powerOn) then
frame = createMessageAsHex(actions.SET, commands.SET_POWER, powerStatus.START_UP, debug)
else
frame = createMessageAsHex(actions.SET, commands.SET_POWER, powerStatus.STANDBY, debug)
end
result = sony_sdcp_send_frame(frame, ip, port, debug)
return result
end
-- get projector power status
function getPowerStatus(ip, port, debug)
if (debug) then
log (string.format("getPowerStatus projector:%s:%s", ip, port))
end
frame = createMessageAsHex(actions.GET, commands.GET_STATUS_POWER, nil, debug)
result = sony_sdcp_send_frame(frame, ip, port, debug)
powerstatus = false
if (result and result.reply and result.reply.data) then
powerstatus = result.reply.data
end
return powerstatus
end
-- get projector power status as a boolean
function getPowerStatusAsBool(ip, port, debug)
if (debug) then
log (string.format("getPowerStatusAsBool projector:%s:%s", ip, port))
end
result = false
powerstatus = getPowerStatus(ip, port, debug)
if (powerstatus) then
result = powerstatus == powerStatus.POWER_ON or powerstatus == powerStatus.START_UP or powerstatus == powerStatus.START_UP_LAMP
end
return result
end
-- convert hex char array to string
function string.fromhex(str)
result =''
for c in str:gmatch"." do
result = result .. string.format("%02x", string.byte(c))
end
return string.upper(result)
end
-- convert string to hex char array
function string.tohex(str)
result =''
for cc in str:gmatch".." do
result = result .. string.char(tonumber(cc, 16))
end
return string.upper(result)
end
-- create a sdcp message
function createMessageAsHex(action, command, data, debug)
if (command and type(command) ~= "string") then
alert(string.format("Accepts command only as String (HEX) for now, was %s", type(command)))
end
if (string.len(command) ~= 4) then
alert(string.format("Command must be 4 bytes long"))
end
if (data and type(data) ~= "string") then
alert(string.format("Accepts data only as String (HEX) for now, was %s", type(data)))
end
if (not data) then
data = ""
end
dataLength = string.format('%02x', string.len(data)/2)
if (debug) then
-- log ("dataLength:" .. dataLength)
end
frame = VERSION .. CATEGORY.. COMMUNITY.. action.. command.. dataLength.. data
if (debug) then
-- log ("frame:" .. frame)
end
return string.tohex(frame)
end
-- parse a sdcp message
function parseResponse(value, debug)
if (not value) then
alert(string.format("Empty value passed"))
assert(not value, string.format("Empty value passed"))
end
frame = string.fromhex(value)
if (string.len(frame) < 20) then
alert(string.format("Unknown response %s, length %d",frame,string.len(frame)))
assert(string.len(frame) < 20, string.format("Unknown response %s",frame))
end
version = string.sub(frame, 1, 2)
category = string.sub(frame, 3, 4)
community = string.sub(frame, 5, 12)
success = string.sub(frame, 13, 14)
command = string.sub(frame, 15, 18)
dataLength = string.sub(frame, 19, 20)
if (tonumber(dataLength, 16) > 0) then
data = string.sub(frame,21, 21 + tonumber(dataLength, 16) * 2)
end
result = {
version=version,
category=category,
community=community,
command=command,
dataLength=dataLength,
data=data,
success=success ~= '00',
raw = frame
}
return result
end
-- funtion to send basic commands to the projector
function sony_sdcp_send_frame(frame, ip, port, debug)
require 'socket.http'
response_tbl = { }
-- fill up optional parameters
if (ip == nil) then
ip = BASEMENT_PROJECTOR_IP
end
if (port == nil) then
port = BASEMENT_PROJECTOR_PORT
end
response_tbl['ip']=ip
response_tbl['port']=port
response_tbl['frame']=frame
if (debug) then
log("ip:" .. ip)
log("port:" .. port)
log("transmit frame char:" .. string.fromhex(frame))
log("transmit frame hex:" .. frame)
log("transmit frame length:" .. string.len(frame))
end
-- Send command to projector
sock = socket.tcp()
sock:settimeout(3)
result, err = sock:connect(ip, port)
response_tbl['result']=result
response_tbl['error']=err
if result then
result, err = sock:send(frame .. "\r")
response_tbl['result']=result
response_tbl['error']=error
-- Check response from projector
if result then
if (debug) then
log ('transmit succesfully send')
end
-- read the response
resframe = ''
while true do
resp, err, part = sock:receive(1)
if resp ~= nil then
resframe = resframe .. resp
else
break
end
end
resstring = string.fromhex(resframe)
resdecoded = parseResponse(resframe)
response_tbl['reply']=resdecoded
if (debug and resframe) then
log("response frame char:" .. resstring)
log("response frame hex:" .. resframe)
log("response frame length:" .. string.len(resframe))
log("response frame decoded", resdecoded)
end
else
alert('send failed: ' .. tostring(err), response_tbl)
end
else
alert('connect failed: ' .. tostring(err), response_tbl)
end
sock:close()
return response_tbl
end
add trigger scripts to call the functions in the user lib
Code: -- activate projector
setPower(bStatus, BASEMENT_PROJECTOR_IP, BASEMENT_PROJECTOR_PORT)
-- check if it is active or starting up
-- warning takes a few seconds before projector boots up
bPowerStatus = getPowerStatusAsBool(BASEMENT_PROJECTOR_IP, BASEMENT_PROJECTOR_PORT)
-- set the input
setInput(input.HDMI1, BASEMENT_PROJECTOR_IP, BASEMENT_PROJECTOR_PORT, true)
-- check the input state
sInputStatus = getInputStatus(BASEMENT_PROJECTOR_IP, BASEMENT_PROJECTOR_PORT)
|