Archive for the ‘documentation’ Category

PGAPy: Genetische Algorithmen mit Python

Sunday, October 25th, 2009

[Dies ist der Abstract meines Vortrags am Linuxwochenende im Metalab, die alte Version des Abstracts ist inzwischen von der Linuxwochen Homepage verschwunden…]

PGAPy ist ein Python-Wrapper für PGAPack, eine der vollständigsten Bibliotheken für genetischen Algorithmen. Die Python-Bibliothek eignet sich für eigene Experimente mit genetischen Algorithmen, aber auch für die Implementierung von kompletten Anwendungen.

Der Vortrag gibt eine kurze Einführung in genetischen Algorithmen mit Beispielen in Python. Vorgestellt wird u.a. ein Programm zum automatischen Erzeugen der bekannten Sudoku Zahlenrätsel. Dabei wird schrittweise die Bewertungsfunktion für ein Sudoku entwickelt.

Linuxwochenende 2009

Friday, October 23rd, 2009

Dieses Wochenende ist wieder Linuxwochenende im metalab, ich werde wieder einen Vortrag zu Open Money halten, ausserdem einen Lightning-Talk zu Generierung von Morse-Code als MIDI-Datei (was für einige Anwärter auf die Amateurfunklizenz vielleicht interessant ist, auch wenn das Code-Requirement inzischen gefallen ist.)

Cloud computing, Vendor Lock-In and the Future

Tuesday, August 4th, 2009

Cloud Computing is becoming increasingly popular — and it is a danger to your freedom. But we can do something about it.
First, when the term Cloud Computing was introduced, it meant a set of low-level services like virtual machines, databases and file storage. Examples of these are Amazon Elastic Computing Cloud and related services. Since these services are quite low-level, they can be replicated by others, an example is the Eucalyptus project.
This means if you aren’t satisfied with the service one cloud computing provider offers, you either can change the provider or — e.g., using Eucalyptus — roll your own.
But increasingly cloud-computing is a relaunch of the old Software as a Service paradigm under a new name. This means that applications like Textprocessing, Spreadsheets, Wiki, Blog, Voice and Video over IP, collaboration software in general is made available as so-called “Web 2.0″ applications — now called “Cloud Applications” on the web.
When using these services, there is a severe risk of Vendor Lock-In — since the applications may not be available elsewhere, you cannot easily switch the provider. Worse: From some of the Web 2.0 Services like social networks (e.g., Xing, LinkedIn, Facebook) you can’t retrieve your own data. Xing for example has a “mobile export” for data, but this works only for paying customers and only exports address data.
And people have started to realize — e.g., in this facebook group — that multiple incompatible applications — escpecially in the social network sector — puts a large burdon on customers to update multiple personal profiles on multiple sites.
But although it has been noted by the Free Software and Open Source community (e.g., in an interview with Richard Stallman and by Eric S. Raymond in his blog) it has not been widely recognized that cloud computing or software as a service — in particular in the form called “Web 2.0″ — creates a vendor lock-in worse than for proprietary software.
For your social networks this may mean that when you retrieve your data (remember, you helped them build that data!), the social network may throw you out as it happened in that case mentioned by Henry Story and later updated here.
The solution to this problem? Don’t get trapped in a data silo. This may still mean that there can be software as a service offerings. But the software needs to be free (as in free speech). So we can still switch to another provider or decide to host our own service.
But companies won’t do it for us. As Doc Searls notes in Silos End: “These problems cannot be solved by the companies themselves. Companies make silos. It’s as simple as that. Left to their own devices, that’s what they do. Over and over and over again.”
So this can only change if customers make and demand the change. A good rule-of-thumb for software as a service is on the page of the Open Cloud Initiative in the article The four degrees of cloud computing openness. While being a customer of a closed/proprietary cloud with “no access” is clearly a bad idea, open APIs and formats don’t work too well — you don’t have the software to work with your data. So the only valid options that remain are Open APIs, Open Formats and Open Source, and in some cases Open Data.
Still most web applications — like most social network software — are of the completely closed type. There are no open formats and no open APIs. So check your dependencies: What web-applications are you depending on and what is their degree of cloud computing openness?
A word on the license to guarantee openness in cloud-computing. As mentioned in the above-cited interview with Richard Stallman, the GNU General Public License is not enough to keep software in a cloud open. The cloud provider could take the software, make own modifications (which you will depend upon) and not release the modified software to you as a customer. Again you have a vendor lock-in. To prevent this, the GNU Affero General Public License has been designed that prevents closed-source modifications to hosted applications.
Finally, for all sorts of social software — not just social network software but everything that creates more value for more people, usually by linking information — should follow a distributed peer-to-peer approach. We don’t want this data to be a siloed application hosted by a single company. And if there are multiple companies hosting the data we already see the problem with multiple social network providers.
So we need standards and distributed protocols. And the implementation should follow a peer-to-peer approach — like seen in filesharing applications today — to make it resilient to failure and/or take-down orders of hostile bodies (like, e.g., some governments). Lets call this “Web 3.0″.
Examples of such social software are of course the social network sector. We already have a distributed protocol for social networking based on the Friend of a Friend Semantic Web Ontology. With this approach everyone can publish his social networking data and still be in control of who can see what. And the data is under user-control, so it’s possible to remove something.
Another example of social software is probably Money (in the sense of micro- or macro payments in the net). Thomas Greco in the book The End of Money and the Future of Civilization asks for separation of money and the state. A future implementation of money may well be based on a peer-to-peer social software implementation.
These social software needs security solutions. We want to model trust-relationships. Parts of the puzzle are probably OpenID and a newly-proposed scheme by Henry Story called FOAF+SSL mainly used for social networking 3.0 but probably very useful for other social software solutions.
So lets work on solutions for the future.

