Home > OS >  How to architect an embedded system with multiple input and output capabilities. Some based on hardw
How to architect an embedded system with multiple input and output capabilities. Some based on hardw

Time:04-28

I have an ESP8266 project programmed in the Arduino framework that gathers data from the network and then displays on a display. The device can be built with a few different display hardware types (eink, led, oled). These are set at compile time with #defines. However there are also a few different type of data and data transport mechanisms that can be used. Some require hardware (LoRa TX/RX) and are enabled at compile time but some can be changed at runtime based on user settings (eg. HTTP or MQTT).

I'm already using a factory design pattern to instantiate the Data transport object at runtime but still use compile time build flags to select which display hardware to use. I have a Display class, a Datasource class and a Config class. This has worked well but is now reaching its limit as I try to add Cellular functionality.

I wonder if there is a good design pattern / architecture design that will facilitate this kind of flexibility without having to keep adding more and more intrusive #ifdef statements all over my code.

Attached is a little mind map of the basic layout of possibilities of this device.enter image description here

CodePudding user response:

If you want to make a decision what algorithn should be injected at runtime, then you can try to use Strategy pattern.

As wiki says about strategy pattern:

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use

So you can read your config file and choose what object should be instantiated. For example, you have many displays:

public enum DisplayMark
{
    Samsung, Sony, Dell
}

and then yoy should create a base class Display:

public abstract class Display 
{
    public abstract string Show();
}

And then you need concrete implementations of this base class Display:

public class SamsungDisplay : Display
{
    public override string Show()
    {
        return "I am Samsung";
    }
}

public abstract class SonyDisplay : Display
{
    public override string Show()
    {
        return "I am Sony";
    }
}

public abstract class DellDisplay : Display
{
    public override string Show()
    {
        return "I am Dell";
    }
}

So far, so good. Now we need something like mapper which will be responsible to bring correct instance by selected display from config:

public class DisplayFactory
{
    public Dictionary<DisplayMark, Display> DisplayByMark { get; private set; } 
        = new Dictionary<DisplayMark, Display>
    {
        { DisplayMark.Sony, new SonyDisplay()},
        { DisplayMark.Samsung, new SamsungDisplay()},
        { DisplayMark.Dell, new DellDisplay()},
    };
}

and then when you will know what display should be used from config file, then you can get desired instance:

public void UseDisplay(DisplayMark displayMark) 
{
    DisplayFactory displayFactory = new DisplayFactory();
    Display display = displayFactory.DisplayByMark[displayMark];
    // Here you can use your desired display class
    display.Show();
}
  • Related