ferdinandkeil.com

Archive for the ‘microcontroller’ tag

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

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

without comments

Note: This article is a follow-up to Use the TP-LINK TL-WR703N as a WiFi client with OpenWrt.

The reason why I bought the TL-WR703N orignally was to connect my cigar humidifier circuit to the internet. In the last article I established a WiFi connection to my local network. Now I needed a way to talk to my circuit over USB. It includes a USB port, which I used so far to update the firmware. So all that was left was some firmware based on V-USB and a script/program on the router side talking to it.

I soon found out, that the easiest way to get talking to USB devices was using Lua with libusb. However, the original OpenWrt package for the TL-WR703N doesn’t include a libusb binding for Lua. I’m not going to compile OpenWrt, so… to the Googles! As it happenend to be, Madox had already done all the hard work for me. He posted several firmware images for the TL-WR703N, of which I chose the Standard version. Updating OpenWrt given a proper firmware image is really easy, so I won’t go into detail here.

Now I needed a known-good firmware for my circuit to get started developing Lua scripts. Objective Development, the people behind V-USB, developed several example firmwares demonstrating their library. I chose the PowerSwitch, as it comes with a command-line utility that works under Windows.

I then adapted the firmware for my needs. In the end it would only toggle one PIN which is connected to an LED. To make sure it’s working, I then connected the device to my PC and ran the command-line utility. It worked flawlessly first try. Now, I went on to duplicate this functionality in Lua. The source for the command-line utility is included with the PowerSwitch example, so I started from that. The resulting Lua script is shown below. The script does not implement all functions of the PowerSwitch. It only works on port 0 and can not return the status of the device.

-- usage: lua powerswitch.lua on|off [duration]
-- duration (in seconds) is optional

PSCMD_STATUS =  1
PSCMD_ON =      2
PSCMD_OFF =     3

usb = require('libusb1')

local handle = usb.open_device_with_vid_pid(0x16C0, 0x05DC);

if (handle~=nil) then
	print('Device initialised with success !!')
	
	local port = 0
	local requesttype = usb.LIBUSB_REQUEST_TYPE_VENDOR + usb.LIBUSB_RECIPIENT_DEVICE + usb.LIBUSB_ENDPOINT_IN
	local request = PSCMD_STATUS
	if (arg[1] == 'on') then
		request = PSCMD_ON
	end
	if (arg[1] == 'off') then
		request = PSCMD_OFF
	end
	
	local duration = 0
	if (arg[2] ~= nil) then
		duration = arg[2] * 5
	end
	
	usb.control_transfer(handle, requesttype, request, duration, port, 8, 5000)
else
	print('Device initialisation failed !!')
end

There is not much documentation available on how to use libusb with Lua. I first looked at the Interfacing with Lua tutorial for the Pinguino, but soon found out it was written for a different libusb binding. The binding for Lua included in Madox’s package is lualibusb1, for which a function reference is available. Some important things to know about lualibusb1 are:

  • open_device_with_vid_pid([ctx, ] vendorid, productid) is implemented, which makes finding the right device really easy.
  • When requiring the library, type usb = require('libusb1') and then work with the object usb.
  • Constants from libusb are available, but some have different names. All are prefixed with LIBUSB_, but I still had to look up the names in the source of lualibusb1.

One important thing I almost forgot to mention: it is not possible to hook up a V-USB device directly to a TL-WR703N. That is because the TL-WR703N only supports USB2.0 or high-speed devices. So the V-USB device has to be connected to a USB hub, which is in turn connected to the router. Any cheap USB2.0 hub will do, I used this one: LOGILINK UA0136.

Written by Ferdinand

März 30th, 2013 at 17:07

Humidor Steuerung – ein Hygrostat für die Zigarrenlagerung

with one comment

Vor mittlerweile fast drei Jahren habe ich auf dieser Seite die Schaltung für meinen Humidor Lüfter veröffentlicht. Die Schaltung entstand aus der Idee, die Luft im Humidor regelmäßig umzuwälzen um eine möglichst gleichmäßige Feuchtigkeitsverteilung zu erreichen. Die Schaltung funktioniert bis heute ohne Probleme – nachdem ich sie um eine Batterieüberwachung erweitert habe macht sie auch keine Akkus mehr kaputt. Aus der gleichen Zeit stammte die Idee ein digitales Hygrometer für die Zigarrenlagerung zu entwerfen.

IMG_0016-1600

Die erste Version der Humidor Steuerung