Verletzung der Netzneutralität durch Inode/UPC

Monday, June 15th, 2009

Seit einiger Zeit bekommt man von Inode/UPC einen automatischen Nameserver (DNS) zugeteilt der bei offensichtlichen Tippfehlern (wenn ein Benutzer sich beim Namen einer Webseite verschreibt) eine Werbe- und Suchmaschine von UPC zurückliefert — statt dem Benutzer mitzuteilen, dass es diese Domain nicht gibt.

Diese Verhalten eines der wichtigsten Services im Internet — der Namensauflösung — verletzt klar die Netzneutralität wie auch schon im Jahr 2007 von Ed Felten in seinem Blog im Falle Verizon festgestellt wurde. Verizon hat wohl seither auf diese Praxis — nach vielen Protesten — wieder verzichtet. Berechtigterweise wurde UPC für diese Praxis für den Big Brother Award nominiert — die gesammelten Daten gehen offensichtlich an einen ausländischen Werbeunternehmer. Zumindest in einem Fall kommt es bei diesem Verhalten zu Probleme bei Telefonie im Internet wie einem Forum Posting zu entnehmen ist.

Schlimmer noch: UPC liefert falschen Information auch für Domains die ganz klar von jemand anderem reserviert sind. Wenn ich also nonexistent.source-forge.org ansurfe komme ich auch auf die Werbeseite von UPC — obwohl source-forge.org (ja mit Bindestrich) von dem grossen Open Source Projekthoster sourceforge reserviert ist. Hoffentlich lässt es da mal jemand auf ein Gerichtsverfahren wegen unlauterem Wettbewerb ankommen. Wäre vermutlich recht lukrativ, jedenfalls bei anderen Firmen als Sourceforge.

Mir war das bisher nicht aufgefallen, aber offensichtlich wurden ehemalige Inode-Kunden erst vor kurzem umgestellt, UPC-Telekabel Kunden offensichtlich schon früher.

Heute habe ich mich bei der UPC-Hotline beschwert. Man werde an dem Fall arbeiten und “den Fehler” beheben. Leider könne man mir keine Ticket-Nummer geben unter welcher ich meine Beschwerde nochmal urgieren kann — aber ich könne ja “in einigen Tagen” wieder anrufen. Ein Rückruf wurde mir versprochen, mal sehen ob da was kommt. Ich habe den Hotline-Bearbeiter freundlich darauf hingewiesen, dass es sich ja nicht um einen unbekannten Fehler handeln kann, wenn UPC dafür bereits für den Big Brother Award nominiert wurde.

Inzwischen habe ich einen Workaround gefunden: UPC hat die alten, funktionierenden Nameserver nicht abgeschaltet, in obigem Link des ip-phone-forums findet man funktionierende DNS-Server: 195.34.133.25 und 195.34.133.26 die man fix einstellen kann. Weitere Tips finden sich als Antwort auf mein Posting an die LUGA-Mailingliste. Die alternative ist, gleich einen eigenen Nameserver zu betreiben (unter Linux sehr einfach möglich) oder auf alternative Namenshierarchien umzusatteln wie z.B. opennicproject — auch wenn man bei Verwendung von Alternativen von einer deutschen Ministerin gleich als pädophil eingestuft wird. Es ist kaum zu glauben, was manche Politiker für einen Schwachsinn von sich geben.

grml to the rescue

Friday, June 5th, 2009

I recently needed to recover data from a “dead” notebook. The only hardware I had available that had a connector for an ATA notebook harddisk was my Soekris net4801. This device doesn’t have VGA on board, so we need to boot GRML with a serial console. First I was unable to get GRML to correctly start a getty process. Meanwhile I’ve found out that the recipe in issue485 of the GRML-Bugtracker does the trick (I’ve modified the console speed to the speed I’m using in the Soekris bootloader):

grml console=tty 1console=ttyS1,38400n8

I had tried console=ttyS1,38400n8 before which doesn’t work. So I added the ssh= boot-options found out via the grml cheatsheet. I could ping the machine but no SSH. Turns out it takes a loooong time until grml starts up the ssh-daemon for two reasons

  • The net4801 is really slow
  • GRML creates new SSH Host-keys before starting up SSH. Thats good. But a newly-started box without a Keyboard has a really small random-number pool, so the box sits there waiting for randomness to happen for generating the keys. So it helps to run several parallel pings to the machine to create some network traffic the timing of which slowly fills the randomness pool …

Turns out that process took several minutes on the Soekris net4801. After waiting I was finally able to log into grml and rescue the data using ddrescue. Thanks GRML!

Good support from Beronet

Monday, April 27th, 2009

On April 9 I blogged here some firmware bugs of the Beronet bero*fos failover switch. They now have a new firmware 1.3.5 — a little over two weeks after reporting the bug with easter holidays in between. I’ve not had time yet to test this but wanted to blog this as an example of good support. I’ll blog the test results when I’ve had time to test the device with the new firmware.

Beronet bero*fos failover switch

Thursday, April 9th, 2009

Update 2009-04-27: There is a new firmware: good support from beronet
Update 2009-04-10: I’ve written a config-utility for the device, available in my rsclib on sourceforge (in python)

