Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor of the Fsm to be more modular (component based) + various improvments #60

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added SenseoWifi-PCB/Customizable Buttons Addon.fzz
Binary file not shown.
Binary file added SenseoWifi-PCB/SenseoWifi with Addon-PCB.fzz
Binary file not shown.
9 changes: 8 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ platform = espressif8266
board = d1_mini
framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
lib_deps = git+https://github.com/homieiot/homie-esp8266.git#8e905f0
lib_deps =
git+https://github.com/homieiot/homie-esp8266.git#e0347c0
marvinroger/AsyncMqttClient @ ^0.9.0
arduinogetstarted/ezBuzzer @ ^1.0.0
erriez/ErriezCRC32 @ ^1.0.1

monitor_speed = 115200

;build_type = debug
monitor_filters = esp8266_exception_decoder, default

; Enable and update MQTT details to use MQTT based firmware update (not possible for initial flash!)
;upload_protocol = custom
;upload_command = python3 ota_updater_202011.py -l 192.168.0.75 -u user -d password -t "homie/" -i "senseo-wifi" "$BUILD_DIR/firmware.bin"
80 changes: 0 additions & 80 deletions src/Cup.cpp

This file was deleted.

37 changes: 0 additions & 37 deletions src/Cup.h

This file was deleted.

106 changes: 106 additions & 0 deletions src/HomeAssistant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <Homie.h>
#include "HomeAssistant.h"

HomeAssistantDiscovery::HomeAssistantDiscovery()
{
binarySensorTopic = String("homeassistant/binary_sensor/") + String(Homie.getConfiguration().deviceId) + String("/");
sensorTopic = String("homeassistant/sensor/") + String(Homie.getConfiguration().deviceId) + String("/");
switchTopic = String("homeassistant/switch/") + String(Homie.getConfiguration().deviceId) + String("/");
buttonTopic = String("homeassistant/button/") + String(Homie.getConfiguration().deviceId) + String("/");

machineTopic = String(Homie.getConfiguration().mqtt.baseTopic) + String(Homie.getConfiguration().deviceId) + String("/machine/");
statsTopic = String(Homie.getConfiguration().mqtt.baseTopic) + String(Homie.getConfiguration().deviceId) + String("/$stats/");
availabilityTopic = String(Homie.getConfiguration().mqtt.baseTopic) + String(Homie.getConfiguration().deviceId) + String("/$state");
uniqueIdBase = String("uniqueid__") + String(Homie.getConfiguration().deviceId) + String("_");
}

void HomeAssistantDiscovery::preparePayload(DynamicJsonDocument & jsonPayload, const char * friendlyName,const char * topicName, const std::map<String,String> &attributes)
{
jsonPayload["name"] = String(Homie.getConfiguration().name) + String(" ") + String(friendlyName);
jsonPayload["unique_id"] = uniqueIdBase + String(topicName);

jsonPayload["availability_topic"] = availabilityTopic.c_str();
jsonPayload["payload_available"] = "ready";
jsonPayload["payload_not_available"] = "lost";

for(auto attribute: attributes)
{
jsonPayload[attribute.first] = attribute.second;
}
}

bool HomeAssistantDiscovery::publishBinarySensorConfig(const char * friendlyName, const char * topicName,const std::map<String,String> &attributes)
{
DynamicJsonDocument jsonPayload(1024);

preparePayload(jsonPayload,friendlyName,topicName,attributes);

jsonPayload["state_topic"] = machineTopic + String(topicName);
jsonPayload["payload_off"] = "false";
jsonPayload["payload_on"] = "true";

char payloadBuffer[1024];
size_t payloadSize = serializeJson(jsonPayload,payloadBuffer);
return Homie.getMqttClient().publish((binarySensorTopic + String(topicName) + String("/config")).c_str(),0,true,payloadBuffer,payloadSize) != 0;
}

