ferdinandkeil.com

What’s inside a cheap TOSLINK, S/PDIF to Cinch adapter

without comments

toslink_dac-600

I wanted to connect my TV to my Hifi system but found it lacking an analog output. On Amazon.de I stumbled upon this cheap audio DAC, converting a TOSLINK input to line level stereo outputs. About 17 EUR shipped sounded reasonable to me, so I ordered it right away. However at that price (including a wall plug) I was curious what components are inside this thing. So you know how Dave Jones says, don’t turn it on, turn it apart!

20140308_222114-1600 20140308_222003-1600

The magic is obviously happening on the bottom side of the board. You can find a high resolution scan of it below. The main ICs are both Cirrus Logic parts: the CS8416 192 kHz digital audio receiver and the CS4344 24-bit, 192 kHz stereo DAC. As both parts support sample rates up to 192 kHz the device likely exceeds its spec of 96 kHz. The DAC features a dynamic range of 105 dB and -90 dB THD+N, which should be good enough for my purposes.
There also is an LDO and some passives. The soldering looks rather good, at least for the price. The board layout is also reasonable, analog and digital parts are somewhat separated, the digital lines going to the DAC have series resistors, the DAC’s outputs even got protection diodes.

cs4344_spdif_dac_hires

I’ve used the device for more than a week now and I’m quite pleased with it’s performance. It’s compact size makes it easy to hide it behind the TV. In a next step I’ll build a USB-to-barrel-plug adapter to power it from one of the TV’s USB ports. That way it will be turned on and off together with the TV.

Written by Ferdinand

März 16th, 2014 at 14:56

Connect V-USB devices to the Internet with the TL-WR703N – Part 2

without comments

In the first part of this mini-series I established a basic communication between my device and the TL-WR703N. In this part I will request data from my device over USB and then push it to Cosm. Cosm is a service that can store and display sensor data, which is exactly what I want to do. Cosm recently became Xively (the third name-change in the history of the service…). The good news is, that the old API still works.

You can talk to Cosm over a REST API. I searched the web for a Lua implementation of this API and finally found some code by a guy named Shadock. To simplify usage I created a small Lua library:

-- save as 'luacosm.lua'

-- load libs
require("ltn12")
socket = require("socket")
http = require("socket.http")

-- cosm_put(apikey, feed, datastream, value)
-- Push's a value to the specified Cosm feed.
-- Has to be given an API-Key with the necessary permissions.
--
-- based on Shadock's code as published on
-- http://forum.micasaverde.com/index.php?topic=7396.0
function cosm_put(apikey, feed, datastream, value)
	local base_url = "http://api.cosm.com/v2/feeds/"
	local method = "PUT"

	local json_data = '{ "version":"1.0.0","datastreams":[ {"id":"' .. datastream .. '", "current_value":"' .. value .. '"}]}'
	local response_body = {}
	local response, status, header = http.request{
		method = method,
		url = base_url .. feed .. ".json",
		headers = {
			["Content-Type"] = "application/json",
			["Content-Length"] = string.len(json_data),
			["X-ApiKey"] = apikey
		},
		source = ltn12.source.string(json_data),
		sink = ltn12.sink.table(response_body)
	}
	return response, status, header
end

A short example shows how to use the library:

require("math")
require("luacosm")

local apikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
local feedid = 123456
math.randomseed( os.time() )
local humidity = math.random(60, 80)
print("Humidity: " .. humidity .. "%relH")

local response, status, header = cosm_put(apikey, feedid, "Humidity", humidity)

To start sending data, you first have to create so called feed in Cosm. This gives you the feed ID. You also need your personal API key, which can also be found on the Cosm website. Then all that is left is pushing some data to your feed.
The code shown above runs on the firmware image Madox generated for the TL-WR703N. It ships with all necessary Lua libraries, so no need to compile anything.

After verifying that I can upload random data to Cosm, I still need to retrieve some real sensor readings from my device. Again I used V-USB and lualibusb1 to connect my device to the router. I adapted the script presented in the first part of the series for this purpose.

require("luacosm")

usb = require("libusb1")

PSCMD_STATUS =  1
PSCMD_ON =      2
PSCMD_OFF =     3

local apikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
local feedid = 123456

local handle = usb.open_device_with_vid_pid(0x16C0, 0x05DC);
if ((usb.get_string_descriptor_ascii(handle, 1) ~= "www.ferdinandkeil.com")
	and (usb.get_string_descriptor_ascii(handle, 2) ~= "Humidor Steuerung")) then
	handle = nil;
end