I’m now experimenting with the Beronet bero*fos failover switch. I need this for a project where two redundant asterisks should be switched by the bero*fos.
To get the following into proportion: I’m a customer of Beronet and usually like their products. But selling a device for around 700.- Euro we should expect working firmware and working configuration software. Especially since the device sits at a crucial point from a safety point of view: it’s used in scenarios where we want failover capabilities for telephone equipment.
The config-software is open source, so we can work around it’s shortcomings. But there is a firmware bug, setting some configuration variables via web interface has side-effects on other configuration variables. (we can work around that by writing our own config program). So I’d really like a more open design here: I’m voting for open firmware and a hardware documentation. But that might lead to others building the device for less money…
I would also prefer a documentation of the parameter interface in addition to (or instead of) a configuration program. Integrating the device into other infrastructures where we don’t want a binary configuration program requires reverse-engineering. I’ve done that in the following.
In the following I’m referring to berofos Firmware 1.3.3 which is the latest on Beronets webpage and in my device. The berofos tools for Linux on the webpage were apparently last updated in December 2007 and don’t have a version number.
The device has four groups of 4 ports each, A, B, C, D. These can be switched in two scenarios, a fallback scenario, which can connect A-B or A-D and a bypass scenario which can connect A-B and C-D or A-D. The first scenario is useful if there are redundant devices where one device can replace another (e.g. as in our scenario with two asterisk boxes), the second scenario is useful when you have an asterisk connected in between the telephone network and an old PBX. In case the asterisk fails, the PBX can be directly connected to the telephone network.
I won’t rehash the features and documentation of the device here, the berofos docs and tools page has a link to the manual (and to the command-line tool for both, Linux and Windows).
The device has a web-interface and a command-line interface written in C under the GPL version 2 license without a version-upgrade clause. The individual source files refer to a LICENSE file which isn’t included in the distribution.
The web interface has several bugs, some changes of config variables will change variables in other configuration pages. A notable example is the defaults page. In this page the default state of the relais can be set. When changing anything on that page, the device will also change the scenario to bypass.
Worse, when changing the mailserver page (the device is able to notify you via email if something bad happens) the dhcp setting is reset. This means on next powerup the device probably won’t try to get it’s ip via dhcp but use whatever happens to be the currently configured IP address. I didn’t try to reboot the device in this state because I noticed (and was looking for) this side-effect because I was already searching for a pattern in the failures.
Getting the config is easy, it’s under the url http://fos/config.txt where fos is the device. The following text file is retrieved:

bnfos_confmap_magic=0.1
1_sz=0
4_mode=0
1_rm=checked
5_p0=0
1_p0=
5_p1=0
1_p1=
3_dn=
3_ip=10.23.5.100
3_nm=255.255.255.0
3_gw=10.23.5.254
3_dns=10.23.5.254
3_dhcp=checked
3_port=80
3_pwd=
2_mhost=0.0.0.0
2_mfrom=
2_mto=
3_log=
3_loghost=0.0.0.0
6_wen=0
2_wen=
6_wstate=0
2_wintv=60
2_as=checked
2_men=
0_wretv=0

Apparently all configuration variables that influence other variables are in the same group: They have the same number in front.
The bugs of the web interface are not browser-specific. In fact the command-line tools also use the http-interface of the device to set and get options:

% bnfos/bnfos --get scenario -h 10.23.5.100
scenario = 0
zsh: exit 167   bnfos/bnfos --get scenario -h 10.23.5.100
% bnfos/bnfos --set modedef=0 -h 10.23.5.100
Setting modedef succeeded!
% bnfos/bnfos --get scenario -h 10.23.5.100
scenario = 1
zsh: exit 167   bnfos/bnfos --get scenario -h 10.23.5.100

Exit-code of the bnfos tool when querying a variable is always 167. It also doesn’t follow the UNIX mantra for command-line tools: Be silent on success, noisy on error. But we also see here that the bug appears with the command-line tool too: changing the default relais mode also changed the scenario.
When looking with wireshark we see that for setting the variable with the command-line tool it just retrieved the URL /?cmd=1&rm=0 with a HTTP Get-request.
When using the --show switch, output is on stderr so piping the result needs special shell commands ( |& is a zsh shortcut for piping both, stdout and stderr):

% bnfos/bnfos --show -h 10.23.5.100 |& grep dhcp
 dhcp      = 1
zsh: exit 167   bnfos/bnfos --show -h 10.23.5.100 2>&1 |

Setting the mail parameters smtpserv, smtpfrom and smtpto is impossible via the command-line interface. We always the the cryptic error message:

% bnfos/bnfos --set smtpto='10.23.5.5' -h 10.23.5.100
Setting smtpto failed: Could not parse!
zsh: exit 1     bnfos/bnfos --set smtpto='10.23.5.5' -h 10.23.5.100

Studying the code of the config-tool reveals that there are two configuration tables, one in src/beronet/confmap_fos.h named bnfos_confmap which includes all info about the low-level device parameters:

