Skip to content

andrewjfreyer/jurabridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jurabridge ☕

TL;DR

An ESP32 dev board coupled to the service port of a Jura ENA Micro 90 can (1) poll machine data (e.g., sensors, memory, settings) via UART and (2) issue commands to the machine so as to act as a bridge between the machine and Home Assistant. See here for hardware and software requirements. Connections are straightforward.

Home Assistant Forum Link

Summary

ESP32 Arduino project for bridging a Jura ENA Micro 90 to home automation platforms via MQTT. A 3.3v to 5v level shifter is required between hardware UART of the ESP32 to the service port of the Jura. The ESP32 polls the Jura for status information via reverse-engineered Jura Service Port calls, calculates/determines device state and meta statuses and reports changes to an MQTT broker. Added custom preparation functionality via a separate input button.

Jura ENA Micro 90

Why? It's a discontinued machine...

For fun, that's all. The machine is awesome and still available as a refurb.

I saw this and other projects for other Jura models and thought I'd begin investigating the ENA Micro 90. Some existing comands worked, most did not. I needed to investigate and characterize a lot of the output from the machine and iterate through hundreds of commands to determine which ones the machine responded to, note what the response was, and then set about decoding/understanding what the data meant. One thing led to another, and now we're here.

So what?

With 'jurabridge' we can/have:

  • Power control. Automatically turn the machine on and off in response to whatever triggers I want (via Home Assistant). Personally, mine is triggered from a motion sensor nearby the kitchen in the mornings.

  • Custom coffee drinks. Automatically create custom coffee or milk drinks with an arbitrary number of shots, arbitrary milk quantity, arbitrary water quantity (e.g., cortado, americano, lungo, ristretto, short cap, and so on).

  • Tuning. Fine-tune shots to improve quality. By default, the machine doses 8 - 9g of coffee (it seems) and pushes through it 30ml of water, which ends up being a 3:1 grounds to water dosing ratio. That's about half-strenth compared to a traditional pull. Also, after extraction starts, channeling is almost guaranteed to take over and extraction will suffer even before we consider how much water is being forced through. To improve shot quality then, we need to reduce water and/or increase coffee. We can't increase coffee because of torque limitations of the brew group drive motor during tamping. So our best option to improve our shot is to pull 15 - 17ml through a standard 8g puck twice, back-to-back. This would give about 30ml of espresso pulled through 16g of coffee, in aggregate. The shots are much better to my palate using this technique.

  • Quality control. The thermoblock does not have fine temperture control, so there is quite a large range of possible extraction temperatures for different shots. The system can warn if the thermoblock is too high (meaning overextraction is likely), or if the thermoblock is too low (meaning underextraction is likely). In either case, running water through the machine can serve three purposes: (1) pre-warm a mug, (2) normalize thermoblock temperature below or around 100°C by purging heat or forcing the thermoblock on, and (3) clean the mug. This is not necessary in all cases, but is a good idea for best shot performance.

  • Automation. Automate our fine-tunings and/or temperature purge so that a perfect pull (sequence) executes without supervision.

  • Convenience & sanitation. Automate maintenance operations such as brew group rinse and milk system rinse when the machine is idle. This will save internal silicon tubing from absorbing and accumulating burned coffee flavors, and it'll improve the sanitation of the milksystem.

  • Reminders. Create maintence reminders or alerts via Home Assistant (e.g., iOS notifications, Alexa notifications, UI alerts or badges)

  • Alerts. iOS alerts when a coffee product is ready.

  • Improved safety. Ensure the machine turns off at an appropriate time, or in response to certain conditions. ⚠️ NOTE: If a milk clean operation is not completed and the machine is waiting for "water for milk clean," the thermoblock is held around 150 degrees celcius for sanitation purposes. There DOES NOT appear to be a timeout apart from the machine's power off timer, which may be on the order of hours. If the machine is left in this state, it'll consume signifiant power and pose a minor fire risk.

  • Ridiculousness. Due to the connection to Home Assisant, we have connection to voice assistants too: "Alexa, make me a short espresso."

  • Overwhelming data. We retreive all meters from the machine, including service life of the grinder, the drive motor of the brew group, and others. Can inform when other maintenance is necessary.

Table of Contents

Reverse Engineering

Building & Programming the jurabridge

Using jurabridge for Stuff

Acknowledgements & Further Reading

Home Assistant

The bridge automatically reports back to HA via MQTT Discovery, divided into a number of different devices for readability. Once secrets.h is configured with appropriate values, and device discovery finishes, size different devices will appear:

Jura Ena Micro 90

Main Controller

Jura Ena Micro 90

Brew Group

Jura Ena Micro 90

Dosing System

Jura Ena Micro 90

Milk System

Jura Ena Micro 90

Water System

Jura Ena Micro 90

Thermoblock

Jura Ena Micro 90

User Interface Example

With all the automatically-exposed sensors, a UI can be built for Home Assistant that provides tons of information about the machine and its preparations. For example, with button-card and others:

Jura Ena Micro 90

jurabridge Hardware

Here is what the jurabridge looks like, attached to the machine after a few destructive modifications to feed a ribbon cable through the housing to the debug port.

Jura Ena Micro 90

Jura Ena Micro 90

In v2, I added a momentary pushbutton to Pin 21 that allows for custom menus that leverage the machine's own display:

Jura Ena Micro 90

The data output from the machine is received and presented by Home Assistant presented via MQTT Device Discovery.


Maintenance Automation

