<link rel="stylesheet" id="astra-theme-css-css" href="https://pmo-it.de/wp-content/themes/astra/assets/css/minified/style.min.css?ver=3.6.9" media="all">

Erinnerungsassistent für Medikamenteinnahme mit Sprachausgabe

Erinnerungsassistent an Medikamenteinnahme

Motivation

Bevor Corona began, besuchten Ich und meine Familie meine Großeltern regelmäßig. Dabei erinnerten wir sie auch daran, ihre Medikamente zu bestimmten Uhrzeiten einzunehmen. Durch die Kontaktbeschränkung fiel dieser Umstand gänzlich weg. Da beide kein Smartphone oder Vergleichbares besitzen, erschien mir das nächstbessere ein Erinnerungsprogramm mit Sprachausgabe zu entwerfen.
Dieser Assistent ist voll konfigurierbar und hat zwei Stimmen – eine männlich, eine weiblich- um es dem Betroffenen einfacher zu gestalten.
Jeden Tag zur vorgegebenen Uhrzeit spricht der Assistent seine vorgegebene Phrase. Mit einem Service-Script ist der Assistent auf einem Raspberry Pi 3 komfortable lauffähig und startet nach dem Bootvorgang automatisch. Somit ist sichergestellt, das selbst nach einem Abschaltvorgang sich alles automatisch wieder einrichtet. Zum Einsatz kommt die MaryTTS Plattform.

Benötigte Komponenten

  1. Raspberry Pi
  2. Java ab Version 8
  3. Boxen

Maven

Um MaryTTS zu nutzen, benötigen wir sowohl die basis Bibliotheken als auch die passenden Stimmen. Stimmen gibt es zahlreiche, für viele Sprachen in jeweils männlich und weiblich und auch mit jeweils unterschiedlichen Klängen.

Basisbibliotheken

<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-runtime</artifactId>
	<version>5.2</version>
</dependency>
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-common</artifactId>
	<version>5.2</version>
</dependency>
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>marytts-signalproc</artifactId>
	<version>5.2</version>
</dependency>

Stimmen

<!-- Weiblich deutsch -->
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>voice-bits1-hsmm</artifactId>
	<version>5.2</version>
</dependency>
<!-- Männlich deutsch -->
<dependency>
	<groupId>de.dfki.mary</groupId>
	<artifactId>voice-bits3-hsmm</artifactId>
	<version>5.2</version>
</dependency>

Abhängig von der verwendeten Sprache, muss über Maven die zugehörige Bibliothek hinzugefügt werden, erst dann ist diese auch verwendbar. Mehr Informationen dazu finden sich auf der offiziellen Webseite von Mary: http://mary.dfki.de.

Konfiguration

Die Konfiguration erfolgt über eine simple CSV-Datei. Die data.csv liegt standardmäßig im resources– Ordner.

Hour,Minute,Second,Repeatrate,Phrase,Voice
23,20,00,10000,Oma Aspirin einnehmen,bits3-hsmm
21,56,00,15000,Opa Vitamine einnehmen,bits1-hsmm

Die Überschriften der ersten Zeile sind für das Programm nicht relevant, da sie nur der Erklärung dienen.

Repeatrat
Die Zeit in Millisekunden, wann die Phrase wiederholt wird.

Phrase
Die ausgesprochene Nachricht des Assistenten

Voice
Die zu verwendende Stimme für diese Zeile.

Der Code

Das Programm besteht im wesentlichen aus zwei Komponenten: Die Sprachausgabe und dem Intervalltimer.
 

Sprachausgabe

Die Sprachausgabe wiederum besteht aus zwei Klassen: TextToSpeech und AudioPlayer.
Während in der TextToSpeech Class die Konfiguration des AudioPlayers stattfindet, handelt dieser die Sprachausgabe.
Begonnen wird mit dem Laden des MaryInterfaces.
 
marytts = new LocalMaryInterface();

Im nächsten Schritt wird ein Audiostream aus der vorgegeben Phrase generiert.

AudioInputStream ais = marytts.generateAudio(phrase);

Dieser generierte AudioInputStream wird nun an den erstellen AudioPlayer übertragen. Der Audioplayer läuft in einem eigenen Thread, somit wird ein laufendes Programm nie durch das Sprechen unterbrochen.

