top of page

How we created a Digital Twin of our Hannover Messe WMF Coffee Machine


This is a technical description on how to visualize data from a websocket interface using the 3DVisualizr tools as a Digital Twin monitoring application.


I wanted to share a hands-on example on how we used our 3DVisualizr technology to visualize data from our Hannover Messe coffee experience as can be seen here:


Data integration:

The WMF machine shares quite a handful of performance and status data through a websocket interface in "real-time". Fetching the data only required connecting the machine via an RJ45 cable to our FritzBox, and of course a couple of lines of code in javascript that ran in our Visual Studio project:

function initWebSocket() {
 wsUri = 'ws://';

  try {
    if (typeof MozWebSocket == 'function')
      WebSocket = MozWebSocket;
    if ( websocket && websocket.readyState == 1 )
    websocket = new WebSocket( wsUri );
    websocket.onopen = function (evt) {
    websocket.onclose = function (evt) {
    websocket.onmessage = function (evt) {
      handleReceivedMessage( );
    websocket.onerror = function (evt) {
      debug('ERROR: ' +;
  } catch (exception) {
    debug('ERROR: ' + exception);

The we started fetching the messages from the machine:

function onConnectedToMachine() {

  sendMessage('startPushCleaningRinsingNotifications', { a_uiPreWarningTimeInSeconds: 0 });


function onDisconnectedToMachine() {

function sendMessage(type, opts) {
  var msg = JSON.stringify({ function: type, ...opts });
  if ( websocket != null ) {
    websocket.send( msg );
    debug('sent: ' + msg)

function startPollingForMessage(type, opts) {
  sendMessage(type, opts);
  setTimeout(() => {
    startPollingForMessage(type, opts)
  }, 2000);

function getValueFromMessage (message, value) {
  const el = message.find(v => v[value])
  if (el) return el[value]
  else return null

Now, the data connection is established.

Scene creation

The scene creation was done by reusing elements from our 3DV asset library, and creating a BabylonJS file from the machine's CAD file using our CAD converter. Finally, the walls received a little touch up in Blender by our 3D Artists to show the actual wall designs.

Our 3DV scene for HannoverMesse
Our 3DV scene for HannoverMesse

Digital Twin implementation:

With the scene and the data connection in place we can start with the implementation.

Machine Data Status

First, we want to change the machine's data label color and status definition based on the actual machine's status. Below example defines a selection of error codes to be handled as "maintenance" events.

   case 'startPushErrors':
      debug('Started receiving errors', { message }) 

        if ([74, 84].includes(getValueFromMessage(message, 'ErrorCode'))) {
          window.coffeMachineData.maintenance = getValueFromMessage(message, 'Error Text')  

        } else {
          window.coffeMachineData.error = getValueFromMessage(message, 'Error Text')

For the data label we defined a couple of different status themes. Standard is idle, unless we receive a message from the machine to change it. Error code 74 will change the status to Maintenance.

   var status = rsm.getStoredStyle("Idle")

        if (window.coffeMachineData.error) {
            status = rsm.getStoredStyle("Error")
        } else if (window.coffeMachineData.maintenance) {
            status = rsm.getStoredStyle("Maintenance")
        } else if (window.coffeMachineData.currentDrink) {
            status = rsm.getStoredStyle("Running")
        } else if ( {
            status = rsm.getStoredStyle("Cleaning")

Each status has its own color code and can have distinguished data label behaviors. One very common setup is to have "error" status in show-full mode so that users will immediately recognize the issue.

Recipe information

Next, let's also fetch the current recipe from the machine to show it in the data label. The machine shares the recipe and some consumption data whenever it dispenses a drink. Here, we combine the recipe number with the amount of water that was consumed.

case 'startPushDispensingStarted':
      debug('Started receiving "drink starts"', { message })
        window.coffeMachineData.currentDrink = getRecipeName(getValueFromMessage(message, 'RecipeNumber')) + ' (' + getValueFromMessage(message, 'QtyWater') + 'ml Water)' 

This can now also be handled by our data label:

input.labels.push(new E3D.LabelData({
            id: "Coffee Machine"
            , mesh: "Coffee Machine.Coffee Machine" //the ID from the created scene
            , title: "WMF 1500S+" //title to be shown in the label
            , status 
            , layout: E3D.LABEL_LAYOUT.LAYOUT_TITLE
            , animationMode: 0
            , components: [
                id: "CurrentDrink"
                , title: "Currently dispensing:"
                , type: E3D.COMPONENT_TYPE.DEFAULT
                , value: window.coffeMachineData.currentDrink 


The final implementation showed temperature date from the machine, beverage statistics, in combination with the current machine's status. We furthermore were able to embed dashboards from our Elisa IndustrIQ partners.

And even though it was conceived as a show case to attract visitors to the booth, there were plenty of times that I noticed an issue with the machine through the dashboard as I wasn't facing the machine's display. So I was actually using the demo as a monitoring tool to check on the machine and make sure it was handled properly.

3DV at the Hannover Messe
3DV at the Hannover Messe

47 views0 comments

Recent Posts

See All
bottom of page