static const struct {
  char *key;
  char type;
  int cmd;
  char *parm;
  char *macro;
} bnfos_confmap[BNFOS_MAX_KEYS] = {
  { "sz"     , 'b', 1, "sz=%s"    , "szenario(0)"},
  { "mode"   , 'b', 4, "mode=%s"  , "mode(0)"},
  { "rm"     , 'b', 1, "rm=%s"    , "config(1,1)"},

  { "p0"     , 'b', 5, "p=0&s=%s" , "pwrport(0,0)"},
  { "p0"     , 'b', 1, "p0=%s"    , "config(2,1)"},
  { "p1"     , 'b', 5, "p=1&s=%s" , "pwrport(0,1)"},
  { "p1"     , 'b', 1, "p1=%s"    , "config(3,1)"},

  { "dn"     , 'h', 3, "dn=%s"    , "hostname(1)"},
  { "ip"     , 'a', 3, "ip=%s"    , "netconf(0)"},
  { "nm"     , 'a', 3, "nm=%s"    , "netconf(1)"},
  { "gw"     , 'a', 3, "gw=%s"    , "netconf(2)"},
  { "dns"    , 'a', 3, "dns=%s"   , "netconf(3)"},
  { "dhcp"   , 'b', 3, "dhcp=%s"  , "config(4,1)"},
  { "port"   , 'p', 3, "port=%s"  , "netconf(6)"},
  { "pwd"    , 'b', 3, "pwd=%s"   , "config(5,1)"},
  { "apwd"   , 'd', 3, "apwd=%s"  , NULL},

  { "mhost"  , 's', 2, "mhost=%s" , "netconf(5)"},
  { "mfrom"  , 's', 2, "mfrom=%s" , "netconf(7)"},
  { "mto"    , 's', 2, "mto=%s"   , "netconf(8)"},
  { "XXXXX"  , 'n', 7, ""         , NULL},

  { "log"    , 'b', 3, "syslog=%s", "config(10,1)"},
  { "loghost", 'a', 3, "slgip=%s" , "netconf(9)"},
  { "logport", 'p', 3, "slgpt=%s" , "netconf(10)"},

  { "wen"    , 'b', 6, "wen=%s"   , "wdog(0)"},
  { "wen"    , 'b', 2, "wen=%s"   , "config(6,1)"},
  { "wstate" ,   0, 6, "wstate=%s", "wdog(0)"},
  { "wintv"  , 'p', 2, "wintv=%s" , "config(8,?)"},
  { "as"     , 'b', 2, "as=%s"    , "config(9,1)"},
  { "men"    , 'b', 2, "men=%s"   , "config(7,1)"},
  { "wretv"  ,   0, 0, NULL       , "wdog(2)"},
};

and one in bnfos/main.c that maps the high-level command-line paramters to the low-level http requests:

/* keyword description for --set / --get */
static struct {
  char *keyword;
  char *descr;
} keys[BNFOS_MAX_KEYS] = {
  {"scenario", "scenario (0=fallback; 1=bypass)"},

  {"mode", "relais mode (0=A--D; 1=A--B or A--B,C--D)"},
  {"modedef", "default relais mode (0=A--D; 1=A--B or A--B,C--D)"},

  {"power1", "state of powerport 1 (0=off; 1=on)"},
  {"power1def", "default state of powerport 1 (0=off; 1=on)"},
  {"power2", "state of powerport 2 (0=off; 1=on)"},
  {"power2def", "default state of powerport 2 (0=off; 1=on)"},

  {"hostname", "device hostname"},

  {"address", "ip address"},
  {"netmask", "netmask address"},
  {"gateway", "gateway address"},
  {"dns", "dns server address"},
  {"dhcp", "query dhcp server (0=off; 1=on)"},
  {"port", "http listen port"},
  {"pwd", "http password protection (0=off; 1=on)"},
  {"apwd", "admin password"},

  {"smtpserv", "smtp server"},
  {"smtpfrom", "smtp sender address"},
  {"smtpto", "smtp destination address"},
  {"smtptest", "trigger testmail"},

  {"syslog", "syslog logging (0=off; 1=on)"},
  {"slgip", "syslog server ip"},
  {"slgpt", "syslog server port"},
  {"wdog", "watchdog enable (0=off; 1=on)"},
  {"wdogdef", "default watchdog enable (0=off; 1=on)"},
  {"wdogstate", "watchdog state (0=off; 1=on; 2=failure)"},
  {"wdogitime", "watchdog intervall time"},
  {"wdogaudio", "watchdog audio alarm (0=off; 1=on)"},
  {"wdogmail", "watchdog alarm mails (0=off; 1=on)"},
  {"wdogrtime", "watchdog remaining time to failure"},
};

I haven’t found a mechanism that keeps these two tables in different source files in sync (they currently seem to be), looks like both tables need to have the matching options in the same place in both tables. The code for matching options to low-level commands just uses the same index to navigate in both tables.
The bnfos_confmap table has a s for the type of the smtp parameters. This type isn’t handled in the config-tool and leads to the cryptic error message above. Patching the table to specify the type h (there is a comment XXX check hostname for validy for that type this checking apparently isn’t done yet, so we can use the code there to parse normal strings) would work. After applying a patch to src/beronet/confmap_fos.h, the sources aren’t recompiled, seems that the Makefile is broken, too. So after a make clean ; make I’m finally able to set the smtp parameters via the command-line interface:

% bnfos/bnfos --set smtpserv='10.23.5.5' -h 10.23.5.100
Setting smtpserv succeeded!

Looking over this again, I prefer to do the following patch that adds support for the ‘s’ type:

--- bntools/src/bnfos.c 2007-08-28 09:27:46.000000000 +0200
+++ bntools.hacked/src/bnfos.c  2009-04-09 12:10:46.000000000 +0200
@@ -379,6 +379,14 @@
     set->val = strdup(val);
     return BNFOS_RET_OK;