In der run-Methode des Audioplayers wird dieser Stream auf ein Stereo Ausgabeformat festgelegt, Format-Metainformationen ausgelesen und entsprechend in ein ByteArray eingelesen. Zum Abschluss empfängt die Javax-Sound Bibliothek das ByteArray und sendet es an das Tonausgabegerät.

@Override
public void run() {
    ais = new StereoAudioInputStream(ais, OutputFormat.STEREO.getValue());
    AudioFormat audioFormat = ais.getFormat();
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

    try {
        if (sdLine == null) {
            sdLine = ( SourceDataLine ) AudioSystem.getLine(info);
        }
        sdLine.open(audioFormat);
        sdLine.start();

        writeDataToLine();
        if ( ! stopThread) {
            sdLine.drain();
        }
    } catch (Exception e) {
       //Handle Exception
        return;
    } finally {
        sdLine.close();
    }
}

private void writeDataToLine() throws IOException {
    int readLine = 0;
    byte[] data = new byte[65532];
    while ( ( readLine != - 1 ) && ( ! stopThread )) {
        readLine = ais.read(data, 0, data.length);
        if (readLine >= 0) {
            sdLine.write(data, 0, readLine);
        }
    }
}

Intervalltimer 

Der Intervalltimer besteht aus der Klasse ReminderTask und dem zugehörigen Datenobjekt Reminder.
Reminder nimmt die CSV Einträge entgegen und wandelt diese in ein Calendarobjekt um.
In der Klasse ReminderTask wird der Intervalltimer vorbereitet, sie nimmt die Text-to-Speech Referenz entgegen und sorgt dafür das eine Sprachausgabe angesteuert wird.
public ReminderTask(TextToSpeech tts, String speakResult) {
    this.tts = tts;
    this.speakResult = speakResult;
}
    
@Override
public void run() {
    tts.speak(speakResult);
}

Main

In der Mainmethode läuft alles zusammen, sie baut die Objekte zusammen, liest die CSV-Datei aus und startet schließlich den Intervalltimer.
public class Main {
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File currentDataFilePath = CsvFile.getCurrentDataFilePath();
        Reader fileReader = new InputStreamReader(new FileInputStream(currentDataFilePath), "ISO-8859-1");
        CsvReader csvReader = new CsvReader();

        ArrayList<Reminder> reminders = csvReader.readCsvData(fileReader);
        Timer timer = new Timer();
        for (Reminder reminder : reminders) {
            TextToSpeech tts = new TextToSpeech();
            tts.setVoice(reminder.voice);
            timer.scheduleAtFixedRate(new ReminderTask(tts, reminder.phrase), reminder.firstExecutionTime,
                reminder.repeatrate);
        }
    }
}

Raspberry Pi Service-Startscript

Um das Programm auf einem Raspberry zu starten, muss es zunächst in eine startbare .jar-Datei exportiert werden. Anschließend ist auf dem Raspberry die Csv-Datei zu erstellen. Dafür eignet sich z.B. der Nano-Editor.
Das Programm verwendet standardmäßig den user.dir- Pfad. Auf dem Pi ist dies nicht automatisch der pi-user home folder.
[Unit]
Description=MedicationReminder service mit systemd

[Service]
ExecStart=/usr/bin/java -jar /home/pi/reminder/MedicationReminder.jar
Restart=always

[Install]
WantedBy=multi-user.target

Starten des Service

Startscript kopieren:

sudo cp /home/pi/reminder/MedicationReminder.service /etc/systemd/system/MedicationReminder.service

Script beim Booten starten

sudo systemctl enable MedicationReminder

Fazit

Durch die flexible Konfiguration können auch allgemeine Erinnerungen einprogrammiert werden. Mit einem Raspberry nimmt der ganze Assistent auch kaum Platz in Anspruch und ist eine kostengünstige Anschaffung ohne unnötig komplizierte Funktionen. Da die Konfigurationsdatei bequem über SSH bearbeitet werden kann, ist eine Fernwartung auch problemlos möglich. Darüber hinaus  ist eine Onlineanbindung auch nicht nötig und ermöglicht höchste Sicherheit.

Das gesamte Projekt ist auf Github zu finden.