HTTPS SSH

Pasettimino

Pasettimino is a very lite native Pascal ethernet communication library for S7 Siemens Simatic PLC. S7 200/300/400/400H/1200/1500 and LOGO 0BA7 PLC families are supported. You can read PLC info and status, start/stop PLC, read/write from/to any PLC area (full access to I, Q, M, DB, C and T), get a list of existing PLC blocks (OB, DB, SDB, FC, SFC, FB, SFB), read CPU product number and firmware revision, read leds, read/write PLC time, and get PLC battery status. It is a greatly extended FreePascal port of Arduino Settimino library with Lazarus examples, and besides Synapse for ethernet communication it does not have any other dependency (although example projects themselves need BitHelpers package). Pasettimino is running 24/7 in industrial environment since 2016. If you need more heavy weight S7 ethernet/serial libraries, S7 PLC protocol simulator or S7 rich graphical client then take a look at Snap7 and LibNoDave. They both have Pascal wrappers, but unlike Pasettimino their core is not native Pascal.

Prerequisites

  1. Synapse.
  2. BitHelpers.

Installation

  1. If Synapse package is not installed, then the easiest way to do it would be through Lazarus Online Package Manager. If you want to download it manually then download trunk version and compile the laz_synapse.lpk package.
  2. While you can simply copy Pasettimino s7pas and s7extended units to your project directory and start using the library, recommended way would be to open pasettimino_pkg.lpk package and compile it. That would add Pasettimino source directory to Lazarus and make it available to all your projects.
  3. Provided project examples will also need BitHelpers package. Pasettimino can work without it, but those bit helpers for standard Pascal types will greatly simplify bit extraction and presentation.

