Elixir for Your IIoT Edge

Umair Iftikhar
6 min readAug 1, 2024

Scenario:

You have various temperature sensors, Modbus PLCs, and TCUs, all connected to a single-edge device. This device gathers data from the sensors, buffers it if the internet is down, and uploads it to the cloud using MQTT.

Why Elixir is a Great Choice:

Elixir is an excellent choice for this system because of its ability to handle multiple communication protocols (like Modbus TCP, Modbus RTU, OPC-DA, OPC-UA) simultaneously. Elixir uses a model where each protocol is managed separately, making it efficient at handling incoming data streams. Additionally, Elixir’s built-in tools make it easy to set up a robust data buffering system, ensuring that your data remains safe even during internet outages.

Elixir also offers mature libraries for MQTT, facilitating a seamless connection to the cloud. Its inherent fault tolerance is crucial for managing potential communication errors, sensor failures, or network disruptions, making the system highly reliable.

Useful Elixir Libraries and Tools:

For building embedded systems, Nerves provides a solid foundation. Gin is a popular framework for creating concurrent applications, and MQTTex is a reliable Elixir library for MQTT. More we discuss in the Architecture section.

Additional Tips:

For real-time data processing, consider using Elixir’s built-in performance optimization techniques. Implement robust data validation to ensure data quality, and prioritize security measures due to the sensitive nature of IoT data. Elixir offers tools for secure code development and deployment. Additionally, write comprehensive tests to ensure code reliability.

Architecture for IIoT Edge with Elixir

The proposed architecture leverages Elixir’s strengths in concurrency, fault tolerance, and functional programming to create a robust IIoT edge device capable of handling multiple communication protocols, buffering data, and transmitting it to the cloud via MQTT.

Core Components

  1. Supervisor
    - The heart of the system, responsible for overseeing all child processes (actors).
    - Implements a hierarchical structure for effective management and error handling.
    - Restarts failed child processes to ensure system resilience.
  2. Protocol Actors
    - Separate actors for each communication protocol (Modbus TCP, Modbus RTU, OPC-DA, OPC-UA).
    - Responsible for establishing and maintaining connections.
    - Continuously read data from connected devices.
    - Format data into a standardized structure.
    - Forward processed data to the data processing actor.
  3. Data Processing Actor
    - Aggregates data from different protocol actors.
    - Performs necessary calculations or transformations (e.g., unit conversions, data normalization).
    - Implements data validation and quality checks.
    - Stores data in a local buffer for offline storage or later transmission.
  4. MQTT Actor
    - Monitors internet connectivity.
    - Manages the MQTT connection to the cloud.
    - Periodically checks the data buffer for unsent data.
    - Publishes data to the cloud as MQTT messages.
    - Implements retry mechanisms for failed transmissions.
  5. Data Flow
    - Data Acquisition: Protocol actors continuously read data from connected devices.
    - Data Preprocessing: Data processing actor performs initial processing and validation.
    - Data Buffering: Processed data is stored in a local buffer.
    - Data Transmission: MQTT actor sends buffered data to the cloud when internet connectivity is available.

Understanding the Requirements

The proposed architecture involves:

  1. Protocol Actors: These handle communication with different devices using their respective protocols (Modbus TCP, Modbus RTU, OPC-DA, OPC-UA).
  2. Data Processing Actor: This centralizes data from various protocol actors, performs necessary transformations, and prepares data for transmission.
  3. MQTT Actor: This handles the connection to the cloud and publishes processed data via MQTT.

Key benefits of this approach:

  • Decoupling: Protocol actors and MQTT actor operate independently, improving system flexibility.
  • Data centralization: Data processing occurs in a single place, allowing for efficient management and analysis.
  • Scalability: The system can easily accommodate additional protocol actors or changes in data processing logic.

While this structure is sound, consider these points:

  • Data Buffering: Implement a buffer in the data processing actor to handle temporary data storage, especially during network disruptions or high data rates.
  • Error Handling: Robust error handling mechanisms are crucial for each component.
  • Data Security: Ensure data is encrypted before transmission.
  • Performance Optimization: Profile your code to identify bottlenecks and optimize accordingly.

Before diving into specific libraries, let’s re-iterate the core components of our IIoT edge architecture:

  • Supervisor: Oversees all child processes (actors).
  • Protocol Actors: Handle communication with different devices (Modbus TCP, Modbus RTU, OPC-DA, OPC-UA).
  • Data Processing Actor: Aggregates, processes, and validates data.
  • MQTT Actor: Handles communication with the cloud via MQTT.

Elixir Library Recommendations

Supervisor

  • Supervisor: Elixir's built-in supervisor module is the standard choice. It provides a robust mechanism for managing and restarting child processes.

