Skip to content

Commit

Permalink
Added espresso mode
Browse files Browse the repository at this point in the history
  • Loading branch information
orlopau committed Jan 29, 2023
1 parent 0d762a8 commit 77a31f0
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 3 deletions.
13 changes: 13 additions & 0 deletions code/include/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class MockWeightSensor : public WeightSensor
bool isNewWeight() override { return newWeight; }
void tare() override { weight = 0; }
void setScale(float scale) override {}
float getLastWeight() override { return weight; }
float weight = 0;
bool newWeight = false;
};
Expand Down Expand Up @@ -113,6 +114,13 @@ class MockDisplay : public Display
recipeTimeToFinishMs = timeToFinish;
recipeIsPause = isPause;
};
void espressoShot(uint32_t currentTimeMs, uint32_t timeToFinishMs, int32_t currentWeightMg, uint32_t targetWeightMg, bool waiting) override
{
espressoCurrentTimeMs = currentTimeMs;
espressoTimeToFinishMs = timeToFinishMs;
espressoCurrentWeightMg = currentWeightMg;
espressoTargetWeightMg = targetWeightMg;
};
void text(const char *text){};
void drawTextAutoWrap(const char *text, int yTop){};
void clear()
Expand Down Expand Up @@ -145,4 +153,9 @@ class MockDisplay : public Display
uint32_t recipeWeightToPourMg = 0;
uint64_t recipeTimeToFinishMs = 0;
bool recipeIsPause = false;

uint32_t espressoCurrentTimeMs = 0;
uint32_t espressoTimeToFinishMs = 0;
int32_t espressoCurrentWeightMg = 0;
uint32_t espressoTargetWeightMg = 0;
};
2 changes: 2 additions & 0 deletions code/src/data/localization.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#define MODE_NAME_CALIBRATE "Kalibrierung"
#define MODE_NAME_RECIPES "Rezepte"
#define MODE_NAME_SCALE "Waage"
#define MODE_NAME_ESPRESSO "Espresso"
//////////////////////////////////////////////
#else
/////////////////// ENGLISH ///////////////////
Expand Down Expand Up @@ -109,5 +110,6 @@
#define MODE_NAME_CALIBRATE "Calibration"
#define MODE_NAME_RECIPES "Recipes"
#define MODE_NAME_SCALE "Scale"
#define MODE_NAME_ESPRESSO "Espresso"
//////////////////////////////////////////////
#endif
7 changes: 6 additions & 1 deletion code/src/embedded/dev/main_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

U8GDisplay display(PIN_I2C_SDA, PIN_I2C_SCL, U8G2_R1);

#define SWITCHER
// #define SWITCHER
#define ESPRESSO

void setup()
{
Expand All @@ -38,6 +39,10 @@ void setup()
}
}
#endif

#ifdef ESPRESSO
display.espressoShot(4000, 26000, 5000, 18000);
#endif
}

void loop()
Expand Down
6 changes: 4 additions & 2 deletions code/src/embedded/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "mode.h"
#include "mode_manager.h"
#include "modes/mode_scale.h"
#include "modes/mode_espresso.h"
#include "modes/mode_calibrate.h"
#include "modes/mode_recipe.h"
#include "u8g_display.h"
Expand Down Expand Up @@ -37,10 +38,11 @@ void saveScale(float scale)
}

ModeScale modeDefault(weightSensor, input, display, stopwatch);
ModeEspresso modeEspresso(weightSensor, input, display, stopwatch);
ModeCalibration modeCalibration(loadcell, input, display, stopwatch, saveScale);
ModeRecipes modeRecipes(weightSensor, input, display, RECIPES, RECIPE_COUNT);
Mode *modes[] = {&modeDefault, &modeRecipes, &modeCalibration};
ModeManager modeManager(modes, 3, display, input, battery);
Mode *modes[] = {&modeDefault, &modeRecipes, &modeEspresso, &modeCalibration};
ModeManager modeManager(modes, 4, display, input, battery);

EncoderDirection encoderDirection;

Expand Down
53 changes: 53 additions & 0 deletions code/src/embedded/u8g_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,57 @@ void U8GDisplay::drawOpener()
u8g.sendBuffer();
}

