Archive for the ‘Programmierung’ Category.

ESP32 unter Windows programmieren

Ein ESP32 ist ein sehr preiswerter Microcontroller, der aber einiges an Leistung „unter der Haube“ hat:

Wie kann man ihn unter Windows programmieren? Ich mache es wie folgt:

  1. Arduino IDE von https://www.arduino.cc/ installieren
  2. In den Voreinstellungen unter „Zusätzliche Boardverwalter-URLs“ die folgende URL hinzufügen: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. Boardverwalter-URL

  4. Unter Boardverwalter nach „ESP“ suchen und das Paket installieren
  5. ESP

  6. Eigenen Code oder Demo-Code schreiben/kompilieren und auf den ESP hochladen. Bei mir hat es mit den folgenden Einstellungen geklappt, zusätzlich musste ich aber beim Upload (nach dem Drücken des Upload-Knopfs) einmal den Boot-Button auf dem ESP drücken.
  7. Upload-Settings

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Siemens Nixdorf VFD Displays ansteuern mit Python

Vor einigen Monaten habe ich mir günstig auf Ebay ein Siemens Nixdorf BA 63 USB Display gekauft, das VFD (vacuum fluorescent display) Technologie für die Anzeige nutzt und 2×20 Zeichen bietet.

Als passendste Python-Bibliothek nutze ich https://github.com/stephanemouton/VFD-WCN, die Anleitung zur Einrichtung wird im github sehr gut beschrieben.

Hier der Quellcode, der die Ausgabe im Bild erzeugt:

from vfdpos import *
factory=WincorNixdorfDisplayFactory()
VFDs = factory.get_vfd_pos()
MyVFD = VFDs[0]

MyVFD.clearscreen()

MyVFD.poscur(1, 1)
MyVFD.write_msg("Hallo")
MyVFD.write_msg("Welt")
MyVFD.write_msg("1234567abcd\r")
MyVFD.poscur(2, 1)
MyVFD.write_msg("ABCDEFüäößi")

MyVFD.close()

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Fortnite teilweise blockieren – Erweiterung um Status

This entry is part 2 of 2 in the series Pi-Hole

Im ersten Artikel zu diesem Thema hatte ich schon beschrieben, wie man mit Pi-Hole DNS Lookups so unterbinden kann, dass man Fortnite oder andere Zeitfresser temporär blockieren kann. In diesem Teil erweitern wir das Skript um eine Statusabfrage. Dazu fügen wir einfach eine Route hinzu, die die URL mit dem Parameter /status abfragt. Dann wird der Inhalt der epicstatus.txt abgefragt, die von den Routen /on und /off mit dem Zeitstempel versehen wurde.

import os
from datetime import datetime
from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return('<h1>Use /off and /on to enable/disable blocking, /status to get epic.log</h1>')
 