+  case 's':
+    /* Allow empty strings */
+    if (!val) {
+        val = "";
+    }
+    set->val = strdup(val);
+    return BNFOS_RET_OK;
+
   case 'p':
     {
       int v;

This is a cleaner way to make configuring the smtp parameters work. Turns out that setting the mail gw does not influence the dhcp setting. But in the web-interface, the mail gateway and the syslog server are combined in one page. so trying that:

% bnfos/bnfos --show -h 10.23.5.100 |& grep dhcp
 dhcp      = 1
zsh: exit 167   bnfos/bnfos --show -h 10.23.5.100 2>&1 |
zsh: done       grep dhcp
% bnfos/bnfos --set slgip='10.23.5.5' -h 10.23.5.100
Setting slgip succeeded!
% bnfos/bnfos --show -h 10.23.5.100 |& grep dhcp
 dhcp      = 0
zsh: exit 167   bnfos/bnfos --show -h 10.23.5.100 2>&1 |
zsh: done       grep dhcp

we see that changing the syslog server also changes the dhcp setting like in the web-interface. When looking more closely, we see that the dhcp and the syslog IP are in the same cmd group. Thats the number in column 3 of the bnfos_confmap and the number in from of each line in config.txt retrieved via the web interface.
So the workaround for the bug in the firmware is to write a config program that retrieves all variables in the same cmd group and, when setting one of the variables in that group, also send all the other current settings in the same get-request.
Fortunately the bnfos_confmap table has the command pattern for generating the get-request for each of the variables in column 4 (parm). So it shouldn’t be too hard to write a new config utility (and of course I won’t do that i C either) that works around the firmware bugs.
I already said that I would have preferred an open firmware to fix the bugs at the source, did I?

Open Source Document Licensing

Thursday, March 19th, 2009

I’m currently preparing a technical college lecture. The slides for the lecture should become open source. To reduce my overhead I want to use existing source (mainly pictures) from wikipedia.

Open source licensing should really make it easier to re-use material in other open source projects. As far as I can tell the current mess with different documentation licenses does not achieve that goal.

Sad fact: To understand what is possible with the current licensing is nearly as time-consuming as re-creating the material from scratch. So I’ve chosen to document what I’ve learned here, so others may have a faster learning curve and can contribute their experience.

In addition I hope for comments from people involved in the licensing jungle to comment on my views here.

Typically wikipedia pictures come in three license variants, see the Wikipedia Copyrights page, the german version Wikipedia Lizenzbestimmungen has specific sections on picture use:

Some pictures are dual-licensed under GFDL and CC-BY-SA.

Since the GFDL typically is used with a version-upgrade clause, e.g., "Version 1.2 or any later version published by the Free Software Foundation", upgrade to a later version of the license by the user is possible. This is typically not the case with CC-BY-SA.

I’ve decided that CC-BY-SA version 3.0 best fits my license requirements. The GFDL with its front-cover, back-cover and invariant sections is too complicated and CC-BY-SA is much clearer concerning reuse and remix of the material.

One problem I’m having is that when "performing" my slides (thats the term CC-BY-SA is using for e.g. using the slides in a presentation) I want to use either my company logo or I’m forced to use the logo of the teaching institution I’m working for. So I’ve come up with the following addition to the pointer of the licensing terms:

When performing this work (e.g. teaching using these slides) you may use your company and/or teaching institution logo in the header of each slide without putting the logo under the license above. When distributing derived works, make sure you distribute the document without the company or teaching institution logo.

So I’m specifically allowing to use a logo in the header of each slide when performing. I hope this is compatible with the CC licensing terms.

The next problem I’m facing is reuse of pictures. Pictures licensed under a CC-BY-SA license (also earlier than 2.5) shouldn’t pose a problem, because CC-BY-SA explicitly distinguishes derivative work and collective work. Collective work is defined as (cited from version 2.5 of CC-BY-SA as that is the relevant version for most pictures on Wikipedia):

"Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.

So I guess my use of the unmodified pictures in slides is collective work not derivative work. That means I can use CC-BY-SA pictures from wikipedia in a CC-BY-SA document that uses these pictures similar to the usage of pictures in Wikipedia articles, even if the version of the CC-BY-SA license is not the same.

The question if I can use pictures licensed unter GFDL in my slides licensed under CC-BY-SA is still not fully clear for me. Since the pictures typically contain the license-version upgrade clause mentioned above, I could use version 1.3 of the GFDL that includes permission to relicense the work under the CC-BY-SA license under specific circumstances — but my interpretation of that clause allows this only for Wikipedia, not for me as a user of the content on Wikipedia.

Putting my work under a dual-license (CC-BY-SA + GFDL) is also not a solution because this effectively constitutes relicensing of the used content.

So the question remains if I can use GFDL pictures in CC-BY-SA slides and if this is permitted by the GFDL. The GFDL has one paragraph (7) on "aggregation with independent works":

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works
of the Document.

So, hmm, are my slides a "compilation with other separate and independent documents or works" — probably yes. Are they in a "in or on a volume of a storage or distribution medium"? Hard to say. My "copyright resulting from the compilation [provided it is a compilation in the sense of GFDL] is not used to limit the legal rights of the compilation’s users beyond what the individual works permit". So I guess I can use these pictures without the GFDL applying to my document (I want to use the CC-BY-SA).

Thats my due diligence investigation before using this material.

But I’m not a lawyer.

Howto get Asterisk with mISDN V2 and Linux Call Router running on debian lenny

Monday, March 9th, 2009

Update 2009-03-31: provide signed archive with archive key, add udev rules, add /etc/modules entries, add amd64.
Update 2009-04-05: typos fixed

This is a short howto how I built the debian patches and how you can — as a user — install everything needed for mISDN version 2 and Linux Call Router (LCR) with asterisk chan_lcr running on debian lenny.

I’m providing debian packages for Kernel (v 2.6.28.5), an updated zaptel (debian lenny zaptel doesn’t compile with newer kernels and zaptel wctdm uses some settings for analogue phones that don’t work with german and austrian phone like the “R”-key or optional pulse dialling), finally I’m providing a slightly patched asterisk for larger buffer sizes when playing long tones, LCR and misdnv2user packages originally built by Joerg Dorchain. My misdnv2user is the same as Joergs. The lcr package contains my bug-fix for DTMF digits A-F (also in Joergs packages now) which don’t work in upstream LCR version 1.3 and an updated /etc/init.d/lcr for querying the status of lcr.

I’m also providing source packages, except for the kernel — the kernel is stock kernel.org 2.6.28.5 configured for use of mISDN. The kernel was built using debians make-kpkg from the kernel-package debian package. And the config used for building the kernel is in the binary package.

I hope I can contribute something in order to get mISDN V2 and LCR into debian… in the meantime others may want to uses these on debian stable.

Installation

apt-get install vim less ssh ntp
apt-get install python-dev openbsd-inetd postfix madplay

Add following lines to /etc/apt/sources.list:

deb http://project.runtux.com/asterisk/debian/ lenny main
deb-src http://project.runtux.com/asterisk/debian/ lenny main

If you want to avoid warnings about an untrusted archive key from apt, you should import the following archive key. Save the key to a file and then issue the command
apt-key add file

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)

