I'm trying to implement a basic Observer pattern in for an Arduino project (UNO WiFi Rev2). For my project, I'd like to be able to provide several methods of sending commands (e.g. MQTT, REST API, timers, pushbuttons) to manipulate outputs (e.g. leds, lights, pumps, MQTT subscribers, etc.). I thought it would be slick to implement these command sources as Subjects, while the outputs would be the Observers. I'm still very early in developing and implementing this model. To be transparent, I'm kind of a Google, Copy/Paste, tweak to fit type of developer. So my knowledge of c++ pointers and all could be classified as weak.
This tester project implements a PushButton class as the Subject and a Device as the Observer. I'm not to the point of implementing hardware, so the "push()" of the PushButton is soft. The issue I'm having is with managing adding/removing Observers, then calling their methods. I'm having trouble calling the update() method on the Device (Observer) from the PushButton (Subject) notifyObservers() method.
When I try to compile, I get this error:
C:\Users\xxxxx\AppData\Local\Temp\ccxkQzRt.ltrans0.ltrans.o: In function `notifyObservers':
sketch/PushButton.cpp:20: undefined reference to `Observer::update(arduino::String)'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Uno WiFi Rev2.
ObserverPattern.ino
#ifndef MY_OBSERVER_H
#define MY_OBSERVER_H
#include <Arduino.h>
class Observer {
public:
void update(String message);
};
#endif
#ifndef MY_SUBJECT_H
#define MY_SUBJECT_H
#include <Arduino.h>
class Subject {
public:
void registerObserver(Observer *observer);
void removeObserver(Observer *observer);
void notifyObservers();
};
#endif
#ifndef MY_PUSH_BUTTON_H
#define MY_PUSH_BUTTON_H
#include <Arduino.h>
class PushButton : public Subject {
private:
Observer observers[5];
int observerIndex;
byte id;
public:
PushButton(byte id);
void registerObserver(Observer *observer);
void removeObserver(Observer *observer);
void notifyObservers();
void push();
};
#endif
PushButton::PushButton(byte id) {
this->id = id;
observerIndex = 0;
}
void PushButton::registerObserver(Observer *observer) {
observers[observerIndex] = *observer;
observerIndex = observerIndex + 1;
Serial.print("observerIndex = ");
Serial.println(observerIndex);
}
void PushButton::removeObserver(Observer *observer) {
}
void PushButton::notifyObservers() {
for (int i = 0; i < observerIndex; i++) {
observers[i].update(String("hello")); //this is where it breaks
}
}
void PushButton::push() {
notifyObservers();
}
#ifndef MY_DEVICE_H
#define MY_DEVICE_H
#include <Arduino.h>
class Device : public Observer {
private:
byte id;
public:
Device(byte id);
void update(String message);
};
#endif
Device::Device(byte id) {
this->id = id;
}
void Device::update(String message) {
Serial.print("Client ");
Serial.print(id);
Serial.print(" : ");
Serial.print(message);
Serial.println();
}
//ObserverPattern.ino
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Serial started");
PushButton pushButton(2);
Device one(1), two(2), three(3);
// register Device one to listen for pushButton pushes
pushButton.registerObserver(&one);
pushButton.push();
// register Device two to listen for pushButton pushes
pushButton.registerObserver(&two);
pushButton.push();
// register Device three to listen for pushButton pushes
pushButton.registerObserver(&three);
pushButton.push();
// just to prove update method works
one.update("on");
two.update("off");
}
void loop() {
// put your main code here, to run repeatedly:
}
Device.cpp (228 Bytes)
Device.h (252 Bytes)
Observer.h (167 Bytes)
ObserverPattern.ino (784 Bytes)
PushButton.cpp (558 Bytes)
PushButton.h (442 Bytes)
Subject.h (274 Bytes)