fischertechnik 30402 – Python im Active Mode

fischertechnik 30402 – Python im Active Mode

2021 habe ich das alte fischertechnik Intelligent Interface 30402 mit Python unter Linux passiv angesteuert. Heute kann ich den active-mode, ohne serielle Verbindung!

Hier der Beitrag aus 2021

Der Rechner musste damals ständig mit dem Interface verbunden bleiben.

Das Python-Programm lief auf dem Rechner und schickte laufend Befehle über RS232 an das Interface.

Genau da habe ich jetzt weitergemacht mein Ziel:

Python soll das Programm erzeugen und auf das 30402 übertragen. Danach wird das serielle Kabel abgezogen und das Interface arbeitet vollkommen selbständig.

Und tatsächlich: es ist soweit.


Das fischertechnik Intelligent Interface 30402 bekommt sein Programm über RS232 – danach arbeitet es vollkommen autonom, der RaspberryPi Zero kann abgenommen werden.


Passive Mode und Active Mode

Bisher hatte ich das Interface im Prinzip im Passive Mode benutzt wie ich hier schonmal beschrieben habe.

Der Rechner gehört also zur laufenden Steuerung. Das 30402 kann aber noch etwas anderes.

Es besitzt einen Active Mode. Dabei wird ein Programm in das Interface geladen und anschließend dort selbständig ausgeführt.

Der Einstieg in den Active Mode

Nach dem Einschalten lässt sich der Active Mode mit einem einzelnen Byte ansprechen:

ser.write(b'\xE0')

Das Interface antwortet ebenfalls:

E0

Ein kleiner Test sieht so aus:

import serial
import time
ser = serial.Serial('/dev/ttyS0', 9600)
time.sleep(0.2)
ser.write(b'\xE0')
ser.flush()
data = ser.read(1)
print("Antwort:", data)
if data:
    print("Hex:", "{:02X}".format(data[0]))

Interessant war dabei eine kleine Gemeinheit, eines meiner Interfaces reagierte zuverlässig, ein anderes zunächst überhaupt nicht.

Der Grund war nicht die Baudrate und auch nicht die Verkabelung. Der zweite Kandidat brauchte nach dem Öffnen der seriellen Schnittstelle einfach einen kurzen Moment, bevor E0 gesendet wird.

Mit rund 200 ms Pause funktioniert auch dieses Interface zuverlässig, außerdem muss vor einem neuen Active-Mode-Download das Interface einmal komplett aus- und wieder eingeschaltet werden.


Ein Programm herunterladen

Mit E0 alleine ist natürlich noch nichts gewonnen.

Der eigentliche Download benutzt ein Blockprotokoll mit Steuerzeichen wie:

05       ENQ
10 02    DLE STX
10 17    DLE ETB
10 03    DLE ETX
04       EOT

Dazu kommt eine 16-Bit-Prüfsumme.

Am Anfang eines Programmdownloads wird logisch unter anderem folgendes übertragen:

01 06 00

Die eigentlichen Programmblöcke beginnen mit:

05

und der Abschluss mit:

03

Darum kümmert sich inzwischen meine Python-Bibliothek.

Ich muss beim eigentlichen Anwenderprogramm nicht mehr mit diesen Bytes hantieren.


Das Interessante im Interface: ATT

Das Interface kann Informationen über die eingebauten Programmroutinen liefern, darin finden sich Namen und Adressen wie beispielsweise:

SMOT   0x401F
SVAR   0x40FC
IVAR   0x439C
CMW    0x4414
EVF    0x487D
FD10   0x4CB9

Das sind Routinen, die das Programm im Interface benutzen kann.

  • SMOT ist beispielsweise für die Motorausgänge interessant.
  • SVAR, IVAR und CMW ermöglichen Variablen, Zähler und Vergleiche.

Damit lässt sich schon erstaunlich viel machen.


Python erzeugt jetzt das Programm

Mein Python-Code steuert das Interface also nicht mehr permanent, stattdessen beschreibe ich in Python den gewünschten Ablauf.

Zum Beispiel:

p = Program()

p.motor(1, Direction.LEFT)
p.motor(2, Direction.LEFT)

Oder eine Abfrage:

p.if_input(
    3,
    then=avoid_right,
    else_=forward,
)

Daraus baut Python intern einen Programmablauf, der Compiler übersetzt diesen anschließend in das Format, das das 30402 versteht und danach wird das Ergebnis über RS232 heruntergeladen.

Die Compilerkette aufs Interface 30402


Aus der Python-Beschreibung entsteht zunächst ein Programmgraph. Erst daraus erzeugt der Compiler die Binärdaten für das 30402.


Was wird eigentlich genau übertragen?

Das wollte ich natürlich irgendwann selbst sehen und kann inzwischen unterschiedliche Darstellungen ausgeben.

Zum Beispiel ein lesbares Listing:

FT30402 PROGRAM LISTING
=======================

N001  PROGRAM START
N002  SMOT   M1 = LEFT
N003  SMOT   M2 = LEFT
N004  IF     E8 = ON
...

Dann die tatsächlich erzeugten Binärblöcke:

