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): 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): 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; Regs: TArray; 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.