mQENBEnREAIBCADM8+KpoC/HJUCEsx8KZhGgsX/G3ouR4/xkgIuIPgz+t6JoTisj
9QmymDZKUXSy04WmbLjU/088xD5A9ukOEYxoFCGqwWf1tPOKqN1oKpVCkjJb8Dht
vvebqOCzJSV0nfqmIfkpbX+6dUssx+9u0BiFK3aj/GilkEloZl2g+vIT6fveJtKE
qmxz19vL516TDhsbsv3/AKfNKc7QRpsgvPmnNE2IL0CTgQYs26WtnJASlu1MQpwo
Qfb1PrO7ufq9eO58HjEBdfbSNjalQjVj7vLvE4GQglHULO500H9UlfOm2zpO0Vzs
5lGGbwLJdTpAS3HIRhQAW0pueRsQ8zagMn5lABEBAAG0OFJhbGYgU2NobGF0dGVy
YmVjayAoRGViaWFuLVBhY2thZ2UtS2V5KSA8cnNjQHJ1bnR1eC5jb20+iQE2BBMB
AgAgBQJJ0RACAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ5CizCR9G97ah
pAf/eLRYtPVs1apI3+AVi//8y1/r6uL+IxI/Tlt53jCtX/dy3Q3FeAEJt/7fbvcW
TBDnP5K8vWaYUlHHaz+6lbcQyV/KAH4LKJEKkyoINc9ytG1qEG6z8NPfDmKiEluy
HksgLpAqUBrdZy46iWQhcg7f3fpcUIsHHcXrOd2Ip5G9DL2q4/UoRrhBhHC3GNX7
ERaeAKZTF1JRaVN6KSWPC2+yaNmuGn1yoSChG0Q/bBTgzv2fm9Jzvok546f9LE0q
k2q5PvjlUSMGHHojTzzR6tGhnbw5mOfyMUDDs5LuAN1aWbDatepJgiC+dYasprQ5
pZygpoCASqIhWjjCZd3XI5mAEIhGBBARAgAGBQJJ0nZlAAoJEIO0FkDz/lcw0xYA
njBSGef/4KhZpuspIh6WnLM7ORKNAKCw28et9bUoaGu4ESRpIwtwj4asQoicBBAB
AgAGBQJJ0ncuAAoJEJWCQpSoBzk1hpcD/2KXiuvE2Nm0oOi0jBVEjT/Tu/GGkG5m
lf97/I6TMcJxlMpeBlv9SiJD+/BBQo0MGMxmkCwU4t+eBCBsCVcr/bJnrlrKa4Ab
9SR9WQ8PGrSQ+AwMePCDKngqFd5EERz8bxz4sZKGCxn9JVRQOGp03eKSGDG/Yh0v
FY3v7nV0BUaE
=mPtt
-----END PGP PUBLIC KEY BLOCK-----

Then install:

apt-get update
apt-get install linux-headers-2.6.28.5-i686 linux-image-2.6.28.5-i686 
    asterisk zaptel lcr zaptel-modules-2.6.28.5-i686

If you’re on the amd64 architecture, you should replace i686 in the packages above with amd64.

and optionally (for misdn_info):

apt-get install misdnv2user

Edit /etc/default/asterisk and set RUNASTERISK=yes. Then make several directories (should be done by a future version of the lcr package):

mkdir /var/run/lcr
chown asterisk.asterisk /var/run/lcr
mkdir /var/log/lcr
chown asterisk.asterisk /var/log/lcr

I’ve also made a start-script for lcr (for use as /etc/init.d/lcr) ,
downloadable at http://project.runtux.com/asterisk/init.d:lcr
this probably should also be part of the lcr package.

Config file examples used for lcr — these pass
everything to asterisk. File /etc/lcr/interface.conf:

[Ext1]
portnum 0
ptp
nodtmf

[Ext2]
portnum 1
ptp
nodtmf

[Int1]
portnum 2
nt
ptp
nodtmf

[Int2]
portnum 3
nt
ptp
nodtmf

