This document describes the main components within the VLCB library and how they interact.
This VLCB library is based on Duncan Greenwood's CBUS library and extended with VLCB specific features.
This library is still work in progress.
The code is organised as a central controller object that controls functionality via a storage object and a set of service objects.
Services implement various groups of functionality within VLCB such as events or DCC control. The user sketch can select the set of services needed to provide the necessary functionality for the VLCB module that is created. There are also services for user interfaces and communication over different transports such as CAN, Wi-Fi and BLE. Currently only a CAN transport using the CAN2515 chip is included in the library.
The library supports storage for node variables and event variables in EEPROM or Flash memory. A Configuration object controls persistence of node specific data such as parameter, node variables and events using the chosen storage.
A class diagram is shown below with a selection of concrete implementations of transports, storage,
and services.
- There is no message queuing in this diagram. Message queuing is expected to happen in the transport layer. Some transport hardware has message queues builtin, for others the transport implementation will include queues.
The Controller maintains a Action
bus, which is implemented as a circular buffer.
Each service can put actions on this bus and act on actions placed there by other services.
An action represents tasks or information to be shared with other services.
The Action bus decouples services from each other and makes it easier to add new services.
The main workflow is that the VLCB Controller object runs every so often from the sketch loop() function. During each iteration the controller calls out to each service to do any processing it needs to do. The top element on the action bus (if any) is included in this call.
The CanService
checks for incoming messages on the CAN bus and if the action object passed
from the controller is an outgoing message it sends it to the CAN bus.
The EventConsumerService
may react to consumed events by calling a user registered callback so that
the user sketch can act on this event for example to turn on an LED or move a servo.
The user sketch may produce events that are managed by the EventProducerService
object which then passes
the event as an Action via the Controller to the transport object.
Most of the VLCB functionality uses a message object VlcbMessage
that passes incoming and
outgoing messages around via the Action bus.
The VlcbMessage
object contains 8 bytes where the first is the op-code and the remaining 7 bytes
are any optional data bytes for that op-code.
The CanService
translates the VlcbMessage
to/from a CANFrame
class that contains
CAN specific fields and a VlcbMessage
in its data portion.
The CanTransport
object translates the CANFrame
object to and from an object that represents
a CAN frame containing an id, 8 bytes of data (same as the VlcbMessage) and the flags
rtr
and ext
.
This CAN frame is then passed to or from a CAN driver, such as CAN2515.
CAN drivers may need to convert this CAN frame to a data structure used by any library
that is used by that driver.
The Configuration object stores node variables (NV) and event variables(EV) and any other configuration that is required. It makes use of a storage object that has different implementations for different storage types. Not all Arduino modules have EEPROM or enough EEPROM, in which case external EEPROM or Flash memory can be used. See further details in Persistent Storage documentation.
The configuration support is divided into a Configuration object that manages NVs and EVs. It provides functions that deal with these NVs and EVs. It makes use of a Storage interface where implementing classes store the data at a given address.
There are a few implementing storage classes:
EepromInternalStorage : Stores data in EEPROM directly on the processor.
EepromExternalStorage : Stores data in external EEPROM connected via I2C.
DueEepromEmulationStorage : Support by emulating EEPROM for the Arduino DUE.
FlashStorage : Stores data in Flash memory. Useful for modules that do not have onboard EEPROM or too little EEPROM.
The interpretation of incoming messages is handled by a set of services. VLCB offers up a message to each service in turn. If a service is able to handle that message no further services will be offered the message. Thus, the order of configured services is important.
Read more about the Service
interface and how services work in
Service documentation.
Examples of some services:
MinimumNodeService : Handles all the mandatory op-codes that all VLCB modules must implement. These op-codes involve running modes and basic node configuration such as node number and module parameters.
EventConsumerService : Handles incoming events that shall result in actions on the module. If the COE parameter is set it will also handle outgoing events.
ConsumeOwnEventsService : Enables passing events produced by the producer service back to the consumer service. It doesn't do anything else than setting the COE parameter flag which also tells the EventConsumerService to also look for outgoing events on the action bus.
LongMessageService : Handles the long message extension to CBUS as defined in RFC005.
LEDUserInterface : Implements a low level UI using a push button, a green LED and a yellow LED.
It will be possible to implement new user interfaces that make use of, for example, an OLED screen or simply use the USB connection for serial communication.
Diagnostics are optional. The implementation of diagnostics uses substantial amount of memory which might not be available in smaller processors. This library provides a choice to enable or omit diagnostics by using a variation of service classes.
The services listed above do not have diagnostics included.
To enable diagnostics choose service classes with a "WithDiagnostics" suffix.
E.g. The CAN service class CanService
does not provide diagnostics
while the class CanServiceWithDiagnostics
does provide diagnostics.
Note: Not all services have a diagnostics enabled counterpart yet.
A user sketch needs to set up the required VLCB objects and then call VLCB.process()
from
the main loop.
The setup code may look like:
// Global definitions
VLCB::LEDUserInterface userInterface(greenLedPin, yellowLedPin, pushButtonPin);
VLCB::CAN2515 can2515(interruptPin, csPin);
VLCB::CanService canService(&can2515);
VLCB::EepromInternalStorage eepromStorage;
VLCB::MnsService mnsService;
VLCB::EventConsumerService eventConsumerService(myActionCallback);
VLCB::Controller moduleController(eepromStorage, {mnsService, userInterface, canService, eventConsumerService});
setup()
{
canTransport.setNumBuffers(2);
canTransport.setOscFreq(OSC_FREQ);
canTransport.begin();
}
See also the example sketches how to use the VLCB library.