COMPILED BLOCKS
===============

Block   1: ...
Block   2: ...
Block   3: ...

Und schließlich die logischen Daten für den Download:

LOGICAL WIRE PAYLOADS
=====================

START      : 01 06 00
PROGRAM   1: 05 ...
PROGRAM   2: 05 ...
END        : 03

Auf Wunsch lässt sich das beim Upload einschalten:

report = Interface30402(PORT).upload(
    p,
    show_listing=True,
    show_hex=True,
    show_wire=True,
)

Damit kann ich inzwischen ziemlich genau verfolgen:


Das alte Fahrzeug fährt jetzt autonom

Als Test habe ich wieder mein kleines Fahrzeug benutzt, mit der gleichen Hardware wie damals. Zwei Motoren für den Radantrieb, Impulszähler und Eingänge für die Bumper. Zusätzlich einen Schiebeschalter für Stromversorgung und den E8 als Start/Pause-Taste.

[Bild 6 – Fahrzeug]


Das Testfahrzeug verwendet zwei Motoren, zwei Impulseingänge für die Wegstrecke sowie zwei Bumper. Der RaspberryPi Zero ist auf dem Fahrzeug montiert

Die Motordefinition sieht bei meinem Aufbau inzwischen so aus:

M1_FORWARD = Direction.LEFT
M1_REVERSE = Direction.RIGHT

M2_FORWARD = Direction.LEFT
M2_REVERSE = Direction.RIGHT

Eine normale Vorwärtsfahrt:

def forward(p):
    p.motor(1, M1_FORWARD)
    p.motor(2, M2_FORWARD)
    return p

Beide Motoren ausschalten:

def stop_all(p):
    p.motor_off(1)
    p.motor_off(2)
    return p

Wegstrecke zählen

Das war bereits beim alten Python-Programm elementar für die Wegstreckenmessung. Ein Nocken betätigt einen Eingang und daraus werden vollständige Impulse gezählt. Damals musste mein Python-Programm selbst feststellen:

Taster gedrückt
Taster wieder losgelassen
= ein Impuls

Jetzt macht das Programm im Interface die Zählung selbst.

Zum Beispiel:

p.count_pulses(
    1,
    pulses,
    variable=COUNTER_VAR,
    debounce_ms=30,
)

Intern werden dafür unter anderem Variablen und Flankenfunktionen des 30402 verwendet.

Damit kann das Fahrzeug beispielsweise zwei Impulse rückwärts fahren:

def backward_for(p, pulses):
    p.motor(1, M1_REVERSE)
    p.motor(2, M2_REVERSE)

    p.count_pulses(
        1,
        pulses,
        variable=COUNTER_VAR,
        debounce_ms=30,
    )

    return p

Das Entscheidende, es läuft kein Python-Code mehr auf dem Raspberry Pi, das passiert alles im Interface.


Ausweichen wie früher

Der Ablauf entspricht weitgehend meinem alten Programm.

Wird links angestoßen:

def avoid_left(p):
    backward_for(p, 2)
    left_reverse_for(p, 3)
    stop_all(p)
    return p

Rechts entsprechend:

def avoid_right(p):
    backward_for(p, 2)
    right_reverse_for(p, 3)
    stop_all(p)
    return p

Danach geht es wieder vorwärts.


Das komplette Ausweichmanöver läuft im 30402 – der Rechner ist nicht mehr beteiligt.


Start erst nach Tastendruck

Eins war bei den ersten Versuchen unpraktisch, nach dem Download begann das Programm sofort zu laufen ohne Unterbruch, und ohne Vorwarnung. Ich musste aber erst das serielle Kabel abziehen und das Fahrzeug auf den Boden stellen.

Deshalb gibt es jetzt eine Startfreigabe über E8, der früher ein Not-Aus war.

def wait_for_start(p):
    stop_all(p)

    p.wait_until_input_off(8)
    p.wait(200)

    p.wait_until_input(8)
    p.wait(200)

    p.wait_until_input_off(8)
    p.wait(200)

    return p

Nach dem Download passiert also zunächst gar nichts.

Dann:

Kabel abziehen
Fahrzeug hinstellen
E8 drücken
E8 loslassen
Fahrzeug fährt

E8 ist inzwischen Pause statt Not-Aus

Anfangs hatte ich E8 als endgültigen Programmstopp benutzt und musste nach jedem Programmstop erneut compilieren und runterladen auf Interface.

def pause_and_resume(p):
    stop_all(p)
    p.wait_until_input_off(8)
    p.wait(200)
    p.wait_until_input(8)
    p.wait(200)
    p.wait_until_input_off(8)
    p.wait(200)
    return p

Der Ablauf:

Damit kann ich das Modell anhalten, umsetzen und anschließend wieder starten.

Kein neuer Compilerlauf.

Kein neuer Download.

Und vor allem:

Kein serielles Kabel am fahrenden Modell.


Fortschrittsanzeige beim Download

Der Download auf das alte Interface ist nicht besonders schnell.

Je nach Programm kann man fast eine Minute davor sitzen und sich fragen, ob überhaupt noch etwas passiert.

