Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Design patterns in action with Arduino

4.62/5 (10 votes)
19 Feb 2014CPOL11 min read 57.4K   565  
A sample project with Arduino.

Introduction

I have a daughter at primary school. As a father, I have some responsibilities, of course. I like being a father, I have no problem with that. What bothers me is the endless math study sessions in that business. It is really killing me sometimes. Again and again, 2+2, 5+6, 4+3. That was start, now it is time for subtraction: 5-4, 10-4 .. and everybody knows that it has no end. I said enough and decided to leverage the technology. You know, technology for people, and now for me. Come here my dear Arduino, I need you.

I was hoping that this project would be easy to implement at the very beginning. All I would need to do is to write some functions to show some numbers and maybe a buzzer and some LED to make it interesting. However, things were changed after I started to think about it elaborately, Some hardware management issues came out, then UI management, then content management. Our small Arduino application changed into something that had to be handled more seriously which leads me to write this article. Let's start with the requirements.

Project Requirements

  • System shall show a menu on a display for the basic operations: addition, subtraction, multiplication, division
  • User (my daughter) can select an arithmetic operation to study from the menu with a keypad.
  • There shall be some difficulty levels. After operation selection, difficulty level selection will appear on the display.
  • According to difficulty level, random questions shall be shown on the display and user can answer the question with the keypad.
  • User can correct her answer before entering it.
  • A message shall be displayed according to the correctness after entering an answer.
  • The correct answer shall be displayed after 3 wrong answers.
  • User can browse menu (menu-up or selecting menu items)
  • The system shall have audio and visual warning infrastructure. Messages will be accompanied by that infrastructure.
  • Each operation will have a time-limited quiz section.
  • Quiz section shall start to randomize questions beginning with the easiest level and goes up to higher difficulty levels.
  • Statistics will be displayed at the end of the quiz (how many questions asked, how many of them answered correctly)
  • System can take attention when she comes around.
  • There shall be some fun part out of math which directs user to do something like 'sing a song', 'kiss your daddy' etc. Else the user acceptance test is absolutely going to fail!
  • Warning infrastructure can be used as fun material in the fun part.

Hardware

What we need in this project:

  1. Arduino Mega
  2. Serial LCD
  3. Matrix keypad
  4. Analog keypad
  5. PIR motion sensor
  6. LED & 400-ohm resistor
  7. Digital buzzer module
  8. Hook-up wires

and see the following for the hardware design.

Image 1

Notes for mismatching parts:

  1. LCD is a serial LCD.
  2. Arduino UNO should be replaced with Mega.

