Original posts including reverse engineering here:

http://hackcorrelation.blogspot.com/2013/07/building-new-firmware-for-senseo-coffee.html

http://hackcorrelation.blogspot.com/2013/11/senseo-custom-firmware-update.html

State of the project: pending deployment, some important details are missing:

Hardware - mostly stock, I'm just putting in the MSP430 chip and a regulator. I kept the original board and removed the uC and EEPROM chips.

Operation:

Why?

The original machine annoyed me (and my work mates) for various reasons:

Things to do after real-world testing is complete:

Code and 'schematic', written for the Energia framework:

/***************************************************************************************
 * // LEGEND: < from pin = digital output
 * //         > to pin = digital input
 * //         ^ pullup
 * //         $ analog
 * //         % pwm
 * //
 * //                MSP430G2231 - on launchpad
 * //             -----------------               ^
 * //            |VCC           GND|             /|\
 * //            |                 |              |
 * //  PWRLED  %<|P1.0/A0/LED   XIN|<^P2.6 CAFBTN |
 * //            |                 |              |
 * //            |P1.1/A1 TXD  XOUT|>P2.7 HEATER  |
 * //            |                 |              |
 * //            |P1.2/A2 RXD  TEST|              |
 * //            |                 |              |
 * //  CAF2BTN ^>|P1.3/A3 S2   nRST|------------- /
 * //            |                 |
 * // NTC/1.8k $>|P1.4/A4   A7/P1.7|< WATERSNS
 * //            |                 |
 * //  PWRBTN  ^>|P1.5/A5   A6/P1.6|> PUMP (LED2)
 * //             -----------------
 ***************************************************************************************/
// NTC is 0.77k@93C in divider configuration with 1.8k so boiling voltage is less than Vcc*1.8/(1.8+0.77)=0.7*Vcc
// this means that for 3.3V going into divider the boiling point value is > 0.7*1024 = 717;
// 87C is about 0.643*Vcc (658) so cca 58 ADC points hysteresis
// pump debit is marked 2L/min but this does not seem right
//#define DEBUG_ON 1
//#define TEMPERATURE_DEBUG
#include <Energia.h>

static const uint8_t PWRLED = P1_0;
static const uint8_t CAF2BTN = P1_3;
#ifdef TEMPERATURE_DEBUG
static const uint8_t NTC = A10; //internal temperature sensor
#else
static const uint8_t NTC = P1_4;
#endif
static const uint8_t PWRBTN = P1_5;
static const uint8_t CAFBTN = P2_6;
static const uint8_t HEATER = P2_7;
static const uint8_t WATERSNS = P1_7;
static const uint8_t PUMP = P1_6;

static const uint32_t COFFEE_TIME = (uint32_t)25*1000;
static const uint32_t IDLE_TIME = (uint32_t)30*60*1000;

#ifdef TEMPERATURE_DEBUG
static const uint16_t BOILING_POINT = 307;
static const uint16_t TEMP_HYSTER = 3;
static const uint16_t HIGH_TEMP_SCALE = 315;
static const uint16_t LOW_TEMP_SCALE = 300;
#else
static const uint16_t BOILING_POINT = 717;
static const uint16_t TEMP_HYSTER = 3;
static const uint16_t HIGH_TEMP_SCALE = 700;
static const uint16_t LOW_TEMP_SCALE = 400;
#endif

// coffee state
static const uint16_t NOT_REQUESTED = 0;
static const uint16_t REQUESTED = 1;
static const uint16_t IN_PROGRESS = 2;

static const uint16_t _TEMP_SCALE = HIGH_TEMP_SCALE - LOW_TEMP_SCALE;
static const uint16_t _PWM_SCALE = 16;

unsigned long lastUserTime;

void setup() {
#if 0
  BCSCTL1 = 0x8E; // 14.76MHz
  DCOCTL = 0xE0;  // 14.76MHz
  BCSCTL1 = 0x8F; // 15.92MHz
  DCOCTL = 0x90;  // 15.92MHz
  BCSCTL1 = 0x8F; // 15.95MHz
  DCOCTL = 0x91;  // 15.95MHz
  BCSCTL1 = 0x8F; // 15.99MHz
  DCOCTL = 0x92;  // 15.99MHz
  BCSCTL1 = 0x8F; // 16.03MHz
  DCOCTL = 0x93;  // 16.03MHz

  P1DIR |= BIT4; // output dco clock on P1.4
  P1SEL |= BIT4; // measure with scope or frequency counter
#endif

  P2SEL &= ~(BIT6|BIT7);

  //  DCOCTL = 0;     // avoid glitches
  //  BCSCTL1 = 0x8F; // 15.92MHz
  //  DCOCTL = 0x90;  // 15.92MHz

  pinMode(PWRLED, OUTPUT);
  pinMode(CAF2BTN, INPUT_PULLUP);
  pinMode(NTC, INPUT);
  pinMode(PWRBTN, INPUT_PULLUP);
  pinMode(CAFBTN, INPUT_PULLUP);
  pinMode(HEATER, OUTPUT);
  pinMode(WATERSNS, INPUT);
  pinMode(PUMP, OUTPUT);

  digitalWrite(HEATER, 0);
  digitalWrite(PUMP, 0);

#ifdef DEBUG_ON
  Serial.begin(4800); // msp430g2231 must use 4800
#endif
  lastUserTime = millis();
}