@app.route('/<param>')
def setter(param):
    if param=='status':
        with open('/home/pi/epicstatus.txt') as f:
            content = f.read()
            return '<h2>' + content + '</h2>'
    if param=='off':
        os.system("/usr/local/bin/pihole regex '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now + ' off')
        return '<h1>Turning off Fortnite</h1>'
    elif param=='on':
        os.system("/usr/local/bin/pihole regex -d '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now+ ' on')
        return '<h1>Turning on Fortnite</h1>'

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Liste meiner genutzten VSC-Plugins

Neben UltraEdit, Notepad++, Emacs, etc. nutze ich auch Visual Studio Code. Hier die Liste der wichtigsten von mir genutzten Plugins:

  • German Language Pack for Visual Studio Code für deutsche Menüs
  • Render Line Endings, um zwischen LF und CRLF umzuschalten
  • Python für die Python-Unterstützung
  • LaTeX Workshop
  • Graphviz (dot) language support for Visual Studio Code für gv
  • Graphviz Preview für ebendas
  • Encryptor for VS Code, Text mit AES 256 zu verschlüsseln
  • Bracket Pair Colorizer um zueinandergehörige Klammern zu markieren

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Dateien kopieren, zippen und löschen mit Python

Hier ein kurzes Beispiel, wie man mit Python-Modulen Dateien kopieren, zippen und löschen kann.

import zipfile
from shutil import copyfile
from os import unlink
 
# copy file
copyfile('dtk-authoryear.bbx'  , './dtk-bibliography/dtk-authoryear.bbx')
copyfile('dtk-authoryear.dbx'  , './dtk-bibliography/dtk-authoryear.dbx')
copyfile('dtk-bibliography.pdf', './dtk-bibliography/dtk-bibliography.pdf')
copyfile('dtk-bibliography.tex', './dtk-bibliography/dtk-bibliography.tex')
 
# create the zip file
with zipfile.ZipFile('dtk-bibliography.zip', 'w', zipfile.ZIP_DEFLATED) as z:
    z.write('./dtk-bibliography/README.md')
    z.write('./dtk-bibliography/dtk-authoryear.bbx')
    z.write('./dtk-bibliography/dtk-authoryear.dbx')
    z.write('./dtk-bibliography/dtk-bibliography.pdf')
    z.write('./dtk-bibliography/dtk-bibliography.tex')
 
# delete copied files
unlink('./dtk-bibliography/dtk-authoryear.bbx')
unlink('./dtk-bibliography/dtk-authoryear.dbx')
unlink('./dtk-bibliography/dtk-bibliography.pdf')
unlink('./dtk-bibliography/dtk-bibliography.tex')

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Mailman Spammer mit Python blocken

Für Dante e.V. betreue ich einige E-Mail-Listen auf mailman-Basis, die seit einigen Tagen von Spammern geflutet werden. Jeden Tag sind dutzende bis hunderte Aufnahme-Requests in der Liste, die ich manuell wegwerfen müsste. Nachdem ich dies einmal händisch getan hatte, musste eine automatische Lösung gefunden werden.

Die Lösung bestand darin, einen Treiber für Firefox („geckodriver“) zu installieren, der das Fernsteuern des Browsers erlaubt. Dann kann mittels selenium Modul die Steuerung aus Python heraus erfolgen. Unten der wesentliche Quellcode als Basis für eigene Arbeiten, den Teil zum Erkennen von legitimen Anfragen habe ich weggelassen.

Nachtrag vom 25.10.2020: Siehe auch die Erweiterung unter https://www.uweziegenhagen.de/?p=4420

# -*- coding: utf-8 -*-
"""
https://www.edureka.co/community/47679/is-it-possible-to-run-headless-browser-using-python-selenium
"""
 
from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
 
opts = Options()
#opts.set_headless() # Ich will sehen, wie selenium arbeitet
#assert opts.headless  # Operating in headless mode
browser = Firefox(executable_path=r"C:\Users\Uwe\Downloads\geckodriver-v0.27.0-win64\geckodriver.exe", options=opts)
browser.implicitly_wait(3)
 
# einloggen
browser.get('<url des mailman admin panels')
search_form = browser.find_element_by_name('<passwortfeld_ID>')
search_form.send_keys('<adminpasswort>')
search_form.submit()
 
#wegwerfen Button pro Zeile
fields = browser.find_elements_by_xpath("//input[@value='3']")
#email Adresse des Spammers
emails = browser.find_elements_by_xpath('//td[contains(text(),"@")]')
 
if len(fields) == len(emails):
    zipped_list = list(zip(emails, fields))
 
    for i in zipped_list:
        email, field = i
        field.click()

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Fehlerhafte SQLite Updates abfangen

Bei einem kleinen Projekt, an dem ich arbeite, fiel mir auf, dass fehlerhafte — d.h. syntaktisch richtige, aber inhaltlich falsche — SQLite Updates im Nirvana verschwinden. Danke Google und Stackexchange war die Lösung wie folgt: fehlerhafte SQL-Updates erzeugen keine Exception, hier muss man nach dem Update den rowcount prüfen, um die Anzahl der aktualisierten Zeilen zu checken. Das folgende Programm illustriert das ganze:

import sqlite3
 
conn = sqlite3.connect(':memory:')
conn.execute('create table if not exists mytable(id INTEGER PRIMARY KEY \
             AUTOINCREMENT, status TEXT, prio TEXT)')
 
today = '2020-08-06'
cursor = conn.cursor()
task = "Foobar"
 
cursor.execute('insert into mytable(status, prio) values (?, ?)',
               [task, today])
conn.commit()
 
my_id = 1 # For my_id = 1 it works, for any other number rowcount is 0
prio = 'A'
cursor = conn.cursor()
cursor.execute('UPDATE mytable SET prio = ? WHERE id = ?', [prio, my_id])
conn.commit()
print(cursor.rowcount)
 
try:
    cursor = conn.cursor()
    cursor.execute('select * from mytable')
    results = cursor.fetchall()
    for entry in results:
        print(entry)
except Exception as err:
    print('Listing failed: %s\nError: %s' % str(err))
 
finally:
    cursor.close()

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Fortnite zeitweise blockieren

This entry is part 1 of 2 in the series Pi-Hole

Ich wurde von Freunden gebeten, eine Lösung für das temporäre Blockieren von Fortnite zu geben, um ihren Kindern a) die Möglichkeit zum Spielen zu geben, aber b) auch eine zeitliche Vorgabe wirksam umsetzen zu können.