void U8GDisplay::espressoShot(uint32_t currentTimeMs, uint32_t timeToFinishMs, int32_t currentWeightMg, uint32_t targetWeightMg,
bool waiting)
{
u8g.clearBuffer();
int width = u8g.getDisplayWidth();
int height = u8g.getDisplayHeight();

u8g.setFont(u8g2_font_logisoso18_tf);
int ascent = u8g.getAscent();

int barHeight = 5;
int barWidth = width;

int yy = 4;
int xx = 2;

// draw time to finish
float timeToFinishS = timeToFinishMs / 1000.0;
float currentTimeS = currentTimeMs / 1000.0;
static char buffer[16];
if (waiting)
{
sprintf(buffer, "%.1fs", currentTimeS);
}
else
{
sprintf(buffer, "%.1fs|%.1fs", -timeToFinishS, currentTimeS);
}
int textWidth = u8g.getUTF8Width(buffer);
u8g.drawUTF8(width / 2.0 - textWidth / 2.0, yy + ascent, buffer);

// change font
u8g.setFont(u8g2_font_logisoso16_tf);
ascent = u8g.getAscent();
yy = 2 + ascent;

// draw current weight in g
float currentWeightG = currentWeightMg / 1000.0;
float targetWeightG = targetWeightMg / 1000.0;
sprintf(buffer, "%.1fg/%.1fg", currentWeightG, targetWeightG);
textWidth = u8g.getUTF8Width(buffer);
u8g.drawUTF8(width / 2.0 - textWidth / 2.0, height - barHeight - 8, buffer);

// draw full width weight progress bar on bottom
int barY = height - barHeight;
int barX = 0;
int barProgress = (currentWeightG / targetWeightG) * barWidth;
u8g.drawFrame(barX, barY, barWidth, barHeight);
u8g.drawBox(barX, barY, barProgress, barHeight);

u8g.sendBuffer();
}

#endif
1 change: 1 addition & 0 deletions code/src/embedded/u8g_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class U8GDisplay : public Display
void recipeConfigRatio(const char *header, uint32_t coffee, uint32_t water) override;
void recipeInsertCoffee(int32_t weightMg, uint32_t requiredWeightMg) override;
void recipePour(const char *text, int32_t weightToPourMg, uint64_t timeToFinishMs, bool isPause, uint8_t pourIndex, uint8_t pours) override;
void espressoShot(uint32_t currentTimeMs, uint32_t timeToFinishMs, int32_t currentWeightMg, uint32_t targetWeightMg, bool waiting) override;
void text(const char *text) override;
void update(){};
void clear() override;
Expand Down
2 changes: 2 additions & 0 deletions code/src/loadcell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ float DefaultWeightSensor::getWeight()
return (getRawWeight() - offset) * scale;
}

float DefaultWeightSensor::getLastWeight() { return (ringBuffer->getRelative(0) - offset) * scale; }

bool DefaultWeightSensor::isNewWeight()
{
return newWeight;
Expand Down
12 changes: 12 additions & 0 deletions code/src/loadcell.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ class WeightSensor
virtual ~WeightSensor(){};
virtual void begin(){};
virtual void update(){};
/**
* @brief Gets the scaled weight in grams. Could be averaged depending on the set auto averaging mode.
*
* @return weight in grams
*/
virtual float getWeight() = 0;
/**
* @brief Gets the latest scaled weight in grams. This is not averaged.
*
* @return weight in grams
*/
virtual float getLastWeight() = 0;
virtual bool isNewWeight() = 0;
virtual void tare() = 0;
virtual void setScale(float scale) = 0;
Expand All @@ -43,6 +54,7 @@ class DefaultWeightSensor : public WeightSensor
void begin() override;
void update() override;
float getWeight() override;
float getLastWeight() override;
bool isNewWeight() override;
void tare() override;
void setScale(float scale) override;
Expand Down
1 change: 1 addition & 0 deletions code/src/mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Mode
public:
virtual ~Mode() {}
virtual void update() = 0;
virtual void enter() {};
virtual bool canSwitchMode() = 0;
virtual const char* getName() = 0;
};
1 change: 1 addition & 0 deletions code/src/mode_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ void ModeManager::update()
if (input.getEncoderClick() == ClickType::SINGLE)
{
inModeChange = false;
modes[currentMode]->enter();
}
}
else
Expand Down
67 changes: 67 additions & 0 deletions code/src/modes/mode_espresso.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "modes/mode_espresso.h"
#include "data/localization.h"

