Vor ein paar Wochen hatte ich ein Olimex ESP8266-EVB bestellt und bin am
Wochenende endlich mal tatsächlich dazu gekommen, damit herumzuspielen.
Als erste Fingerübung habe ich IRMP darauf zum Laufen gebracht. Siehe
die beiden Anlagen. 1.patch sind die Diffs zu der aktuellen SVN-Version
von IRMP, user_main.c ist das Hauptprogramm.
Ein Gag am Rande: die sich im Beispieloutput als SAMSG32 meldende
Fernbedienung taucht in der kürzlichen Umfrage nicht auf. Das liegt
daran, daß die mit "LG" beschriftete Fernbedienung eines Blu-Ray-Players
normalerweise zwar in dieser Zunge spricht, aber auf NECisch umschaltet,
wenn man einen rechts oben mit "TV" beschrifteten Block benutzt.
Noch schlimmer ist eine originale Philips-Fernbedienung einer alten
Stereoanlage, die schaltet ihre Tasten zwischen verschiedenen Codes um
(Adresse und Commando) und ggfs. auch ganz ab, je nachdem, welches Gerät
man einschaltet. Für den Benutzer soll das konsistent aussehen:
Lautstärke ist Lautstärke und das Radio kann man nicht zurückspulen. :-)
Wolfgang S. schrieb:> Ein Gag am Rande: die sich im Beispieloutput als SAMSG32 meldende> Fernbedienung taucht in der kürzlichen Umfrage nicht auf.
Ich hab's nicht aufgeführt, weil Gerät und Fernbedienung ohne IRMP
funktionieren, aber auf meinem IR-Spion sehe ich das Protokoll.
Ich hatte mal eine Fernbedienung mit abweichendem Protokoll bei der
On/Off-Taste. Damit rechnet man ja erstmal nicht.
Wolfgang S. schrieb:> Als erste Fingerübung habe ich IRMP darauf zum Laufen gebracht.
Klasse. Ich werde Deine Änderungen gern in IRMP einbauen.
Planst Du auch noch eine Portierung von IRSND? ;-)
> Das liegt> daran, daß die mit "LG" beschriftete Fernbedienung eines Blu-Ray-Players> normalerweise zwar in dieser Zunge spricht, aber auf NECisch umschaltet,> wenn man einen rechts oben mit "TV" beschrifteten Block benutzt.
Ja, sowas ist mir auch schon mal untergekommen. Ist aber selten.
Frank M. schrieb:> Wolfgang S. schrieb:>> Als erste Fingerübung habe ich IRMP darauf zum Laufen gebracht.>> Klasse. Ich werde Deine Änderungen gern in IRMP einbauen.
Wie ich gerade sehe, hast Du schon. Das ging aber flott. :).
> Planst Du auch noch eine Portierung von IRSND? ;-)
Im Moment brauche ich das zwar nicht, da es aber keine besonderen neuen
Anforderungen stellt, erledige ich das bei Gelegenheit nebenher.
Ein kurzer Bericht zum aktuellen Stand. Das Basteln mit dem ESP8266
gestaltet sich zäher als erwartet, hauptsächlich aufgrund der etwas
unübersichtlichen Gemengelage aus unterschiedlichen SDK nebst eher
dürftiger Dokumentation.
Angefangen habe ich mit dem esp_iot_sdk_v1.4.1_pre5_15_10_27.zip
Ziel ist zunächst mal eine kleine Anwendung, die auf bequeme Weise die
von IRMP verstandenen Tastendrücke auf einem geeigneten Gerät per
Browser oder App anzeigen kann. Code bereitstellen möchte ich noch
nicht, da der ganze Aufbau noch zu sehr mit der heißen Nadel gestrickt
ist, aber schon mal ein paar Zwischenergebnisse diskutieren.
Das aktuelle Demoprogramm realisiert einen Accesspoint, der mit der
festen SSID irmp432, via WPA2 und einer festen Passphrase einen DHCP-
und einen Webserver auf der Adresse 192.168.4.1 realisiert und auf
jeglichen http-Request mit einer Standard-HTML-Seite antwortet, wie sie
in den ersten beiden Bildern gezeigt wird, einmal auf einem
Android-Handy, dann im Firefox. Zu diesem Zweck habe ich ein
Espressif-Beispiel gnadenlos abgespeckt.
Gezeigt werden in umgekehrter chronologischer Reihenfolge die letzten
maximal vierzig von IRMP erkannten Tastendrücke, die zu diesem Zweck in
einem Ringpuffer aufgehoben werden. Bei jedem Webrequest wird der
aktuelle Zustand gerendert, wobei der erneute Abruf der Seite mit einem
Refresh-Tag getriggert wird, wenn der Browser mitspielt. Primitiv, aber
wirksam.
Zusätzlich schickt das Programm gleich beim Erkennen eines Tastendrucks
eine entsprechende Teile an das UART sowie ein UDP-Paket. Eine
entsprechende App (basierend auf dem hier
https://play.google.com/store/apps/details?id=de.mystrobl.rechner)
horcht auf diese Pakete und zeigt sie ohne zeitliche Verzögerung an,
siehe drittes Bild. Falls jemand den aktuellen Stand ausprobieren
möchte und weil das Binary nicht sonderlich groß ist, habe ich es
ebenfalls hier angehängt. boot_v1.1.bin kann man aus dem SDK nehmen.
Geflasht wird das hier wie folgt:
SSID wie oben genannt, pw ist 0123456789zwotzeL
Abschließend noch zwei WifiAnalyzer-Screenshots.
Inzwischen ist der esp_iot_sdk_v1.5.0 herausgekommnen, wie ich gerade
gesehen habe. Nächstes Wochenende. :-)
Statt den im Vorposting gezeigten Entwurf weiter zu polieren, habe ich
mich darauf beschränkt, diesen i.W. unverändert mit dem IOT-SDK 1.5.0 zu
übersetzen, dies zusammen mit einem älteren Android-Tablet als
kabelloses Kontrolldisplay für IR-Codes zu verwenden und erst mal eine
andere Idee zu verfolgen, nämlich IRMP in die unter dem Namen NodeMCU
bekannte Lua-Adaption (https://en.wikipedia.org/wiki/NodeMCU)
einzubauen.
Trotz einiger überraschender Hürden (s.w.u.) war das insgesamt weniger
Aufwand als erwartet.
http://nodemcu.com/index_en.html vermittelt zunächst den Eindruck einer
Hardwareplattform. Tatsächlich ist aber die auf GitHub gehostete
Firmware https://github.com/nodemcu/nodemcu-firmware problemlos mit dem
ESP-Open-SDK https://github.com/pfalcon/esp-open-sdk übersetzbar und
läuft dann anstandslos auch auf dem Olimex-Board
(https://www.olimex.com/Products/IoT/ESP8266-EVB/open-source-hardware).
So weit, so gut. Dann fangen die Probleme aber an.
In der mit dem unveränderten Espressif-SDK realisierten IRMP-Adaption
hatte ich den sog. Hardware-Timer aus der driver_lib verwendet, um
Timer-Interrupts zuverlässig alle 67 µS (bei 15 kHz) realisieren zu
können.
Dummerweise fehlt aber NmiTimSetFunc(func) im esp-open-sdk in ets_sys.h
und das aus gutem Grunde, denn ein dafür wiederum benötigtes externes
Symbol fehlt in einer der nur als Object-Module verfügbaren Library, die
im open-sdk verwendet wird. Selbst der auf Minimum 100 µs beschränkte
Softwaretimer ist nicht verfügbar. Damit fehlt die wesentliche Grundlage
für den Betrieb der IRMP-ISR.
Es gibt aber einen Ausweg. Es läßt sich nämlich vergleichsweise einfach
ein Interrupt an die steigende, fallende oder an beide Flanken eines
Pegelwechsels an einem Pin binden. Der genaue Zeitpunkt steht über
einen API-Call µs-genau oder über inline-Assembly sogar taktgenau zur
Verfügung:
Zwar besagt das nichts über die zeitliche Genauigkeit des Interrupts
selber, in der Praxis ist es aber offenbar hinreichend präzise für IRMP.
Zusammen mit einem kleinen Ringpuffer läßt sich so ganz einfach eine
Pulse-Train mitschreiben. Jedoch ist dies nicht das, was IRMP erwartet.
Da IRMP allerdings völlig egal ist, wann und wie irmp_ISR() aufgerufen
wird, lassen sich die Aufrufe ohne weiteres vor dem Aufruf von
irmp_get_data nachholen. Ein positiver Nebeneffekt dieses Verfahrens
besteht darin, daß der IR-Empfang nebenläufig und gepuffert erfolgt.
Der Beispieloutput im Anhang zeigt z.B. das Dekodieren von 29
IRMP-Telegrammen am Klump aus bereits gepufferten IR-Daten.
Modul irmp mit den drei Funktionen init, get_data und close.
----------
Funktionen
----------
irmp.init(int queuesize) -> 0 (no memory) oder 1 (ok)
queuesize: Gesamtzahl von Pulsen und Lücken, die gepuffert werden. Wie
viele pro Tastendruck verwendet werden, hängt vom Protokoll ab.
irmp.get_data() -> protokollname(string) adress(int) command(int)
flag(int) oder 0 (nichts) oder -1 (keine queue, closed)
irmp.close() -> Queue wieder freigeben.
Für das Herumprobieren damit war ESPlorer (http://esp8266.ru/esplorer/)
nützlich.
Lua, das ich mir erst bei der Gelegenheit oberflächlich angeeignet habe,
hat ein etwas ungewöhnliches Features, das mit der Implementation von
mehrfachen Rückgabewerten von Funktionen zu tun hat:
http://www.lua.org/pil/5.1.html und das einige Verrenkungen erfordert,
wenn man dergl. Listen tatsächlich verwenden möchte. Zwar ist es
ziemlich einfach und elegant, mehrere Werte, auch unterschiedlich viele,
von einem C-Programm aus abzuliefern - man kippt sie einfach
nacheinander auf dem Stack ab - so einfach das aber ist, so umständlich
ist es dann wieder in Lua, mit diesen etwas anzufangen. Leider hat eine
der dafür benötigten Funktionen ("unpack") zwischen 5.1 und 5.3 ihren
Namen bzw. Ort gewechselt (von globalem Namensraum zu Attribut von
"tables"). Die einzige simple, auch mit variabel langen Rückgabelisten
zurechtkommende Verfahrensweise, ein direkt auf einen Funktionsaufruf
angewandter Table Constructor http://www.lua.org/pil/3.6.html
funktioniert in der vorliegenden NodeMCU-Version nicht, es kommen nur
zwei von vier oder mehr Rückgabewerten an. Zum Glück gibt es einen
Workaround, das Einsammeln der Rückgabewerte läßt sich nachbilden:
http://www.lua.org/pil/5.2.html.
Dies erklärt die Funktion collect in dem nachfolgenden Schnipsel.
1
require "irmp"
2
function collect(...)
3
return arg
4
end
5
function irmpg()
6
local r
7
repeat
8
r=collect(irmp.get_data())
9
if #r>2 then print(string.format("%s(%d): addr=%04x cmd=%04x f=0x%02x ",unpack(r)))
10
else break end
11
until #r==1
12
end
13
print (irmp.init(1000))
Wenn dieses Schnipsel ausgeführt wurde, können nachfolgend alle
zwischenzeitlich aufgelaufenen Tastendrücke mit einem einfachen Aufruf
von irmpg() dekodiert und über das UART angezeigt werden. Siehe
Beispieloutput.
Es wäre schön, wenn IRMP statt per Polling wahlweise auch per
Pulse-Train mit Input versorgt werden könnte. Wenn ich mich erinnere,
hattte Frank vor einer Weile mal angedeutet, über so etwas nachdenken zu
wollen.