Hier möchte ich kurz beschreiben, wie ich das umgesetzt habe. Hinweis: Leider eignet sich das Skript (noch) nicht dazu, eine Spielbeschränkung hart durchzusetzen. Das Betriebssystem cached DNS-Lookups, so dass auch nach dem Ende der Spielzeit, wenn die URL wieder auf der Blacklist steht, keine Spielunterbrechung stattfindet.

Schritt 1: Ein Pi-Hole auf einem Raspberry Pi 4 wird als DNS Server im Netzwerk eingerichtet. Der Raspi 4 war bereits vorhanden, alternativ hätte es auch ein älteres Modell getan.

Zur Installation von Pi-Hole siehe beispielsweise https://www.youtube.com/watch?v=ubzd2H1wZxE oder https://www.heise.de/tipps-tricks/Pi-Hole-auf-dem-Raspberry-Pi-einrichten-so-geht-s-4358553.html.

Schritt 2: Eine Web-Anwendung mit Flask bauen. Flask ist ein Python-Framework für Web-Applikationen, mit dem man recht schnell zu guten Ergebnissen kommt. Wir legen die folgende Datei server.py an:

import os
from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return('<h1>Use /on and /off to enable/disable blocking</h1>')
 
@app.route('/<status>')
def setter(status):
    if status=='off':
        os.system("/usr/local/bin/pihole regex '.*\.epicgames.com' > /home/pi/epic.log")
        return '<h1>Turning off Fortnite</h1>'
    elif status=='on':
        os.system("/usr/local/bin/pihole regex -d '.*\.epicgames.com' >/home/pi/epic.log")
        return '<h1>Turning on Fortnite</h1>'

Je nachdem, ob diese server.py mit dem Parameter /on oder /off aufgerufen wird, wird epicgames geblockt oder nicht. Den entsprechenden Aufruf von pihole habe ich unter https://www.reddit.com/r/pihole/comments/a51wjr/blocking_fortnite_monday_to_friday/ gefunden.

Schritt 3: Setzen der Umgebungsvariablen für die Flask-App mittels export FLASK_APP=server.py. Optional kann man noch export FLASK_DEBUG=1 für das Debugging nutzen.

Schritt 4: Starten der Server.py mittels nohup flask run --host=192.168.0.91 & Dieser Aufruf sorgt dafür, dass auch nach dem Beenden der Shell das Programm weiterläuft. Als host muss man natürlich die IP-Adresse des Raspi nutzen.

Unter 192.168.0.91/on bzw. 192.168.0.91/off kann man jetzt die Blockierung an- bzw. ausschalten, diese URLs lassen sich auch gut auf dem Mobiltelefon wie eine App ablegen.