bool HomeAssistantDiscovery::publishSensorConfig(const char * friendlyName, const char * topicName,const std::map<String,String> &attributes)
{
DynamicJsonDocument jsonPayload(1024);

preparePayload(jsonPayload,friendlyName,topicName,attributes);

jsonPayload["state_topic"] = machineTopic + String(topicName);

char payloadBuffer[1024];
size_t payloadSize = serializeJson(jsonPayload,payloadBuffer);
return Homie.getMqttClient().publish((sensorTopic + String(topicName) + String("/config")).c_str(),0,true,payloadBuffer,payloadSize) != 0;
}

bool HomeAssistantDiscovery::publishStatConfig(const char * friendlyName, const char * topicName,const std::map<String,String> &attributes)
{
DynamicJsonDocument jsonPayload(1024);

preparePayload(jsonPayload,friendlyName,topicName,attributes);

jsonPayload["state_topic"] = statsTopic + String(topicName);

char payloadBuffer[1024];
size_t payloadSize = serializeJson(jsonPayload,payloadBuffer);
return Homie.getMqttClient().publish((sensorTopic + String(topicName) + String("/config")).c_str(),0,true,payloadBuffer,payloadSize) != 0;
}

bool HomeAssistantDiscovery::publishSwitchConfig(const char * friendlyName, const char * topicName,const std::map<String,String> &attributes)
{
DynamicJsonDocument jsonPayload(1024);

preparePayload(jsonPayload,friendlyName,topicName,attributes);

jsonPayload["state_topic"] = machineTopic + String(topicName);
jsonPayload["state_on"] = "true";
jsonPayload["state_off"] = "false";

jsonPayload["command_topic"] = machineTopic + String(topicName) + String("/set");
jsonPayload["payload_on"] = "true";
jsonPayload["payload_off"] = "false";

char payloadBuffer[1024];
size_t payloadSize = serializeJson(jsonPayload,payloadBuffer);
return Homie.getMqttClient().publish((switchTopic + String(topicName) + String("/config")).c_str(),0,true,payloadBuffer,payloadSize) != 0;
}

bool HomeAssistantDiscovery::publishButtonConfig(const char * friendlyName, const char * topicName,const char * buttonPayload,const std::map<String,String> &attributes)
{
DynamicJsonDocument jsonPayload(1024);

String buttonHaTopic = String(topicName) + String("_") + String(buttonPayload);
preparePayload(jsonPayload,friendlyName,buttonHaTopic.c_str(),attributes);

jsonPayload["command_topic"] = machineTopic + String(topicName) + String("/set");
jsonPayload["payload_press"] = buttonPayload;

char payloadBuffer[1024];
size_t payloadSize = serializeJson(jsonPayload,payloadBuffer);
return Homie.getMqttClient().publish((buttonTopic + String(buttonHaTopic) + String("/config")).c_str(),0,true,payloadBuffer,payloadSize) != 0;
}

33 changes: 33 additions & 0 deletions src/HomeAssistant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <map>
#include <ArduinoJson.h>

/**
* Helpers to publish HomeAssistant Discovery Config
*/

class HomeAssistantDiscovery
{
public:
HomeAssistantDiscovery();
bool publishBinarySensorConfig(const char *friendlyName, const char *topicName, const std::map<String, String> &attributes);
bool publishSensorConfig(const char *friendlyName, const char *topicName, const std::map<String, String> &attributes);
bool publishStatConfig(const char *friendlyName, const char *topicName, const std::map<String, String> &attributes);
bool publishSwitchConfig(const char *friendlyName, const char *topicName, const std::map<String, String> &attributes);
bool publishButtonConfig(const char *friendlyName, const char *topicName, const char *buttonPayload, const std::map<String, String> &attributes);

const String & getMachineTopic() const { return machineTopic; }

private:
void preparePayload(DynamicJsonDocument &jsonPayload, const char *friendlyName, const char *topicName, const std::map<String, String> &attributes);

String binarySensorTopic;
String sensorTopic;
String buttonTopic;
String switchTopic;
String machineTopic;
String statsTopic;
String availabilityTopic;
String uniqueIdBase;
};
84 changes: 84 additions & 0 deletions src/LedObserver/HwTimerLedObserver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "HwTimerLedObserver.h"
#include "Arduino.h"
#include "constants.h"
#include <Homie.h>