if (handle~=nil) then
	print("Device successfully initialised!")
	
	local type = usb.LIBUSB_REQUEST_TYPE_VENDOR + usb.LIBUSB_RECIPIENT_DEVICE + usb.LIBUSB_ENDPOINT_IN
	local request = PSCMD_STATUS
	
	print("Request data from device...")
	local response = usb.control_transfer(handle, type, request, 0, 0, 8, 5000)
	
	local hi = string.byte(response, 2)
	local lo = string.byte(response, 1)
	local humidity = ( (hi*255) + lo ) / 100
	print("Measured Humidity: " .. humidity .. "%relH")

	print("Try to upload to Cosm...")
	local response, status, header = cosm_put(apikey, feedid, "Feuchtigkeit", humidity)
	print("Finished")
else
	print("Device initialisation failed!")
end

Now let’s go through some of the important parts of the code:

local handle = usb.open_device_with_vid_pid(0x16C0, 0x05DC);
if ((usb.get_string_descriptor_ascii(handle, 1) ~= "www.ferdinandkeil.com")
	and (usb.get_string_descriptor_ascii(handle, 2) ~= "Humidor Steuerung")) then
	handle = nil;
end

I still use the VID/PID supplied by Objective Development, but this time I tried to honour their license terms. They basically say you can use their VID/PID freely, but you have to give your device a unique descriptor. So I ask the device for its descriptor and only accept the one I have chosen for my device.

local hi = string.byte(response, 2)
local lo = string.byte(response, 1)
local humidity = ( (hi*255) + lo ) / 100

On the micro-controller side the humidity is a 16 bit value, so it is send as two bytes. On the router side I have to reassemble these two bytes into one value. That is done with the code shown above. string.byte(s, i) gives me the raw byte value for the i-th byte in string s. I then shift the high byte to the left (hi*255) and then add the low byte.
It took me some time to figure this one out, as I am accustomed to the way C handles data. But you can’t easily typecast variables in Lua, so you have to use some helper functions. Also there seems to be no shift operator, so you have to rely on multiplication and division.

Written by Ferdinand

August 7th, 2013 at 15:49

Ändern der I2C Adresse eines HYT-131 Sensors

with one comment

Auf der Suche nach genauen und günstigen Sensoren für meine Humidor Steuerung bin ich auf den HYT-131 von B+B Sensors aufmerksam geworden. Für knapp 20 Euro inkl. MwSt. bekommt man einen maximal ±2,0%relF genauen, kombinierten Temperatur- und Feuchtigkeitssensor. Das ist deutlich günstiger als der Sensirion SHT15 mit vergleichbaren Werten. Ein weiterer Vorteil des Sensors ist, dass er über ein Standard I²C-Interface kommuniziert.
Der I²C-Bus unterstützt bis zu 112 Geräte, vorausgesetzt diese haben unterschiedliche Adressen. Das Datenblatt des HYT-131 erwähnt zwar die Möglichkeit die voreingestellte Adresse zu ändern, erläutert aber nicht wie. Über einen Beitrag im Raspberry Pi Forum wurde ich dann darauf aufmerksam, dass der HYT-131 genauso wie der HYT-221 und HYT-271 auf einem IC von ZMDI basieren. Der IC heißt ZSSC3122, das Datenblatt habe ich angehängt. Im Datenblatt zu diesem IC finden sich allerhand Informationen, darunter wie die I²C-Adresse geändert werden kann. Der Ablauf ist wie folgt:

  1. Sobald der Sensor mit der Versorgungsspannung verbunden wurde muss innerhalb von 3-10 ms (abhängig von der internen Konfiguration) ein Befehl gesendet werden um in den Command Mode zu gelangen.
  2. In diesem Modus kann der interne EEPROM beschrieben werden, wobei die I²C-Adresse im Register Cust_Config hinterlegt ist.

Um diesen Vorgang automatisch und zuverlässig durchführen zu können habe ich eine Arduino Sketch geschrieben. Die bisherige (default 0×28) und neue Adresse werden über defines angegeben. Der Sensor wird mit dem Arduino verbunden und dieser mit der Sketch programmiert. Dann muss nur noch der Serielle Monitor gestartet und ein beliebiges Zeichen gesendet werden um den Prozess zu starten.
Bei den Sensoren die mir von B+B Sensors geliefert worden sind war das Comm_lock Flag im Register ZMDI_Config nicht gesetzt. Dadurch antwortet der Sensor auf allen I²C-Adressen. Die Sketch setzt dieses Flag, da sonst nicht getestet werden kann ob die Änderung der Adresse erfolgreich war. Mit der enthaltenen Funktion changeCommLock(...) kann diese Änderung aber auch wieder rückgängig gemacht werden.

Hinweis: Falls ein Fehler auftritt ist der Sensor unter Umständen weder unter seiner alten noch unter der neuen Adresse ansprechbar. In diesem Fall hilft es den gesamten I²C-Adressraum zu durchlaufen und auf Geräte zu prüfen.

page_white_acrobat ZSSC3122_cLite_Data_Sheet_rev_1_30

Download Icon hyt131_i2c_addr_change_release

Written by Ferdinand

April 1st, 2013 at 12:23