BusBricks: BusBricks
BusBricks  0.1
Customize bus-communication
Loading...
Searching...
No Matches
BusBricks

OSI-oriented modularization to customize communication between Layer-7-services on interchangeable Layer-1/2-interfaces.
The architecture is usable for all MCUs of the Arduino-platform.
📖 Checkout detailed Documentation

Vision

Provide configurable software-modules to customize the communication between services, hosted on different MCUs.
BusBricks are templates to derive classes for mapping service-specific rules to different bus-systems. The communication between the hosting devices is therefore separated from the inter-service-communication. That means, the communication between the services can follow it's own (from the chosen communication-interface independent) rules.

example use-case: Messenger-service

Hosting a messenger on different host-devices. Some of those hosts support a serial commuication for modbus-RTU, some only support I2C.

Roadmap:

Message-Service implementation
  • Message content and payload definition
Modbus-RTU implementation
  • Response-Timeout for Message-Service
I2C implementation
  • Definition of a Frame derived I2C-Frame-class
  • Setup of an I2C service-interface
User-interaction
  • Setup of a tiny chat-interface in main

Basic Concepts

Service- and Communication-Layer

The separation of inter-service- and inter-mcu-communication-rules requires a separation of the software modules.

Service-Layer

Services are defined in the service-layer. All implementations in the service-layer are Service-specific and therefore they should not depend on the chosen communication-interface. That means, the service-layer-modules should be reusable, independent which communication-interface the host-device supports.

Communication-Layer

Modules being part of the Communication-Layer are specific for the chosen bus (e.g. I2C, OneWire, Musbus-rtu...) the host device provides. They are responsible for applying the bus-protocol-specific rules (e.g. CRC-check and -calculation, communication-timeouts, framelength...) to the content (PDU) provided by the services.

Content and representation

The cascading of processing information and the rules applied to it lead to the concept of content- and representation.
In every iteration, an information is processed and the rules of the next level, closer to the physical layer are applied to it, the information closer to the format the service is able to process is called Content.
Conversely, the format, the information has after applying the rules of the next level closer to "Layer-0" is called representation.

Classdiagram

classdiagram

Protocoll-stack

protocollstack

Stackprocessing

stackProcessing

Error-handling

The Errors of a device are managed by a Service-derived ErrorService. Every component (no matter, if Service, ServiceInterface or CommInterface) can be enabled to raise errors by deriving the ErrorState and calling raiseError().

public:
}
Communcation-Interface-Base-Class of the CommInterface template specifies a standardized interface to...
Definition CommInterface.h:54
CommInterfaceBase()
Construct a new Comm Interface Base object.
Definition CommInterface.h:96
A class to manage and track error states using error codes.
Definition ErrorState.h:49

The class-instance is now able to raise Errors and, if applicable, to handle them within the class itself. E.g.:

// wait for Frame-timeout to ensure frame is complete, raise Error, if the silence-time is violated
if((_clearRxBuffer()>0) & (receiveBuffer->getSize()!=0)){
(numBytes>=MAXFRAMESIZE) ? raiseError(frameLengthError):raiseError(arbitrationError);
}
// handle the Arbitration-Error with waiting for bus-silence
if(getErrorState()==arbitrationError) while(_clearRxBuffer()!=0);
#define MAXFRAMESIZE
debugging flag to print debugging-information on Serial
Definition CommInterface_modbusRTU.h:43
@ arbitrationError
Violated silence-time between frames.
Definition Error.h:66
@ frameLengthError
Maximum framelength violated.
Definition Error.h:78

To handle the Error outside the instance, the Error-state of the components has to be processed within the ServiceInterface by checking the ErrorState of the called instances and, if applicable raising the Error with the same Error-code within the ServiceInterface:

errorCodes commInterfaceErrorState = comm_interface->getErrorState();
if (commInterfaceErrorState!=noError) raiseError(commInterfaceErrorState);
errorCodes
Enumeration for various error codes.
Definition Error.h:47
@ noError
no Error
Definition Error.h:51

Only Errors raised in ServiceInterface are forwarded to an eventually registered ErrorService, that can execute error-code-specific actions (like printing the Error or broadcasting errors to the network).
After handling the Error of the called instance, the ErrorState has to be cleared:

comm_interface->clearErrorState();

The Error-codes are enumerated in the Content-derived Error-class. To add new Error-cases, define a new Error-code in enum ErrorCodes and add an Error-message to the getErrorMessage(errorCodes code)function within the Error-class. The Error-Service will display those messages after an Error is either raised by another component on the local device or an Error-Frame was received.

Deriving a custom Service

To derive a Layer-7-service, that can be used in combination with the predefined Layer-2-communication-interfaces, you have to

  • define content to handle
  • define a service to handle the specified content

Define the content

Each Service has to know, how the conversion between the processable data structure (content) and the payload, that is sended or received by the communication-layer (representation) is defined. This is done by

The mapping between content and representation (in this case String) is defined by:

  • overwriting and implementing the template functions content_to_rep() and rep_to_content() of Content

Define the service

A Service is meant to process incoming payload and eventually generate new payload to be send. Received Payload is added to the Service with impart_pdu and the services response is able to be picked up by calling get_response. Both functions of the service-template are using the representation (for services contents always String) of the Content.
Defining a custom service is done by

  • defining a default Service-ID (unique for each service)
  • define construction with Service-ID and Instance-ID (unique for each instantiation of a service), e.g.:
    #define SERVICEID 'e'
    ErrorService::ErrorService(uint8_t instance_id): Service<Error, STACKSIZE>(SERVICEID, instance_id){}
    #define STACKSIZE
    Number of elements the services stacks can store.
    Definition ErrorService.h:38
    #define SERVICEID
    Service-id ASCII: "e".
    Definition ErrorService.h:41
    Represents an error with it's error-content (error-code and an error-message), and provides methods f...
    Definition Error.h:176
    ErrorService(uint8_t instance_id)
    Constructor for ErrorService with default Service ID "m".
    Definition ErrorService.cpp:26
    Service-Template to derive a Service class by defining the Content (derived Class of "Content") to ha...
    Definition Service.h:115

The stack_processing function has to process all payloads from the receive-stack and add the payloads to be send to the send-stack. Check ErrorService or MessageService for example implementations.

  • adding functions to interact with the service besides imparting/picking-up payloads (e.g. sendMessage of the MessageService) (optional)

Build environments and testing

clang-build for local debugging

The project is implemented to be debugged on the local engineering-device (PC). Therefore testing-functions are defined for the local execution with clang (possible with other compilers, but only tested with clang).
Those functions are more thought as a "playground" for setting up scenarios to debug, than for approving complete functionality of the Program.
The clang-build is customized in tasks.
Because of the usage of Arduino-specific functions not supported by c++ natively, those functions are replaced by using the namespaces in Arduino-Mocking and Serial-Mocking.

native environment

The pio native-environment is setup in platformio.ini. All automated tests are executed in this environment.

uno and nano328p environment

The environments for the target-architecture are configured in platformio.ini.

License

GNU Affero General Public License v3.0

This project is licensed under the GNU Affero General Public License v3.0. See the LICENSE file for details.

Copyright (c) 2024 Felix Schuelke