Deutsch: Ich stelle eine
firmware in Entwicklung vor, die für unterschiedliche Microcontroller und Hardware-Kombinationen genutzt werden kann, um (z.B. per I2C, WiFi, BT etc.) ferngesteuerte Motorcontroller mit Drehwertgeber/Encoder und Endstops zu realisieren, die autonom und mit PID-Regelung Aufgaben wie 'fahre bis X', 'fahre mit Geschwindigkeit Y' etc. erledigen können. Die firmware ist flexibel nutzbar, soll aber natürlich vor allem in ft Lösungen zum Einsatz kommen. Kommentare sind willkommen.
Hi all,
@rubem, your new PCB looks very neat! And I understand only too well why you went for a non sandwiched design. I used one in the (unpublished) Adafruit V2 motor controller shown above, and also
here, and things tend to become very complex and tangled up soon with these designs.
A bit of an update on my own progress. After having finished my hardware tests, I've been working on the firmware the last couple of weeks.
The discussion we've had here and my prior attempts at this issue tell me that the general idea (µC + encoder + motor driver + endstops + remote control) is quite a universal one. So I realized I'd find it unsatisfactory to develop one specific solution for one specific hardware setup.
Wouldn't it be nice to have a firmware framework that could be adapted to different microprocessors, motor drivers, rotary encoder, endstop setups, and interfaces?
This is the concept that emerged under the acronym "QPID". The goal is to have a generic, adaptable firmware prototype which provides
- up to 16 "QPID" units,
- with each QPID unit consisting of one motor driver, one rotary sensor, and up to two endstops,
- with each QPID unit being able to autonomously perform PID controlled tasks like running to a given position, running at a given speed for some time or until told otherwise, finding endstops, self-calibration etc.,
- generic way of remote access usable by any kind of interface (I2C, ISP, ESP-now, Bluetooth...)
- flexible configuration of pin assignments, hardware setup etc. via interface
Problem is, this idea calls for an objective oriented design, which led my to overcome my frustration with c++ and have another look on how it's done there (and yes, I still think that it's a mess of a programming language, and still oftentimes struggle with its cluttered design).
My approach is to have a class library which abstracts all of the above features into base classes, from which classes representing specific hardware have to be derived. Most of the work is already done, with very little testing yet. And the communication aspect is still in an early stage of development, I chose CBOR encoding to make it as flexible and future proof as possible, too.
This for example is the abstract definition for the most trivial class, the endstop class:
Code: Alles auswählen
class QPID_Endstop : public QPID_Object {
public:
virtual bool isOn(); ///< true if switch currently activated
virtual bool processMessage(uint8_t *m);
};
All it defines is an abstracted access to the endstop's state (on or off).
This is its derived class "QPID_Digital_Endstop" which implements the simple case that each endswitch has its own input pin:
Code: Alles auswählen
class QPID_Endstop_Digital : public QPID_Endstop {
public:
bool isOn() override;
bool processMessage(uint8_t *m) override;
void init(int8_t switchPin, bool useInternalPullup, bool activeHigh);
private:
int8_t pin;
bool activeLow;
};
As we don't want to hardcode the configuration into the firmware and leave it up to the master to tell the system what setup to use, all configuration will usually be done after the object's creation via the message system. That's why I don't use constructors, but an init() method for initial configuration. The message system is implemented by the processMessage() method that interprets messages handed down by the main system locally at the subclass level.
The object oriented design allows to easily add other endstop types, e.g. pinshared analog or hooked up to an I2C port expander. It could also be used to implement debouncing, hysteresis etc. without having to tinker with the overall system. Motor drivers and rotary encoders follow the same pattern. This enables maximum flexibility hardwarewise.
As I feel it is too early to share the code, I uploaded the
doxygen documentation here. This is the
current class diagram with two kinds of motor drivers, two kinds of encoders, and two kinds of endstops already implemented:
The
QPID-Unit is where to main work is happening. It bundles one motor driver, one encoder and up to two endstops objects and is internally controlled by a state machine which performs the currrent task it has received by a command either by a direct function call or via communication interface:
This is my first serious effort in programming a larger project in more than 25 years, so I invite everyone for comments. Particularly I'd be interested if I overlooked some possible use cases in designing the overall system, e.g. in the above commands and states offered by the system.
Best
Jan