// variable use both inside and outside the ISR should be volatile as they could be modified from outside the current code path
volatile static ledStateEnum s_ledState = LED_unknown;
// volatile static int pulseDuration = 0; // don't forget to remove the variable declaration in ledChangedIsr()

static const uint32_t s_timerTick = uint32_t(float((pulseDurLedSlow + 100) /*ms*/ * 1000 /*to us*/) / 3.2 /*to tick*/);
static int s_ledPin = -1;

ledStateEnum HwTimerLedObserver::getState() const
{
return s_ledState;
}

void IRAM_ATTR HwTimerLedObserver::ledChangedIsr()
{
static unsigned long ledChangeMillis = 0;

unsigned long now = millis();
int pulseDuration = now - ledChangeMillis;
if (abs(pulseDuration - pulseDurLedFast) < pulseDurTolerance)
s_ledState = LED_FAST;
else if (abs(pulseDuration - pulseDurLedSlow) < pulseDurTolerance)
s_ledState = LED_SLOW;
else
pulseDuration = 0; // longer pulse duration means "no pulse" (continuous on or continuous off)

// each time the led blink, we push back the timer
timer1_write(s_timerTick);
ledChangeMillis = now;
}

void IRAM_ATTR HwTimerLedObserver::timerElapseIsr()
{
s_ledState = !digitalRead(s_ledPin) ? LED_ON : LED_OFF;
}

HwTimerLedObserver::HwTimerLedObserver(HomieNode &senseoNode, int pin)
: ledPin(pin), senseoNode(senseoNode)
{
assert(s_ledPin == -1); // you can't create two HwTimerLedObserver instance or terrible things will most likely happen
pinMode(ledPin, INPUT_PULLUP);
s_ledPin = ledPin;
}

void HwTimerLedObserver::attachInterrupt()
{
::attachInterrupt(digitalPinToInterrupt(ledPin), HwTimerLedObserver::ledChangedIsr, CHANGE);
timer1_attachInterrupt(HwTimerLedObserver::timerElapseIsr);
timer1_enable(TIM_DIV256, TIM_EDGE, TIM_SINGLE);
timer1_write(s_timerTick);
}

void HwTimerLedObserver::detachInterrupt()
{
::detachInterrupt(digitalPinToInterrupt(ledPin));
timer1_detachInterrupt();
timer1_disable();
}

void HwTimerLedObserver::updateState()
{
/*static int lastPulseDuration = 0;
if (lastPulseDuration != pulseDuration)
{
//pulseDuration is not thread safe. So the debug value display here could be glitchy every now and then
if (pulseDuration > 0)
{
//pulse duration of 0 means continuous state
Homie.getLogger() << "LED observer, last pulse duration: " << pulseDuration << endl;
}
lastPulseDuration = pulseDuration;
}*/

if (s_ledState != prevState)
{
Homie.getLogger() << "LED state machine, new LED state: " << getStateAsString() << endl;
senseoNode.setProperty("ledState").send(getStateAsString());
prevState = s_ledState;
}
}
27 changes: 27 additions & 0 deletions src/LedObserver/HwTimerLedObserver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "ILedObserver.h"
class HomieNode;

// LedObserver based on the hardware timer.
// More accurate than the "regular" Led observer and not impacted by the time spent in the main loop as this is 100% interrupt based
// Unfortunately the esp8266 has only two hardware timer, timer0 is used for the wifi and timer1 that I used here is used as well by the tone() function
// So basicaly, it's either the buzzer or the timer :-/

class HwTimerLedObserver : public ILedObserver
{
public:
HwTimerLedObserver(HomieNode &senseoNode, int pin);
virtual void attachInterrupt() override;
virtual void detachInterrupt() override;
virtual ledStateEnum getState() const override;
virtual void updateState() override;

private:
static void timerElapseIsr();
static void ledChangedIsr();

int ledPin;
HomieNode &senseoNode;
ledStateEnum prevState = LED_unknown;
};
Loading