ECE 4760: Latte Art Machine


For our final project ECE4760: Microcontrollers, our group created a cappuccino art designer that would automatically pour milk into coffee, while creating the classical latte art design.

For this project, our group used a Houston Instrument Omnigraphic 2000 X-Y Plotter and a peristaltic pump to create a cappuccino art designer. One end of the pump is attached to the pencil-holder of the plotter while the other end is immersed in a cup of milk foam. With a press of a button, we are able to control the plotter and pump from the PIC32 to make the plotter make motions that will result in the placement of milk-foam in particular locations while the pump is transferring milk form from the cup through the tubing.

The reason why we decided to pursue this project is because we were intrigued by latte art and wanted to see whether we would be able to recreate the sensitive motions with machinery. For more practical applications, developing a device like this has the potential to improve coffee shop workflows, as latte art designs are highly desired by many trendy consumers, but can be time consuming to create. This device can greatly reduce the amount of waste that is produced when new baristas are learning how to make latte art, and reduce the amount of time it takes between orders during a rush. Additionally with its usage, individuals who may not have had the motor skills necessary to work in coffee shops that produce high volumes of artistic lattes may be able to operate it as a part of their work functions.

High Level Design

For this project, we set up the plotter to stand up vertically as shown in the image below to take advantage of the vertical and horizontal movement it provides. On top of the plotter sits the pump, which has the inlet tube sitting inside of a cup of foamed milk and the outlet tube secured in the plotter’s pencil holder. When the plotter is turned on, the pencil-holder portion of the device can be controlled from the PIC32 to move vertically in the y-direction or horizontally in the x-direction.

Figure 1. Image of final device

The high level design of the device is depicted in the image below. The PIC32 takes in an input from the user via one of the three pushbuttons. Each pushbutton has a unique set of instructions associated to it that will control the plotter and pump differently. Once the button press is registered, the PIC32 sends signals to the plotter and pump. While the plotter and pump and working, the PIC32 prints a message to the TFT that tells the user what process is currently running. For instance, if pushbutton B1 was pressed, the PIC32 would print “Program 1 in process…” to the TFT.

Figure 2. Diagram of High Level Design
Milk Science

To create the milk foam,

  1. Heat 100 mL of whole milk to 140℉, on a stove or microwave.
  2. Put the milk in a french press and add a little bit of vegetable oil.
  3. Pump the press until it fills with milk foam.

To figure out how to make milk foam, we looked at a lot of youtube videos, and experimented a lot. We particularly looked in depth to figure out how the consistency of milk foam affects the latte when attempting to make latte art.

One of the videos we referenced in our research “Sunergos Milk Training Video: Learn Milk Science, Steaming, and Latte Art”

This video explains what happens to milk as it is heated. If it is above 160℉ the milk protein denatures, and the milk tastes bad. This is why we only heated the milk to 140℉. Another issue we found was that the milk foam starts out as a liquid, but over time solidifies. Once the foam is solid, it doesn’t flow in the way needed to make latte art. We found that adding a little vegetable oil slows down the solidification, but we still had to start making the pattern immediately after making the foam.

The milk foam is also very delicate, that is why the milk and pump are on top of the device, we wanted the milk foam to mostly travel down by gravity, because otherwise the tension needed to pull the foam up could damage the foam. Damage to the foam could result in a consistency that is unworkable to create a latte art design.

For discussing relevant standards, it is important to reference the many food standards that relate to the preparation of drinks for consumption, and the standards for the handling of milk products. On the national level, the US Department of Agriculture is responsible for enforcing food safety regulations, and there are a variety of sub-departments that have additional oversight over certain areas. In the case of our device, our standards would most likely fall under the FDA (Food and Drug Administration) or under the AMS (Agricultural Marketing Service), with the caveat that this would likely be a retail or consumer product, as opposed to a large scale manufacturing/ food processing device. Aside from these two governmental bodies, there are other regulatory bodies that exist to provide sanitation standards.

The two that likely apply the most to our project in terms of standards at the 3-A Sanitary Standards, and the National Sanitation Foundation. The 3-A Sanitary standards are aimed at different types of food processing equipment, with dairy products being a particular focus. Additionally, they provide guidelines and resource papers on good manufacturing practices on what materials to choose for dairy products with respect to metals, plastics and rubbers. The National Sanitation Foundation outlines the standards for the retail and food service, and has guidelines on design standards for food processing equipment in the manufacturing space. Additionally, they provide online training for the food safety and quality, which deals with public health and safety standards in the United States, and includes an overview of HACCP (Hazard Analysis and Critical Control Point). For our project as it stands, it is mindful of these regulations, and makes sure that no components come in contact with the milk product (our reasoning for choosing a peristatltic pump). However, due to the prototype nature of this, we still do not have a fully compliant machine that has regulated and robust santization procedures.