void ModeEspresso::enter() { buttons.resetEncoderTicks(); }

void ModeEspresso::update()
{
// start stopwatch and tare on click
if (buttons.getEncoderClick() == ClickType::SINGLE)
{
stopwatch.toggle();

if (stopwatch.isRunning())
{
weightSensor.tare();
approximator.reset();
}
}

// clamp target weight to min and max
targetWeightMg += (buttons.getEncoderTicks() * ENCODER_MG_PER_TICK);
if (targetWeightMg < MIN_TARGET_WEIGHT_MG)
{
targetWeightMg = MIN_TARGET_WEIGHT_MG;
}
else if (targetWeightMg > MAX_TARGET_WEIGHT_MG)
{
targetWeightMg = MAX_TARGET_WEIGHT_MG;
}

if (buttons.getEncoderTicks() >= 1 || buttons.getEncoderTicks() <= -1)
{
buttons.resetEncoderTicks();
}

// new weight must be handled for regression
if (weightSensor.isNewWeight())
{
handleNewWeight();
}

// display
int32_t remainingTime = lastEstimatedTime - stopwatch.getTime();
bool waiting =
!stopwatch.isRunning() || remainingTime < 0 || remainingTime > REGRESSION_MAX_TIME || stopwatch.getTime() < REGRESSION_GRACE_PERIOD;

display.espressoShot(stopwatch.getTime(), remainingTime, weightSensor.getWeight() * 1000, targetWeightMg, waiting);
}

void ModeEspresso::handleNewWeight()
{
if (stopwatch.isRunning())
{
int32_t lastWeightMg = weightSensor.getLastWeight() * 1000;
approximator.addPoint({(long)stopwatch.getTime(), (float)lastWeightMg});
lastEstimatedTime = approximator.getXAtY(targetWeightMg);

if (lastWeightMg >= targetWeightMg)
{
stopwatch.stop();
}
}
}

bool ModeEspresso::canSwitchMode() { return true; }

const char *ModeEspresso::getName() { return MODE_NAME_ESPRESSO; }
41 changes: 41 additions & 0 deletions code/src/modes/mode_espresso.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include "loadcell.h"
#include "mode.h"
#include "stopwatch.h"
#include "user_interface.h"
#include "regression.h"

#define ENCODER_MG_PER_TICK 100

#define MIN_TARGET_WEIGHT_MG 1000
#define MAX_TARGET_WEIGHT_MG 100000

#define REGRESSION_BUFFER_SIZE 50
#define REGRESSION_MAX_TIME 3 * 60 * 1000
#define REGRESSION_GRACE_PERIOD 1000

class ModeEspresso : public Mode
{
public:
ModeEspresso(WeightSensor &weightSensor, UserInput &buttons, Display &display, Stopwatch &stopwatch)
: weightSensor(weightSensor), buttons(buttons), display(display), stopwatch(stopwatch),
targetWeightMg(36 * 1000), approximator(REGRESSION_BUFFER_SIZE), lastEstimatedTime(0){};
~ModeEspresso(){};
void update() override;
void enter() override;
bool canSwitchMode() override;
const char *getName() override;

private:
WeightSensor &weightSensor;
UserInput &buttons;
Display &display;
Stopwatch &stopwatch;

Regression::Approximator approximator;
int32_t targetWeightMg;
long lastEstimatedTime;

void handleNewWeight();
};
1 change: 1 addition & 0 deletions code/src/user_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Display
virtual void recipeConfigRatio(const char *header, uint32_t coffee, uint32_t water) = 0;
virtual void recipeInsertCoffee(int32_t weightMg, uint32_t requiredWeightMg) = 0;
virtual void recipePour(const char *text, int32_t weightToPourMg, uint64_t timeToFinishMs, bool isPause, uint8_t pourIndex, uint8_t pours) = 0;
virtual void espressoShot(uint32_t currentTimeMs, uint32_t timeToFinishMs, int32_t currentWeightMg, uint32_t targetWeightMg, bool waiting) = 0;
virtual void text(const char *text) = 0;
virtual void update() = 0;
virtual void clear() = 0;
Expand Down
Loading

0 comments on commit 77a31f0

Please sign in to comment.