Ich würde das was Du vor hast mit einem Timer Interrupt lösen.
Ein Timer wird so programmiert, das er in festen Zeitintervallen einen Interrupt auslöst.

In diesem Interrupt werden dann ( im Prinzip beliebig viele ) Variablen heruntergezählt, bis sie den Wert 0 erreicht haben.

Im Hauptprogramm werden dann die entsprechenden Relais eingeschaltet und die Werte für die Variablen gesetzt.
Ebenfalls im Hauptprogramm wird das entsprechende Relais ausgeschaltet wenn die dazugehörige Variable den Wert 0 erreicht hat.

Das ist im Prinzip auch easy zu proggen.
Dein Mehrfachdruck ist auch einfach zu realisieren indem man z.B. bei 3 Tastendrücken zu der entsprechenden Variable einfach den gewünschten Wert dreifach addiert.
Bei den Variablen musst Du aufpassen. Eine 8Bit Variable geht maximal bis 255, eine 16Bit bis 65536 usw.
Wird der Wertebereich überschritten fängt die Variable wieder bei 0 an. Das ist gerade bei deinen Mehrfachdrücken gefährlich!

Tastendrücke zu erkennen ist schwieriger, da normale Tasten ( auch Relaiskontakte ) prellen - D.h. bei einem Druck mehrfach öffnen und schließen.
Das muss entweder in der Software, oder der Hardware berücksichtigt werden. Stichwort: Debouncing.

Ich mach das immer so, das ein Taster bei einer Änderung seines Zustandes nach 2, 5, 10ms ( Je nachdem wie "schlecht" der Schalter ist ) nochmals abgefragt wird.
Bestätigt sich die Änderung wird der Tastendruck akzeptiert.
Für die Zeitspanne verwende Ich ebenfalls einen Timer mit Interrupt der aber sehr viel schneller läuft, als der für die Relais - Dauern.

Damit hast Du eigentlich schon alles Rüstzeug für dein Projekt.