As for current patents, there are other devices on the market which create latte art (such as the Ripple Maker and Steam CC), but they had a very fundamentally different mechanism of producing the designs. Where as our device pumps milk and milk foam directly to create a traditional design, the other devices operate as a printer head with food safe inks to develop photorealistic designs. On the other side of this, there are multiple companies which produce machines capable of distributing the milk ratios which are able to generate lattes, but none of them currently incorporate any designs. This is the only area that might be a bit tricky in terms of intellectual property due to the method of dispensing, however, these are mostly consumer grade, and not widely used in the retail or commercial space.

Program & Hardware Design

Software Design

The software component of our design consists of the ISR, timer thread, main, helper functions to print to the TFT, and a helper function to read button inputs.

Trajectory Data Structure

The trajectory to make a pattern is stored in a 2d array of integers.

Each row of this array has four elements x(0-4095), y(0-4095), dt(1- … ), flow rate (0-39999).

The first and second elements are the final x and y position of the step. These are in the range 0 to 4095, where the origin is in the top right.

The third element is the time to complete the step in ms. During the step the plotter linearly moves from the previous step’s x and y to the current step’s x and y and takes dt ms to get there this should not be zero except for the first and last row where it must be zero.

The fourth element is the flow rate during the step, this controls the duty cycle of the pump during the step. 0 is off and 39999 is full power. This is not interpolated between steps, because it doesn’t have to be smooth.

The first and last rows must be all zero. This lets the program know when the trajectory ends, and properly initializes the start.

Here is an example of a trajectory that just runs the pump for 5s.