Usage

  • Each call takes one communication message to read a single PLC tag (without error checking):
  uses
    s7pas;
  ...
  var
    DB1_INT0: int;
    DB3_FLOAT8: single;
    S7: TS7Client;
  begin
    S7 := TS7Client.Create;
    S7.ConnectTo(127, 0, 0, 1 {IP_ADDR}, 0 {RACK}, 2 {SLOT}); // connect to snap7 plc simulator
    DB1_INT0   := S7.ReadInteger(S7AreaDB, 1, 0);             // read integer with one call
    DB3_FLOAT8 := S7.ReadFloat(S7AreaDB, 3, 8);               // read float with another call
    S7.Destroy;
  end;
  • Read PLC status and whole DB in a single call and then parse PLC tags (without error checking):
  uses
    s7pas;
  ...
  var
    CpuStatus, Size, DB1_INT0: int;
    DB1_FLOAT2: single;
    DataBuffer: array[0..4095] of byte; // DB1 size must be less or equal to 4096 bytes
    S7: TS7Client;
  begin
    S7 := TS7Client.Create;
    S7.ConnectTo(192, 168, 15, 40 {IP_ADDR}, 0 {RACK}, 3 {SLOT}); // connect to S7-400
    S7.GetPlcStatus(CpuStatus);
    if (CpuStatus = S7CpuStatusRun) or (CpuStatus = S7CpuStatusRun_Redundant) then
    begin // S7CpuStatusRun_Redundant only with S7-400H, S7CpuStatusRun with everything else
      Size := S7.GetDBSize(1 {DB_NUM});
      S7.ReadArea(S7AreaDB,     // read from DB area
                  1,            // DB number,
                  0,            // start from byte 0
                  Size,         // number of bytes to read
                  @DataBuffer); // destination
      DB1_INT0   := IntegerAt(@DataBuffer, 0);
      DB1_FLOAT2 := FloatAt(@DataBuffer, 2);
    end;
    S7.Destroy;
  end;
  • Read PLC information, CPU order code, firmware, and battery status (without error checking):
  uses
    s7pas, s7extended;
  ...
  var
    S7: TS7ExtendedClient; // GetPlcOrderCode() and GetPlcInfo() need extended S7 client
  begin
    S7 := TS7ExtendedClient.Create;
    S7.ConnectTo(127, 0, 0, 1 {IP_ADDR}, 0 {RACK}, 2 {SLOT}); // connect to snap7 plc simulator
    S7.GetPlcOrderCode; // get connected CPU product number info
    Memo1.Append('CPU product number = ' + S7.Info.OrderCode.ProductNumber);
    Memo1.Append('Firmware revision  = ' + S7.Info.OrderCode.Version);
    S7.GetPlcInfo;   // get info about connected CPU
    Memo1.Append('Project name       = ' + S7.Info.Cpu.ProjectName);
    Memo1.Append('Module name        = ' + S7.Info.Cpu.ModuleName);
    Memo1.Append('Copyright          = ' + S7.Info.Cpu.Copyright);
    Memo1.Append('Serial number      = ' + S7.Info.Cpu.SerialNumber);
    Memo1.Append('Module type name   = ' + S7.Info.Cpu.ModuleTypeName); 
    S7.GetPlcBatteryStatus;
    Memo1.Append('Power supply battery status = ' + S7.Battery.ToString);
    S7.Destroy;
  end;
  • Read current PLC time and write new time to PLC (without error checking):
  uses
    s7pas, s7extended;
  ...
  var
    S7: TS7ExtendedClient; // GetPlcTime() and SetPlcTime() need extended S7 client
  begin
    S7 := TS7ExtendedClient.Create;
    S7.ConnectTo(127, 0, 0, 1 {IP_ADDR}, 0 {RACK}, 2 {SLOT}); // connect to snap7 plc simulator
    S7.GetPlcTime;
    Memo1.Append('Old PLC time   = ' + DateTimeToStr(S7.Time));
    S7.SetPlcTime(now);
    S7.GetPlcTime;
    Memo1.Append('New PLC time   = ' + DateTimeToStr(S7.Time));
    S7.Destroy;
  end;
  • Get a list of existing PLC blocks (without error checking):
  uses
    s7pas, s7extended;
  ...
  var
    S7: TS7ExtendedClient; // GetPlcOrderCode() and GetPlcInfo() need extended S7 client
    i: int;
    BlockID: TS7BlockID;
    s: string;     
  begin
    S7 := TS7ExtendedClient.Create;
    S7.ConnectTo(127, 0, 0, 1 {IP_ADDR}, 0 {RACK}, 2 {SLOT}); // connect to snap7 plc simulator
    S7.GetPlcAllBlocksList;
    for BlockID in S7BlockIDSet do // [blkOB, blkDB, blkSDB, blkFC, blkSFC, blkFB, blkSFB]
    begin
      if S7.GetBlockPointer(BlockID).Count <> 0 then
      begin
        s := '';
        for i := 1 to S7.GetBlockPointer(BlockID).Count do
        begin
          s := s + S7.GetBlockPointer(BlockID).List[i-1].ToString;
          if i < S7.GetBlockPointer(BlockID).Count then
             s := s + ', ';
        end;
        Memo1.Append('S7.Blocks.' + BlockID.ToString + '.Count = ' + S7.GetBlockPointer(BlockID).Count.ToString);
        Memo1.Append('S7.Blocks.' + BlockID.ToString + '.List = (' + s + ')');
      end;
    end;
    S7.Destroy;
  end;
  • Read PLC leds and use bit helpers (with error checking and proper try..finally block):
  uses
    s7pas, s7extended, bithelpers; 
  ...
  procedure TForm1.Button1Click(Sender: TObject); // create a form with button and memo first
  var
    S7: TS7ExtendedClient; // GetPlcLeds() needs extended S7 client
    MyLed: TS7Led;
    Err: int;
  begin
    S7 := TS7ExtendedClient.Create;
    try
    Err := S7.ConnectTo(127, 0, 0, 1 {IP_ADDR}, 2 {RACK_NUM}, 0 {SLOT_NUM});
      if Err = ErrNone then
        Memo1.Append('Connected!')
      else
      begin
        Memo1.Append('Connection error ' + S7.ErrorToString(Err));
        Exit;
      end;
      Err := S7.GetPlcLeds; // read all leds from PLC
      if Err <> ErrNone then // Err = (S7CpuStatusUnknown, S7CpuStatusStop, S7CpuStatusRun)
      begin
        Memo1.Append('GetPlcLeds error ' + S7.ErrorToString(Err));
        Exit;
      end;
      Memo1.Append('Led(S7_LED_RUN_ID).On = ' + S7.Led(S7_LED_RUN_ID).On.ToOneZeroString);
      MyLed := S7.Led(S7_LED_RUN_ID); // S7_LED_RUN_ID, S7_LED_RUN_TXT or 'RUN', all are equal
      Memo1.Append('MyLed.Found = ' + MyLed.Found.ToTrueFalseString(scfUpperCase));
      Memo1.Append('MyLed.On = ' + MyLed.On.ToTrueFalseString(scfLowerCase));
      Memo1.Append('Leds.Cpu[0].Master = ' + S7.Leds.Cpu[0].Master.ToString('Укључено', 'Искључено') + '   (localized bit status strings)');
      Memo1.Append('Leds.Cpu[0].Led[GetLedIndex(S7_LED_MSTR_ID)].On = ' + S7.Leds.Cpu[0].Led[S7.GetLedIndex(S7_LED_MSTR_ID)].On.ToTrueFalseString);
      Memo1.Append('Led(S7_LED_MSTR_ID).On = ' + S7.Led(S7_LED_MSTR_ID).On.ToOneZeroString);
    finally
      S7.Destroy;
      Memo1.Append('Disconnected!');
    end;// Leds[] record structure uses indexes, while Led() functions uses led id or led name
  end;  // To convert led id or name to index, use appropirate GetLedIndex() function      
  • For more detailed usage take a look at the provided examples. You can also take a look into Settimino and Snap7 reference manuals and online documentation since they have a lot in common with Pasettimino.

