Skip to content

Building-blocks to customize communication for Layer-7 services on interchangeable Layer-2/Layer-1 interfaces

License

Notifications You must be signed in to change notification settings

F-L-X-S/BusBricks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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
Modbus-RTU implementation
I2C implementation
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().

class CommInterfaceBase: public ErrorState{
    public:
        CommInterfaceBase(): ErrorState(){};
}

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);

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);

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 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:

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

  • deriving a service-class from the service-template
  • 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){}

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

About

Building-blocks to customize communication for Layer-7 services on interchangeable Layer-2/Layer-1 interfaces

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages