A simple robot-companion for my cat. Main project features:
🔶 Architecture-oriented
🔹➡️ Develop the right architecture using architectural best practices
🔶 Result-oriented and minimalistic
🔹➡️ Progress over perfection
🔶 Integration-oriented.
🔹➡️ Reusing existing libraries, projects, etc. Minimum development of individual pieces
🔶 Reusable in my other projects
🔹➡️ It should be done automatically with a good architecture. But I want to make several special decisions to make the project easy to transfer to my another robotic project.
Separation of the Software (independent of the hardware logic) and the Firmware (dependent on the hardware) has allowed me to implement the main robot logic on Windows without involving MCUs at all.
I packed all the code in a set of CMake projects producing static libraries. Including main.cpp - it is also a component. Then I assemble everything into an executable file - currently for the Windows platform. It can be called a Windows robot simulator:
The next step is to implement the driver part and then connect them together.
Quick QnA:
Can it be done directly on MCU? Yes.
Would it be faster to do this? Yes!
Would it be scalable when the project grows? No!
Could I easily port parts of the code to my other projects? No.
Initially I wanted to implement interfaces as simple header files, but after reading articles from Jacob Beningo (e.g. https://www.beningo.com/how-to-write-epic-hardware-abstraction-layers-hal-in-c/) I’ve decided to go with abstract classes and structures with function pointers. This allows me to pack the interface as separate components, decoupling other components even more. The result is on the updated component diagram:
Before coding, let’s decide on the Software Architecture. Based on my previous decision, I’m going to have two main parts:
Main Application
Movement Server
Movement Interface - to communicate between them (e.g. CAN library)
Each part will have a high-level and a low-level logic. I will use two terms, movement - for transportation in space, and motion - for any other physical activities. In total, we will have:
Motion Controller
Motion Driver
Movement Controller
Movement Driver
To break the direct dependency, Controller→Driver let’s use a Hardware Abstraction Layer Interface, so we will have a dependency on a stable interface rather than on a driver that can be changed in the future:
Controller→HAL←Driver
By doing so, we isolate the high-level logic of hardware changes.
Let's introduce two more terms:
Software - logic, depending on a use case, a practical application.
Meanwhile, I’m preparing a simple prototype, since my architecture already has enough information for hardware steps.
I'm going to go cheap 28BYJ-48 stepper motors and probably a DC motor for the cat toy. When it is done, I would like to port it to a different hardware and verify that my architecture does not require any changes, something like this:
The system contains four main building blocks. I'll describe them next. I tried to be general in the terms, so I could decide on the details later, keeping the architecture relevant whatever I will decide to change later.
1 - The Main Controller
It consists of a Controller module and a CAN Module. The Controller uses GPIO to interact with the Cat Interaction Mechanism and the CAN module.
2 - Physical Transportation System
Also has a brain, interaction via CANbus with the main controller. Uses some physical transportation mechanism (wheels, legs, etc.) via an Interface module (e.g. a motor controller)
3 - Cat Interaction Mechanism
It is a mechanical device connected to the main controller. The interface module takes a logical commands (e.g. via GPIO configuration) and translates it to the mechanical movement
After some thinking process, I’ve split the robot into 4 subsystems
Main Controller
Physical Transportation Subsystem
Power Subsystem
Cat Interaction Mechanism
To illustrate the structure, I used the Block Definition Diagram of SysML (I hope, correctly):
As you can see, the CAN interface is already part of the architecture. It is so, because I want to reuse it in my other robot that already uses CAN but.
Meanwhile, I’m not sure 100% about the interface detail for the Cat Interaction Mechanism… Probably I’ll remove it, since I don’t have a reason to decide about it right now, and according to the Clean Architecture principles, decisions about details should be delayed as much as possible.
Next time I'm going to develop white boxes for the subsystems.