Esphome: Philips Series 2200/3200 Smart Coffee Machine


esphome:
  name: philips-sniffer-3347
  on_boot:
    priority: 900
    then:
      - output.turn_off: display_trip
      - delay: 350ms
      - output.turn_on: display_trip
      - delay: 1200ms
      - output.turn_off: display_trip
      - delay: 200ms
      - output.turn_on: display_trip

esp8266:
  board: d1_mini

logger:
  baud_rate: 0
  level: NONE

api:
ota:
  - platform: esphome

wifi:
  ssid: "Bakro 4G"
  password: "Bakro@JW"
  ap: {}

captive_portal:

external_components:
  - source:
      type: local
      path: ./components

uart:
  # Mainboard side
  - id: uart_mainboard
    tx_pin: GPIO1
    rx_pin: GPIO3
    baud_rate: 115200

  # Display side
  - id: uart_display
    tx_pin: GPIO15
    rx_pin: GPIO13
    baud_rate: 115200

philips_coffee_machine:
  id: coffee_machine
  display_uart: uart_display
  mainboard_uart: uart_mainboard
  power_pin: GPIO16
  invert_power_pin: false
  model: EP_3243

globals:
  - id: last_drink
    type: std::string
    restore_value: no
    initial_value: '"N/A"'

text_sensor:
  - platform: philips_coffee_machine
    controller_id: coffee_machine
    id: status
    name: "Status"

  - platform: template
    id: selected_drink
    name: "Selected Drink"
    update_interval: 1s
    lambda: |-
      std::string s = id(status).state;
      std::string lower = s;
      for (auto &c : lower) c = (char) tolower((unsigned char) c);

      if (lower.find("iced coffee") != std::string::npos) return {"Iced Coffee"};
      if (lower.find("latte") != std::string::npos) return {"Latte Macchiato"};
      if (lower.find("cappuccino") != std::string::npos) return {"Cappuccino"};
      if (lower.find("hot water") != std::string::npos) return {"Hot Water"};
      if (lower.find("americano") != std::string::npos) return {"Americano"};
      if (lower.find("espresso") != std::string::npos) return {"Espresso"};
      if (lower.find("coffee") != std::string::npos) return {"Coffee"};
      if (lower == "off") return {"Off"};
      if (lower == "idle") return {"Idle"};
      if (id(last_drink).empty()) return {"N/A"};
      return {id(last_drink)};

  - platform: template
    id: selected_cup_size
    name: "Selected Cup Size"
    update_interval: 1s
    lambda: |-
      std::string d = id(selected_drink).state;
      if (d == "Espresso" || d == "Coffee" || d == "Iced Coffee" ||
          d == "Cappuccino" || d == "Latte Macchiato" || d == "Americano" ||
          d == "Hot Water") {
        int v = (int) id(cup_size_state).state;
        if (v >= 1 && v <= 3) return {to_string(v)};
      }
      return {"N/A"};

  - platform: template
    id: selected_intensity
    name: "Selected Intensity"
    update_interval: 1s
    lambda: |-
      std::string d = id(selected_drink).state;
      if (d == "Hot Water" || d == "Off" || d == "Idle" || d == "N/A")
        return {"N/A"};
      int v = (int) id(bean_strength_state).state;
      if (v >= 1 && v <= 3) return {to_string(v)};
      return {"N/A"};

  - platform: template
    id: selected_milk
    name: "Selected Milk"
    update_interval: 1s
    lambda: |-
      std::string d = id(selected_drink).state;
      if (d == "Cappuccino" || d == "Latte Macchiato") {
        int v = (int) id(milk_foam_state).state;
        if (v >= 1 && v <= 3) return {to_string(v)};
      }
      return {"N/A"};

  - platform: template
    name: "Selection Summary"
    update_interval: 1s
    lambda: |-
      std::string out = "Drink: " + id(selected_drink).state +
                        " | Size: " + id(selected_cup_size).state +
                        " | Intensity: " + id(selected_intensity).state +
                        " | Milk: " + id(selected_milk).state;
      return {out};

number:
  - platform: philips_coffee_machine
    controller_id: coffee_machine
    status_sensor_id: status
    id: cup_size_state
    type: SIZE
    source: ANY
    name: "Cup Size State"

  - platform: philips_coffee_machine
    controller_id: coffee_machine
    status_sensor_id: status
    id: bean_strength_state
    type: BEAN
    source: ANY
    name: "Bean Strength State"

  - platform: philips_coffee_machine
    controller_id: coffee_machine
    status_sensor_id: status
    id: milk_foam_state
    type: MILK
    source: ANY
    name: "Milk Foam State"

script:
  - id: apply_max_defaults
    then:
      - delay: 150ms
      - number.set:
          id: cup_size_state
          value: 3
      - delay: 150ms
      - number.set:
          id: bean_strength_state
          value: 3
      - delay: 150ms
      - number.set:
          id: milk_foam_state
          value: 3

button:
  - platform: template
    name: "Recover Display"
    on_press:
      - output.turn_off: display_trip
      - delay: 300ms
      - output.turn_on: display_trip
      - delay: 800ms
      - output.turn_off: display_trip
      - delay: 200ms
      - output.turn_on: display_trip

  - platform: template
    name: "Power On"
    on_press:
      - repeat:
          count: 20
          then:
            - uart.write:
                id: uart_mainboard
                data: [0xD5, 0x55, 0x0A, 0x01, 0x05, 0x01, 0x17, 0x00, 0x00, 0x00, 0x32, 0x23]
      - repeat:
          count: 20
          then:
            - uart.write:
                id: uart_mainboard
                data: [0xD5, 0x55, 0x01, 0x01, 0x05, 0x01, 0x17, 0x00, 0x00, 0x00, 0x19, 0x16]
      - output.turn_off: display_trip
      - delay: 800ms
      - output.turn_on: display_trip

  - platform: template
    name: "Power Off"
    on_press:
      - repeat:
          count: 20
          then:
            - uart.write:
                id: uart_mainboard
                data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x01, 0x00, 0x00, 0x21, 0x0A]

  - platform: template
    name: "Start Pause"
    on_press:
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x00, 0x00, 0x01, 0x25, 0x03]

  - platform: template
    name: "Espresso"
    on_press:
      - lambda: |-
          id(last_drink) = "Espresso";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x02, 0x00, 0x00, 0x35, 0x1C]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Coffee"
    on_press:
      - lambda: |-
          id(last_drink) = "Coffee";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x08, 0x00, 0x00, 0x05, 0x2D]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Iced Coffee"
    on_press:
      - lambda: |-
          id(last_drink) = "Iced Coffee";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x20, 0x00, 0x00, 0x1C, 0x26]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Cappuccino"
    on_press:
      - lambda: |-
          id(last_drink) = "Cappuccino";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x04, 0x00, 0x00, 0x1D, 0x30]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Latte Macchiato"
    on_press:
      - lambda: |-
          id(last_drink) = "Latte Macchiato";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x10, 0x00, 0x00, 0x35, 0x17]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Hot Water"
    on_press:
      - lambda: |-
          id(last_drink) = "Hot Water";
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x00, 0x01, 0x00, 0x21, 0x0B]
      - script.execute: apply_max_defaults

  - platform: template
    name: "Bean Strength"
    on_press:
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x00, 0x02, 0x00, 0x35, 0x1E]

  - platform: template
    name: "Milk Foam"
    on_press:
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x00, 0x08, 0x00, 0x07, 0x25]

  - platform: template
    name: "Cup Size"
    on_press:
      - uart.write:
          id: uart_mainboard
          data: [0xD5, 0x55, 0x00, 0x01, 0x05, 0x01, 0x17, 0x00, 0x04, 0x00, 0x1C, 0x34]

output:
  - platform: gpio
    pin: GPIO12
    id: display_trip

This is what i currently have

Oh, you are using completely different software compared to others in this topic.
Do you have a reason for it ?

Aw, not really. I wasn’t sure how to get it going, so I uploaded your GitHub source zip and got help from codex to get it working. As my machine is an 3347/23, the pins you had in the yaml examples didn’t work for me. The ones in my yaml worked for me.

Can you elaborate on what’s different? I can hopefully try to fix it.

Been messing around a bit took the EP3243 yaml and added my pin configs instead but nothing has worked so far

Here’s a log at the end after powering it on machine comes on but not the display.