Darum gibt es inzwischen eine Fortschrittsanzeige:

ft30402 stable-v1.0m

1/4 Active Mode ... OK
2/4 ROM/ATT lesen ... OK
3/4 Programm kompilieren ... OK
4/4 Programm uebertragen ...

Upload [##############----------------] 47%

So sieht man wenigstens, dass das bald 30 Jahre alte Interface tatsächlich beschäftigt ist.


Der Programmdownload ist nicht besonders schnell. Eine Fortschrittsanzeige macht sichtbar, dass der Transfer noch läuft.


Was funktioniert inzwischen?

Hardwaregetestet sind unter anderem:

  • Active-Mode-Handshake
  • Programmdownload
  • autonomer Betrieb ohne RS232-Kabel
  • Motoren
  • digitale Eingänge
  • E1/E2 als Impulsgeber
  • E3/E4 als Bumper
  • E8 als Start/Pause
  • Variablen
  • Zähler
  • Vergleiche
  • Flankenerkennung
  • EX im Active Mode
  • Endlosschleifen und Verzweigungen


Und EY?

Das ist unerwarteterweise noch eine offene Baustelle. Im Passive Mode kann ich sowohl EX als auch EY problemlos abfragen.

Im Active Mode habe ich die interne Zuordnung von EX bei DW105 gefunden.

Die entsprechende Zuordnung von EY habe ich trotz einiger Versuche bisher nicht gefunden.


Was steckt eigentlich auf der Platine?

Interessant sind besonders diese Bausteine:

TS80C32X2     CPU
M27C512       EPROM
GM76C256      SRAM
MM74HC573     Adress-Latch
MAX323x       RS232-Pegelwandler

Der 80C32 gehört zur 8051/8052-Familie und verwendet externen Programmspeicher.

Daneben sitzt ein M27C512, das sind 64 KiB als EPROM.

Es liegt sehr nahe, dass dort die eigentliche Firmware des Interfaces gespeichert ist.

Zum Beispiel:

SMOT = 0x401F
SVAR = 0x40FC
CMW  = 0x4414

[Bild 8 – Platinenoberseite]


Blick in ein 30402 von 2002. CPU, EPROM, SRAM und RS232-Schaltung sind noch schön diskret aufgebaut.


Kann ein autonomes Programm seriell etwas ausgeben?

Hardwareseitig kann das Interface selbstverständlich senden, das macht es bei jeder Kommunikation mit dem Rechner. Der Prozessor hängt über einen RS232-Pegelwandler direkt an der seriellen Buchse. Was mir bisher fehlt, ist ein bekannter Befehl für ein geladenes Anwenderprogramm nach dem Muster:

PRINT
SEND
TX
PUTC

Falls ich irgendwann einen vollständigen Dump des M27C512 bekomme, kann ich dort nach den UART-Routinen des 8052 suchen.

Interessant sind insbesondere Zugriffe auf:

SCON = 0x98
SBUF = 0x99

Findet sich eine zentrale Sendefunktion und lässt sie sich aus einem geladenen Programm aufrufen, könnte die Python-Bibliothek irgendwann vielleicht so etwas können:

p.serial_byte(0x41)

Das wäre dann ein ziemlich elegantes Upgrade für ein Interface aus den 1990er Jahren.


Originalsoftware

Die ursprüngliche Windows-Software liegt mir derzeit nicht mehr vor und das macht die Sache deutlich umständlicher.

Inzwischen habe ich:

  • das serielle Passive-Protokoll
  • den Active-Mode-Einstieg
  • das Blockprotokoll
  • die Prüfsumme
  • die ATT-Struktur
  • einen Teil der Programmbefehle
  • Variablen und Zähler
  • die Programmlogik
  • einen kleinen Compiler
  • und eine Python-API

Das war der Punkt an dem ich 2021 hängen geblieben bin.

Wie geht es weiter?

Als nächstes stehen ein paar interessante Dinge an:

  • meine anderen 30402-Interfaces vergleichen (3 Stück)
  • unterschiedliche Firmwarestände untersuchen
  • unbedingt die alte Originalsoftware wiederfinden
  • seriellen Originalverkehr mitschneiden
  • vollständigen EPROM-Inhalt sichern
  • 8052-Code disassemblieren
  • UART-Routinen suchen
  • EY im Active Mode finden

Vielleicht liegt die alte LLWin-Diskette oder CD ja noch in irgendeiner Schachtel.


Ein kleiner Nebenschauplatz waren die vielen Versuche und der gleichzeitige Betrieb eines RaspberryPi Zero, versorgt vom Interface.

Klar, das ging.

Kurz.

Dann war der Akku leer.

Der zweite auch.

Erst der Dritte Akku hat eine ganze Weile gehalten. Ich habe dann einen Adapter für meinen Akku-Lader angefertigt und mit mehreren Be- und Entladezyklen die Kapazität geprüft. Die ersten zwei hatten gerademal 10% vom Nennwert. Ich habe mir hier neue 7er-Packs als Replacement für den alten Inhalt bestellt.

An der Doku muss ich noch arbeiten, dann gibt es hier einen Link zum Git.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden.