rob00

In 2012 I wrote about my “first” robot, the “rob01“. But actually it is not true that this was my first autonomous robot. Lately my father found the long lost “rob00″ on his attic. In ~1988 I build a robot at school as part of an entry to “Jungend Forscht“, together with my fried Gustav (he did the mechanics, I did the electronics and coding). Also this robot was what one would call a “hack” today. It was remote controllable from a computer, and it was able to drive autonomous, somewhat :-). Here are some of the details (as I remember them):

Turtle front

Turtle front

Turtle side

Turtle side

Turtle back

Turtle back

The computer used to remote operate was an Atari1040ST, black-and white screen, disk drive only. A kind of IO card was connected to the parallel port which gave 8 digital outputs, and 2 or so inputs. To transmit the commands from the Atari to the robots DC motors, a Graupener Varioprop C4 remote control (27Mhz band) was used. To be able to send the signals from the computer through the remote, the remote was broke open. Then I disconnected the internal potentiometers and connected instead a set of external resistors which then where switched (indirectly by some relays) from the IO card. Thisworked very well :-). Instead of connecting servos at the receiver side, some CD4001BE (CMOS NOR Gates) based converts wher used which switched a relay to turn the motor on/off. The motors (12V DC Mabuchi gear motors) where powered by two totally over-sized 6V/3Ah/s lead acid batteries from “Sonnenschein“.

Sender RC and relay card to RC

Sender RC and relay card to RC

Sender RC

Sender RC

Relay card to fake RC commands

Relay card to fake RC commands

Relay card to fake RC commands, bottom

Relay card to fake RC commands, bottom

Inside the hacked RC

Inside the hacked RC

Now to have an idea how far the robot moved, a back channel was integrated into the robot. Therefore an other 27MHz remote control was broke open (since it is without cover I can not tell what brand/model it was – I guess it was a Graupner too) and mounted on the robots second platform.

Back channel to transmit wheel movement (RC and relay cards)

Back channel to transmit wheel movement (RC and relay cards)

Then with some other relay cards, the signals of two infrared sensors on the wheels (detecting black/white areas on a disk) where transmitted back to the receiver on the computer side.

Receiver for wheel turns

Receiver for wheel turns

Receiver for wheel turns, bottom

Receiver for wheel turns, bottom

Servo to relay card for motor control

Servo to relay card for motor control

The software unfortunately is lost completely. As far as I remember it was a GUI based software, written in GFA Basic. One could draw a room (walls, obstacles etc.) with the mouse. Then draw a path which the robot was supposed to drive. With some luck, the robot did its path as programmed then :-).

Battery packs

Battery packs

Battery packs

Battery packs

Haha, and by the way. The hand drawn turtle on the back of the acrylic case is there to cover a nasty gap in the case because the case was slightly to small. So dammit, after all I still (again) do the same stuff as I did almost 30 years ago …

Verilog Module to Receive 434MHz RC Switch Commands

I added a Verilog receiver module to my FPGA based RC switch. With this module it is possible to receive RC switch commands from a remote, or from an other FPGA running the the Verilog for sending RC commands. For demonstration I made a simple example which uses one of the build in LEDs of the FPGA for each of the channels (A, B, C, D) on the remote. Thus it is possible to turn the four LEDs on/off with the original remote. As an receiver a cheap module like this will do the trick. For the FPGA, I used again the Lattice MachXO2.

FPGA sender and receiver

FPGA sender and receiver

The updated code and more information could be found on github.

Verilog Module to Operate 434MHz RC Switches

Wrote a simple Verilog module to operate a 434MHz RC switch from an FPGA.

Basically this is a personal exercise of mine to get familiar with Verilog HDL. If you interested in a more practical approach on operating a 434MHz switch, see for example this article.

For some theory on the protocol to operate the switches, you could have a look at this article, or read the comment in rcswitch.v.

MachXO2 and RC switch

MachXO2 and RC switch

The current setup (as shown in the picture) connects a cheap 434MHz transmitter to the FPGA (Lattice MachXO2, also cheap) as well as two push buttons. If the red button is pressed, the swich is turned on, if the black button is pressed, it is turned off. In general this is done by sending 1 and 0 to the radio in the right frequency. At the end it looks like shown in the GTKWave screenshot.