[18:53:55.531][D][api:222]: Accept 192.168.1.23
[18:53:55.538][D][api.connection:2229]: Home Assistant 2026.3.1 (192.168.1.23): connected
[18:53:58.421][D][switch:026]: 'Power' Turning OFF.
[18:53:58.421][D][number:025]: 'Coffee beans' >> nan
[18:53:58.428][D][number:025]: 'Coffee size' >> nan
[18:54:01.824][D][switch:026]: 'Cleaning Power' Turning OFF.
[18:54:06.279][D][switch:026]: 'Power' Turning OFF.
[18:54:06.823][D][switch:022]: 'Power' Turning ON.
[18:54:06.823][W][component:579]: api took a long time for an operation (56 ms)
[18:54:06.823][W][component:582]: Components should block for at most 30 ms
[18:54:07.987][W][component:579]: philips_coffee_machine.switch took a long time for an operation (800 ms)
[18:54:07.988][W][component:582]: Components should block for at most 30 ms
[18:54:15.319][E][philips_power_switch:024]: Power tripping display failed!

I was about to order a set of PCBs but, @belastingvormulier if you still have units available from your last order I will love to participate. I’m based in the US in case shipping doesn’t work.

The US is not a problem. the shipping cost is horrendous from the EU tough. + The cables are on back order for over a month now I am still waiting they should arrive next week? but my hopes are low.

Digikey has it all in stock in the US with decent shipping costs if you live there.
For me that is 20$ shipping to the EU and then 20$ shipping back to the US potentially…

So if you are in the US and handy with a iron i would go that way for sure.

1 Like

Thanks for the reply @belastingvormulier ! I can source the cables from Digi-Key, but I’m running into issues with ordering the PCB (the small board).

I don’t have soldering experience or equipment, so I’m trying to use the assembly option on JLCPCB. However, a few components are out of stock:
• J1 and J2 (6-pin headers)
• The Wi-Fi module

JLCPCB is suggesting the following replacements:
• Wi-Fi module: ESP-12F(ESP8266MOD) | Ai-Thinker | WiFi Modules | JLCPCB
• Headers (J1, J2): PZ254V-11-06P | XFCN | Pin Headers | JLCPCB

Do these substitutions look correct/compatible?

Also, I noticed that the PicoFlex connectors appear to be misaligned (horizontally placed) in the component placement file. (I cannot put screenshots since I am too new :slight_smile: )

My assumption is that they should be positioned vertically, but I’m unsure how precise the placement needs to be.

Any guidance would be really helpful before I submit the assembly order.

Also, if getting the PCB directly from you (without the cables) is an option, I’d be happy to go that route as well. Thanks again for the help!

Hi,
is the AZDelivery ESP32 D1 Mini NodeMCU WiFi Module + Bluetooth pin compatible with the board: [quote=ā€œThe-May, post:1, topic:911784ā€]
big ā€œsimpleā€ with normal parts → easy soldering
[/quote]

Thanks,
Michael

I could send a pcb without cables, but the last time I did that I think the shipping was about 20 bucks + pcb + components.
(also not sure about the tariffs as that should have changed now again with the court ruling)

So all in all its getting towards 40/50$ by the time its all in on doorstep. and then you still need to buy the atom + cables. Its a lot of $$ for automated coffee :coffee:

Is the m5 stack more stable than the esp if switched over to it? Can get the display on for around 20 mins then goes off so unsure if it’s the esp now.

Not more stable… Just smaller + usb-c + in a plastic case.

20 min → off = default coffee machine behavior? it turns itself off normally to save energy.

Machine doesn’t go off just the display, With the esp removed and screen directly plugged in my machine takes almost an hour to go fully off (display stays on until machine turns off) thinking may be an esp issue since it seems to work perfectly at first.

I have fixed the issue my YAML posted
before is all good. The issue got fixed after switching to ESPHome version 2023.5, as mentioned in ESP-Philips-5400-Coffee-Machine/README_EN.md at 276c0d353495f1f47bf0dab6289c9df575aafab3 Ā· DivanX10/ESP-Philips-5400-Coffee-Machine Ā· GitHub.

Unsure exactly why this version doesn’t break it, but I am glad to finally get it working. I just need to polish it up a bit, then I will add it to HomeKit.

In case anyone with the same or similar machine faces the issue, I’d suggest going that route.

Unfortunately went off again the 8266 is not stable has anyone managed a board that used an esp32? Or a drop in replacement for the same board to replace the 8266.

Hi, do you still have a board for sale? I would be interested (just the board) and I am located in germany.