boolean isCooling = false;
uint16_t analogPowerPWM;
boolean poweredOff = 0;
uint16_t coffeeState = 0;
unsigned long coffeeStartTime;

void loop() {
  // for safety
  if (poweredOff){
#ifndef DEBUG_ON
    digitalWrite(HEATER, 0);
#endif
    digitalWrite(PUMP, 0);
    digitalWrite(PWRLED, 0);
    coffeeState = NOT_REQUESTED;
  }

  volatile uint16_t currentCycle = (uint16_t)millis();

  uint16_t temperatureValue = analogRead(NTC);

  if (!poweredOff){
    digitalWrite(PWRLED, analogPowerPWM>(currentCycle%_PWM_SCALE));

    // TODO: water sensor

    // heater management
    if (currentCycle % (1<<4)){
      // check water level
      if (digitalRead(WATERSNS)){

        if (temperatureValue>BOILING_POINT){
          digitalWrite(HEATER, 0);
          isCooling = true;
        }
        else if (temperatureValue>(BOILING_POINT-TEMP_HYSTER) && isCooling){
          digitalWrite(HEATER, 0);
        }
        else{
          digitalWrite(HEATER, 1);
          isCooling = false;
        }

        //check water and coffee requested
        if (coffeeState == REQUESTED && (temperatureValue>(BOILING_POINT-TEMP_HYSTER))){
          coffeeState = IN_PROGRESS;
          coffeeStartTime = millis();

          // force heater on if needed
          isCooling = false;
        }

        //handle coffee progress
        if (coffeeState == IN_PROGRESS){
          if (((uint32_t)(millis() - coffeeStartTime) >= COFFEE_TIME)){
            // we linger for one more loop cycle but it's ok
            coffeeState = NOT_REQUESTED;
            lastUserTime = millis();
          }
          digitalWrite(PUMP, 1);
        }
        else{
          digitalWrite(PUMP, 0);
        }
      }
      else{
        // no water
        digitalWrite(PWRLED, ((currentCycle >> 6 ) % 2));
        digitalWrite(PUMP, 0);
        digitalWrite(HEATER, 0);
        // we should reset the coffee state but then the pending request will be lost
      }
    }

    if (currentCycle % (1<<9)){
      // alternate between odd/even codes at same frequency
      if ((currentCycle >> 9  ) % 2){
        analogPowerPWM = coffeeState != NOT_REQUESTED ? 0 : 255;
      }
      else{
        analogPowerPWM = (temperatureValue-LOW_TEMP_SCALE)*_PWM_SCALE/_TEMP_SCALE;
      }

      // check timeout
      if (((uint32_t)(millis() - lastUserTime) >= IDLE_TIME)){
        poweredOff = 1;
      }
    }

#ifdef DEBUG_ON
    if (currentCycle % (1<<8)){
      Serial.println(coffeeState);
    }
#endif

    if ((currentCycle % (1<<7)) && !digitalRead(CAF2BTN) && (coffeeState != IN_PROGRESS)){
      lastUserTime = millis();
      coffeeState = REQUESTED;
    }

  }//end if !poweredOff

  // everything below happens even when powered off

  if (currentCycle % (1<<7)){
    if (!digitalRead(PWRBTN)){
      delay(200);
      // DEBUG MODE 1 - PWR + 1 COFEE button pressed
      if (!digitalRead(CAF2BTN)){
        // TODO
      }
      lastUserTime = millis();
      poweredOff = !poweredOff;
    }


    if (poweredOff){
      // DEBUG MODE 2 - 1 COFFEE + 2 COFFEE buttons pressed while off -> purge water
      if (!digitalRead(CAF2BTN) && !digitalRead(CAFBTN)){
        while (!digitalRead(CAF2BTN) && !digitalRead(CAFBTN)){
          digitalWrite(PUMP, 1);
        }
        digitalWrite(PUMP, 0);
      }
    }
  }

  //TODO: add wake on buttons and go to LPM in between
  //_BIS_SR(LPM4_bits | GIE);
}