GTKWave Output

GTKWave Output

For more detail see the comments in the code found on github.

EDIMAX Smart Plug SP-1101W – Python API Update

In a previous post, I already tried to describe how the scheduling format of the EDIMAX Smart plug is to be encoded. Actually I think what was described there was confusing and not a 100% correct. Thus, here some additions and corrections. (Actually, all is pretty simple, since each minute of the day is represented by a bit. Every character in the schedule string represents four of the bits).

Also the code on github is updated.

General XML Format

When retrieving the schedules from the Plug, the following XML is returned:

<?xml version="1.0" encoding="UTF8"?>
<SMARTPLUG id="edimax">
<CMD id="get">
<SCHEDULE>
<Device.System.Power.Schedule.0 value="ON">F80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.0>
<Device.System.Power.Schedule.1 value="ON">000000000000000FFFFFFFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.1>
<Device.System.Power.Schedule.2 value="OFF">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.2>
<Device.System.Power.Schedule.3 value="OFF">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFF000000000000000000000000000000000000000000000</Device.System.Power.Schedule.3>
<Device.System.Power.Schedule.4 value="OFF">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.4>
<Device.System.Power.Schedule.5 value="OFF">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.5>
<Device.System.Power.Schedule.6 value="OFF">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</Device.System.Power.Schedule.6>
</SCHEDULE>
</CMD>
</SMARTPLUG>

For each day an entry like this is included:

<Device.System.Power.Schedule.X value="ON">...</Device.System.Power.Schedule.X>