Verbesserungspotential besteht noch:

  • Nach dem Neustart des Raspi muss auch das Skript neu gestartet werden, das sollte man automatisieren. (Hinweis: Ich habe versucht, https://blog.miguelgrinberg.com/post/running-a-flask-application-as-a-service-with-systemd zu folgen, klappt aber noch nicht.)
  • Es kann sein, dass Pi-Hole auch ein Caching vornimmt und das Blockieren nicht sofort greift.
  • Flask läuft hier in einem DEV-Modus, für „Produktionsumgebungen“ empfiehlt sich ein WSGI-Server
  • Ein Aktivieren für einen bestimmten Zeitraum mit folgender automatischer Blockierung ist noch nicht implementiert, mit atd und cron sollte das aber möglich sein.

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Normalisierung von Datenbanktabellen – Die 3. Normalform

This entry is part 3 of 3 in the series Data Warehouse, 3NF und Dimensional Modelling

In diesem Artikel geht es um die 3. Normalform. 3. Normalform bedeutet, dass die Daten

  • in der 2. Normalform sind und
  • kein Nichtschlüsselattribut transitiv von einem Kandidatenschlüssel abhängt.

Diese transitive Abhängigkeit erklärt man am besten an einem Beispiel:

CD

CD-ID Interpret Album Erscheinungsjahr Geburtsjahr
1234 Gabi Mustermann Gabi singt 2001 1963
2345 Max Mustermann Debütalbum 2001 1960
  • Aus der CD-ID folgt der Interpret oder die Interpretin
  • Aus dem Interpreten können wir nicht auf die CD schließen, denn jeder Interpret oder jede Interpretin kann mehr als eine CD veröffentlichen.
  • Aus dem Interpreten folgt aber das Geburtsjahr

Damit hängt das Geburtsjahr (ein Nichtschlüsselattribut) transitiv von der CD-ID (dem Schlüssel/Schlüsselkandidaten) ab. Transitiv bedeutet formell ausgedrückt: „Eine zweistellige Relation R heißt transitiv, wenn aus a R b und b R c stets a R c folgt.“

Wie löst man die transitive Abhängigkeit auf? Indem man weitere Tabellen erstellt. In unserem Beispiel erstellen wir eine Interpreten-Tabelle und verweisen in der CD-Tabelle nur noch auf den entsprechenden Schlüssel.

Interpret

Interpret-ID Name Geburtsjahr
1 Gabi Mustermann 1963
2 Max Muster 1960

CD

CD-ID Interpret-ID Album Erscheinungsjahr
1234 1 Gabi singt 2001
2345 3 Debütalbum 2001

Titel

CD-ID Tracknummer Titel
1234 1 Gabi singt laut
1234 2 Gabi singt leise
1234 3 Gabi singt weiter
2345 1 Von der Liebe
2345 2 Vom Leben
2345 3 Vom Ableben
2345 4 Duett mit Gabi

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Normalisierung von Datenbanktabellen – Die 2. Normalform

This entry is part 2 of 3 in the series Data Warehouse, 3NF und Dimensional Modelling

Im ersten Artikel dieser Reihe hatten wir uns die 1. Normalform angeschaut, in diesem Artikel soll es um die 2. Normalform gehen.

Die Wikipedia sagt zur 2. Normalform: „Eine Relation ist genau dann in der zweiten Normalform, wenn die erste Normalform vorliegt und kein Nichtprimärattribut (Attribut, das nicht Teil eines Schlüsselkandidaten ist) funktional von einer echten Teilmenge eines Schlüsselkandidaten abhängt.“

Das hört sich etwas sperrig an, am besten betrachten wir unser Beispiel, das wir in die 1. Normalform gebracht hatten, die Schlüsselspalten seien CD ID und Tracknummer.

CD-ID Interpret Album Erscheinungsjahr Geburtsjahr Tracknummer Titel
1234 Gabi Mustermann Gabi singt 2000 1963 1 Gabi singt laut
1234 Gabi Mustermann Gabi singt 2000 1963 2 Gabi singt leise
1234 Gabi Mustermann Gabi singt 2000 1963 3 Gabi singt weiter
2345 Max Mustermann Debütalbum 2001 1960 1 Von der Liebe
2345 Max Mustermann Debütalbum 2001 1960 2 Vom Leben
2345 Max Mustermann Debütalbum 2001 1960 3 Vom Ableben
2345 Max Mustermann Debütalbum 2001 1960 4 Duett mit Gabi

Wir erkennen, dass viele Informationen redundant sind, was leicht zu Dateninkonsistenzen führen kann. Ändern wir beispielsweise in einer Zeile den Albumtitel und nur den, so haben wir zwei unterschiedliche Albentitel für ein und das selbe Album. Außerdem hängen Albumtitel, Interpret und Erscheinungsjahr nur vom Schlüssel CD-ID ab, nicht von der Track-ID.

Man löst diese Probleme und Redundanzen auf, indem man die Daten auf zwei Tabellen aufteilt, CD und Titel.

CD

CD-ID Interpret Album Erscheinungsjahr Geburtsjahr
1234 Gabi Mustermann Gabi singt 2001 1963
2345 Max Mustermann Debütalbum 2001 1960

Titel

CD-ID Tracknummer Titel
1234 1 Gabi singt laut
1234 2 Gabi singt leise
1234 3 Gabi singt weiter
2345 1 Von der Liebe
2345 2 Vom Leben
2345 3 Vom Ableben
2345 4 Duett mit Gabi

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website