1. Description
1.1. Ada for Automation
Ada for Automation (A4A in short) is a framework for designing industrial automation applications using the Ada language.
It makes use of the libmodbus library to allow building a ModbusTCP client or server, or a Modbus RTU master or slave.
It can also use Hilscher communication boards allowing to communicate on field buses like AS-Interface, CANopen, CC-Link, DeviceNet, PROFIBUS, EtherCAT, Ethernet/IP, Modbus TCP, PROFINET, Sercos III, POWERLINK, or VARAN.
With the help of GtkAda, the binding to the Graphic Tool Kit, one can design Graphical User Interfaces.
Thanks to Gnoga, built on top of Simple Components, it is also possible to provide a Web User Interface.
Nice addition is the binding to the Snap7 library which allows to communicate with SIEMENS S7 PLCs using S7 Communication protocol ISO on TCP (RFC1006).
Of course, all the Ada ecosystem is available.
Using Ada bindings, C, C++, Fortran libraries can also be used.
And, since it is Ada, it can be compiled using the same code base to target all major platforms.
1.2. This demo application
This is a demo application featuring:
-
a basic command line interface,
-
a basic web user interface making use of Gnoga,
-
a kernel managing one Hilscher cifX channel (K6),
-
a trivial application that mimics 16 push buttons and 16 LEDs with a web interface.
2. Projects diagram
The following picture shows the diagram of projects :

3. License
Those files are included in the Ada for Automation root folder :
COPYING3
The GPL License you should read carefully.
GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
COPYING.RUNTIME
GCC RUNTIME LIBRARY EXCEPTION Version 3.1, 31 March 2009
Hence, each source file contains the following header :
------------------------------------------------------------------------------
-- Ada for Automation --
-- --
-- Copyright (C) 2012-2023, Stephane LOS --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
4. Building
The provided makefile uses GPRbuild and provides six targets:
-
all : builds the executable,
-
app_doc : creates the documentation of the source code,
-
clean : cleans the space.
Additionally one can generate some documentation using Asciidoctor with :
-
read_me_html : generates the README in HTML format,
-
read_me_pdf : generates the README in PDF format,
-
read_me : generates the README in both formats.
5. Running
Of course, this application is of interest only if a master application is talking to it.
Good candidates are a SCADA or a PLC but, if none is at your disposal, you could use one of :
-
062 a4a-k3-wui,
-
your own.
In a console:
Build the application:
make
Optionally create the documentation:
make app_doc
Run the application:
make run
Use Ctrl+C to exit.
Optionally clean all:
make clean
6. Directories
bin
Where you will find the executable.
doc
The place where GNATdoc would create the documentation.
obj
Build artifacts go here.
src
Application source files.
7. Application
This is a basic Ada for Automation application, managing one Hilscher cifX channel, that mimics 16 push buttons and 16 LEDs with a web interface.
It has a Command Line and Web User Interfaces and communicates with any master using major standard industrial protocols.
The Hilscher cifX channel can be any of a cifX board, comX communication module, or a netX SoC.
The application expects the fieldbus channel to be already configured using SYCON.net.
7.1. Deployment diagram

7.2. Activity diagram
The Kernel manages the communication channel and provides an interface to it, namely the package "A4A.Memory.Hilscher_Fieldbus1".