X = 0 is Sunday, X = 1 is Monday, X = 6 is Saturday. The “value” attribute is “ON” if the schedule for this day is active or “OFF” if disabled. The data of the “Device.System.Power.Schedule.X” tag is 360 characters long. Each hour is represented by 15 characters, thus 360 characters = 24h. For further discussion we will focus on that 360 characters (let’s call this “packed schedule”.

Packed Schedule Format in Detail

Once again, we have 360 characters representing 24 hours. Thus 15 characters represent 1 hour. This again tells us, that one characters needs to represent 4 minutes to allow us to represent every minute of an hour.

The interesting question now is: how is the 60 minute information encoded (packed) into 15 characters? Well this is very simple. Each character represents four bits (or four minutes). If a bit is set, the minute is set. Then if one transforms each 4-bit value into its corresponding hex value, and if this hex values are joined together, the 15 character string for each hour could be build. By joining together all the 15 characters hour values, we get a whole days schedule.

The above could be best illustrated by example.

To schedule the minute from 00:00 – 00:01 the bits for the first hour would look like this:

1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

Which would be in hex:

8 0 0 0 0 0 0 0 0 0 0 0 0 0 0

And for 00:00 – 00:02:

1100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

Again in hex:

C 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Now 00:00 – 00:03:

1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

In hex:

E 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Then 00:00 – 00:04:

1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

In hex:

F 0 0 0 0 0 0 0 0 0 0 0 0 0 0

The same for the next for bytes with 00:00 – 00:05:

1111 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

In hex:

F 8 0 0 0 0 0 0 0 0 0 0 0 0 0

… And so forth

VoCore, Micropython, more Blinking

Lately I measured the maximum frequency I could get out of the VoCore by toggling a GPIO as fast as possible using various approaches. Today I wrote a simple Kernel module which allows me to set/clear a GPIO through IOCTL. Also I created a FFI binding to IOCTL for micropython and the usual test program for toggling the pin. And, no surprise, this is the fastest I was able to get so far from user space and micropython: 9kHz (remember, pure Kernel implementation was 14kHz).

Thus, the ranking now is:

Code Freq
Shell, sysfs 1.2kHz
Micropython, sysfs 1.6kHz
Python 2.7, sysfs 0.7kHz
C, sysfs 3.2kHz
Micropython, IOCLT, kmod 9.2kHz
Pure kmod 14kHz

The full source code could be found in this gist.

VoCore Maximum Blink Frequency

Today I did some testing on the maximum blink frequency I could get out of the VoCore using the “sysfs“ GPIO interface. I used four different candidates:

  • A shell script
  • A Python 2.7 program, basically the one described here (gist)
  • A Micropython program, more or less the same as the above, but using native libc file IO trough FFI as described here (gist)
  • And finally a C program

To summarize up the code used here the relevant snippets:

Shell Test Code

while true                           
do                                   
  echo 1 > /sys/class/gpio/gpio12/value
  echo 0 > /sys/class/gpio/gpio12/value
done

Python/Micropython Test Code

pin = DigitalIO.get_output(GPIO12)

while True:
  pin.set()
  pin.clear()

Full source:

C Test Code

#include <sys/types.h<
#include <sys/stat.h<
#include <fcntl.h<

#define SYSFS_ENTRY     "/sys/class/gpio/gpio22/value"

static int f;

void gpio_init()
{
        f = open(SYSFS_ENTRY, O_WRONLY | O_SYNC);
}

void gpio_term()
{
        close(f);
}

void gpio_set(void)
{
        write(f, "1", 1);
}

void gpio_clear(void)
{
        write(f, "0", 1);
}

int main()
{
        gpio_init();

        for(;;)
        {
                gpio_set();
                gpio_clear();
        }

        gpio_term();

        return 0;
}

Results

The results measured through OpenBench Logic Sniffer:

Code Freq
Shell 1.2kHz
Micropython 1.6kHz
Python 2.7 0.7kHz
C 13kHz

Addition: I also tried the same from within a Kernel module. By doing so, I was able to get about 14kHz.

The clear winner is C (note, that it is essential for the performance to NOT open/close the FD each time, but keep it open), followed by Micropython and the shell script. At the end is Python with very poor performance (which I could not explain up to now).

VoCore, Micropython and the SYSFS

As I wrote a while ago I was not able to access the Kernels SYSFS through the build-in “open/read/write” methods from micropython. I thought this might be because of the old version of micropython which comes from the OpenWrt binary repository, but even rebuilding OpenWrt did’t change anything.

Then I realized, there is FFI (foreigen function interface) support build into micropython. This allowed me to wrap the libc’s open/read/write operations directly into micropython. And finally, when using the wrapped libc methods, I am able to blink a LED from micropython on the VoCore. The source for this looks like so:

Simple Snap-In Case for Hacklace2 v2.0

I refined the previous case a little to have less height and rounded corners. Looks much lighter now. The new design could be found on thingiverse as usual.

Hacklace2 Snap-In Case v2.0

Hacklace2 Snap-In Case v2.0

Hacklace2 Snap-In Case v2.0

Hacklace2 Snap-In Case v2.0

Hacklace2 Snap-In Case v2.0

Hacklace2 Snap-In Case v2.0

VoCore Arrived

Today my VoCore modules arrived from China (they are amazingly tiny). So time for a first LED blink right? Ok, I wanted to get things working quickly so I decided to go with the pre-installed OpenWrt and Python + sysfs. Unfortunately there is not Python on the device, and also the right package sources are missing. However, this could be easily fixed.

VoCore Top

VoCore Top

VoCore Size

VoCore Size

VoCore Bottom

VoCore Bottom

VoCore compared to ESP8266

VoCore compared to ESP8266

1) Add additional package source to /etc/opkg.conf

src/gz cheese_packages http://downloads.openwrt.org/snapshots/trunk/ramips/packages/packages

2) Update package index

opkg update

3) Install Python

Unfortunately the Python package has a unresolved dependency (to libcrypt). Thus, we need to fore the install:

opkg install --force-depends python

Now we are able to use this simple script (since the sysfs GPIO support is already enabled on the VoCore):

VoCore 1st Blink

VoCore 1st Blink


Note on Micropython

I was very happy to see that micropython is available as a package from the OpenWrt repository. So this was my first try. Unfortunately, mircropython was not able to read/write the sysfs pseudo-files, and thus I had to revert to “real” Python.

Simple Snap-In Case for Hacklace2

Just created a simple snap-in case for the Hacklace2. Two versions are provided: One which houses a Hacklace2 with soldered on headers, one which houses a Hacklace2 without headers. The OpenSCAD sources and printable STL files could be found on thingiverse.

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case

Hacklace2 Case