Ada Embedded Linux Framework

Last updated on Sep 15, 2017
SUMMARY

This Make With Ada 2017 project is a framework for developing Ada Internet of Things/Physical Computing applications that can run on low cost Linux single board microcomputers such as the Beagle Bone Black and the Raspberry Pi 2 or 3, using the GNAT GPL 2016 toolchain for the Raspberry Pi 2.

Introduction

This Make With Ada 2017 project is a framework for developing Ada Internet of Things/Physical Computing applications that can run on low cost Linux single board microcomputers such as the Beagle Bone Black and the Raspberry Pi 2 or 3, using the GNAT GPL 2016 toolchain for the Raspberry Pi 2.

A shim library called libsimpleio wraps certain Linux system calls in C functions that are callable from Ada.

A large body of Ada packages layered on top of libsimpleio provide an object oriented component framework for dealing with sensors and actuators such as A/D converters, GPIO pins, PWM outputs and many other kinds of devices.

Many of the Ada packages target inexpensive I/O modules such as Mikroelektronika Click Boards and Seeed Studio Grove modules. Three exemplar Internet of Things Thermometer systems using a common code base but running on very different hardware platforms will be presented.

Rationale

The retail sales cost for Linux microcomputer boards has fallen dramatically in recent years. Devices like the Raspberry Pi Zero W (https://www.raspberrypi.org/products/raspberry-pi-zero-w) and the C.H.I.P. (https://getchip.com/pages/chip) have proven that it is possible to manufacture and sell a Linux microcomputer with a wireless network interface for less than USD $10. This price point allows using a Linux microcomputer in applications that formerly have been limited to a single chip microcontroller such as the STM32F4 family.

A Linux microcomputer brings three great advantages to embedded system design:

  • Much more sophisticated network stack

  • Improved software development environments

  • Ability to reuse vast numbers of Linux software libraries

Single chip microcontrollers have limited and nonstandard development environments that are largely incompatible between different manufacturers. In contrast, programs developed for Linux microcomputers are standard Linux programs and can be written in a highly portable manner. In many cases, the exact same program will run unmodified on very different Linux microcomputers if board specific characteristics (such as a GPIO pin number or an SPI device node name) are passed at run time from either environment variables or command line parameters.

With respect to Ada, a Linux microcomputer allows using the full Ada programming language, including all tasking facilities and standard libraries like Ada Web Server.

External Software Components

The Ada Embedded Linux Framework is built upon a foundation comprised of the following external software components.

MuntsOS Embedded Linux Framework

MuntsOS (http://git.munts.com/arm-linux-mcu) is a stripped down Linux distribution that includes a small compressed root file system within the kernel binary itself. At boot time the file system is unpacked into RAM and thereafter the system runs entirely in RAM. The kernel image file, including the compressed root file system, runs about 9 MB.

The goal of the MuntsOS project is to deliver a turnkey, RAM resident Linux operating system for very low cost single board microcomputers. With MuntsOS installed, such microcomputers can be treated as components, as Linux microcontrollers, and integrated into other projects just like traditional single chip microcontrollers.

The hardware platform boot files and the MuntsOS kernel files are combined to build a distribution .zip file called a Thin Server (http://repo.munts.com/muntsos/thinservers). The simplest way to use MuntsOS is to download one of the prebuilt Thin Server .zip files and extract it to a freshly formatted FAT32 SD card. You can then modify autoexec.d/00-wlan-init on the SD card to preconfigure it for your wireless network environment, if desired, before inserting it in the target board. After booting MuntsOS, log into the target board and run sysconfig to perform more system configuration, which may include installing any of the three examples that are part of this Make With Ada project.

Linux Simple I/O Library

The Linux Simple I/O library libsimpleio (http://git.munts.com/libsimpleio) is a library of standard C functions that attempts to encapsulate and hide the ugliness of Linux I/O device services. It provides services for the following kinds of I/O devices:

The libsimpleio distribution includes Ada packages (http://git.munts.com/libsimpleio/ada) for each of the above types of I/O devices. These packages contain the Ada constant, type, and procedure declarations (using pragma Import) necessary for calling the C functions that comprise libsimpleio.

The shared library file libsimpleio.so, which will be required by any application programs using the Ada packages, is included in the MuntsOS root file system.

Ada Web Server Library

Some of the components in the Ada Embedded Linux Framework also use services in the Ada Web Server library (http://libre.adacore.com/tools/aws).

Hardware Platforms

The Ada Embedded Linux Framework currently targets the following hardware platforms, all running MuntsOS and all using the AdaCore GNAT GPL 2016 ARMv7 cross-toolchain for the Raspberry Pi 2:

Note: AdaCore does not supply a free cross-toolchain for the ARMv6 Raspberry Pi 1, but all of the Ada packages and example programs can be compiled with the native FSF GNAT toolchain provided by the Raspbian Linux distribution. Most of the example programs can also be compiled and run on common desktop Linux distributions like Debian or Ubuntu, using the native FSF GNAT toolchain provided by the distribution.

Click Boards

Mikroelektronika, a producer of microcontroller development tools based in Belgrade, Serbia, sells a line of small I/O modules called Click Boards (https://shop.mikroe.com/click). These modules all have a standardized form factor and pin out called mikroBUSTM (https://www.mikroe.com/mikrobus) and are available for a huge variety of functions, including sensors, actuators, displays, and much more. Mikroelektronika sells at least 300 different Click Boards at time of writing.

They also sell a variety of Click Shields for microcomputer platforms like the Arduino, BeagleBone, and the Raspberry Pi. Each Click Shield mounts on top of its particular host microcomputer board and provides 1 to 4 mikroBUSTM sockets in which the various Click Boards may be installed.

Other manufacturers (notably Microchip and NXP) have also started providing mikroBUSTM compatible sockets on their development boards.

With very few exceptions (e.g. those with analog sensors or 5V logic), any Click Board can be installed in any socket on any shield. Therefore, a small inventory of interesting Click Boards may be reused with many different microcomputer platforms. Many of the packages and example programs in the Ada Embedded Linux Framework are for Click Boards and all three of the examples for this Make With Ada project use at least one Click Board.

Grove Modules

The Grove System (http://wiki.seeed.cc/Grove_System) from Seeed Studios of Guangdong, China, is another line of small and inexpensive I/O modules. Each Grove Module has 1 or more 4-pin, 2 mm pitch sockets called Grove Connectors. Each Grove Connector has a dedicated function (analog I/O, digital I/O, I2C, serial port, etc.) Grove Modules are interconnected in a building block fashion using small 4 pin cables plugged into the Grove Connectors.

Most Grove master devices supply 5V power at their Grove Connectors. A few, such as the Seeed Studios Seeeduino V4.2, can be switched to operate at either 3.3V or 5V; this affects both the power supply and the signal levels at their Grove Connectors. Most Grove Modules are designed for 5V power but will work at 3.3V. A few are limited to 3.3V, and will require an adapter (https://www.tindie.com/products/jdn/i2c-35v-adapter) to be usable with a 5V master device.

Seeed Studios sells shields for microcomputers like the Arduino, BeagleBone, and the Raspberry Pi. These shields provide 12 to 16 (or more) Grove Connectors.

Seed Studios also sells the BeagleBone Green, a cost reduced version of the BeagleBone Black Linux microcomputer, which has two 3.3V Grove Connectors (one I2C and one serial). The BeagleBone Green is the platform used with Grove Modules for this Make With Ada Project.

Component Packages

Introduction

With 106 component packages and 52 test programs (at time of writing) in the Ada Embedded Linux Framework, it is beyond the scope of this Make With Ada project to describe more than a few in any detail. Please refer to the MuntsOS Ada examples directory (http://git.munts.com/arm-linux-mcu/examples/ada) to see what components and test programs are available, and for more information about any particular component.

Object Hierarchy Example

We will work through the full object hierarchy for one device, the Seeed Studios Grove I2C Analog to Digital Converter (http://wiki.seeed.cc/Grove-I2C_ADC), which is used in the third example program below.

Package IO_Interfaces

-- Generic package for I/O interfaces

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

GENERIC

  TYPE Property IS PRIVATE;

PACKAGE IO_Interfaces IS

  -- Define an abstract input only interface

  TYPE InputInterface IS INTERFACE;

  -- Define an access type compatible with any subclass implementing
  -- InputInterface

  TYPE Input IS ACCESS ALL InputInterface'Class;

  -- Define a method for reading from an input

  FUNCTION Get(self : IN OUT InputInterface) RETURN Property IS ABSTRACT;

  ----------------------------------------------------------------------------

  -- Define an abstract input/output interface

  TYPE InputOutputInterface IS INTERFACE;

  -- Define an access type compatible with any subclass implementing
  -- InputOutputInterface

  TYPE InputOutput IS ACCESS ALL InputOutputInterface'Class;

  -- Define a method for reading from an input

  FUNCTION Get(self : IN OUT InputOutputInterface) RETURN Property IS ABSTRACT;

  -- Define a method for writing to an output

  PROCEDURE Put(self : IN OUT InputOutputInterface; value : Property) IS ABSTRACT;

  ----------------------------------------------------------------------------

  -- Define an abstract output only interface

  TYPE OutputInterface IS INTERFACE;

  -- Define an access type compatible with any subclass implementing
  -- OutputInterface

  TYPE Output IS ACCESS ALL OutputInterface'Class;

  -- Define a method for writing to an output

  PROCEDURE Put(self : IN OUT OutputInterface; value : Property) IS ABSTRACT;

END IO_Interfaces;


At the top of the object hierarchy is the generic package IO_Interfaces. This package has one generic formal type parameter Property and defines three abstract interfaces: InputInterface, OutputInterface, and InputOutputInterface.

A classwide access type is defined for each of the three abstract interfaces: Input, Output, and InputOutput. An access for any object implementing on of the three abstract interfaces can be assigned to the corresponding classwide access type.

Get methods are defined for InputInterface and InputOutputInterface. Put methods are defined for InputOutputInterface and OutputInterface. The Get methods return an object of the type specified for Property when IO_Interfaces is instantiated. The Put methods require a single value of the type specified for Property.

Since the generic formal parameter Property is define as just PRIVATE, IO_Interfaces can be instantiated for both scalar and composite types.

Package ADC

-- Abstract Analog to Digital Converter interface definitions

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH Ada.Text_IO;
WITH IO_Interfaces;

PACKAGE ADC IS

  ADC_Error : EXCEPTION;

  -- Define a type for sampled analog data

  TYPE Sample IS MOD 2**32;

  -- Instantiate text I/O package

  PACKAGE Sample_IO IS NEW Ada.Text_IO.Modular_IO(Sample);

  -- Instantiate abstract interfaces package

  PACKAGE Interfaces IS NEW IO_Interfaces(Sample);

END ADC;


The next package in the hierarchy is ADC, which defines an abstract interface for all analog to digital converters that return digital data samples. ADC defines an exception ADC_Error, and a 32-bit unsigned sampled data type Sample.

It also instantiates IO_Interfaces with Sample as the child package ADC.Interfaces, thereby creating an abstract interface called ADC.Interfaces.InputInterface and a classwide access type ADC.Interfaces.Input.

Each component package for a particular analog to digital converter will implement ADC.Interfaces.InputInterface and provide a constructor function returning ADC.Interfaces.Input.

Finally, for convenience, ADC also instantiates Ada.Text_IO.Modular_IO with Sample as the child package ADC.Sample_IO.

Package Voltage

-- Voltage measurement and control definitions

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH Ada.Text_IO;
WITH IO_Interfaces;

PACKAGE Voltage IS

  TYPE Volts IS NEW Float;

  -- Instantiate text I/O package

  PACKAGE Volts_IO IS NEW Ada.Text_IO.Float_IO(Volts);

  -- Instantiate abstract interfaces package

  PACKAGE Interfaces IS NEW IO_Interfaces(Volts);

END Voltage;


The Voltage package defines abstract interfaces for voltage I/O devices that either measure voltage inputs (e.g. a voltmeter) or control voltage outputs (e.g. a variable voltage power supply).

Voltage defines the floating point type Volts, derived from Float. It instantiates IO_Interfaces with Volts, as the child package Voltage.Interfaces, thereby defining the abstract interface Voltage.Interfaces.InputInterface (along with Voltage.Interfaces.InputOutputInterface and Voltage.Interfaces.OutputInterface) and the classwide access type Voltage.Interfaces.Input (along with Voltage.Interfaces.InputOutput and Voltage.Interfaces.Output).

Like ADC, for convenience, Voltage instantiates Ada.Text_IO.Float_IO with Volts as the child package Voltage.Volts_IO.

Package ADC.DAS

-- Generic package for a Data Acquisition System

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH ADC;
WITH Voltage;

GENERIC

  TYPE InputClass(<>) IS NEW ADC.Interfaces.InputInterface WITH PRIVATE;

  Resolution : IN Positive;
  Reference  : IN Voltage.Volts;
  Gain       : IN Voltage.Volts := 1.0;
  Offset     : IN Voltage.Volts := 0.0;

PACKAGE ADC.DAS IS

  -- Define subclass of InputClass

  TYPE InputSubclass IS NEW InputClass AND
    Voltage.Interfaces.InputInterface WITH PRIVATE;

  -- Constructor

  FUNCTION Create(o : ADC.Interfaces.Input) RETURN Voltage.Interfaces.Input;

  -- Methods

  FUNCTION Get(self : IN OUT InputSubclass) RETURN Voltage.Volts;

PRIVATE

  TYPE InputSubclass IS NEW Inputclass AND
    Voltage.Interfaces.InputInterface WITH NULL RECORD;

END ADC.DAS;


Next is another generic child package of ADC, ADC.DAS. This generic package can be instantiated with a type that implements ADC.Interfaces.InputInterface (generic formal type InputClass) and some numeric values (generic formal value parameters Resolution, Reference, Gain, and Offset) that fully characterize a typical data acquisition system.

ADC.DAS, when instantiated, will define a type InputSubclass that inherits from the type passed for InputClass and implements Voltage.Interfaces.Input.

ADC.DAS defines a constructor function Create that requires an ADC.Interfaces.Input access and returns a Voltage.Interfaces.Input access.

Finally, ADC.DAS defines a Get method for InputSubclass that returns Voltage.Volts.

The body of ADC.DAS, which is not listed here, contains the code that converts ADC.Interfaces.Input to Voltage.Interfaces.Input (in the body of Create) and ADC.Sample to Voltage.Volts (in the body of Get).

Package I2C

-- Abstract interface for I2C bus controllers

-- Copyright (C)2016-2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

PACKAGE I2C IS

  -- Define an exception for I2C errors

  I2C_Error : EXCEPTION;

  -- Type definitions

  TYPE Address IS RANGE 0 .. 127;

  TYPE Byte IS MOD 256;

  TYPE Command IS ARRAY (Natural RANGE <>) OF Byte;

  TYPE Response IS ARRAY (Natural RANGE <>) OF Byte;

  -- Define an abstract interface for I2C bus controllers

  TYPE BusInterface IS INTERFACE;

  -- Define an access type compatible with any subclass implementing
  -- I2C.BusInterface

  TYPE Bus IS ACCESS ALL I2C.BusInterface'Class;

  -- I2C clock speed constants

  SpeedStandard : CONSTANT Positive := 100_000;
  SpeedFast     : CONSTANT Positive := 400_000;
  SpeedFastPlus : CONSTANT Positive := 1_000_000;

  -- Read only I2C bus cycle method

  PROCEDURE Read
   (self    : BusInterface;
    addr    : Address;
    resp    : OUT Response;
    resplen : Natural) IS ABSTRACT;

  -- Write only I2C bus cycle method

  PROCEDURE Write
   (self   : BusInterface;
    addr   : Address;
    cmd    : Command;
    cmdlen : Natural) IS ABSTRACT;

  -- Combined Write/Read I2C bus cycle method

  PROCEDURE Transaction
   (self    : BusInterface;
    addr    : Address;
    cmd     : Command;
    cmdlen  : Natural;
    resp    : OUT Response;
    resplen : Natural) IS ABSTRACT;

END I2C;


Next is the package I2C, which defines an abstract interface for all I2C bus controllers. It defines an exception I2C_Error, an I2C address type Address limited to the values 0 through 127, an 8-bit unsigned modular type Byte, and two Byte array types, Command and Response.

I2C also defines the abstract interface BusInterface and the corresponding classwide access type Bus. BusInterface has three methods: Read, Write, and Transaction (which is an atomic Write followed by Read).

I2C also defines some constants for the standard I2C bus speeds of 100 kHz, 400 kHz, and 1 MHz.

Package ADC121C021

-- ADC121C021 Analog to Digital Converter services

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH ADC;
WITH I2C;

PACKAGE ADC121C021 IS

  -- ADC121C021 configuration constants

  Resolution : CONSTANT Positive := 12;

  -- Define a subclass of ADC.Interfaces.InputInterface

  TYPE InputSubclass IS NEW ADC.Interfaces.InputInterface WITH PRIVATE;

  -- Constructors

  FUNCTION Create
   (bus  : I2C.Bus;
    addr : I2C.Address) RETURN ADC.Interfaces.Input;

  -- Methods

  FUNCTION Get(self : IN OUT InputSubclass) RETURN ADC.Sample;

PRIVATE

  TYPE InputSubclass IS NEW ADC.Interfaces.InputInterface WITH RECORD
    bus     : I2C.Bus;
    address : I2C.Address;
  END RECORD;

END ADC121C021;


Next in the hierarchy is the package ADC121C021, which exports services for the Texas Instruments ADC121C021 (http://www.ti.com/product/ADC121C021), a 12-bit single channel Analog to Digital Converter.

ADC121C021 first defines a constant Resolution, which is the number of bits of resolution. 12 bits implies 4096 different voltage levels can be distinguished.

Next, ADC121C021 defines InputSubclass, a tagged record type that implements ADC.Interfaces.Input.

The constructor function Create requires I2C.Bus and I2C.Address parameters and returns ADC.Interfaces.Input. Note that Create is not a primitive operation of InputSubclass.

The method Get returns ADC.Sample.

In the private part of the package specification, we can see that the I2C bus and address (supplied to Create) are stored in the tagged record. This illustrates a general guiding principle of the Ada Embedded Linux Framework, which is the answer to the question: Should a tagged record type extend or contain prerequisites? Most of the tagged record types in the Ada Embedded Linux Framework, like ADC121C021.InputSubclass, will contain prerequisites like I2C.Bus instead of extending them.

Package Grove_I2C_ADC

-- Seeed Studio Grove I2C ADC Module services

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH ADC121C021;
WITH ADC.DAS;
WITH I2C;
WITH Voltage;

PACKAGE Grove_I2C_ADC IS

  DefaultAddress : CONSTANT I2C.Address := 16#50#;

  -- Instantiate ADC.DAS for the Grove I2C A/D module

  PACKAGE DAS IS NEW ADC.DAS(ADC121C021.InputSubclass,
    Resolution => ADC121C021.Resolution, Reference => 3.0, Gain => 0.5);

  -- Constructors

  FUNCTION Create
   (bus  : I2C.Bus;
    addr : I2C.Address := DefaultAddress) RETURN Voltage.Interfaces.Input IS
      (DAS.Create(ADC121C021.Create(bus, addr)));

END Grove_I2C_ADC;

Finally we reach the package Grove_I2C_ADC, which exports services for the Grove module of the same name.

It first defines a constant DefaultAddress for the default I2C address for the ADC121C021 analog to digital converter on the module.

Next Grove_I2C_ADC instantiates ADC.DAS as Grove_I2C_ADC.DAS with the values specific to the Grove I2C ADC module: ADC121C021.Resolution, 3.0 V for the reference voltage, and 0.5 V/V for the gain (because the module has input protection resistors that divide the input voltage in half.)

Finally it defines a constructor function Create that requires an I2C bus object parameter bus and an optional I2C address parameter addr and returns Voltage.Interfaces.Input. The body of Create is an expression function that just calls Grove_I2C_ADC.DAS.Create with the same parameters.

As will be the case for many of the specific device packages in the Ada Embedded Linux Framework, the package Grove_I2C_ADC has no body. Such device packages simply define some constants specific to the device, perhaps instantiate a generic package like ADC.DAS, and define one or more constructor functions with expression function bodies.

Example 1 -- Raspberry Pi Internet Thermometer

-- Mikroelektronika Thermo Click Internet Thermometer Example Program

-- Copyright (C)2016-2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH Ada.Calendar;
WITH Ada.Calendar.Formatting;
WITH Ada.Strings;
WITH Ada.Strings.Fixed;
WITH ClickBoard.Sockets;
WITH ClickBoard.Thermo;
WITH ClickBoard.SevenSegment;
WITH libLinux;
WITH MAX31855;
WITH Temperature; USE Temperature; USE Temperature.Celsius_IO;
WITH Watchdog;
WITH Watchdog.libsimpleio;
WITH Webserver.HashTable;

PROCEDURE thermometer_thermo IS

  newline : CONSTANT String := ASCII.CR & ASCII.LF;
  refresh : CONSTANT String := "<META HTTP-EQUIV='Refresh' CONTENT=5>" &
    newline;
  title   : CONSTANT String := "<h1>Ada Internet of Things Thermometer</h1>" &
    newline & "<h2>Using the Mikroelektronika Thermo Click</h2>" & newline;
  socket1 : ClickBoard.Sockets.Socket;
  socket2 : ClickBoard.Sockets.Socket;
  sensor  : MAX31855.Device;
  display : ClickBoard.SevenSegment.Display;
  wd      : Watchdog.Device;
  error   : Integer;
  T       : Celsius;
  outbuf  : String(1 .. 20);

BEGIN
  socket1 := ClickBoard.Sockets.Create(1);
  socket2 := ClickBoard.Sockets.Create(2);
  sensor  := ClickBoard.Thermo.Create(socket1);
  display := ClickBoard.SevenSegment.Create(socket2);
  display.Clear;

  DELAY 5.0;

  wd := Watchdog.libsimpleio.Create;
  wd.SetTimeout(5.0);

  libLinux.Detach(error);

  Webserver.HashTable.Publish("/", title);
  Webserver.HashTable.Start;

  libLinux.DropPrivileges("nobody" & ASCII.NUL, error);

  LOOP
    T := sensor.Get;

    Put(outbuf, T, 1, 0);

    Webserver.HashTable.Publish("/", refresh & title & "<p>" &
      Ada.Calendar.Formatting.Image(Ada.Calendar.Clock) & " UTC -- " &
      Ada.Strings.Fixed.Trim(outbuf, Ada.Strings.Left) & " &deg;C</p>" &
      newline);

    IF T < -0.5 THEN
      display.Put(Natural(abs T), ClickBoard.SevenSegment.FEATURES_7SEG_LDP);
    ELSE
      display.Put(Natural(abs T));
    END IF;

    DELAY 0.3;

    wd.Kick;
  END LOOP;
END thermometer_thermo;

Introduction

This first example is an Ada Internet of Things Thermometer running on a Raspberry Pi 3 Linux microcomputer. It makes good use of the Click Board packages in the Ada Embedded Linux Framework to capture the temperature from one Click Board and display it to another. It also makes good use of the Ada Web Server library to publish the temperature for client browsers to query.

For clarity, this example does not include any logging or notifications. It would be very easy to extend it to log the temperature, and to send alerts by SMS or email, using packages already present in the Ada Embedded Linux Framework.

Hardware

The hardware for this first example consists of the following (from bottom to top):

The Raspberry Pi 3 has its internal WiFi interface configured to attach to the local access point, and it is available on the local area network.

Note: Both the THERMO Click and the 7 Segment Click are controlled via SPI interfaces. In order to share the SPI bus properly between the two Click Boards, the 7 Segment Click has had pin 5 (MISO) pin removed. This is necessary because the shift register driving the display cannot tri-state the MISO output pin.

Software

The main program source file for this example is thermometer_thermo.adb and is only 70 lines of code (excluding an introductory comment line and boiler-plate license text). The main program procedure thermometer_thermo performs the following operations:

Initialization

Line 54: Instantiate a Click Board shield socket object socket1 for socket 1, by calling the Create function from the ClickBoard.Sockets package.

Line 55: Instantiate a Click Board shield socket object socket2 for socket 2, by calling the Create function from the ClickBoard.Sockets package.

Line 56: Instantiate a MAX31855 temperature sensor device, by calling the Create function from the ClickBoard.Thermo package, passing in socket1 as a parameter. The code within ClickBoard.Thermo determines which SPI device to use by interrogating the socket1 object.

Line 57: Instantiate a seven segment display device object display, by calling the Create function from the ClickBoard.SevenSegment package, passing in socket2. The code within ClickBoard.SevenSegment determines which SPI device to use by interrogating the socket2 object.

Line 58: Clear the display, by calling display.Clear.

Line 60: Wait 5 seconds for the temperature sensor to stabilize.

Line 62: Instantiate a watchdog timer object wd, by calling the Create function from the Watchdog.libsimpleio package.

Line 63: Set the watchdog timer limit to 5 seconds, by calling wd.SetTimeout.

Line 65: Detach the process from the console and run in the background, by calling libLinux.Detach from libsimpleio.

Line 67: Publish an initial web page by calling the Publish procedure from the Webserver.Hashtable package.

Line 68: Start a web server, running as a background task, by calling the Start procedure from the Webserver.Hashtable package. The web server will listen for HTTP queries on TCP port 80.

Line 70: Drop superuser privileges and run as user nobody hereafter, by calling libLinux.DropPrivileges from libsimpleio. If an attacker manages to compromise the web server, he will only gain access to whatever user nobody has access to (not much).

Event Loop

Line 73: Sample the temperature sensor. The method sensor.Get returns a Celsius temperature value as a floating point number.

Line 75: Convert the temperature from floating point to ASCII, by calling the procedure Temperature.Celsius.Put, which comes from the package Temperature.Celsius, an instantiation of the generic package Ada.Text_IO.Float_IO.

Line 77: Publish a web page, that includes client pull instructions, a title, a UTC time stamp for the temperature sample and the temperature, by calling the procedure Webserver.HashTable.Publish.

Line 83/85: Display the temperature on the seven segment display, by calling the method display.Put. The left decimal point indicates a temperature below freezing.

Line 88: Wait 300 milliseconds.

Line 90: Reset the hardware watchdog timer by calling the method wd.Kick.

Note that the web server task, which uses services from the Ada Web Server library, is entirely invisible to the main program procedure. A private callback procedure inside the package Webserver.Hashtable, which is registered when the web server task is started, selects a web page from its private hash table and passes it back to the web server task and on to the browser that requested it.

Example 2 -- BeagleBone Black Internet Thermometer

-- Mikroelektronika Altitude Click Internet Thermometer Example Program

-- Copyright (C)2016-2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

-- NOTE: The temperature reading will be significantly higher than the actual
-- ambient temperature because of heat generated by the CPU board.

WITH Ada.Calendar;
WITH Ada.Calendar.Formatting;
WITH Ada.Strings;
WITH Ada.Strings.Fixed;
WITH ClickBoard.Sockets;
WITH ClickBoard.Altitude;
WITH ClickBoard.SevenSegment;
WITH libLinux;
WITH Pressure; USE Pressure; USE Pressure.Pascals_IO;
WITH Temperature; USE Temperature; USE Temperature.Celsius_IO;
WITH MPL3115A2;
WITH Watchdog;
WITH Watchdog.libsimpleio;
WITH Webserver.HashTable;

PROCEDURE thermometer_altitude IS

  newline : CONSTANT String := ASCII.CR & ASCII.LF;
  refresh : CONSTANT String := "<META HTTP-EQUIV='Refresh' CONTENT=5>" &
    newline;
  title   : CONSTANT String := "<h1>Ada Internet of Things Thermometer</h1>" &
    newline & "<h2>Using the Mikroelektronika Altitude Click</h2>" & newline;
  socket1 : ClickBoard.Sockets.Socket;
  socket2 : ClickBoard.Sockets.Socket;
  sensor  : MPL3115A2.Device;
  display : ClickBoard.SevenSegment.Display;
  wd      : Watchdog.Device;
  error   : Integer;
  T       : Celsius;
  P       : Pascals;
  outbuf1 : String(1 .. 20);
  outbuf2 : String(1 .. 20);

BEGIN
  socket1 := ClickBoard.Sockets.Create(1);
  socket2 := ClickBoard.Sockets.Create(2);
  sensor  := ClickBoard.Altitude.Create(socket1);
  display := ClickBoard.SevenSegment.Create(socket2);
  display.Clear;

  DELAY 5.0;

  wd := Watchdog.libsimpleio.Create;
  wd.SetTimeout(5.0);

  libLinux.Detach(error);

  Webserver.HashTable.Publish("/", title);
  Webserver.HashTable.Start;

  libLinux.DropPrivileges("nobody" & ASCII.NUL, error);

  LOOP
    T := sensor.Get;
    P := sensor.Get/100.0;

    Put(outbuf1, T, 1, 0);
    Put(outbuf2, P, 2, 0);

    Webserver.HashTable.Publish("/", refresh & title & "<p>" &
      Ada.Calendar.Formatting.Image(Ada.Calendar.Clock) & " UTC -- " &
      Ada.Strings.Fixed.Trim(outbuf1, Ada.Strings.Left) & " &deg;C -- " &
      Ada.Strings.Fixed.Trim(outbuf2, Ada.Strings.Left) & " hPa</p>" & newline);

    IF T < -0.5 THEN
      display.Put(Natural(abs T), ClickBoard.SevenSegment.FEATURES_7SEG_LDP);
    ELSE
      display.Put(Natural(abs T));
    END IF;

    DELAY 0.3;

    wd.Kick;
  END LOOP;
END thermometer_altitude;

Introduction

This example is also an Ada Internet of Things Thermometer. It uses a BeagleBone Black Linux microcomputer and some Click Boards. It publishes both temperature and barometric pressure values.

Hardware

The hardware consists of the following (from bottom to top):

Note: The two socket BeagleBone Click Shield is obsolete, and has been replaced by the four socket mikroBUS Cape (https://shop.mikroe.com/beaglebone-mikrobus-cape). The older shield is used here because it has a very important advantage over its successor: The BeagleBone J1 serial console header is accessible from the top of the BeagleBone Click Shield.

Note: The microprocessor on either a Raspberry Pi or a BeagleBone generates enough heat to significantly raise the temperature seen by the Altitude Click (or any other Click Board with an on-board temperature sensor) above true ambient temperature. There is very little that can be done about this except to use a different sensor placed away from microcomputer board, as is the case with both the first example above and the third example below.

Software

The second example main program thermometer_altitude is very similar to the first. Only the major differences will be discussed here. The Altitude Click has an I2C MPL3112A2 pressure and temperature sensor, so thermometer_altitude instantiates a MPL3115A2.Device sensor object sensor at Line 62. This object has Get methods for both temperature (in degrees Celsius) and pressure (in Pascals), both returning floating point values.

The event loop in thermometer_thermo samples the temperature and pressure sensors (Lines 79 and 80), converts them to ASCII (Lines 82 and 83), and publishes the results in a web page (Line 85). It also writes the temperature to the display (Lines 89 to 93).

Example 3 -- BeagleBone Green Internet Thermometer

-- Seeed Studio Grove Temperature Sensor Internet Thermometer Example Program

-- Copyright (C)2017, Philip Munts, President, Munts AM Corp.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

WITH Ada.Calendar;
WITH Ada.Calendar.Formatting;
WITH Ada.Strings;
WITH Ada.Strings.Fixed;
WITH ClickBoard.Sockets;
WITH ClickBoard.SevenSegment;
WITH Grove_I2C_ADC;
WITH Grove_Temperature;
WITH I2C;
WITH I2C.libsimpleio;
WITH libLinux;
WITH Temperature; USE Temperature; USE Temperature.Celsius_IO;
WITH Voltage;
WITH Watchdog;
WITH Watchdog.libsimpleio;
WITH Webserver.HashTable;

PROCEDURE thermometer_grove_temperature IS

  newline : CONSTANT String := ASCII.CR & ASCII.LF;
  refresh : CONSTANT String := "<META HTTP-EQUIV='Refresh' CONTENT=5>" &
    newline;
  title   : CONSTANT String := "<h1>Ada Internet of Things Thermometer</h1>" &
    newline & "<h2>Using the Seeed Studio Grove Temperature Sensor</h2>" &
    newline;
  bus     : I2C.Bus;
  input   : Voltage.Interfaces.Input;
  sensor  : Temperature.Interfaces.Input;
  socket2 : ClickBoard.Sockets.Socket;
  display : ClickBoard.SevenSegment.Display;
  wd      : Watchdog.Device;
  error   : Integer;
  T       : Celsius;
  outbuf  : String(1 .. 20);

BEGIN
  bus     := I2C.libsimpleio.Create("/dev/i2c-2");
  input   := Grove_I2C_ADC.Create(bus);
  sensor  := Grove_Temperature.Create(input);

  socket2 := ClickBoard.Sockets.Create(2);
  display := ClickBoard.SevenSegment.Create(socket2);
  display.Clear;

  DELAY 5.0;

  wd := Watchdog.libsimpleio.Create;
  wd.SetTimeout(5.0);

  libLinux.Detach(error);

  Webserver.HashTable.Publish("/", title);
  Webserver.HashTable.Start;

  libLinux.DropPrivileges("nobody" & ASCII.NUL, error);

  LOOP
    T := sensor.Get;

    Put(outbuf, T, 1, 0);

    Webserver.HashTable.Publish("/", refresh & title & "<p>" &
      Ada.Calendar.Formatting.Image(Ada.Calendar.Clock) & " UTC -- " &
      Ada.Strings.Fixed.Trim(outbuf, Ada.Strings.Left) & " &deg;C</p>" &
      newline);

    IF T < -0.5 THEN
      display.Put(Natural(abs T), ClickBoard.SevenSegment.FEATURES_7SEG_LDP);
    ELSE
      display.Put(Natural(abs T));
    END IF;

    DELAY 0.3;

    wd.Kick;
  END LOOP;
END thermometer_grove_temperature;

Introduction

This third example is also an Ada Internet of Things Thermometer. It uses a BeagleBone Green Linux microcomputer, one Click Board, and two Grove modules.

Hardware

The hardware consists of the following (from bottom to top):

Note: The two socket BeagleBone Click Shield is obsolete, and has been replaced by the four socket mikroBUS Cape (https://shop.mikroe.com/beaglebone-mikrobus-cape). The older shield is used here because it has a very important advantage over its successor: The BeagleBone J1 serial console header is accessible from the top of the BeagleBone Click Shield.

Software

The main program thermometer_grove_temperature is very similar to the first and second example programs. Only the major differences will be discussed here.

The Grove Temperature Sensor module contains a thermistor (temperature dependent resistor) and a buffer amplifier. It emits an analog voltage that depends, using a complicated formula, on the temperature. The grove_temperature package body instantiates the generic package thermistor with the characteristics of the thermistor device in the Grove Temperature Sensor module, using the “B Parameter” thermistor model.

Since the sensor module emits an analog voltage, an analog to digital convert (ADC) will be needed to sample the voltage. This example uses the Grove I2C ADC module for this purpose. First, thermometer_grove_temperature creates an I2C bus object bus (Line 59) for the I2C bus available at the BeagleBone Green Grove Connector J4. Next, it creates an analog voltage input object input (Line 60). Then, it creates a temperature sensor object sensor for the Grove Temperature Sensor.

The progression from Line 59 to 61 is a good illustration of how increasingly more specific objects are bound together to produce the final desired temperature sensor object. The bus object is passed to the analog input object constructor. The analog input object is passed to the temperature sensor object constructor.

The rest of the program is similar to the first and second example programs: In an event loop, the temperature is sampled using sensor.Get and the result published in a web page and written to the display.