Die Idee blieb in der Planungsphase stecken, bis ich mir Anfang 2012 endlich einen Ruck gab das Ganze umzusetzen (auch bedingt durch den Fund passender LC-Displays in meiner Teile-Kiste). Daraus entstand die erste Version der Humidor Steuerung. Bei Jakob Kleinen’s Sammelbestellung im mikrocontroller.net Forum ließ ich zwei Platinen fertigen und baute sie anschließend auf. Bis auf einen kleinen Fehler bei der Beschaltung des Drehgebers lief die Steuerung ohne Problem. Aus einem Testprogramm entwickelte sich die Firmware mit allen nötigen Features. Meine Ergebnisse veröffentlichte ich im Zigarrenforum. Die positiven Kommentare dort ermutigten mich schließlich eine zweite, verbesserte Version zu entwerfen. Die möchte ich nun hier vorstellen.

humidor_controller_top 20121028_215039

Die neue, verbesserte Version

Die Steuerung ist letztlich ein Hygrostat mit integrierter Zeitschaltuhr um handelsübliche PC-Lüfter anzusteuern. Insgesamt stehen vier Lüfter-Kanäle zur Verfügung die unabhängig voneinander programmiert werden können. An den Sensor Anschluss können verschiedene Sensor-Typen angeschlossen werden, die Firmware erkennt den richtigen Typ dann automatisch. Nicht auf dem Bild zu sehen (weil auf der Rückseite) ist ein Summer, der bei unterschreiten einer eingestellten Feuchtigkeit Alarm gibt. Außerdem ist eine Alarm-LED integriert, die durch das Loch über dem Glock-Symbol scheint.

humidor_controller_v35_sch detail_luefter_kanal

Schaltplan und Beschaltung eines Lüfter-Kanals

Die Lüfter-Kanäle sind alle identisch aufgebaut. Der Summer ist an die 12V Versorgungsspannung angeschlossen und wird ähnlich wie die Lüfter geschaltet. Jeder Kanal verfügt über einen N-Kanal Schalt-MOSFET im SOT-23 Package. Mögliche Typen sind z.B. der IRLML2502 oder GF2304. Jeder Kanal kann mit mindestens 500mA belastet werden, damit kann die Schaltung nahezu alle erhältlichen Lüfter betreiben.
Zum Schutz des MOSFETs und zur Entstörung der Schaltung sind dem Lüfter eine Schutzdiode und eine Kondensator parallel geschaltet.

Die Steuerung kann über USB mit einer neuen Firmware geflasht werden. Dazu wurde der verbaute ATmega168 mit dem USBaspLoader programmiert. Um den Bootloader zu aktivieren muss der Drehgeber gedrückt werden wenn die Schaltung mit der Spannungsversorgung verbunden wird.

Die fertige Schaltung sitzt in einem Standard-Gehäuse wie eine Frontplatte. Es muss nur ein Loch für die Hohlstecker-Buchse gebohrt werden, die auf der Rückseite der Platine angeschlossen wird.

Das verwendete Display HMC16223SG ist leider nicht mehr erhältlich. Aufgrund seiner kleinen Abmessungen ist es perfekt für ein kompaktes Gerät geeignet. Einen adäquaten Ersatz konnte ich bisher nicht finden, die nächste Version wird wohl ein anderes Display verwenden.

Die Firmware für die Steuerung basiert auf meinem Taskmanager. Sie ist so weit wie möglich modular aufgebaut. Es wurde auch ein Menü-System verwendet, das auf dem Code des Butterfly GCC-Ports aufbaut.
Die Firmware um Funktionen für weitere Lüfterkanäle oder mehrere Sensoren zu erweitern, sollte kein Problem sein. Die aktuelle Firmware verbraucht ~10,5kB Flash, der Bootloader 4kB – es ist also noch Platz für Erweiterungen. Alternativ kann ein ATmega328 verbaut werden.

sensor_board_v10 20130320_133231-1600

Die Sensor-Platine: als 3D-Modell und fertig aufgebaut



Die Sensor-Platine ist mit folgenden Sensoren kompatibel:

Die Sensor-Platine wird mit 12V versorgt und hat einen lokalen Spannungsregler (LP2980-N), um Spannungsverluste über die Zuleitung sowie Störungen zu reduzieren.

Downloads

Die Firmware steht unter MIT Lizenz, die Hardware Quelldateien unter der TAPR Lizenz. Die Dateien sind hier zu finden:

Written by Ferdinand

März 20th, 2013 at 15:47