Files
f.bittiandClaude Opus 5 4a01a2ec88 Plancia configurabile su Modbus RTU
Due programmi che condividono uModbusRTU e uGauge:

- Console/PlanciaConsole: plancia nautica descritta da file XML, con
  modalita' plancia e modalita' configurazione. Comandi, spie, selettori,
  strumenti e allarmi sonori; il bus gira in un thread suo perche' la
  finestra non si fermi mai.
- ProjectPlancia: il programma di prova piu' vecchio, usato per collaudare
  i canali.

Le plance sono in Console/*.xml, la documentazione in Console/LEGGIMI.md.
Esclusi dal versionamento i compilati (.exe, .dcu), i file dell'IDE e un
audio da 17 MB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 16:56:34 +02:00

142 lines
3.8 KiB
ObjectPascal

program scanbus;
{
Scanner del bus Modbus RTU.
Interroga gli indirizzi slave uno per uno e riporta chi risponde, con quali
funzioni e che dati restituisce. Serve a scoprire l'indirizzo di un modulo
appena inserito e a capire da che canale partono i suoi ingressi.
Uso: scanbus [porta] [baud] [ultimoIndirizzo]
Es.: scanbus COM7 9600 16
}
{$APPTYPE CONSOLE}
uses
System.SysUtils,
uModbusRTU in '..\..\uModbusRTU.pas';
const
PROBE_TIMEOUT_MS = 200; // basso: un indirizzo morto non deve costare mezzo secondo
PROBE_COUNT = 8; // 8 canali: la taglia tipica di questi moduli
/// Rende leggibili gli 8 bit come "1 0 0 1 ...", con i numeri di canale sopra.
function BitsToStr(const ABits: TArray<Boolean>): string;
var
I: Integer;
begin
Result := '';
for I := 0 to High(ABits) do
if ABits[I] then Result := Result + '1 ' else Result := Result + '0 ';
end;
function RegsToStr(const ARegs: TArray<Word>): string;
var
I: Integer;
begin
Result := '';
for I := 0 to High(ARegs) do
Result := Result + IntToStr(ARegs[I]) + ' ';
end;
var
Port: string;
Baud: Cardinal;
LastAddr: Integer;
Bus: TModbusRTU;
Addr: Integer;
Alive: Boolean;
Found: Integer;
Bits: TArray<Boolean>;
Regs: TArray<Word>;
begin
try
Port := 'COM7';
Baud := 9600;
LastAddr := 16;
if ParamCount >= 1 then Port := ParamStr(1);
if ParamCount >= 2 then Baud := StrToUIntDef(ParamStr(2), 9600);
if ParamCount >= 3 then LastAddr := StrToIntDef(ParamStr(3), 16);
Writeln(Format('Scansione %s a %d baud, indirizzi da 1 a %d.', [Port, Baud, LastAddr]));
Writeln('Chiudi PlanciaConsole prima di lanciarlo: la porta si apre una volta sola.');
Writeln;
Bus := TModbusRTU.Create(Port, Baud, PROBE_TIMEOUT_MS);
try
Bus.Connect;
Found := 0;
for Addr := 1 to LastAddr do
begin
Write(Format(#13' provo indirizzo %d... ', [Addr]));
Alive := False;
// FC 02 - ingressi digitali: e' cio' che interessa a un modulo IO
try
Bits := Bus.ReadDiscreteInputs(Addr, 0, PROBE_COUNT);
if not Alive then Writeln(#13 + Format('SLAVE %d trovato', [Addr]) + StringOfChar(' ', 20));
Alive := True;
Writeln(' FC02 ingressi (canali 0..7): ' + BitsToStr(Bits));
except
on E: Exception do ;
end;
// FC 01 - bobine: le uscite rele'
try
Bits := Bus.ReadCoils(Addr, 0, PROBE_COUNT);
if not Alive then Writeln(#13 + Format('SLAVE %d trovato', [Addr]) + StringOfChar(' ', 20));
Alive := True;
Writeln(' FC01 bobine (canali 0..7): ' + BitsToStr(Bits));
except
on E: Exception do ;
end;
if not Alive then
Continue; // indirizzo morto: inutile insistere con i registri
// Solo per chi ha gia' risposto: vale la pena chiedere anche i registri
try
Regs := Bus.ReadHoldingRegisters(Addr, 0, 4);
Writeln(' FC03 holding (reg 0..3): ' + RegsToStr(Regs));
except
on E: Exception do ;
end;
try
Regs := Bus.ReadInputRegisters(Addr, 0, 4);
Writeln(' FC04 input reg (reg 0..3): ' + RegsToStr(Regs));
except
on E: Exception do ;
end;
Writeln;
Inc(Found);
end;
Writeln(#13 + StringOfChar(' ', 40));
if Found = 0 then
begin
Writeln('Nessuno slave ha risposto.');
Writeln('Controlla: baud del modulo, scambio A/B del RS485, alimentazione,');
Writeln('e che PlanciaConsole non tenga gia'' aperta la porta.');
end
else
Writeln(Format('Trovati %d slave.', [Found]));
finally
Bus.Free;
end;
except
on E: Exception do
Writeln('ERRORE: ' + E.Message);
end;
Writeln;
Write('Premi INVIO per chiudere.');
Readln;
end.