I’m using a Beronet 4 port ISDN card, your config will probably differ: This system only expects incoming calls and needs to check on which line a call comes in. So I distinguish all external interfaces as separate interfaces of LCR. I also need to check an interface by calling out via that interface, you probably would want to make all external ports a trunk by grouping them into one LCR interface.

And the routing config needs to match your interface definition. This config will pass all calls — if asterisk is running — to asterisk. If asterisk isn’t running, I’m calling a test application (untested). The context in asterisk will be the interface name. Again, if you’re using a trunk here, be sure to match the routing config with your interface config. /etc/lcr/routing.conf:

[main]
remote=asterisk interface=Ext1 : remote application=asterisk
remote=asterisk interface=Ext2 : remote application=asterisk
remote=asterisk interface=Int1 : remote application=asterisk
remote=asterisk interface=Int2 : remote application=asterisk
default                        : efi

Update /etc/modules to include the following lines (the command appends the lines between cat and EOF):

cat >> /etc/modules << EOF
mISDN_core debug=0x0
mISDN_dsp debug=0x0 options=0x0
hfcmulti debug=0x0
EOF

Linux udev must be configured to correctly set the user for the isdn device(s):

cat > /etc/udev/rules.d/91-isdn.rules << EOF
ACTION!="add|change", GOTO="permissions_end"

KERNEL=="mISDN*",       GROUP="dialout"

LABEL="permissions_end"
EOF

After a reboot asterisk and lcr should be running.

Building

Getting kernel:

wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.28.tar.bz2
wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.28.tar.bz2.sign
wget http://kernel.org/pub/linux/kernel/v2.6/patch-2.6.28.5.gz
wget http://kernel.org/pub/linux/kernel/v2.6/patch-2.6.28.5.gz.sign

For compilation (zlib isn’t checked by make-kpkg!):

apt-get install kernel-package bzip2 libncurses5-dev zaptel-source 
    zlib1g-dev fakeroot

Compile Kernel:

tar xvf linux-2.6.28.tar.bz2
cd linux-2.6.28
zcat ../patch-2.6.28.5.gz | patch -N -p1 | less 2>&1
cp /boot/config-2.6.28.5-i686 .config
make oldconfig
make menuconfig # just to be sure

For amd64:

make-kpkg --append-to-version -amd64 --revision 2.6.28.5.1.rsc --us 
    --uc --initrd --rootcmd fakeroot binary > m.out 2> m.err

For i686:

make-kpkg --append-to-version -i686 --revision 2.6.28.5.1.rsc --us 
    --uc --initrd --rootcmd fakeroot binary > m.out 2> m.err

The following doesn’t seem to work although zaptel is installed:
probably need to unpack /usr/src/zaptel.tar.bz2 into
/usr/src/modules/zaptel (tar file contains modules directory!)
this would save us from the m-a a-i step below. amd64:

make-kpkg --append-to-version -amd64 --revision 2.6.28.5.1.rsc --us 
    --uc --initrd --rootcmd fakeroot modules > mo.out 2> mo.err
cd ..

For i686:

make-kpkg --append-to-version -i686 --revision 2.6.28.5.1.rsc --us 
    --uc --initrd --rootcmd fakeroot modules > mo.out 2> mo.err
cd ..

Make a debianized zaptel for new kernel:

apt-get install devscripts libnewt-dev quilt libusb-dev asciidoc
svn checkout http://svn.digium.com/svn/zaptel/branches/1.4 zaptel
apt-get source zaptel-source
cp zaptel/kernel/ztdummy.* zaptel-1.4.11~dfsg/kernel
cd zaptel-1.4.11~dfsg
# Add "Fix compilation for newer kernels"
dch -i
dpkg-buildpackage
cd ..
dpkg -i zaptel-source_1.4.11~dfsg-3.1_all.deb
m-a a-i zaptel

The following installs my patched asterisk, I’m modifying some buffer sizes because I want to play long tones (I’m generating a faked modem guard-tone that is needed in a project). You probably won’t need the patches asterisk, but it won’t hurt to install it. The create-patches script is available from
http://project.runtux.com/asterisk/create-patches

apt-get install libreadline5-dev libgsm1-dev libssl-dev libtonezone-dev 
    libvpb-dev autotools-dev libsqlite-dev libspeex-dev libspeexdsp-dev 
    graphviz libcurl4-openssl-dev doxygen libpopt-dev libopenh323-dev   
    libiksemel-dev libradiusclient-ng-dev freetds-dev libvorbis-dev     
    libsnmp-dev libc-client2007b-dev libcap2-dev libpq-dev unixodbc-dev 
    libpri-dev
apt-get source asterisk
scp ralf@bee:checkout/own/config/asterisk/create-patches .
cd asterisk-1.4.21.2~dfsg/
sh ../create-patches
# Hunk #1 succeeded at 25 (offset 3 lines).
# Add "runtux.com local buffer-size patches"
# and new version-number 1:1.4.21.2.1~dfsg-3
dch -i # add comment
dpkg-buildpackage -rfakeroot
cd ..

For mISDNuser and chan_lcr I’m using Joerg Dorchains packages with my added patches for DTMF codes A-F.

Dämmerungsgesteuerte Hühnerstalltür mit Arduino

Thursday, January 8th, 2009

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 — die Hühner gehen, im Gegensatz zu den Enten die wir früher hatten, von selber bei Dämmerung in den Stall — brauchten wir eine Lösung, die automatisch die Tür schließt. Es gibt fertige Hühnerställe mit einer zeitgesteuerten Tür — aber weder mit Dämmerungsschalter noch eine Türelektronik einzeln.

-)

v.l.n.r Kokoschka, Leuchtfeder, Gertrude :-)