(I started this project with an Arduino UNO, but decided to continue with Arduino Mega because of more memory need. At the beginning, everything was good with the Arduino UNO. However, when the code started to get bigger, after a point, I couldn't manage to hold the RAM usage below the capacity of the Arduino UNO and as you expect, it ended up with Arduino Mega, which has 8K of SRAM.)

Software Design


Image 2

Image1: Overview of the design.

The system can be divided into 2 main parts. As you see in the Image1, the first part is responsible for the hardware management.

  • Input System: We have 2 different keypads and they are merged into one as seen by other components. Unified keyboard informs registered client when any of keys is pressed from any keyboard (matrix keypad or adkeyboard).
  • Output system: Serial LCD with added functionality.
  • Signalling: Unified signalling sub-system. It is composed of a LED and a digital buzzer and it provides the capability of defining different signal patterns to the client code. Client code can start any pattern according to its need.
  • Motion detection: Motion detection capability with PIR sensor. When somebody is detected, it triggers a signal to take attention.

The presentation layer is responsible for the user interaction. It contains infrastructure for visual items (menus and pages) and content management sub-system.

  • UI management: In this sub-system, visual items are defined. A menu lists items on the display for user selection. A menu item may represent a sub-menu or a page. User can select a menu item with its displayed index by pressing corresponding key on the keyboard. One can go up in the menu by pressing 'Escape' key. If the selected menu item is a page, it is displayed, then. A page is responsible to show the content attached to it. Page waits for user input to change its content, or owner menu is displayed if 'Escape' is pressed. While in a page, pressing 'Enter' key results in evaluation of the answer. If user makes a mistake, she can delete her answer by pressing 'Backspace' key. According to the correctness of the answer, appropriate signal is triggered.
  • Content management: This sub-system provides content to be displayed on the screen. Algorithms for different levels of arithmetic operations are provided. Client code (a page) requests content from this sub-system.

Simplified class diagrams are the following. These images show the basic structure to make easy to understand the actual class implementations.

Hardware Management

MFK_InputDevice merges Keypad2 and AdKeyboard into one device. It handles their events and produces a new event for its clients with a new set of keys which are given below.

 


Image 3

 

Image2: Input sub-system.

Key mappings:

Keypad Button Key Value (hex)
Matrix 0 '0' 0x30
Matrix 1 '1' 0x31
Matrix 2 '2' 0x32
Matrix 3 '3' 0x33
Matrix 4 '4' 0x34
Matrix 5 '5' 0x35
Matrix 6 '6' 0x36
Matrix 7 '7' 0x37
Matrix 8 '8' 0x38
Matrix 9 '9' 0x39
Matrix * Escape 0x1B
Matrix # Enter 0x0D
AD S1 Backspace 0x08
AD S2 F1 0x80
AD S3 F2 0x81
AD S4 F3 0x82
AD S5 F4 0x83

 

 

MFK_OutputDevice is derived from SerialLCD class. It enhances SerialLCD for that project by combining functions of SerialLCD.

 

 

Image 4

Image3: Output sub-system.



A signal pattern is built by using signal sources. A pattern should be stored in the signal controller with an index. The only thing to start a signal pattern is to call it from the signal controller with its index.

Image 5
Image4: Signalling sub-system.

At the top of the hardware management layer, MFK_Hardware class is placed. It orchestrates all other hardware devices and hides redundant complexity from clients. For example, PIRMotion and SignalController is not exposed to clients. However, input & output devices are mandatory to expose to the outer world since the UI system needs direct access to their functions. Signal patterns are also constructed in that class. They are ready to use by their named indices.

Image 6

Image5: Hardware management

Presentation

This layer is responsible for user interaction. It provides visual elements and content.

ContentFactory creates ContentProviders according to ContentTypeEnum and ContentLevelEnum. Client gets the instance of the content factory, then it can request a content provider.


Image 7

Image6: Content management

VisualItem is the base class for visual elements (menu & page). It also binds hardware management and presentation. 'show' and 'msgbox' functions use the output device (MFK_OutputDevice) and the input device (MFK_InputDevice) calls callback function provided by VisualItem. 'msgbox' function has also the ability of starting a signal pattern by calling 'signal' function of the hardware (MFK_Hardware).

Menu, as the name implies, provides a list of menu items to select. User can select an item with its index, and menu 'show's that VisualItem.

Page is the visual item for displaying content. Except for fun content, it expects an input from user. User 'Enter's her answer, then a message is displayed to inform user. After the message, new content is required from the content management.

Chapter is the intermediate class between ContentProvider and Page. When a page is shown for the first time, its chapter and related ContentProvider are created. User answers are directed to the chapter and correctness of an answer is evaluated in the chapter. Chapter also holds statistics for a study session in that page. FunChapter is a kind of chapter in which an answer is not expected from user. QuizChapter is the time-limited chapter. In a quiz chapter, questions are asked until the end of the time frame.

 

Image 8

Image7: UI management

Implementation

I hope the general structure of the system is clear. Now, it is time to dive into the code where the caption of this article means something.

I'd like to start with MathForKid.ino. It is the main code which is uploaded to Arduino Mega.

 

C++
// File: MathForKid.ino
// hardware management
MFK_Hardware* hw;
 
// presentation
Menu* mainMenu;
 
void setup() {
    // for debugging purposes
    Serial.begin(9600);
  
    // get the instance and initialize it
    hw = MFK_Hardware::getInstance();
    hw->begin();
 
    // create user interface
    CreateUI();
    // show the main menu
    mainMenu->show();
}
 
void loop() {
    // update hardware
    hw->update();
    
    // update active visual item
    VisualItem *v = VisualItem::getActiveItem();
    if(v!=NULL)
        v->update();
}

 

That is all. Run your application on the Arduino. OK, maybe a little explanation would be better Smile | :)

As I said at the very beginning of the 'Software Design' section, we have two main components: one for hardware management and one for presentation. They are defined globally at the top of the code and we initialize both of them in the 'setup' function. 'loop' function invokes the code to update them.

Actually, CreateUI function is also implemented in that file. It creates the user interface where user interaction occurs. mainMenu, all sub-menus and all pages are created and chapter properties are specified for pages in this function.

