Free Pascal

Un banco di test per sensori con Lazarus e la LCL

calendar_today personTeam EGSOFT schedule4 min di lettura
info

Disclaimer: le informazioni pubblicate in questa sezione hanno finalità di pura divulgazione tecnica. EG Software S.r.l. non si assume alcuna responsabilità per un utilizzo improprio dei contenuti, né per eventuali danni diretti o indiretti derivanti dalla loro applicazione, e non garantisce l'aggiornamento, l'accuratezza o la completezza delle informazioni riportate. Prima di utilizzare in produzione codice o procedure qui descritte, verificane sempre l'adeguatezza al proprio contesto.

Quando si sviluppa un'applicazione destinata a dialogare con hardware reale — un sensore collegato a un Raspberry Pi, un modulo GPIO, una scheda di acquisizione analogica — è utile poter verificare la logica applicativa su un normale PC, prima ancora di avere il dispositivo sotto mano. Lazarus, con la sua libreria di componenti LCL (Lazarus Component Library), si presta bene a questo: in pochi minuti si costruisce un pannello di controllo multipiattaforma che simula il comportamento dei sensori, riutilizzabile poi con il driver vero.

Bottoni, timer e memo: lo scheletro dell'interfaccia

Per un banco di test tipico bastano pochi componenti, tutti dichiarati a design time nel file .lfm della form e collegati ai rispettivi gestori di evento: alcuni TButton per avviare la simulazione dei diversi sensori, un TMemo per il log testuale e un TTimer che scandisce il ciclo di lettura periodica.

type
  { TMainForm }
  TMainForm = class(TForm)
    btnRainSensor: TButton;
    btnFireDetector: TButton;
    memoOutput: TMemo;
    tmrSensor: TTimer;
    procedure btnRainSensorClick(Sender: TObject);
    procedure tmrSensorTimer(Sender: TObject);
  private
    FCurrentSensor: string;
    procedure Log(const AMessage: string);
    function SimulateADCRead: Byte;
  end;

procedure TMainForm.btnRainSensorClick(Sender: TObject);
begin
  FCurrentSensor := 'Rain';
  Log('Avvio sensore pioggia...');
  tmrSensor.Enabled := True;
end;

Un livello di astrazione per l'hardware

La parte interessante non è l'interfaccia in sé, ma il modo in cui si isola l'accesso all'hardware dietro classi di utilità come TADC e TGPIO. Durante lo sviluppo desktop questi metodi si limitano a scrivere nel log; una volta portato il progetto su Raspberry Pi, la stessa interfaccia pubblica viene implementata appoggiandosi a una libreria GPIO reale, senza toccare il resto del codice.

type
  TGPIO = class
    class procedure Setup(pin: integer; mode: integer);
    class function Input(pin: integer): integer;
  end;

class procedure TGPIO.Setup(pin: integer; mode: integer);
begin
  // In produzione: chiamata alla libreria GPIO reale
  MainForm.Log('GPIO pin ' + IntToStr(pin) + ' impostato a modo: ' + IntToStr(mode));
end;

class function TGPIO.Input(pin: integer): integer;
begin
  // Simulazione: valore casuale al posto della lettura fisica
  Result := Random(2);
end;

Il timer come ciclo di polling

Il TTimer sostituisce il classico ciclo while True che si trova nei programmi console per Raspberry Pi: mantiene l'interfaccia reattiva perché ogni iterazione viene eseguita nel thread principale della LCL senza bloccare la form. Un semplice case sulla stringa che identifica il sensore attivo decide quale logica eseguire a ogni tick.

procedure TMainForm.tmrSensorTimer(Sender: TObject);
var
  value: Integer;
begin
  case FCurrentSensor of
    'Rain':
      begin
        value := SimulateADCRead;
        Log('Valore ADC: ' + IntToStr(value));
      end;
  end;
end;

LCL contro VCL: cosa cambia davvero

Chi arriva da Delphi ritrova quasi tutto: stessa logica a eventi, stesso concetto di form e componenti. Alcune differenze pratiche però contano. Il file di progetto include sempre Interfaces prima delle altre unit: è quella unit a selezionare il widgetset del sistema operativo (GTK, Qt, Win32, Cocoa...), motivo per cui lo stesso eseguibile — ricompilato — gira senza modifiche su Linux, Windows o macOS. Su sistemi Unix, se si usano thread, va aggiunta la unit CThreads nella clausola uses del programma principale, altrimenti il runtime FPC non li gestisce correttamente. Il file .lfm riporta anche la versione della LCL con cui è stato salvato (LCLVersion), utile per capire con quale IDE è stata generata la form quando si riprende un progetto dopo tempo. Infine, per interfacce leggibili anche su schermi ad alta densità, vale la pena impostare Application.Scaled := True nel file .lpr, equivalente concettuale dello scaling DPI che in VCL si gestisce diversamente.

Altri articoli su Free Pascal grid_viewTutte le tematiche
Scrivici su WhatsApp