Examples

Besides Pasettimino package, examples also use BitHelpers package, so you will need to install it in Lazarus first.

  • s7demo demonstrates reading PLC information, reading/writing PLC tags from/to DB, reading whole DB and parsing it's content, writing tag value into a DB, starting/stopping PLC, getting a list of existing PLC blocks (OB, DB, SDB, FC, SFC, FB, SFB), reading CPU product number and firmware revision, reading and writing PLC time, getting PLC battery status, and bit helpers. Make sure that you read a warning section below before first attempt to write anything to a real PLC.
  • s7led demonstrates reading leds, reading redundant CPUs info (if you have them) and bit helpers.

Units

  • s7pas is almost 1:1 port of Settimino to native Pascal.
  • s7extended is an extension of s7pas to read all PLC leds, get correct PLC running status for all S7 families (Settimino and Snap7 give wrong running status for redundant S7-400H), get a list of existing PLC blocks, read PLC info and battery status, and read and write PLC time.
  • bithelpers unit contains helpers to manipulate bit info from all standard types in an easy and convenient way.

Warning

Writing to a real world production PLC can be very dangerous, and wrong commands under certain circumstances can even be executed by PLC as dangerous or deadly actions. You take full responsibility for sending any data to a PLC, and it is strongly advised to practice first with a S7 PLC simulator (like the one from Snap7) or a test PLC running on your desk, before communicating to a real world live production PLC running some industrial process. Only after studying full PLC source code and full Pasettimino source code and Pasettimino examples you will know what is safe and what is not safe. All data reading is considered safe, but any data writing needs special care and treatment. If you do not know how to analyze PLC source code yourself then you must consult relevant PLC engineer and ask him where and what are you allowed to write before doing any actual write. This step should be taken literally and seriously and must not be skipped, otherwise you risk to become a great danger for yourself and others.

Download

If for some reason you do not handle git, then full repository can be downloaded manually from here.

License

Author

Made by Zeljko Avramovic (user Avra in Lazarus forum). Big thanks go to Davide Nardella, the author of the original Settimino and Snap7 libraries.

Versions

  • 1.2 Added read/write PLC time. Added reading PLC and project information, reading CPU order code, and reading firmware version. Added reading a list of all existing PLC blocks. Added reading power supply battery status (only on PLCs which have battery backed up memory). Fixed Settimino GetPlcStatus() bug on S7-400H. Added all new supported features to example projects.
  • 1.0 Full Settimino compatibility. Added CPU leds reading. Added GUI example projects.