void CreateUI() {
    mainMenu = new Menu("main");
 
    // addition
    Menu* m = new Menu("+");
    mainMenu->addMenuItem(m);
 
    // level-1 page
    Page* p = new Page("L1");
    p->setChapterProperties(Chapter::NormalChapter, \
            ContentFactory::Addition, ContentFactory::Level1Content);
    m->addMenuItem(p);
 
    // level-2 page
    p = new Page("L2");
    p->setChapterProperties(Chapter::NormalChapter, \
            ContentFactory::Addition, ContentFactory::Level2Content);
    m->addMenuItem(p);
...

Let's continue with the specific design patterns in this application.

As you may expect, MFK_Hardware is an example of facade pattern. It hides complexity of underlying hardware management issues and provides a clean interface to its clients. It is also a singleton since there exists only one instance of it all over the system. To achieve that, its constructor, copy and assignment operator are declared in the private section of the class declaration:

C++
// File: MFK_Hardware.h
// private constructor to achieve singleton pattern
    MFK_Hardware();
    MFK_Hardware(MFK_Hardware const&); // copy disabled
    void operator=(MFK_Hardware const&); // assigment disabled 

and you can only use getInstance static function to access it, which is in the public section:

C++
// File: MFK_Hardware.h
// static method to get the instance 
    static MFK_Hardware* getInstance() {
        static MFK_Hardware hw;
        return &hw;
    };

From http://www.dofactory.com/Patterns/Patterns.aspx:

  • Facade: A single class that represents an entire subsystem (structural pattern)
  • Singleton: A class of which only a single instance can exist (creational pattern)

MFK_InputDevice is also a good example of facade pattern. It merges two different input devices (matrix keypad and adkeyboard) into one. Moreover, it has the subject role of observer pattern. It is a ClientOwner of type MFK_InputDeviceClient where clients can register/unregister to MFK_InputDevice. Therefore, when a change occurs in a state (keypress in that case), all clients are informed. I have used that pattern in many places in that project.

C++
// File: MFK_InputDevice.h
template<>
class MFK_InputDevice<MFK_InputDeviceClient>:
    public ClientOwner<MFK_InputDeviceClient> {
private:
    ...
    // informs registered clients
    void informClients(char);
... 
// File: MFK_InputDevice.cpp
void MFK_InputDevice<MFK_InputDeviceClient>::informClients(char c) {
    for(int i=0; i<5; i++) {
        if(this->client[i]!=NULL)
            this->client[i]->invokeMFKInputCallback(c);
    }
}

ClientOwner implements register/unregister functions. A MFK_InputDeviceClient overrides invokeMFKInputCallback function and registers itself to the ClientOwner. Then MFK_InputDevice can inform them with the callback provided by them. VisualItem inherits from MFK_InputDeviceClient, therefore a visual item can register itself to MFK_InputDevice to observe it.

From http://www.dofactory.com/Patterns/Patterns.aspx:

  • Observer: A way of notifying change to a number of classes (behavioral pattern)

ContentFactory and ContentProvider classes have also some interesting properties. ContentFactory is a singleton and can be thought as some form of factory pattern. It constructs content providers for clients, but content providers vary according to client's need.

C#
// File: ContentFactory.cpp
ContentProvider* ContentFactory::getContentProvider(ContentTypeEnum op,\
        ContentLevelEnum level) {
    if(this->contentProvider[op][level] != NULL)
        return this->contentProvider[op][level];
 
    ContentProvider *p=NULL;
 
    switch(op) {
        case Addition:
            switch(level) {
                case Level1Content:
                    p = new ContentProvider(ContentP_0_0);
                    break;
...
    return p;
}

 

 

From http://www.dofactory.com/Patterns/Patterns.aspx:

  • Factory: Creates an instance of several derived classes (creational pattern)

ContentProvider can be thought as an example of strategy pattern along with Chapter. A chapter is the context where a content provider resides. ContentProvider provides a result (in that case, a question and an answer) with a different algorithm for each instance.
 

C++
// File: Chapter.cpp
char* Chapter::next() {
    char *retval=NULL;
...
    retval = this->contentP[0]->getContent(Chapter::CONTENT, \
                    Chapter::ANSWER);
...
    return retval;
}

From http://www.dofactory.com/Patterns/Patterns.aspx:

  • Strategy: Encapsulates an algorithm inside a class (behavioral pattern)

Conclusion

You know, developing an application is not just coding. Better requirement gathering leads to better design. Better design leads to a better software, and 'better' is defined in many software engineering books (which is not the main subject of this article). To develop better software, it is better to know about design patterns. (how many 'better's do we have in this paragraph, by the way?)

A design pattern, as the name implies, is the generalization of a solution for a kind of problem. Therefore, not to invent the wheel again, a basic knowledge about design patterns makes our job easier.

However, applying a design pattern to a problem is different than knowing it. You cannot force your problem to match the design pattern. Things get worse when you try to change your actual problem to something else. What I do is to tailor the pattern according to my need. If I don't need a structure within the current problem domain, I don't do anything with the expectation of a future release. You may think that statement is very obvious, but I have seen many projects which became overdue just because of that reason. In software development, time is not just money, but equals to money, literally. Whatever you do in excess results in a money loss, both for you and for your client.

There are very good articles about design patterns in our codeproject. I especially recommend you to read design pattern series by . Applause Ian.

I will continue to study on this project. I know there are parts which could be better and any advice will be appreciated. (Fun part is still missing, I had to comment out them because there is something wrong with either the flash library or calls in my code.)

References

  1. http://www.dofactory.com/Patterns/Patterns.aspx
  2. http://www.cplusplus.com/doc/tutorial/
  3. http://arduino.cc/
  4. http://arduiniana.org/libraries/flash/

     

History

This is an ongoing project. Source code may change after the publish of this article. You can download the latest version from https://github.com/ozanoner/arduino.


 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)