Protocol Actors

  • GenServer: This is the most common choice for creating actors that handle stateful computations. It's suitable for protocol actors that need to maintain connections and handle incoming data.
  • Task: For simple, stateless tasks, Task can be considered. However, GenServer is generally preferred for protocol actors due to their potential complexity.
  • Protocol-specific libraries:
  • Modbus: modbux (as mentioned earlier)
  • OPC-UA: elixir_opcua (though check its maturity and features)
  • OPC-DA: While less common, there might be libraries or custom implementations.
  • Serial communication: erlang:io or Port for basic serial communication.

Data Processing Actor

  • GenServer: Again, GenServer is suitable for managing state, such as data buffers and processing logic.
  • Data structures: Elixir’s built-in data structures like Map, List, or Agent can be used for data storage and manipulation.

MQTT Actor

  • MQTTex: A popular choice for MQTT communication in Elixir.

Additional Libraries

  • Error Handling: Use try/catch or Supervisor strategies for error handling.
  • Data Buffering: Consider using ETS or Mnesia for efficient in-memory storage.
  • Performance: Profile your code to identify bottlenecks and optimize accordingly.
  • Testing: Write comprehensive tests to ensure code quality.
  • Configuration: Use configuration files (e.g., config/config.exs) to manage settings.

Elixir vs. Other Languages for IIoT Edge

While other languages offer similar capabilities, Elixir’s unique combination of features makes it particularly well-suited for the challenges of IIoT edge development.

Elixir’s Strong Competitors

  • Rust: Known for its performance and safety, Rust offers strong concurrency and error handling. However, its steeper learning curve and smaller ecosystem compared to Elixir might pose challenges for some projects.
  • Go: Offers concurrency, garbage collection, and a growing ecosystem. It’s simpler to learn than Rust but might lack some of Elixir’s functional programming advantages.
  • Python: Widely used with a vast ecosystem, but its global interpreter lock (GIL) limits concurrency, making it less ideal for real-time applications.

C vs. Elixir for IIoT Edge Development

C is undoubtedly a powerful language with a long history in systems programming and embedded systems. It offers unparalleled control over hardware, making it a strong contender for IIoT edge devices.

C’s Strengths for IIoT Edge:

  • Performance: C is renowned for its efficiency, making it ideal for resource-constrained edge devices.
  • Hardware Control: Direct access to hardware registers and peripherals is essential for many IoT applications.
  • Mature Ecosystem: A vast array of libraries and tools exist for C, supporting various hardware platforms and communication protocols.

C’s Challenges for IIoT Edge:

  • Complexity: Developing complex IoT applications in C can be time-consuming and error-prone due to manual memory management and low-level programming.
  • Concurrency: While possible, handling concurrency in C requires careful programming and is often more challenging than in languages like Elixir or Rust.
  • Development Time: Compared to higher-level languages, C development can be slower.

Elixir’s Advantages Over C for IIoT Edge:

  • Productivity: Elixir’s functional paradigm and rich standard library often lead to faster development times.
  • Concurrency: Elixir’s actor model excels at handling concurrent tasks, making it well-suited for IoT applications with multiple sensors and devices.
  • Fault Tolerance: Built on the Erlang VM, Elixir inherits robust error handling and recovery mechanisms.
  • Rapid Prototyping: Elixir’s interactive development environment facilitates rapid prototyping and experimentation.

Concurrency in IIoT Edge Development

What is Concurrency?

Concurrency refers to the ability of a system to handle multiple tasks or processes simultaneously. This doesn’t necessarily mean that these tasks are executed at the same exact time, but rather that they progress independently, allowing the system to make progress on multiple fronts.

Why Concurrency is Crucial for IIoT Edge

In the context of an IIoT edge device, concurrency is essential for several reasons:

  • Handling Multiple Sensors and Devices: An edge device often interacts with numerous sensors, actuators, and communication protocols. Concurrency allows the device to manage these interactions efficiently without blocking the system.
  • Real-time Processing: Many IoT applications demand real-time data processing. Concurrency ensures that data from different sources can be processed promptly without delays.
  • Network Communication: Concurrent handling of network connections is critical for reliable communication with the cloud or other systems, especially in environments with intermittent connectivity.
  • Resource Optimization: By effectively managing concurrent tasks, an edge device can optimize resource utilization, such as CPU and memory.
  • Fault Tolerance: Concurrency can help to isolate failures and prevent cascading effects, improving system reliability.

Elixir’s actor model is particularly well-suited for handling concurrency in IIoT edge applications. Each actor can independently manage a specific task, such as communicating with a sensor, processing data, or sending data to the cloud. This approach promotes scalability, fault tolerance, and efficient resource utilization.

--

--

Umair Iftikhar

In the tech industry with more than 15 years of experience in leading globally distributed software development teams.