<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Runtux Blog &#187; open hardware</title>
	<atom:link href="http://blog.runtux.com/category/open-hardware/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.runtux.com</link>
	<description>Neues, Interessantes, Skurriles</description>
	<lastBuildDate>Wed, 25 Aug 2010 16:55:38 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Did Ronja Fail?</title>
		<link>http://blog.runtux.com/2009/10/27/142/</link>
		<comments>http://blog.runtux.com/2009/10/27/142/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 17:30:32 +0000</pubDate>
		<dc:creator>Ralf Schlatterbeck</dc:creator>
				<category><![CDATA[documentation]]></category>
		<category><![CDATA[english]]></category>
		<category><![CDATA[licensing]]></category>
		<category><![CDATA[open hardware]]></category>

		<guid isPermaLink="false">http://blog.runtux.com/?p=142</guid>
		<description><![CDATA[Ronja, the optical data link device, is often cited as a failed open source hardware project &#8212; the last one mentioning it I just read is Lawrence Kincheloe&#8217;s excellent essay Musings Upon the Nature of Open Source Hardware as a Business at the end of his project visit summary at Factor e Farm.
Roja did fail [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://ronja.twibright.com/">Ronja</a>, the optical data link device, is often cited as a failed open source hardware project &#8212; the last one mentioning it I just read is Lawrence Kincheloe&#8217;s <em>excellent</em> essay <a href="http://openfarmtech.org/weblog/?p=1187">Musings Upon the Nature of Open Source Hardware as a Business</a> at the end of his project visit summary at <a href="http://openfarmtech.org/index.php?title=Factor_e_Farm">Factor e Farm</a>.<br />
Roja <em>did</em> fail (in the sense that it isn&#8217;t very widespread today not in the sense of being a cool open source project). One of the research studies I know of is the presentation &#8220;<a href="http://www.oekonux-conference.org/program/events/22.en.html">Ronja &#8212; Darknet of Lights</a>&#8221; by Johan <a href="http://www.oekonux-conference.org/program/speakers/33.en.html">S&#246;derberg</a> at the <a href="http://www.oekonux-conference.org/">4th Oekonux conference</a> for which <a href="http://www.oekonux-conference.org/documentation/croome/Soederberg.mp3">Audio</a> is available. The study is very interesting although I don&#8217;t agree with the conclusions. So why did Ronja &#8220;fail&#8221;?<br />
Ronja&#8217;s main application was cheap internet access. At the time of its design in 2001 <a href="http://en.wikipedia.org/wiki/Wireless_LAN">wireless LAN</a> (Wifi) wasn&#8217;t yet available cheaply. And in the Czech Republic <a href="http://en.wikipedia.org/wiki/Digital_subscriber_line">DSL</a> wasn&#8217;t available at the time.<br />
Now consider the technical characteristics of Ronja:
<ul class="simple">
<li>Up to 10MBit/s</li>
<li>Up to 1.4 km range</li>
<li>Light: Doesn&#8217;t work in fog, or other bad weather (snow)</li>
<li>Light: Hard to get the beam to the destination (direction)</li>
<li>Light: Interference with daylight</li>
<li>For full-duplex communication we need two (receiver + transmitter) devices</li>
<li>sold for around 700$ at the time (the LED alone cost 120$ you get these for .75$ now)</li>
<li>needed &#8220;a hell of a lot of time to build one&#8221; according to <a href="http://www.oekonux-conference.org/program/speakers/33.en.html">S&#246;derberg</a></li>
</ul>
<p>And compare these with WLAN:
<ul class="simple">
<li>Up to 54MBit/s</li>
<li>With good antennas several km range (I&#8217;ve built a link with 5.5km)</li>
<li>Antennas are cheap and can even be built at home, e.g., a <a href="http://en.wikipedia.org/wiki/Cantenna">Cantenna</a> &#8212; you can build a cantenna in an evening</li>
<li>Works in fog and bad weather</li>
<li>we need only one antenna at sender and one at receiver</li>
<li>WLAN is <em>very</em> cheap nowadays, it became available (with new frequencies) in 2005 in cz.</li>
</ul>
<p>So I think that Ronja &#8220;failed&#8221; because it was replaced by something better and cheaper that was readily available. It isn&#8217;t an example of a failed open source business model for hardware and shouldn&#8217;t be used as an example. This doesn&#8217;t mean that we already know how a business model for open source hardware should look like, though.<br />
The idea behind Ronja &#8212; according to the <a href="http://en.wikipedia.org/wiki/RONJA">Wikipedia article on Ronja</a> &#8220;User Controlled Technology&#8221; is (mostly) achieved with WLAN technology today: We can use cheap devices and modify them (using open source firmware and homegrown antennas) to suit our needs. And there are large wireless communities now like <a href="http://funkfeuer.at/">Funkfeuer</a> in Vienna who do their own Internet communication.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.runtux.com/2009/10/27/142/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
<enclosure url="http://www.oekonux-conference.org/documentation/croome/Soederberg.mp3" length="65504681" type="audio/mpeg" />
		</item>
		<item>
		<title>(cc)alpsSalon: open everything</title>
		<link>http://blog.runtux.com/2009/09/11/122/</link>
		<comments>http://blog.runtux.com/2009/09/11/122/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 08:37:04 +0000</pubDate>
		<dc:creator>Ralf Schlatterbeck</dc:creator>
				<category><![CDATA[deutsch]]></category>
		<category><![CDATA[english]]></category>
		<category><![CDATA[licensing]]></category>
		<category><![CDATA[neu]]></category>
		<category><![CDATA[open hardware]]></category>

		<guid isPermaLink="false">http://blog.runtux.com/?p=122</guid>
		<description><![CDATA[Update 2009-09-14: Marcin from open source ecology has the video online which we showed at the event &#8212; Marcin von open source ecology hat das Video, das wir auf der Veranstaltung gezeicht haben online (video in enlish only).
Heute abend bin ich mit am Podium im Creative Commons CCalps Salon im Rahmen des Paraflows Festival zum [...]]]></description>
			<content:encoded><![CDATA[<p>Update 2009-09-14: Marcin from open source ecology has the <a href="http://openfarmtech.org/weblog/?p=1071">video online</a> which we showed at the event &#8212; Marcin von open source ecology hat das <a href="http://openfarmtech.org/weblog/?p=1071">Video</a>, das wir auf der Veranstaltung gezeicht haben online (video in enlish only).</p>
<p>Heute abend bin ich mit am Podium im <a href="http://www.creativecommons.at/Members/ben/ccalpssalon/CCalps%20Salon%20Text_long">Creative Commons CCalps Salon</a> im Rahmen des <a href="http://www.paraflows.at/index.php?id=128">Paraflows Festival</a> zum Thema <a href="http://creativecommons.at/salon/everything">Open Everything</a>. Ich werde auf die jetzt stattfindende Anwendung der Open Source Prinzipien die wir von der Software kennen auf andere Bereiche (Open Hardware Design) eingehen. Die Veranstaltung wird vermutlich in Englisch gef&#252;hrt, da Michel Bauwens, der Gr&#252;nder der <a href="http://p2pfoundation.net/The_Foundation_for_P2P_Alternatives">P2P Foundation</a> dort sein wird.<br />
This evening I&#8217;ll participate at the <a href="http://www.creativecommons.at/Members/ben/ccalpssalon/CCalps%20Salon%20Text_long">Creative Commons CCalps Salon</a> an event in the context of the <a href="http://www.paraflows.at/index.php?id=128">Paraflows Festival</a> with the topic <a href="http://creativecommons.at/salon/everything">Open Everything</a>. I&#8217;ll talk about applying the principles of Open Source we know from software development to other areas (like Open Hardware design). The event will probably be in english since Michel Bauwens, founder of the <a href="http://p2pfoundation.net/The_Foundation_for_P2P_Alternatives">P2P Foundation</a> will be there.</p>
<p>Zitat aus der <a href="http://www.paraflows.at/index.php?id=128">Ank&#252;ndigung</a> (only in german, sorry):</p>
<p>Nur wenige Menschen sind in der Lage die Frage &quot;Was ist open everything eigentlich?&quot; auf befriedigende Weise zu beantworten, der &#220;berblick, der durch die mind map pr&#228;sentiert wird, bildet die Basis f&#252;r die eigentliche Erkl&#228;rung. Daher hat der (cc)alpsSalon MICHEL BAUWENS eingeladen, diese Frage zu beantworten und einen &#220;berblick &#252;ber vergangene und gegenw&#228;rtige Entwicklungen in Zusammenhang mit dieser Idee zu geben und die Potentiale aufzuzeigen die f&#252;r jeden gegeben sind, der/die offene Materialien, Quellen, Designs &#8211; einfach alles &#8211; anbietet und n&#252;tzt.<br />
Eine der beeindruckendsten Ausf&#252;hrungen dieses Ethos ist <a href="http://openfarmtech.org/weblog/">open source ecology (OSE)</a>, ein Projekt, das darauf abzielt eine open source Gemeinschaft zu schaffen, die sich auf Nachhaltigkeit, &#246;kologische Verantwortung und die Freiheit des Individuums gr&#252;ndet. FRANZ NAHRADA wird diese innovative Idee genauer darstellen und wird dabei zeigen, wie das Konzept des open everything in Gemeinschaften realisiert werden kann, die willens sind Offenheit tagt&#228;glich zu leben.<br />
Gesellschaft wird durch viele Faktoren beeinflusst, Kultur und Technologie sind zwei der entscheidendsten. Die technische Seite von open everything bildet die Basis f&#252;r eine Kultur der &quot;Macher&quot;, die einen Wechsel von Massenproduktion hin zu selbst gemachten oder selbst entworfenen Produkten kennzeichnet. Diese do it yourself (DIY) Kultur ist abh&#228;ngig von den Verbesserungen, die durch das Teilen von Erfahrungen und Ideen entstehen. RALF SCHLATTERBECK zeigt uns, wie diese Gemeinschaft funktioniert und wie sie vom Ethos des open everything profitiert.<br />
<a href="http://www.paraflows.at/index.php?id=136">Wann/when</a>: 2009-09-11 19:30 Wo/where: <a href="http://qdk.blogsome.com/">Quartier f&#252;r digitale Kultur</a>, Quartier 21, Museumsquartier, Museumsplatz 1, 1070 Wien</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.runtux.com/2009/09/11/122/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dämmerungsgesteuerte Hühnerstalltür mit Arduino</title>
		<link>http://blog.runtux.com/2009/01/08/9/</link>
		<comments>http://blog.runtux.com/2009/01/08/9/#comments</comments>
		<pubDate>Thu, 08 Jan 2009 19:40:59 +0000</pubDate>
		<dc:creator>Ralf Schlatterbeck</dc:creator>
				<category><![CDATA[deutsch]]></category>
		<category><![CDATA[documentation]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[open hardware]]></category>

		<guid isPermaLink="false">http://blog.runtux.com/?p=9</guid>
		<description><![CDATA[Seit einiger Zeit haben wir Hühner. Da wir öfter mal am Abend alle weg sind und dann niemand die Tür vom Hühnerstall zumacht &#8212; die Hühner gehen, im Gegensatz zu den Enten die wir früher hatten, von selber bei Dämmerung in den Stall &#8212; brauchten wir eine Lösung, die automatisch die Tür schließt. Es gibt [...]]]></description>
			<content:encoded><![CDATA[<p>Seit einiger Zeit haben wir Hühner. Da wir öfter mal am Abend alle weg sind und dann niemand die Tür vom Hühnerstall zumacht &#8212; die Hühner gehen, im Gegensatz zu den Enten die wir früher hatten, von selber bei Dämmerung in den Stall &#8212; brauchten wir eine Lösung, die automatisch die Tür schließt. Es gibt fertige Hühnerställe mit einer zeitgesteuerten Tür &#8212; aber weder mit Dämmerungsschalter noch eine Türelektronik einzeln.<br />
<div id="attachment_50" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.runtux.com/files/2009/01/dsc00158.jpg"><img src="http://blog.runtux.com/files/2009/01/dsc00158-300x225.jpg" alt="-)" width="300" height="225" class="size-medium wp-image-50" /></a><p class="wp-caption-text">v.l.n.r Kokoschka, Leuchtfeder, Gertrude <img src='http://blog.runtux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p></div><br />
Mein Sohn Max und ich haben also gemeinsam die Tür mit Mechanik und Elektronik und Software selber gemacht.</p>
<p>Alle verwendeten Teile sind bei Conrad erhältlich, wo wir es noch rekonstruieren können geben wir die Bestellnummern und den Link zum Artikel an. Conrad ist zwar relativ teuer, dafür bekommt man für dieses Einmalprojekt alle Teile bei einem Händler und wer was nachbauen will kann im deutschsprachigen Raum über einheitliche Teilenummern auf die gleichen Teile zugreifen, sei es in Österreich, der Schweiz oder Deutschland.</p>
<p>Einige Teile unserer Lösung eignen sich sicher auch für andere Projekte, nicht viele Leute werden eine Hühnerstalltüre brauchen &#8230;</p>
<p>Die Hühnerstalltüre funktioniert jetzt schon ein paar Wochen. Anfängliche mechanische Probleme (Tür verklemmte sich einige Male beim Herunterfahren) sind wohl gelöst. Unsere Software hat einen Zeitcheck falls beim Runter- oder Rauffahren doch mal was schiefgeht.</p>
<p>In der Folge beschreiben wir den Aufbau, getrennt nach Mechanik, Elektronik und Software &#8212; die Aufteilung zwischen Mechanik und Elektronik ist etwas willkürlich, wir haben die ganze Elektromechanik<br />
(Motor, Schalter) zur Mechanik gerechnet.</p>
<p><strong>Die Mechanik</strong></p>
<p>Mit einem Getriebemotor wird eine Welle über einen Zahnriemen-Antrieb (Übersetzung nochmal 4:1) angetrieben. Der Getriebemotor ist schon 148:1 untersetzt. Die Welle läuft auf Kugellagern, die in einem Holzrahmen montiert sind. Die Lagerflansche sind einfach mit Heisskleber in den ausgestemmten Teil der Holzlatten geklebt. Vorsicht, keinen Heisskleber ins Lager bringen&#8230;</p>
<p>Die Übersetzung wurde so gewählt, dass wir eine bis zu 800g schwere Tür mit einer Welle mit Radius 5mm gut hochziehen können. Das Drehmoment des verwendeten Motors ist etwa 1,2 Ncm, mit der 4:1 Übersetzung mit dem Zahnriemen kommen wir auf 4.8 Ncm.</p>
<p>Die Welle wickelt dann eine Nylonschnur auf, die an der Tür angebunden ist. Die Nylonschnur hat einen Durchmesser von 2mm, die Welle wurde mit einem 2mm Titanbohrer gebohrt. Zum Bohren der Welle haben wir einen Stellring verwendet, einfach durch das Schraubenloch des Stellringes gebohrt, der Stellring war mit Klebeband fixiert.</p>
<p>Das Einfädeln der Nylonschnur kann man sich erleichtern, indem man die Nylonschnur mit einem Feuerzeug an einer Stelle erhitzt und auseinanderzieht, die entstehende Spitze eignet sich recht gut zum Einfädeln.</p>
<p>Motor: Eigentlich wollten wir ursprünglich einen kleineren Motor, da der jetzt verwendete Motor bis zu 2A Strom zieht (bei hoher Last &#8212; unsere Tür ist relativ leicht, dadurch ist die Last und damit der Strom recht niedrig). Das könnte mal zuviel für den verwendeten H-Bridge Motorcontroller werden.</p>
<p>Die Tür selbst hat einen Magneten, der in zwei Stellungen (Tür ganz oben bzw. ganz unten) jeweils einen Magnetschalter auslöst.</p>
<p>Für das Justieren (von Hand Türe rauf- bzw. runterfaheren) gibt es zwei Taster die an digitalen Inputs des Arduino angeschlossen sind.</p>
<div id="attachment_47" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.runtux.com/files/2009/01/dsc00031.jpg"><img src="http://blog.runtux.com/files/2009/01/dsc00031-300x225.jpg" alt="Mechanischer Aufbau" width="300" height="225" class="size-medium wp-image-47" /></a><p class="wp-caption-text">Mechanischer Aufbau</p></div>
<p>Teileliste Mechanik:</p>
<ul>
<li><a href="http://www.conrad.at/goto.php?artikel=222366">222366</a> Getriebemotor</li>
<li><a href="http://www.conrad.at/goto.php?artikel=222374">222374</a> Alternativ: kleinerer Motor</li>
<li><a href="http://www.conrad.at/goto.php?artikel=237205">237205</a> Welle 8mm</li>
<li><a href="http://www.conrad.at/goto.php?artikel=216011">216011</a> Lagerflansch für Kugellager</li>
<li><a href="http://www.conrad.at/goto.php?artikel=225550">225550</a> Stellringe 8mm</li>
<li><a href="http://www.conrad.at/goto.php?artikel=214493">214493</a> Kugellager</li>
<li><a href="http://www.conrad.at/goto.php?artikel=226043">226043</a> Zahnriemenscheibe 40 Zähne, für Welle 8mm</li>
<li><a href="http://www.conrad.at/goto.php?artikel=226106">226106</a> Zahnriemenscheibe 10 Zähne, für Welle 6mm (an Motor)</li>
<li><a href="http://www.conrad.at/goto.php?artikel=226084">226084</a> Zahnriemen</li>
<li><a href="http://www.conrad.at/goto.php?artikel=753360">753360</a> Magnetschalter</li>
<li>Taster ein (2X)</li>
</ul>
<p>Im folgenden Bild sieht man die Tür im Zustand &#8220;NACHT&#8221; (zu). Die Magnetschalter und der Magnet an der Tür sind gut zu erkennen. Oben im Bild ist ein Stück Sperrholz auf Abstandshaltern so montiert, dass die Schnur durchläuft und beim Hochfahren die Tür an die Hüttenwand gedrückt wird.</p>
<div id="attachment_53" class="wp-caption aligncenter" style="width: 235px"><a href="http://blog.runtux.com/files/2009/01/dsc00159-rot.jpg"><img src="http://blog.runtux.com/files/2009/01/dsc00159-rot-225x300.jpg" alt="Hühnerstalltür im geschlossenen Zustand mit Magnet und Magnetschaltern" width="225" height="300" class="size-medium wp-image-53" /></a><p class="wp-caption-text">Hühnerstalltür im geschlossenen Zustand mit Magnet und Magnetschaltern</p></div>
<p><strong>Die Elektronik</strong></p>
<p>Der Dämmerungsschalter ist mit einem einfachen Photowiderstand in einem Spannungsteiler mit einem 10k Widerstand realisiert. Der Photowiderstand ist über ein Lautsprecherkabel mit Heisskleber im Holzdach montiert. Oberhalb des Photowiderstands ist ein Acryl-Welldach. Die Magnetschalter sind an digitale Eingangspins des Arduino angeschlossen.</p>
<p>Die Motorsteuerung erfolgt über eine H-Bridge Schaltung in einem IC. Die Elektronik dazu findet auf einer Lochraster-Platine Platz. Die einfache H-Bridge benötigt keine externen Teile. Wie man das mit dem Arduino verdrahtet ist ganz gut auf der Seite der <a href="http://itp.nyu.edu/physcomp/Labs/DCMotorControl">Physical Computing Labs</a> der Tisch School of the Arts beschrieben &#8212; ist auch der erste Link auf den ich beim googlen nach &#8220;Arduino Motor Control&#8221; gestoßen bin.</p>
<p>Wir wollen aber &#8212; wegen des größeren Motors &#8212; auf eine größere H-Bridge, die braucht externe Schaltdioden (das <a href="http://www2.produktinfo.conrad.com/datenblaetter/150000-174999/156128-da-01-en-DUAL_BRIDGE_DRIV_L298N.pdf">Datenblatt</a> des L298N schlägt Dioden mit höchstens 200ns reverse recovery time (t<sub>rr</sub>) vor, wir werden die <a href="http://www2.produktinfo.conrad.com/datenblaetter/150000-174999/160005-da-01-en-BYV2100.pdf">BYV 2100</a> verwenden mit einer t<sub>rr</sub> von 12.5ns)</p>
<p>Teileliste Elektronik:</p>
<ul>
<li><a href="http://arduino.cc/en/Main/ArduinoBoardDuemilanove">Arduino Duemilanove</a></li>
<li><a href="http://www.conrad.at/goto.php?artikel=145475">145475</a> Photowiderstand</li>
<li>Widerstand 10k</li>
<li><a href="http://www.conrad.at/goto.php?artikel=174003">174003</a> L293D H-Bridge max 1A</li>
<li><a href="http://www.conrad.at/goto.php?artikel=510821">510821</a> Netzteil 9V passend für Arduino</li>
</ul>
<p>Alternative größere H-Bridge:</p>
<ul>
<li><a href="http://www.conrad.at/goto.php?artikel=156128">156128</a> L298N H-Bridge Dual 2A bzw parallelgeschaltet 3A</li>
<li><a href="http://www.conrad.at/goto.php?artikel=160005">160005</a> BYV 2100 Schnelle Dioden dazu</li>
</ul>
<p><strong>Die Software</strong></p>
<p>Der Dämmerungsschalter sollte laut Wikipedia <a href="http://en.wikipedia.org/wiki/Lux">bei ca. 100 lux</a> das Öffnen der Tür veranlassen und bei höchstens 3.4 lx (Dark limit of civil twilight under a clear sky) das Schließen. In dem Wikipedia-Artikel ist eine Tabelle, die einen sehr dunklen Tag mit 100lx angibt, die deutsche Version enthält leider keine ausführliche Tabelle. In unseren Experimenten war ein Meßwert des Arduino von 200 für &#8220;Jetzt ist es dunkel, Tür zu&#8221; und von 300 für &#8220;Jetzt ist es hell, Tür auf&#8221; gut &#8212; auch in den jetzt dunklen Wintertagen (auch bei einem Gewitter) bleibt die Tür tagsüber offen und schliesst zuverlässig bei Dunkelheit. In einer klaren Vollmondnacht blieb die Tür zu.</p>
<p>Die Steuerung ist als Zustandsautomat (State Machine) realisiert. Jenachdem welcher Magnetschalter beim Einschalten Kontakt hat, startet der Automat im Zustand &#8220;TAG&#8221; bzw. &#8220;NACHT&#8221;. Wird keiner der Magnetschalter gesehen, gehen wir in den Zustand &#8220;ERROR&#8221;. Dieser Zustand bildet auch alle anderen Fehlerzustände ab, die im Betrieb auftreten können. Dabei setzen wir eine Fehlermeldung, die dann über die serielle Schnittstelle des Arduino (USB Serial) ausgegeben wird.</p>
<p>Der Zustandsautomat verwendet Statusfunktionen: In einem bestimmten Zustand wird die zugehörige Zustandsfunktion ausgeführt. Wenn diese Funktion 0 zurückliefert, geht der Automat in den nächsten Zustand, sonst bleibt er im alten Zustand.</p>
<p>Durch die Verwendung von Statusfunktionen (und keiner speziellen Funktion beim Zustandsübergang) haben wir zwei zusätzliche Zustände um den Motor einzuschalten, dieser Zustand (START_RAUF bzw. START_RUNTER) schaltet den Motor ein und geht sofort in den nächsten Zustand über.</p>
<p>Die Verdrahtung mit dem Arduino, also an welchen Pins welche Peripherie angeschlossen ist, sind über define&#8217;s am Beginn des Programms festgelegt.</p>
<p>Die Logik für die digitalen Inputs verwendet die Arduino-internen Pull-Up Widerstände. Daher ist die Logik invertiert: Wenn der Taster (oder ein Magnetschalter) geschlossen ist, liefert der entsprechende digitale Input eine 0.</p>
<p>Beim Testen haben wir festgestellt, dass bei laufendem Motor gelegentlich eine gedrückte Taste &#8212; oder ein geschlossener Magnetschalter detektiert wird. Daher haben wir in Software alle digitalen Inputs entprellt (Funktion debounced_read). Vermutlich sollten wir vor der H-Bridge noch einen Entstörkondensator vorsehen.</p>
<p>Die beiden Taster zum manuellen Rauf- und Runterfahren setzen ein Flag das den Zustandsautomaten neu initialisiert &#8212; dadurch muss man nach dem manuellen Kalibrieren kein Reset ausführen: Einfaches Drücken der Rauf- bzw. Runter-Taste reicht, um ein neues Initialisieren durchzuführen.</p>
<p>Im Error-Status warten wir 100ms nach dem Ausgeben der Fehlermeldung. Durch dieses Delay merkt man bei Drücken des Rauf- bzw. Runter-Knopfes sofort, ob sich das Gerät im Error-Zustand befindet: Wenn ein Error-Zustand vorliegt, bewegt sich der Motor nicht sofort, sondern erst nach einem kurzen Delay. Wenn der Zustandsautomat korrekt initialisiert ist, bewegt sich der Motor sofort.</p>
<p>Während der Entwicklung habe ich mit etwas mit einem Bug der Arduino-Entwicklungsumgebung gekämpft: Um Function-Style casts (Typumwandlungen die wie eine Funktion aussehen) zu ermöglichen, macht die Entwicklungsumgebung Code-Rewriting und fügt insbesondere einige Preprozessor-Defines ein, die es verhindern, einen Funktionspointer in C zu deklarieren. Der Workaround ist ein &#8220;#undef int&#8221; vor der Funktionspointer-Deklaration einzufügen. Gleich am Anfang kann man das nicht machen, da das Code-Rewriting ein #include direkt vor dem ersten Statement (nach allen #include und #define Direktiven) einfügt. Dieses &#8220;Feature&#8221; hat durch obskure Fehlermeldungen einiges an Zeit gekostet, was mich veranlasst hat, einen <a href="http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1221741304/48#48">Beitrag im Arduino Forum</a> dazu zu schreiben.</p>
<p>Die verwendeten Timer-Routinen (#include &lt;timer.h&gt; und der Typ Arduino_Timer) dienen dazu, auf eine bestimmte Zeit nach dem Aufruf von millis() zu warten &#8212; auch wenn die von millis verwendete Variable inzwischen überläuft. Der Timer lässt sich fragen, ob der gewünschte Zeitpunkt schon erreicht ist. Ursprünglich habe ich diese Routinen geschrieben,  weil Version 0011 (und früher) der Arduino Entwicklungsumgebung einen Bug hatte, so dass der Timer zu früh überlief. Die entsprechenden Timer Routinen (für Version 0011 oder ab 0012) gebe ich gern auf Anfrage weiter.</p>
<pre>
#include &lt;timer.h&gt;
#include &lt;stdio.h&gt;

# define LED          13
# define MAGNET_OBEN   7
# define MAGNET_UNTEN  8
# define FOTO          0
# define MOTOR         9
# define MOTOR_DIR1    4
# define MOTOR_DIR2    3
# define MOTOR_MIN  0x7F
# define KNOPF_RAUF   12
# define KNOPF_RUNTER  2

# define HELL        300
# define FINSTER     200

# define STATUS_TAG          0
# define STATUS_ABEND        1
# define STATUS_NACHT        2
# define STATUS_MORGEN       3
# define STATUS_START_RAUF   4
# define STATUS_RAUFFAHREN   5
# define STATUS_START_RUNTER 6
# define STATUS_RUNTERFAHREN 7
# define STATUS_ERROR        8

int status   = STATUS_ERROR;
char errbuf [80];
char *errmsg = "";
int neuinitialisieren     = 1;
int debounce_magnet       = 0;
int debounce_knopf_runter = 0;
int debounce_knopf_rauf   = 0;

# define TIMER_MS       10000 // 10 seconds debounce 300000 // 5 minutes
# define FAHRZEIT_MS    75000 // max 75 seconds for up/down of door
Arduino_Timer timer (TIMER_MS);

int debounced_read (int iopin, int *counter)
{
    if (!digitalRead (iopin))
    {
        if ((*counter)++ &gt;= 10)
        {
            *counter = 0;
            return 0;
        }
    }
    else
    {
        *counter = 0;
    }
    return 1;
}

void motor_an ()
{
    digitalWrite (LED,   HIGH);
    digitalWrite (MOTOR, HIGH);
}

void motor_aus ()
{
    digitalWrite (LED,   LOW);
    digitalWrite (MOTOR, LOW);
}

void linksrum ()
{
    digitalWrite (MOTOR_DIR1, LOW);
    digitalWrite (MOTOR_DIR2, HIGH);
}

void rechtsrum ()
{
    digitalWrite (MOTOR_DIR1, HIGH);
    digitalWrite (MOTOR_DIR2, LOW);
}

// Wir benutzen Status-Funktionen fuer jeden Status unserer Maschine:
// Wenn die Funktion 0 zurueckliefert, geht die Maschine in den naechsten
// Zustand, sonst bleibt sie im gleichen Zustand.

int fahren (int magnet)
{
    if (!debounced_read (magnet, &amp;debounce_magnet))
    {
        return 1;
    }
    if (timer.is_reached (millis ()))
    {
        motor_aus  ();
        timer.stop ();
        errmsg = "Zeitueberschreitung fahren";
        status = STATUS_ERROR;
        return 0;
    }
    return 0;
}

int starte_rauffahren ()
{
    if (digitalRead (MAGNET_UNTEN))
    {
        errmsg = "Hochfahren: Tuere nicht unten";
        status = STATUS_ERROR;
        return 0;
    }
    timer.start (millis (), FAHRZEIT_MS);
    debounce_magnet = 0;
    rechtsrum ();
    motor_an  ();
    return 1;
}

int rauffahren ()
{
    return fahren (MAGNET_OBEN);
}

int starte_runterfahren ()
{
    if (digitalRead (MAGNET_OBEN))
    {
        errmsg = "Hochfahren: Tuere nicht oben";
        status = STATUS_ERROR;
        return 0;
    }
    timer.start (millis (), FAHRZEIT_MS);
    debounce_magnet = 0;
    linksrum  ();
    motor_an  ();
    return 1;
}

int runterfahren ()
{
    return fahren (MAGNET_UNTEN);
}

int nacht ()
{
    int val;
    motor_aus ();
    val = analogRead (FOTO);
    Serial.println (val);
    if (val &gt; HELL)
    {
        timer.start (millis ());
        return 1;
    }
    return 0;
}

int tag ()
{
    int val;
    motor_aus ();
    val = analogRead (FOTO);
    Serial.println (val);
    if (val &lt; FINSTER)
    {
        timer.start (millis ());
        return 1;
    }
    return 0;
}

int abend ()
{
    int val;
    motor_aus ();
    val = analogRead (FOTO);
    if (val &gt; FINSTER)
    {
        status = STATUS_TAG;
        return 0;
    }
    if (timer.is_reached (millis ()))
    {
        timer.stop ();
        return 1;
    }
    return 0;
}

int morgen ()
{
    int val;
    motor_aus ();
    val = analogRead (FOTO);
    if (val &lt; HELL)
    {
        status = STATUS_NACHT;
        return 0;
    }
    if (timer.is_reached (millis ()))
    {
        timer.stop ();
        return 1;
    }
    return 0;
}

int error ()
{
    motor_aus ();
    Serial.println (errmsg);
    delay (100);
    return 0;
}

# undef int
struct state {
    int status;
    int next_status;
    int (*statefun)();
};

// Stati muessen in der Reihenfolge der numerischen Zustandswerte sein
// Zustaende muessen lueckenlos nummeriert sein
struct state stati [] =
{ { STATUS_TAG,          STATUS_ABEND,        tag                 }
, { STATUS_ABEND,        STATUS_START_RUNTER, abend               }
, { STATUS_NACHT,        STATUS_MORGEN,       nacht               }
, { STATUS_MORGEN,       STATUS_START_RAUF,   morgen              }
, { STATUS_START_RAUF,   STATUS_RAUFFAHREN,   starte_rauffahren   }
, { STATUS_RAUFFAHREN,   STATUS_TAG,          rauffahren          }
, { STATUS_START_RUNTER, STATUS_RUNTERFAHREN, starte_runterfahren }
, { STATUS_RUNTERFAHREN, STATUS_NACHT,        runterfahren        }
, { STATUS_ERROR,        STATUS_ERROR,        error               }
};

void setup ()
{
    pinMode (LED,         OUTPUT);
    pinMode (MAGNET_OBEN,  INPUT);
    pinMode (MAGNET_UNTEN, INPUT);
    pinMode (MOTOR,       OUTPUT);
    pinMode (MOTOR_DIR1,  OUTPUT);
    pinMode (MOTOR_DIR2,  OUTPUT);
    pinMode (KNOPF_RUNTER, INPUT);
    pinMode (KNOPF_RAUF,   INPUT);

    digitalWrite (MOTOR,   LOW);
    linksrum ();
    digitalWrite (LED,     HIGH);

    digitalWrite (MAGNET_OBEN,  HIGH); // enable pull-up resistor
    digitalWrite (MAGNET_UNTEN, HIGH); // enable pull-up resistor
    digitalWrite (KNOPF_RUNTER, HIGH); // enable pull-up resistor
    digitalWrite (KNOPF_RAUF,   HIGH); // enable pull-up resistor
    Serial.begin (115200);
    if (FINSTER &gt;= HELL)
    {
        status = STATUS_ERROR;
        errmsg = "FINSTER &gt;= HELL";
    }
    Serial.print ("initial state: ");
    Serial.println (status);
}

void loop ()
{
    struct state *st = &amp;stati [status];
    if (!debounced_read (KNOPF_RUNTER, &amp;debounce_knopf_runter))
    {
        debounce_knopf_runter = 10;
        linksrum  ();
        motor_an  ();
        neuinitialisieren = 1;
        return;
    }
    else if (!debounced_read (KNOPF_RAUF, &amp;debounce_knopf_rauf))
    {
        debounce_knopf_rauf = 10;
        rechtsrum ();
        motor_an  ();
        neuinitialisieren = 1;
        return;
    }
    else if (neuinitialisieren)
    {
        motor_aus ();
        errmsg = "Unbekannte Tuerposition bei Start";
        status = STATUS_ERROR;
        if (!digitalRead (MAGNET_OBEN))
        {
            status = STATUS_TAG;
        }
        else if (!digitalRead (MAGNET_UNTEN))
        {
            status = STATUS_NACHT;
        }
        neuinitialisieren = 0;
        debounce_knopf_rauf = debounce_knopf_runter = 0;
        Serial.print ("Initialized to: ");
        Serial.println (status);
        return;
    }
    // Hard-coded error state must work if state-table is broken
    if (status &gt;= STATUS_ERROR)
    {
        error ();
        return;
    }
    if (st-&gt;status != status)
    {
        status = STATUS_ERROR;
        sprintf
            ( errbuf
            , "Error in state-table, expected %d got %d"
            , status
            , st-&gt;status
            );
        errmsg = errbuf;
        return;
    }
    if (st-&gt;statefun ())
    {
        status = st-&gt;next_status;
    }
    if (status != st-&gt;status)
    {
        sprintf (errbuf, "new state: %d-&gt;%d", st-&gt;status, status);
        Serial.println (errbuf);
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.runtux.com/2009/01/08/9/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