Mein Sohn Max und ich haben also gemeinsam die Tür mit Mechanik und Elektronik und Software selber gemacht.

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.

Einige Teile unserer Lösung eignen sich sicher auch für andere Projekte, nicht viele Leute werden eine Hühnerstalltüre brauchen …

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.

In der Folge beschreiben wir den Aufbau, getrennt nach Mechanik, Elektronik und Software — die Aufteilung zwischen Mechanik und Elektronik ist etwas willkürlich, wir haben die ganze Elektromechanik
(Motor, Schalter) zur Mechanik gerechnet.

Die Mechanik

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…

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.

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.

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.

Motor: Eigentlich wollten wir ursprünglich einen kleineren Motor, da der jetzt verwendete Motor bis zu 2A Strom zieht (bei hoher Last — 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.

Die Tür selbst hat einen Magneten, der in zwei Stellungen (Tür ganz oben bzw. ganz unten) jeweils einen Magnetschalter auslöst.

Für das Justieren (von Hand Türe rauf- bzw. runterfaheren) gibt es zwei Taster die an digitalen Inputs des Arduino angeschlossen sind.

Mechanischer Aufbau

Mechanischer Aufbau

Teileliste Mechanik:

  • 222366 Getriebemotor
  • 222374 Alternativ: kleinerer Motor
  • 237205 Welle 8mm
  • 216011 Lagerflansch für Kugellager
  • 225550 Stellringe 8mm
  • 214493 Kugellager
  • 226043 Zahnriemenscheibe 40 Zähne, für Welle 8mm
  • 226106 Zahnriemenscheibe 10 Zähne, für Welle 6mm (an Motor)
  • 226084 Zahnriemen
  • 753360 Magnetschalter
  • Taster ein (2X)

Im folgenden Bild sieht man die Tür im Zustand “NACHT” (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.

Hühnerstalltür im geschlossenen Zustand mit Magnet und Magnetschaltern

Hühnerstalltür im geschlossenen Zustand mit Magnet und Magnetschaltern

Die Elektronik

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.

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 Physical Computing Labs der Tisch School of the Arts beschrieben — ist auch der erste Link auf den ich beim googlen nach “Arduino Motor Control” gestoßen bin.

Wir wollen aber — wegen des größeren Motors — auf eine größere H-Bridge, die braucht externe Schaltdioden (das Datenblatt des L298N schlägt Dioden mit höchstens 200ns reverse recovery time (trr) vor, wir werden die BYV 2100 verwenden mit einer trr von 12.5ns)

Teileliste Elektronik:

Alternative größere H-Bridge:

  • 156128 L298N H-Bridge Dual 2A bzw parallelgeschaltet 3A
  • 160005 BYV 2100 Schnelle Dioden dazu

Die Software

Der Dämmerungsschalter sollte laut Wikipedia bei ca. 100 lux 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 “Jetzt ist es dunkel, Tür zu” und von 300 für “Jetzt ist es hell, Tür auf” gut — 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.

Die Steuerung ist als Zustandsautomat (State Machine) realisiert. Jenachdem welcher Magnetschalter beim Einschalten Kontakt hat, startet der Automat im Zustand “TAG” bzw. “NACHT”. Wird keiner der Magnetschalter gesehen, gehen wir in den Zustand “ERROR”. 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.

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.

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.

Die Verdrahtung mit dem Arduino, also an welchen Pins welche Peripherie angeschlossen ist, sind über define’s am Beginn des Programms festgelegt.

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.

Beim Testen haben wir festgestellt, dass bei laufendem Motor gelegentlich eine gedrückte Taste — 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.

Die beiden Taster zum manuellen Rauf- und Runterfahren setzen ein Flag das den Zustandsautomaten neu initialisiert — 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.

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.

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 “#undef int” 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 “Feature” hat durch obskure Fehlermeldungen einiges an Zeit gekostet, was mich veranlasst hat, einen Beitrag im Arduino Forum dazu zu schreiben.

Die verwendeten Timer-Routinen (#include <timer.h> und der Typ Arduino_Timer) dienen dazu, auf eine bestimmte Zeit nach dem Aufruf von millis() zu warten — 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.

#include <timer.h>
#include <stdio.h>

# 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)++ >= 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, &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 > HELL)
    {
        timer.start (millis ());
        return 1;
    }
    return 0;
}

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

int abend ()
{
    int val;
    motor_aus ();
    val = analogRead (FOTO);
    if (val > 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 < 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 >= HELL)
    {
        status = STATUS_ERROR;
        errmsg = "FINSTER >= HELL";
    }
    Serial.print ("initial state: ");
    Serial.println (status);
}

void loop ()
{
    struct state *st = &stati [status];
    if (!debounced_read (KNOPF_RUNTER, &debounce_knopf_runter))
    {
        debounce_knopf_runter = 10;
        linksrum  ();
        motor_an  ();
        neuinitialisieren = 1;
        return;
    }
    else if (!debounced_read (KNOPF_RAUF, &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 >= STATUS_ERROR)
    {
        error ();
        return;
    }
    if (st->status != status)
    {
        status = STATUS_ERROR;
        sprintf
            ( errbuf
            , "Error in state-table, expected %d got %d"
            , status
            , st->status
            );
        errmsg = errbuf;
        return;
    }
    if (st->statefun ())
    {
        status = st->next_status;
    }
    if (status != st->status)
    {
        sprintf (errbuf, "new state: %d->%d", st->status, status);
        Serial.println (errbuf);
    }
}

Impressum/Kontakt