Difference between revisions of "Forth"

From eLinux.org
Jump to: navigation, search
m (Reflective Sensor)
m (More Gertboard Words)
(29 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Forth on RPi
+
Forth on Raspberry Pi
 
<code><pre>
 
<code><pre>
 
sudo apt-get install wget
 
sudo apt-get install wget
Line 10: Line 10:
 
Start the Forth interpreter with ./atlast and exit with Ctrl-d
 
Start the Forth interpreter with ./atlast and exit with Ctrl-d
  
atlast.html and atlast.pdf is included and is an extensive atlast-forth manual. Read it online [http://www.fourmilab.ch/atlast/] and read about Forth itself here [http://www.forth.com/starting-forth/sf0/sf0.html] and here [http://home.iae.nl/users/mhx/sf.html] (Beware that the Forth dialect in the book Starting Forth is a bit outdated compared to Atlast Forth). Leo Brodie wrote another book, Thinking Forth, read it here [http://www.dnd.utwente.nl/~tim/colorforth/Leo-Brodie/thinking-forth.pdf]
+
atlast.html and atlast.pdf is included and is an extensive ATLAST Forth manual. Read it online [http://www.fourmilab.ch/atlast/] and read about Forth itself here [http://www.forth.com/starting-forth/sf0/sf0.html] and here [http://home.iae.nl/users/mhx/sf.html] (Beware that the Forth dialect in the book Starting Forth is a bit outdated compared to ATLAST Forth). Leo Brodie wrote another book, Thinking Forth, read it here [http://www.dnd.utwente.nl/~tim/colorforth/Leo-Brodie/thinking-forth.pdf]
  
 
== Adding new words ==
 
== Adding new words ==
Most of the power of Atlast Forth derives from the ease with which C coded primitives can be added to the language. In my case I will add some words for controlling my [[RPi Gertboard | Gertboard]].
+
Most of the power of ATLAST Forth derives from the ease with which C coded primitives can be added to the language. In my case I will add some words for controlling my [[RPi Gertboard | Gertboard]].
There is a detailed description on how to do this in the atlast-forth manual. And you can copy much of the word implementations from the gertboard_sw directory if you have downloaded the gertboard demo files.
+
There is a detailed description on how to do this in the ATLAST Forth manual. And you can copy much of the word implementations from the gertboard_sw directory if you have downloaded the gertboard demo files.
 
It is easy to add your own words, just add a "define GERTBOARD" to atlast.c around line 56.  
 
It is easy to add your own words, just add a "define GERTBOARD" to atlast.c around line 56.  
 
<code><pre>
 
<code><pre>
Line 47: Line 47:
 
}
 
}
  
prim P_gert_setport() // channel state ---
+
prim P_gert_setport() // Channel state ---
 
{ // Set a digital io port to a specified state
 
{ // Set a digital io port to a specified state
 
   int rev;
 
   int rev;
 
   Sl(2);
 
   Sl(2);
 
   if (S1 == 21)
 
   if (S1 == 21)
     { // find out which rev of RPi we have
+
     { // Find out which revision of Raspberry Pi we have
 
       rev = pi_revision();
 
       rev = pi_revision();
 
       if (rev != 1)
 
       if (rev != 1)
Line 71: Line 71:
 
   Sl(1);
 
   Sl(1);
 
   if (S0 == 21)
 
   if (S0 == 21)
     { // find out which rev of RPi we have
+
     { // Find out which revision of Raspberry Pi we have
 
       rev = pi_revision();
 
       rev = pi_revision();
 
       if (rev != 1)
 
       if (rev != 1)
Line 138: Line 138:
 
I have a kWh meter that I would like to read with the RPi. On the meter there is a small light that blinks once per 3.6 sec at 1 kW. So I need a way to detect time between pulses. I mounted a simple LDR (2k - 20k) for around €2 over the blinking light and connected it to Gertboard Buf1 and ground. Set B1 as an input with a jumper on the board and connect GP25 to B1.
 
I have a kWh meter that I would like to read with the RPi. On the meter there is a small light that blinks once per 3.6 sec at 1 kW. So I need a way to detect time between pulses. I mounted a simple LDR (2k - 20k) for around €2 over the blinking light and connected it to Gertboard Buf1 and ground. Set B1 as an input with a jumper on the board and connect GP25 to B1.
  
Now that you know how to add a primitive word to atlast I just list the code for the word:
+
Now that you know how to add a primitive word to ATLAST, I just list the code for the word:
 
<code><pre>
 
<code><pre>
 
prim P_gert_getkwh() // channel --- clocks  clocks_per_sec
 
prim P_gert_getkwh() // channel --- clocks  clocks_per_sec
Line 196: Line 196:
 
Result: Jolly good, or as they say in USA, Awesome! Tests shows an accuracy down to a single watt.
 
Result: Jolly good, or as they say in USA, Awesome! Tests shows an accuracy down to a single watt.
  
However, having the kW load on the command line in a terminal is not good enough, I want it online on the web or as a mobile app. If you are interested, follow me over to the [[Erlang | Erlang]] page where I vill use this Atlast Forth application as an Erlang Port to access Gertboard from a web page.
+
However, having the kW load on the command line in a terminal is not good enough, I want it online on the web or as a mobile app. If you are interested, follow me over to the [[Erlang | Erlang]] page where I vill use this ATLAST Forth application as an Erlang Port to access Gertboard from a web page.
  
 
== More Fun ==
 
== More Fun ==
Line 262: Line 262:
 
count
 
count
  
 +
</pre></code>
 +
 +
== More Gertboard Words ==
 +
Here's some more words for reading and writing the analog input and output (DtoA and AtoD). First GETATOD, it takes a channel on the stack and leaves a voltage.
 +
<code><pre>
 +
prim P_gert_getatod() // chan --- voltage
 +
{ // V will be in range 0-1023 (0-3.3V)
 +
    Sl(1);
 +
INP_GPIO(8);  SET_GPIO_ALT(8,0);
 +
INP_GPIO(9);  SET_GPIO_ALT(9,0);
 +
INP_GPIO(10); SET_GPIO_ALT(10,0);
 +
INP_GPIO(11); SET_GPIO_ALT(11,0);
 +
setup_spi();
 +
    S0 = read_adc(S0);
 +
}
 +
</pre></code>
 +
 +
And SETDTOA expects a channel and a voltage on the stack and sets the output accordingly.
 +
<code><pre>
 +
prim P_gert_setdtoa() // chan volt --
 +
{ // V between 0 and 255
 +
    Sl(2);
 +
    INP_GPIO(7);  SET_GPIO_ALT(7,0);
 +
    INP_GPIO(9);  SET_GPIO_ALT(9,0);
 +
    INP_GPIO(10); SET_GPIO_ALT(10,0);
 +
    INP_GPIO(11); SET_GPIO_ALT(11,0);
 +
setup_spi();
 +
    write_dac(S1, S0*16); // V_out = S0*16 / 256 * 2.048
 +
    Pop2;
 +
}
 +
</pre></code>
 +
 +
If you would like to read a burst of values from an analog input, here's how to do it. The word GETBURST expects a channel, a delay in microseconds between samples, and the number of samples you want. It leaves the samples on stack. You may want to increase the stack length constant at line 138 like this: atl_int atl_stklen = 1000;       /* Evaluation stack length */
 +
<code><pre>
 +
prim P_gert_burst() // chan delay samples --- n1, n2, nn
 +
{
 +
    unsigned int i, num, chan, delay;
 +
 +
    Sl(3);
 +
So(S0 - 3);
 +
INP_GPIO(8);  SET_GPIO_ALT(8,0);
 +
INP_GPIO(9);  SET_GPIO_ALT(9,0);
 +
INP_GPIO(10); SET_GPIO_ALT(10,0);
 +
INP_GPIO(11); SET_GPIO_ALT(11,0);
 +
setup_spi();
 +
num = S0;
 +
delay = S1;
 +
chan = S2;
 +
Pop;
 +
Pop;
 +
Pop;
 +
for(i = 0; i < num; i++){
 +
    Push = (stackitem) read_adc(chan);
 +
usleep(delay);
 +
}
 +
}
 +
</pre></code>
 +
And finally, add the new words to the list of primitives:
 +
 +
<code><pre>
 +
{"0GETATOD", P_gert_getatod},
 +
{"0GETBURST", P_gert_burst},
 +
{"0SETDTOA", P_gert_setdtoa},
 +
</pre></code>
 +
 +
== Tellstick ==
 +
In order to have a safe way to handle mains switching, a Tellstick [http://www.telldus.com/products/range] is an excellent choice. It can control many consumer brands of wireless controlled sockets and dimmers. Implementing some words in Forth for switching mains on and off leaves the safe voltages for sensing through the Gertboard interface.
 +
 +
Download and install the development kit from Telldus:
 +
<code><pre>
 +
wget http://download.telldus.se/TellStick/Software/telldus-core/telldus-core-2.1.1.tar.gz
 +
tar -xzvf telldus-core-2.1.1.tar.gz
 +
sudo apt-get install libconfuse-dev
 +
sudo apt-get install libftdi-dev
 +
sudo apt-get install cmake
 +
cd telldus-core-2.1.1
 +
cmake .
 +
make
 +
sudo make install
 +
</pre></code>
 +
 +
When that installation is done you can copy two files over to the ATLAST Forth directory:
 +
<code><pre>
 +
cd /home/pi/atlast-1.2
 +
cp ../telldus-core-2.1.1/client/telldus-core.h .
 +
cp ../telldus-core-2.1.1/client/libtelldus-core.so .
 +
</pre></code>
 +
 +
Then add the lib libtelldus-core.so to the Makefile, at the top, right after -lm:
 +
<code><pre>
 +
LIBRARIES = -lm libtelldus-core.so
 +
</pre></code>
 +
 +
In atlast.c, add define TELLDUS right before #define MATH;
 +
<code><pre>
 +
#define TELLDUS                      /* Tellstick functions */
 +
</pre></code>
 +
 +
In atlast.c, include the telldus_core.h file:
 +
<code><pre>
 +
#ifdef TELLDUS
 +
#include "telldus_core.h"
 +
#endif
 +
</pre></code>
 +
 +
In atlast.c, right after #endif /* COMPILERW */ add the following Tellstick primitives:
 +
<code><pre>
 +
#ifdef TELLDUS
 +
 +
prim P_tell_init() // ---
 +
{
 +
  tdInit();
 +
}
 +
 +
prim P_tell_close() // ---
 +
{
 +
  tdClose();
 +
}
 +
 +
prim P_tell_on() // device ---
 +
{
 +
  Sl(1);
 +
  tdTurnOn(S0);
 +
  Pop;
 +
}
 +
 +
prim P_tell_off() // device ---
 +
{
 +
  Sl(1);
 +
  tdTurnOff(S0);
 +
  Pop;
 +
}
 +
 +
#endif /* TELLDUS */
 +
</pre></code>
 +
 +
And finally, add the new words to the list of primitives:
 +
<code><pre>
 +
#ifdef TELLDUS
 +
  {"0TELLON", P_tell_on},
 +
  {"0TELLOFF", P_tell_off},
 +
  {"0TELLINIT", P_tell_init},
 +
  {"0TELLCLOSE", P_tell_close},
 +
#endif /* TELLDUS */
 +
</pre></code>
 +
 +
Save atlast.c and re-run make to compile and you can try your new words. Remember to configure /etc/tellstick.conf according to the brands of hardware you are using, read the Tellstick documentation.
 +
First, start telldusd and then ATLAST.
 +
<code><pre>
 +
telldusd
 +
./atlast
 +
</pre></code>
 +
In the ATLAST console, type your new Forth words
 +
<code><pre>
 +
tellinit
 +
1 tellon
 +
1 telloff
 +
tellclose
 +
</pre></code>
 +
 +
== Tellstick duo ==
 +
If you are fortunate enough to have a Tellstick Duo [http://www.telldus.com/products/tellstick_duo], you probably want to listen for incoming 433&nbsp;MHz signals so here we go. But first follow the Tellstick instructions above, and test that they work as expected.
 +
 +
Add this primitive word to atlast.c. We will use it to register a callback. (A callback is an ordinary function, nothing strange, but it will be called not by you but by the Tellstick Duo when there is data coming in).
 +
<code><pre>
 +
prim P_tell_rawevent() // string ---
 +
{
 +
  Sl(1);
 +
  tdRegisterRawDeviceEvent( rawcallback, S0);
 +
  Pop;
 +
}
 +
</pre></code>
 +
Then add the callback function, this is the function that will be called. Add it around line 295, just above the line /*  TOKEN  --  Scan a token and return its type.  */
 +
<code><pre>
 +
#ifdef TELLDUS
 +
static void rawcallback(const char *data, int controllerId, int callbackId, void *context)
 +
{
 +
    dictword *dw;
 +
    V strcpy((char *)context, data);
 +
    dw = atl_lookup("rawevent");
 +
    atl_exec(dw);
 +
}
 +
#endif
 +
</pre></code>
 +
Finally, add the new primitive word to the list of primitives
 +
<code><pre>
 +
  {"0TELLRAWCALLBACK", P_tell_rawevent},
 +
</pre></code>
 +
 +
Now, to try it out, save and re-compile with make and then start telldusd and ./atlast. Type the following in the ATLAST console:
 +
<code><pre>
 +
127 string raw
 +
: rawevent raw type cr ;
 +
raw tellrawcallback
 +
</pre></code>
 +
The word rawevent is executed by the callback function, you can re-define it and it will use the latest definition. With this definition it will just type the incoming data in the console. If you take a Nexa remote control and press a few buttons, the incoming data should print as a string, for example
 +
 +
"class:command;protocol:waveman;model:codeswitch;house:A;unit:1;method:turnoff;"
 +
 +
You can even put the three lines of Forth code in a file, e.g. telldus.atl and start ATLAST like this
 +
<code><pre>
 +
./atlast -i./telldus.atl
 
</pre></code>
 
</pre></code>
  
 
[[Category: RaspberryPi]]
 
[[Category: RaspberryPi]]

Revision as of 14:14, 19 January 2014

Forth on Raspberry Pi

sudo apt-get install wget
wget http://www.fourmilab.ch/atlast/download/1.2/atlast-1.2.tar.gz
tar -xzvf atlast-1.2.tar.gz
cd atlast-1.2/
make

Start the Forth interpreter with ./atlast and exit with Ctrl-d

atlast.html and atlast.pdf is included and is an extensive ATLAST Forth manual. Read it online [1] and read about Forth itself here [2] and here [3] (Beware that the Forth dialect in the book Starting Forth is a bit outdated compared to ATLAST Forth). Leo Brodie wrote another book, Thinking Forth, read it here [4]

Adding new words

Most of the power of ATLAST Forth derives from the ease with which C coded primitives can be added to the language. In my case I will add some words for controlling my Gertboard. There is a detailed description on how to do this in the ATLAST Forth manual. And you can copy much of the word implementations from the gertboard_sw directory if you have downloaded the gertboard demo files. It is easy to add your own words, just add a "define GERTBOARD" to atlast.c around line 56.

#define EVALUATE                      /* The EVALUATE primitive */
#define FILEIO                        /* File I/O primitives */
#define GERTBOARD                     /* Gertboard functions */

Include the gb_common.h right after include <math.h>

#ifdef MATH
#include <math.h>
#endif

#ifdef GERTBOARD
#include "gb_common.h"
#endif

Then add your own word definitions at the end of the section with word definitions, around line 2704, right after #endif /* COMPILERW */ in atlast.c

#ifdef GERTBOARD

prim P_gert_io() // state ---
{ // Setup and restore io
  Sl(1);
  if(S0 == 1)
    setup_io(); // Map the I/O sections
  else
    restore_io(); // Unmap and free memory
  Pop;
}

prim P_gert_setport() // Channel state ---
{ // Set a digital io port to a specified state
  int rev;
  Sl(2);
  if (S1 == 21)
    { // Find out which revision of Raspberry Pi we have
      rev = pi_revision();
      if (rev != 1)
        S1 = 27; // GP21 on Gertboard is controlled by GPIO27
  }
  INP_GPIO(S1);
  OUT_GPIO(S1);
  if (S0 == 1)
    GPIO_SET0 = (1<<S1);
  else
    GPIO_CLR0 = (1<<S1);
  Pop2;
}

prim P_gert_getport() // channel --- state
{ // Get a digital io port
  int rev;
  Sl(1);
  if (S0 == 21)
    { // Find out which revision of Raspberry Pi we have
      rev = pi_revision();
      if (rev != 1)
        S0 = 27; // GP21 on Gertboard is controlled by GPIO27
  }
  INP_GPIO(S0);
  S0 = !!(GPIO_IN0 & (1 << S0));
}

#endif /* GERTBOARD */

And finally, add the actual words to the Table of primitive words, right after #endif /* EVALUATE */ at line 2960 or so.

#ifdef EVALUATE
    {"0EVALUATE", P_evaluate},
#endif /* EVALUATE */

#ifdef GERTBOARD
	 {"0GERTBOARD", P_gert_io},	
	 {"0SETIO", P_gert_setport},		
	 {"0GETIO", P_gert_getport},		
#endif /* GERTBOARD */

As we are using code from the Gertboard demos, copy the files gb_common.o and gb_common.h from the gertboard_sw directory to atlast-1.2 directory (it's there if you have run make in this directory as well).

Add gb_common.o to the file Makefile in atlast-1.2.

ATLOBJ = atlast.o gb_common.o atlmain.o

Now, save and run "make" again to recompile atlast.c.

Test the new words

Wire up the Gertboard according to the information you get when you run the command sudo ./leds in the Gertboard demo directory.

Run sudo ./atlast in the atlast-1.2 directory.

Type 1 gertboard

Type 22 1 setio and press enter, the corresponding LED will go on.

Type 22 0 setio and the LED will go off.

Type 0 gertboard

Play with it

Define your own LED demo, start the interpreter with sudo ./atlast. Define these words:

: use 1 ;
: free 0 ;
: leds 25 24 23 22 21 18 17 11 10 9 8 7 ;
: on 12 0 do 1 setio loop ;
: off 12 0 do 0 setio loop ;

Now type:

use gertboard 
leds on
leds off 
free gertboard

A real Use Case

I have a kWh meter that I would like to read with the RPi. On the meter there is a small light that blinks once per 3.6 sec at 1 kW. So I need a way to detect time between pulses. I mounted a simple LDR (2k - 20k) for around €2 over the blinking light and connected it to Gertboard Buf1 and ground. Set B1 as an input with a jumper on the board and connect GP25 to B1.

Now that you know how to add a primitive word to ATLAST, I just list the code for the word:

prim P_gert_getkwh() // channel --- clocks  clocks_per_sec
{ // Get a digital io port negative edge
  unsigned int i;
  clock_t start, end;

  start = clock();
  Sl(1);
  if (S0 == 21)
    { // find out which rev of RPi we have
      rev = pi_revision();
      if (rev != 1)
        S0 = 27; // GP21 on Gertboard is controlled by GPIO27
  }
  INP_GPIO(S0);
  i = 0;
  while(GPIO_IN0 & (1 << S0))
  {
    i++;
    if(i > 100000000) break; // Timeout after 10 seconds
  }
  long_wait(1);
  end = clock();
  S0 = (double) (end - start);
  So(1);
  Push = (stackitem) CLOCKS_PER_SEC;
}
 
Add #include <time.h> at line 20 in atlast.c
Add fflush(stdout); to prim P_cr() at line 1460 or so.

prim P_cr()                           /* Carriage return */
{
    V printf("\n");
    fflush(stdout);
}

Start the interpreter with sudo ./atlast. Test the commands

1 gertboard

( The first 25 getkwh is for detecting an edge, drop the result and find next edge. Convert the two integers to float and divide them.  )
( 3.6 divided by the result. The result is load in kilowatts )
: kw 3.6 25 getkwh 2drop 25 getkwh float 2 roll float 2swap f/ f/ f. cr ;

kw

( The same thing done with integers only, here the result is in W )
: w 25 getkwh 2drop 25 getkwh 36 * swap 100 / / ." "w=" . cr ;

w

0 gertboard

Result: Jolly good, or as they say in USA, Awesome! Tests shows an accuracy down to a single watt.

However, having the kW load on the command line in a terminal is not good enough, I want it online on the web or as a mobile app. If you are interested, follow me over to the Erlang page where I vill use this ATLAST Forth application as an Erlang Port to access Gertboard from a web page.

More Fun

To play a little more with the demo I need another primary word: SLEEP that takes one item on the stack, sleep time in microseconds. This very simple word should have been the word to start with, it actually shows three fundamental things for a primary word in three lines of code. Sl(1) to make sure there is at least one item on the stack. usleep(S0) is using the top stack item S0. Pop; pops the S0 stack item off the stack when it has been used.

Add this primary word to atlast.c. Put it right after the function prim P_quit()

prim P_sleep() // microsec ---
{ 
  Sl(1);
  usleep(S0);
  Pop;
}

Remember to wire up the Gertboard according to the information you get when you run the command sudo ./leds in the Gertboard demo directory. Now we can do:

sudo ./atlast

: use 1 ;
: free 0 ;
: leds 25 24 23 22 21 18 17 11 10 9 8 7 ;
: on 12 0 do 1 setio 500000 sleep loop ;
: off 12 0 do 0 setio 500000 sleep loop ;

Now define the word leddemo:

: leddemo use gertboard leds on leds off leds on leds off free gertboard ;

( and try it )
leddemo

Reflective Sensor

The edge trigger word can be used for other sensors as well, I tried it with this Reflective Sensor [5] and it works right out of the box. Connect VCC to a digital output and the "Out" to a digital input. Gnd to Gnd. I found it more convenient to look for positive edges so here is the word for that:

prim P_gert_getposedge() // channel wait_microsec --- clocks clocks_per_second
{ // Get a digital io port positive edge
  unsigned int i; // wait is duration until port is low again
  clock_t start, end;

  start = clock();
  Sl(1);
  if (S1 == 21)
    S1 = 27;
  INP_GPIO(S1);
  i = 0;
  while(!(GPIO_IN0 & (1 << S1)))
  {
    i++;
    if(i > 100000000) break;
  }
  usleep(S0);
  end = clock();
  S1 = (double) (end - start);
  S0 = CLOCKS_PER_SEC;
}

Define a word COUNT to test the setup (Out on the sensor is connected to Buf2 on Gertboard and VCC is connected to Buf3)

1 gertboard
23 1 setio
: count 1 100 1 do 24 100000 getposedge 2drop 1 + .s cr loop ;
count

More Gertboard Words

Here's some more words for reading and writing the analog input and output (DtoA and AtoD). First GETATOD, it takes a channel on the stack and leaves a voltage.

prim P_gert_getatod() // chan --- voltage 
{ // V will be in range 0-1023 (0-3.3V)
    Sl(1);
	INP_GPIO(8);  SET_GPIO_ALT(8,0);
	INP_GPIO(9);  SET_GPIO_ALT(9,0);
	INP_GPIO(10); SET_GPIO_ALT(10,0);
	INP_GPIO(11); SET_GPIO_ALT(11,0);
	setup_spi();
    S0 = read_adc(S0);	
}

And SETDTOA expects a channel and a voltage on the stack and sets the output accordingly.

prim P_gert_setdtoa() // chan volt --
{ // V between 0 and 255
    Sl(2);
    INP_GPIO(7);  SET_GPIO_ALT(7,0);
    INP_GPIO(9);  SET_GPIO_ALT(9,0);
    INP_GPIO(10); SET_GPIO_ALT(10,0);
    INP_GPIO(11); SET_GPIO_ALT(11,0);
	setup_spi();
    write_dac(S1, S0*16); // V_out = S0*16 / 256 * 2.048	
    Pop2;
}

If you would like to read a burst of values from an analog input, here's how to do it. The word GETBURST expects a channel, a delay in microseconds between samples, and the number of samples you want. It leaves the samples on stack. You may want to increase the stack length constant at line 138 like this: atl_int atl_stklen = 1000; /* Evaluation stack length */

prim P_gert_burst() // chan delay samples --- n1, n2, nn
{
    unsigned int i, num, chan, delay; 
	
    Sl(3);
	So(S0 - 3);
	INP_GPIO(8);  SET_GPIO_ALT(8,0);
	INP_GPIO(9);  SET_GPIO_ALT(9,0);
	INP_GPIO(10); SET_GPIO_ALT(10,0);
	INP_GPIO(11); SET_GPIO_ALT(11,0);
	setup_spi();
	num = S0;
	delay = S1;
	chan = S2;
	Pop;
	Pop;
	Pop;
	for(i = 0; i < num; i++){
	    Push = (stackitem) read_adc(chan);
		usleep(delay);
	}
}

And finally, add the new words to the list of primitives:

	{"0GETATOD", P_gert_getatod},
	{"0GETBURST", P_gert_burst},
	{"0SETDTOA", P_gert_setdtoa},	

Tellstick

In order to have a safe way to handle mains switching, a Tellstick [6] is an excellent choice. It can control many consumer brands of wireless controlled sockets and dimmers. Implementing some words in Forth for switching mains on and off leaves the safe voltages for sensing through the Gertboard interface.

Download and install the development kit from Telldus:

wget http://download.telldus.se/TellStick/Software/telldus-core/telldus-core-2.1.1.tar.gz
tar -xzvf telldus-core-2.1.1.tar.gz
sudo apt-get install libconfuse-dev
sudo apt-get install libftdi-dev
sudo apt-get install cmake
cd telldus-core-2.1.1
cmake .
make
sudo make install

When that installation is done you can copy two files over to the ATLAST Forth directory:

cd /home/pi/atlast-1.2
cp ../telldus-core-2.1.1/client/telldus-core.h .
cp ../telldus-core-2.1.1/client/libtelldus-core.so .

Then add the lib libtelldus-core.so to the Makefile, at the top, right after -lm:

LIBRARIES = -lm libtelldus-core.so

In atlast.c, add define TELLDUS right before #define MATH;

#define TELLDUS                       /* Tellstick functions */

In atlast.c, include the telldus_core.h file:

#ifdef TELLDUS
#include "telldus_core.h"
#endif

In atlast.c, right after #endif /* COMPILERW */ add the following Tellstick primitives:

#ifdef TELLDUS

prim P_tell_init() // ---
{
  tdInit();
}

prim P_tell_close() // ---
{
  tdClose();
}

prim P_tell_on() // device ---
{
  Sl(1);
  tdTurnOn(S0);
  Pop;
}

prim P_tell_off() // device ---
{
  Sl(1);
  tdTurnOff(S0);
  Pop;
}

#endif /* TELLDUS */

And finally, add the new words to the list of primitives:

#ifdef TELLDUS
  {"0TELLON", P_tell_on},
  {"0TELLOFF", P_tell_off},
  {"0TELLINIT", P_tell_init},
  {"0TELLCLOSE", P_tell_close},
#endif /* TELLDUS */

Save atlast.c and re-run make to compile and you can try your new words. Remember to configure /etc/tellstick.conf according to the brands of hardware you are using, read the Tellstick documentation. First, start telldusd and then ATLAST.

telldusd
./atlast

In the ATLAST console, type your new Forth words

tellinit
1 tellon
1 telloff
tellclose

Tellstick duo

If you are fortunate enough to have a Tellstick Duo [7], you probably want to listen for incoming 433 MHz signals so here we go. But first follow the Tellstick instructions above, and test that they work as expected.

Add this primitive word to atlast.c. We will use it to register a callback. (A callback is an ordinary function, nothing strange, but it will be called not by you but by the Tellstick Duo when there is data coming in).

prim P_tell_rawevent() // string ---
{
   Sl(1);
   tdRegisterRawDeviceEvent( rawcallback, S0);
   Pop;
}

Then add the callback function, this is the function that will be called. Add it around line 295, just above the line /* TOKEN -- Scan a token and return its type. */

#ifdef TELLDUS
static void rawcallback(const char *data, int controllerId, int callbackId, void *context)
{
    dictword *dw; 
    V strcpy((char *)context, data);
    dw = atl_lookup("rawevent");
    atl_exec(dw);
}
#endif

Finally, add the new primitive word to the list of primitives

	  {"0TELLRAWCALLBACK", P_tell_rawevent},

Now, to try it out, save and re-compile with make and then start telldusd and ./atlast. Type the following in the ATLAST console:

127 string raw
: rawevent raw type cr ;
raw tellrawcallback

The word rawevent is executed by the callback function, you can re-define it and it will use the latest definition. With this definition it will just type the incoming data in the console. If you take a Nexa remote control and press a few buttons, the incoming data should print as a string, for example

"class:command;protocol:waveman;model:codeswitch;house:A;unit:1;method:turnoff;"

You can even put the three lines of Forth code in a file, e.g. telldus.atl and start ATLAST like this

./atlast -i./telldus.atl