7.3. Fieldbus Configuration
package A4A.Application.Configuration is
--------------------------------------------------------------------
-- Main Task Configuration
--------------------------------------------------------------------
Application_Main_Task_Priority : System.Priority :=
System.Priority'Last - 10;
-- System.Default_Priority;
-- can be cyclic
-- Application_Main_Task_Type : constant Main_Task_Type := Cyclic;
Application_Main_Task_Delay_MS : constant := 50;
-- or periodic
Application_Main_Task_Type : constant Main_Task_Type := Periodic;
Application_Main_Task_Period_MS : constant := 50;
--------------------------------------------------------------------
-- Periodic Task 1 Configuration
--------------------------------------------------------------------
Application_Periodic_Task_1_Priority : System.Priority :=
System.Default_Priority;
Application_Periodic_Task_1_Period_MS : constant := 500;
--------------------------------------------------------------------
-- Fieldbus 1 Configuration
--------------------------------------------------------------------
-- Alias like "DPM"
-- or board name like "cifX0" or "TCP0_cifX0"
Config1 : aliased Fieldbus_Configuration :=
(Fieldbus_Name =>
Fieldbus_Name_Strings.To_Bounded_String ("cifX0"), -- (1)
Fieldbus_Channel => 0, -- (2)
Board_Name => (others => Character'First),
Board_Alias => (others => Character'First),
Channel_Handle => Channel_Handle_Null,
My_Channel => null
);
--------------------------------------------------------------------
-- Fieldbuses Configuration
--------------------------------------------------------------------
-- Declare all Fieldbuses configurations in the array
Fieldbuses_Configuration : Fieldbus_Configuration_Access_Array :=
(1 => Config1'Access);
end A4A.Application.Configuration;
1 | Channel configuration : board name |
2 | Channel configuration : channel number |
7.4. User objects Definition
package A4A.User_Objects is
--------------------------------------------------------------------
-- User Objects creation
--------------------------------------------------------------------
--------------------------------------------------------------------
-- Inputs
--------------------------------------------------------------------
Input_Bits : Bool_Array (0 .. 15) := (others => False); -- (1)
--------------------------------------------------------------------
-- Outputs
--------------------------------------------------------------------
Coils : Bool_Array (0 .. 15) := (others => False); -- (2)
end A4A.User_Objects;
1 | An array of 16 Input bits that a fieldbus master can read is defined. |
2 | As well, an array of 16 Coils can be written by the master. |
7.5. User Functions
package body A4A.User_Functions is
--------------------------------------------------------------------
-- User functions
--------------------------------------------------------------------
procedure Map_Inputs is -- (1)
begin
Byte_To_Booleans (Byte_in => Hilscher_Fieldbus1.Inputs (1),
Boolean_out00 => Coils (0),
Boolean_out01 => Coils (1),
Boolean_out02 => Coils (2),
Boolean_out03 => Coils (3),
Boolean_out04 => Coils (4),
Boolean_out05 => Coils (5),
Boolean_out06 => Coils (6),
Boolean_out07 => Coils (7));
Byte_To_Booleans (Byte_in => Hilscher_Fieldbus1.Inputs (0),
Boolean_out00 => Coils (8),
Boolean_out01 => Coils (9),
Boolean_out02 => Coils (10),
Boolean_out03 => Coils (11),
Boolean_out04 => Coils (12),
Boolean_out05 => Coils (13),
Boolean_out06 => Coils (14),
Boolean_out07 => Coils (15));
end Map_Inputs;
procedure Map_Outputs is -- (2)
begin
Booleans_To_Byte (Boolean_in00 => Input_Bits (0),
Boolean_in01 => Input_Bits (1),
Boolean_in02 => Input_Bits (2),
Boolean_in03 => Input_Bits (3),
Boolean_in04 => Input_Bits (4),
Boolean_in05 => Input_Bits (5),
Boolean_in06 => Input_Bits (6),
Boolean_in07 => Input_Bits (7),
Byte_out => Hilscher_Fieldbus1.Outputs (1)
);
Booleans_To_Byte (Boolean_in00 => Input_Bits (8),
Boolean_in01 => Input_Bits (9),
Boolean_in02 => Input_Bits (10),
Boolean_in03 => Input_Bits (11),
Boolean_in04 => Input_Bits (12),
Boolean_in05 => Input_Bits (13),
Boolean_in06 => Input_Bits (14),
Boolean_in07 => Input_Bits (15),
Byte_out => Hilscher_Fieldbus1.Outputs (0)
);
end Map_Outputs;
procedure Map_HMI_Inputs is
begin
null;
end Map_HMI_Inputs;
procedure Map_HMI_Outputs is
begin
null;
end Map_HMI_Outputs;
end A4A.User_Functions;
User functions are defined to :
1 | get the inputs from the channel, |
2 | set channel ouputs. |
7.6. User Application
package body A4A.Application is
procedure Cold_Start is
begin
null;
end Cold_Start;
procedure Closing is
begin
null;
end Closing;
procedure Main_Cyclic is
My_Ident : constant String := "A4A.Application.Main_Cyclic";
begin
A4A.Log.Logger.Put
(Who => My_Ident,
What => "Yop ! **********************",
Log_Level => Level_Verbose);
Map_Inputs; -- (1)
Map_HMI_Inputs;
-- Playing with tasks interface
Main_Outputs.X := Main_Inputs.A;
Main_Outputs.Y := Main_Inputs.B;
Main_Outputs.Z := Main_Inputs.C;
Map_Outputs; -- (2)
Map_HMI_Outputs;
exception
when Error : others =>
A4A.Log.Logger.Put (Who => My_Ident,
What => Exception_Information (Error));
Program_Fault_Flag := True;
end Main_Cyclic;
procedure Periodic1_Cyclic is
My_Ident : constant String := "A4A.Application.Periodic1_Cyclic";
begin
A4A.Log.Logger.Put (Who => My_Ident,
What => "Hi !",
Log_Level => Level_Verbose);
-- Do something useful here
-- Could be simulate
-- Playing with tasks interface
Periodic1_Outputs.A := not Periodic1_Inputs.X;
Periodic1_Outputs.B := Periodic1_Inputs.Y + 2;
Periodic1_Outputs.C := Periodic1_Inputs.Z + 1;
exception
when Error : others =>
A4A.Log.Logger.Put (Who => My_Ident,
What => Exception_Information (Error));
Program_Fault_Flag := True;
end Periodic1_Cyclic;
function Program_Fault return Boolean is
begin
return Program_Fault_Flag;
end Program_Fault;
end A4A.Application;
The application cyclically :
1 | gets the inputs from the channel, |
2 | sets channel ouputs. |
7.7. Web server and User Interface
Hereafter is a diagram showing architecture and information flow for the Web UI.
An article is available, in French though :
https://slo-ist.fr/ada4automation/a4a-modbus-tcp-server-web-hmi-a4a_piano