unsigned int trajectory3[][4]={

    • //x(0-4095) //y(0-4095) //dt(ms) //flowrate (0-39999)
    • {0,0,0,0},
    • {0,0,5000,25000},
    • {0,0,0,0}

Here is an example of a trajectory that goes in a square, taking 1s per side.

unsigned int trajectory2[][4]={

    • {0,0,0,0},
    • {0,4095,1000,0},
    • {4095,4095,1000,0},
    • {4095,0,1000,0},
    • {0,0,1000,0},
    • {0,0,0,0}
    • };

The trajectories are mapped to a button press by a global array of void pointers. void *trajectorylist[]={trajectory1,trajectory2,trajectory3};

Each button corresponds to a trajectory in trajectorylist.

Timer Interrupt Service Routine

The timer ISR handles the majority of our program. Using the timer ISR ensures accurate timing and repeatability for the motion of the plotter. The timer ISR runs once every millisecond.

At every cycle of the ISR, the program checks for button presses from the user by calling helper function get_button_pressed. get_button_pressed returns an integer value of 0, 1, 2, or 3, with 0 meaning no button and 1,2,3 corresponding to one of the three buttons the user can press. This result is saved in the local variable currentbutton. The program then uses that value saved in currentbutton as an index to access a specific trajectory that are saved as global variables. There exists a particular sequence of arrays for each button press, which allows the PIC32 to send signals to the plotter according to the button that was pressed by the user. Once the correct trajectory has been selected, the ISR traverses through the trajectory. The PIC32 is able to move between (x,y) coordinates of the array sequences through linear interpolation. The trajectory always ends with a step with dt 0, so if the end is detected, global variable pressedButton gets set back to 0 and the pump is turned off, meaning the pattern is complete, and the next button press will be the new pattern to draw.

The goal of the ISR is to:

  1. Process button presses.
  2. Calculate the duty cycle of the PWM going to the pump, and calculate the x, and y positions which are sent by SPI to the DAC.

int currentbutton=get_button_pressed();

if (currentbutton!=0 && pressedButton==0){

    • //start pattern of currentbutton
    • trajectory= trajectorylist[currentbutton-1];
    • starttime=counter;
    • trajectory_step = 1;
    • dutycycle = 0;
    • pressedButton=currentbutton;
    • sprintf(buffer1,”Program %d in process…”, currentbutton);
    • printLine(2, buffer1, ILI9340_YELLOW, ILI9340_BLACK);
    • }

if (pressedButton!=0 ) //doing pattern pressedButton{

    • int dt=counter-starttime;
    • int finalx= trajectory[trajectory_step][0];
    • int initialx= trajectory[trajectory_step-1][0];
    • int total_dt= trajectory[trajectory_step][2];
    • int finaly= trajectory[trajectory_step][1];
    • int initialy= trajectory[trajectory_step-1][1];
    • if(total_dt!=0){
      • DAC_data_B= (finalx-initialx)* dt /total_dt + initialx;
      • DAC_data_A= (finaly-initialy)* dt /total_dt + initialy;
      • dutycycle = trajectory[trajectory_step][3]; if (dt>=total_dt){
        • trajectory_step++;
        • starttime=counter;
        • }

    } else if (total_dt == 0) {

      • pressedButton = 0; //end condition
      • dutycycle = 0;
      • sprintf(buffer1,”%s”, “”);
        • printLine(2, buffer1, ILI9340_YELLOW, ILI9340_BLACK);
      • }



The next part of the ISR sends the data to the DAC over SPI, and sets the PWM.

Timer Thread

The timer thread protothread_timer updates the TFT display every second to display the time since the last reboot. This thread does not serve a significant purpose in the actual final design, however, it has helped us debug by allowing us to see when the program has crashed.


In main, we set up the DAC, set up the timer ISR, set up the ADC, set up compare3 for PWM mode, and initialized and scheduled protothreads.

Output Compare (PWM)

In main output compare setup (from 4760 website):

      • // set up compare3 for PWM mode
      • OpenOC3(OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE , 0, 0); //
      • // OC3 is PPS group 4, map to RPB9 (pin 18)

PPSOutput(4, RPB9, OC3);

In the timer ISR (from 4760 website):



In main SPI setup (from 4760 website):

    • // SCK2 is pin 26
    • // SDO2 (MOSI) is in PPS output group 2, could be connected to RB5 which is pin 14
    • PPSOutput(2, RPB5, SDO2);
    • // control CS for DAC
    • mPORTBSetPinsDigitalOut(BIT_4);
    • mPORTBSetBits(BIT_4);
    • // divide Fpb by 2, configure the I/O ports. Not using SS in this example
    • // 16 bit transfer CKP=1 CKE=1
    • // For any given peripherial, you will need to match these
    • // clk divider set to 4 for 10 MHz
    • // end DAC setup

In the timer ISR (from 4760 website):

Note, DAC_data_B and DAC_data_A are the x and y positions of the plotter, from 0 to 4095.

    • ///////////////////////////////////
    • // Audio DAC ISR
    • // A-channel, 1x, active


    • #define DAC_config_chan_A 0b0011000000000000


    • // B-channel, 1x, active


    • #define DAC_config_chan_B 0b1011000000000000


      • // test for ready


      • while (TxBufFullSPI2());


      • // reset spi mode to avoid conflict with expander SPI_Mode16();


      • // DAC-A CS low to start transaction


      • mPORTBClearBits(BIT_4); // start transaction // write to spi2


      • WriteSPI2(DAC_config_chan_A | (DAC_data_A & 0xfff) );


      • // fold a couple of timer updates into the transmit time


      • // test for done


      • while (SPI2STATbits.SPIBUSY); // wait for end of transaction


      • // MUST read to clear buffer for port expander elsewhere in code


      • junk = ReadSPI2();


      • // CS high


      • mPORTBSetBits(BIT_4); // end transaction


      • // DAC-B CS low to start transaction


      • mPORTBClearBits(BIT_4); // start transaction


      • // write to spi2


      • WriteSPI2(DAC_config_chan_B | (DAC_data_B & 0xfff) );


      • // test for done


      • while (SPI2STATbits.SPIBUSY); // wait for end of transaction


      • // MUST read to clear buffer for port expander elsewhere in code


      • junk = ReadSPI2();


      • // CS high


      • mPORTBSetBits(BIT_4); // end transaction

Hardware Design

The hardware for this project consisted of three separate circuits: the optoisolator circuit for the pump, the voltage divider circuit for the plotter controls, and the button circuit for user input to the PIC32.

Button Circuit

The button circuits, as depicted in the image below, are simple button circuits that use pull-up resistors. For this project, our pull-up resistors have a value of 11k Ohms, and each button is wired to an unused pin on the PIC32.

Figure 3. Button Circuit Diagram

Voltage Divider Circuit

The plotter expects to be attached to a strain gauge. Strain gauges have four terminals, excitation +(E+), excitation -(E-), signal +(S+), and signal -(S-). The plotter supplys a large voltage difference on E+ and E-, and the strain guage outputs a small voltage difference, related to the amount of strain, on S+ and S-. To get the PIC32 to output a strain gauge like signal, we just had to use a 2 channel SPI DAC, the MCP4802, and a voltage divider, to get the correct voltage difference on S+ and S-. By trial and error, we found the above voltage divider to give a fairly good range on the plotter. We also connected the shielding and E- to the PIC32 ground. The plotter also had gain and zero adjustment knobs if we needed to fine tune it.

Figure 4. Voltage divider circuit for plotter control

Optoisolator Divider Circuit

To control the peristaltic pump, we reused the circuit from One Degree-of-Freedom Helicopter lab (lab 3). For the circuit, we used a 4N35 optoisolator chip, and set up a 2SK4017 MOSFET. For the motor, we used a 2.2 uF resistor in conjunction with a 1N4001 diode to short out motor spikes along the PWM pulses.

Figure 5. Optoisolator circuit for pump control

Tube Holding Mechanism

As a part of adapting the plotter for our usage, a holding bracket was developed to adapt what was previously a pencil holder to become an extended arm that the silicone tubing could be secured in. These are made to fit 4-40 screws in two lengths, and allows for the cup to be placed sufficiently far enough away from the device to prevent excessive splatter and consistent placement.

Figure 6. Top Holding Bracket Engineering Drawing
Figure 7. Edge Holding Bracket Engineering Drawing

Results of the Design

A video of the final working product can be found here:

The final state of the device is a latte art maker that allows its user to output one of two designs onto coffee. The device allows for a simple circular design, or a heart shaped design, as depicted in the image below.

Figure 8. Heart design on coffee


Before the device can be used the user must set up a cup of coffee and glass of foamed milk beforehand. The cup of coffee must be placed below the receiving end of the silicone tube that is attached to the pen holder of the plotter (depicted below), and the milk foam on top of the plotter with the other end of the silicone tube placed deeply into the cup. The milk foam should be placed at the same height as the pump, as it makes it easier for the pump to transfer the foam.

Figure 9. Proper coffee cup placement


For our project, it is important for us to consider food safety. Because we are handling food, our device must be easily cleaned so we do not contaminate the next cup of coffee that it will make. We will also be dealing with hot liquids, so we should also consider a mechanism that will reduce spillage to protect both the machine and the user. For safety, we secured the silicone tubing that will be transferring hot liquid to prevent it from moving erratically due to the vibrations created by the pump, and in turn flicking hot liquids onto the user and device. We also secured regions of the plotter that are exposed with paper towels, as evident in the image above, to prevent liquid from entering and damaging the device. Additionally, we created a rudimentary shield to protect our electronics from potential splashes during our adjustment period with determining final positions for the sequences. We currently have no ethical violations with respect to the IEEE Code of Ethics.

Intellectual Property

In this project, we re-used code that our team developed for previous assignments in ECE 4760, and recognize that some portions of it may still have recognizable similarity to Professor Land’s original code. We used no additional code sources beyond this. As a main basis for our design to save on costs, we used the Houston Instrument Omnigraphic 2000 X-Y Plotter as a large component for our design. Due to the number of years it has been since the original device was released, we believe it is highly likely that the patent has expired for all major components of the device. We did not have to sign any non-disclosures for parts. There is a possible potential for securing the design through the USPTO, but we consider this highly unlikely due to its dependence on a predicate piece of technology, and uncertainties on whether it could truly be considered novel and non-obvious. There is a possible potential to publish this work, but it would be of interest to likely only a small niche audience interested in lattes and retrofitted technology. As we do not currently anticipate pursuing either of these pathways, we do not anticipate legal issues.


Our device performed as expected during the final demonstration. In the weeks leading up to the final week of lab, we realized that recreating the motions needed for intricate latte art was much more difficult than anticipated. As a result, we needed to compensate by working with simpler designs. By the final week, our device worked exactly as we expected it to. Through our implementation of the linear interpolation line algorithm, we had complete control of the the plotters movement and the speed of its movements. We were also able to control the flow rate of the pump from the PIC32.

Figure 3. Button Circuit Diagram
Figure 4. Voltage divider circuit for plotter control
Figure 5. Optoisolator circuit for pump contro

Source: ECE 4760: Latte Art Machine

About The Author

Muhammad Bilal

I am a highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.