With machine status accurately known by the jurabridge, we can schedule maintenance operations automatically. This will increase output quality and sanitation to an extent. All this means is that a few minutes after a milk preparation has been completed and the machine has been idle, milk rinse is automatically performed. Similarly, after sitting idle for a time period, a water rinse is automaticaly performed.


Custom Preparations & Actions

I've found out that the default programming dramatically undersells the machine's capabilities. The Jura produces good enough shots as is. All that to say, since I bought the machine, my expectation was set that "this is the best it can do, and that's just fine." It is, after all, a superauto and sacrifies are made over a manual process. We're sacrificing some quality for pushbutton convenience. However, surprising to me was that the ENA Micro 90 only uses 7-10g of coffee per perparation - with 30 some-odd ml of water. That's less coffee and more water than I presumed, without giving it much thought. Plus, pump pressure drops pretty dramatically due to channeling so, the first parts of our shots are the best parts anyway.

It's of course easy to pull two short shots back to back to get to a more traditional 15 - 20g "single" shot, but why not automate it? Lets just automate it. Here, because jurabridge obtains (and/or infers) accurate machine status information, any number of custom recipes or custom instruction sequences can be excuted, without needing to modify EEPROM or to orchestrate a valid sequence of FN: commands, or without waiting for unnecessary long delays to presuming machine state. This command+interrupt+statuswait technique ensures that the machine excutes its own in-built sequences, and there's no risk of incidentally damaging the brewgroup with custom instructions or custom brew sequences.

In this project there's a header above that defines custom functions that are accessible via a pushbutton. As an example, there are three custom operations defined.

Each of the menu array entires conform to a struct:


struct JuraCustomMenuItemConfiguration {
  const char name[255];
  const char topic[255];
  const char payload[255];
};

The name field of this struct should be ten ASCII characters or less, as this will be displayed on the machine's display. The array itself can be modified to include as many custom functions as needed:

static const JuraCustomMenuItemConfiguration JuraCustomMenuItemConfigurations[] {
  {
    " RISTR x2",
    MQTT_ROOT MQTT_SUBTOPIC_FUNCTION "make_espresso",
    "{'add':1,'brew':17}",
  },
  {
    "  CAP +1",
    MQTT_ROOT MQTT_SUBTOPIC_FUNCTION "make_cappuccino",
    "{'milk':60,'brew':17,'add':1}",
  },
  {
    "CORTADO +1",
    MQTT_ROOT MQTT_SUBTOPIC_FUNCTION "make_cappuccino",
    "{'milk':30,'brew':17',add':1}",
  },
  { /* the last element will always be treated as an exit, regardless the command phrase here */
    "   EXIT",
    "",
    "",
  }
};

Jura Ena Micro 90

  1. The first index prepares a double ristretto in which two espresso shots are pulled back to back, each limited to 17ml of espresso output (that's about 2:1 ratio in respect of the grounds volume of 8g).

  2. The second index here prepares a cappuccino with 60ml of milk, 17ml of espresso, plus a second added shot of 17ml of espresso.

  3. The third index here prepares a "cortado" with 30ml of milk, 17ml of espresso, plus a second added shot of 17ml of espresso.

  4. The final operation will cause the secret menu to close. The menu will time out after a short period of time as well. 15 seconds by default.

To add other options, simply insert a new array item. For example, a two-shot americano:

{
  " AMERI x2",
  MQTT_ROOT MQTT_SUBTOPIC_FUNCTION "make_hot_water",
  "{'add':2,'brew':17}",
}

Or a lungo:

{
  "  LUNGO",
  MQTT_ROOT MQTT_SUBTOPIC_FUNCTION "make_espresso",
  "{'brew':40}",
}

In other cases, you can define your own personal brew preferences that will apply to the next physical button press of the machine. In the following example, pressing any of the program buttons will cause the next button press to limit brew output volume to 17 ml.

{
  "ESPRESSO 1",
  MQTT_ROOT MQTT_DISPENSE_CONFIG,
  "{'brew':17}",
}

Or, simply add a shot to any other preparation. In this case, we can automatically add a 17ml espresso pull after any other machine button. After enabling this option, the display will update to "PRODUCT?", encouraging selection of one of the six prefab buttons (espresso, cappuccino, macchiato, water, milk foam, coffee). Once the use makes a selection and the selected program completes, a shot will be pulled:

{
  " ADD SHOT",
  MQTT_ROOT MQTT_DISPENSE_CONFIG,
  "{'add':1,m 'brew':17}",
}

Functions & Buttons in Home Assistant

The JuraConfiguraton.h header lists what buttons are reportable to home assistant. By default, most are disabled as each additional sensor adds reporting time and cycle time.

Disclaimer

This repository is only provided as information documenting a project I worked on for kicks. No warranty or claim that this will work for you is made. I have described some actions that involve modifying a Jura ENA Micro 90, which if performed will absolutely void any warranty you may have (which you probably don't; this machine is old). Some of the modifications described below involve mains electricity; all appropriate cautions are expected to be, and were, followed. Some of these modifications are permanent and irreversible without purchasing replacement parts. Do not duplicate any of this project unless you know exactly what you are doing. I do not take any responsibility for any frustration, spousal irritation, bad coffee, injuries, or damage to your machine, your home plumbing, your home, or anyone or anything that may occur from any accident, lack of common sense or experience, poor planning, or following this project word for word. Responsibility for any effect, including damages or distress, resulting from reliance on any information made available here is not the responsibility of the author or other contributors. All rights are reserved.