RaspiDeck

Deck with RaspberryPi4 and Windows

Dopo aver installato Windows 11 su un Raspberry Pi4 e montato un display da 7 pollici ho deciso di costruire un deck.

Per chi non lo sapesse un deck è un dispositivo connesso al Pc che consente di semplificarci la vita. A seconda della tipologia, un deck, può essere più o meno (utile e) potente.

Nel caso più semplice un deck ha una serie di pulsanti che se premuti eseguono delle azioni sul pc al quale è connesso.

Esempi di azioni sono:

  • Avvia un applicativo
  • Invia delle scorciatoie da tastiera ad una determinata applicazione

Alcuni non hanno dei pulsanti fisici ma un display touch con dei tasti configurabili anche nell'aspetto e in base all'applicazione che si sta utilizzando, altri hanno oltre al display anche dei tasti fisici e dei potenziometri (per regolare il volume o lo zoom).

Alcuni deck hanno un'ottima reputazione tra gli sviluppatori perchè mettono a disposizione anche diversi SDK per i linguaggio più o meno diffusi.

In questo articolo descriverò invece la strada più difficile ma almeno per me più divertente.

Principio di funzionamento

Il server è un'applicazione WPF .NET6 che gira sul pc desktop (Windows 11).

Il client è un'applicazione WPF .NET 6 che gira su un Raspberry Pi4 con Windows 11 ARM.

Compito delll'applicazione server è:

  • stare in ascolto fino a quando non si connette un client
  • gestire le richieste del client che possono essere di più tipi

Compito del client è visualizzare dei tasti (con testo, descrizione e icona) che se premuti inviano un comando al server.

 

Client e server comunicano tra di loro tramite due socket (si scambiano degli oggetti serializzati). I socket sono due perchè la comunicazione deve essere bidirezionale.

In pratica quando il client si connette istanzia anch'esso un server, quando si connette al server invia il suo IP. Il server istanzia anche lui un client e lo connette all'IP inviatogli dal client.  Così facendo sia il server che il client possono "prendere l'iniziativa".

 

Il server invia al client solo l'Id del comando da eseguire. Il server lo cerca tra i comandi inseriti e lo intepreta.

Ho implementato du tipi di comandi.

Il primo è l'esecuzione di un applicativo:

 

Il secondo è l'invio di una combinazione di tasti ad una determinata applicazione.

 

Process.Start(new ProcessStartInfo
{
    UseShellExecute = true,
    FileName = command.CommandContent,
});

Il secondo è l'invio di una combinazione di tasti ad una determinata applicazione.

[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr point);

[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, IntPtr dwExtraInfo);
		
Process p = Process.GetProcesses().SingleOrDefault(x => x.MainWindowTitle == applicationName);

int k = KeyInterop.VirtualKeyFromKey(s1);
keybd_event((byte)k, 0, 0, IntPtr.Zero);
				
Oppure:
int k1 = KeyInterop.VirtualKeyFromKey(s1);
int k2 = KeyInterop.VirtualKeyFromKey(s2.Value);
keybd_event((byte)k1, 1, 0, IntPtr.Zero);
keybd_event((byte)k2, 0, 0, IntPtr.Zero);
keybd_event((byte)k1, 0, 2, IntPtr.Zero);

Ecco il RaspiDeck in azione: