Hykso makes a device that boxers and MMA fighters can use to capture performance data, helping them to measure and improve their punching skills. The company wants to expand the device’s capabilities and so they collaborated in a hackathon with Microsoft. The result was to enable a multi-user scenario by extending sensor data from the local device to the cloud via Azure Event Hubs, coupled with Stream Analytics.

Customer profile

Hykso is a recent Y Combinator startup based in Irvine, California. Its measurement device (approximately the size of a thin USB stick) is placed in the gloves of MMA (Mixed Martial Arts) fighters and boxers. The device is paired with an iOS device. A combination of built-in sensors captures a variety of data, such as velocity, and streams it directly to the paired iOS device for real-time display. More details about the product can be found at Hykso.com.

Screenshot of Hykso website

The team

Half of the solutions described here were implemented during an Azure App Service hackathon in October 2016. The hackathon team members for this project included:

  • Charles Lambert – Engineering Lead, Hykso
  • Jon Galloway – Senior SDE, Microsoft TED
  • Rafael Godinho – Senior SDE, Microsoft TED
  • Justin Henriksen – Principal SDE, Microsoft US-DX
  • Steve Seow – Senior Technical Evangelist, Microsoft US-DX


Team that participated at App Service hackathon (sans Justin H)

Pain points

The current Hykso product is paired (one for the left glove and one for the right) to a single iOS device, typically an iPhone. In the current version of the product, data goes only from the sensors to the mobile device.

While this works tremendously well, it presents challenges to perfecting a few interesting scenarios that are on Hykso’s product roadmap. Hykso has done extensive research to explore several options to enable a few of these scenarios:

  1. Inclusion of devices using other operating systems.
  2. Incorporation of more than a pair of sensors.
  3. Comparison of multiple data across more than one boxer.

Solution, steps, and delivery

Solution 1: The data service

The prerequisite solution was the implementation of a centralized, scalable, and cloud-based database that will allow for the storage of data that can be accessed across devices. The corollary solution was to implement a data service layer in front of a database that is capable of handling the sensor data regardless of the OS of the device.

Technical details

To enable future devices (dedicated server, Android, Windows, and so on), we chose to create a set of RESTful endpoints using WebAPI hosted as a web app. We also needed to make sure the current iOS app was able to pass the data it already collects one more step to the cloud.

Architecture

Standard sets of GET and POST are implemented in the relevant controllers. Since the goal is eventually to hand off development to Hykso, we agreed to use a common framework (among many available) familiar to both parties. We employed C# and LINQ To SQL.

Image of Hykso app on iOS

Code artifacts

The following is an example of a GET call to retrieve the data of a specific punch. Note that appropriate security has not been implemented yet. Additionally, this is assuming that the DataClassesX.dbml and the appropriate data models have been created.

[Route("GetPunch")]
[HttpGet]
public DataModel_Punch GetPunch(int punchId)
{
	DataModel_Punch result = new DataModel_Punch();
	
	try
    {
    	using (DataClasses1DataContext db = new DataClasses1DataContext())
            {
                Table<TablePunch> table = db.GetTable<TablePunch>();

                var item = (from e in table
                            where e.Id == punchId &&
                            e.Active == true
                            select e).SingleOrDefault();

                if (item != null)
                {
                    //because we are going to use this quite a few times
					//we'll create a function inside a helper (helpers.cs)
                    result = helpers.ConstructPunchObject(item);
                }
            }
        }
        catch (Exception)
        {
        }

        return result;
    }


Key learnings

Specific to WebAPI: The inclusion of [Route("GetPunch)] must be used in conjunction with [RoutePrefix("api/Punch")] so that it is possible to make multiple GET (or POST) calls at the same controller. The specific implementation of this is well-documented.

Helper classes such as helpers.ConstructPunchObject(item), in addition to copious comments, were used to ensure that developers who are assigned the code in the future are presented with a clean logic flow that is well-documented.

This web service layer will do just fine to read and write small amounts of data, but if the incoming data is too voluminous—for example, in a training session with 12 boxers/fighters, each with two devices strapped into their gloves—then it becomes necessary to open up more lanes of traffic, which leads us to the next solution.

Solution 2: Data ingestion

The second solution required an ingestion and processing (aggregation) of outgoing data from the iOS devices.

Technical details

For that we chose Azure Event Hubs to ingest the data, and pointed Azure Stream Analytics to perform on-the-fly aggregation, which is then dumped into a second event hub for consumption.

Architecture

During the App Service hackathon (October 2016), we followed a tutorial by Luis Delgado published on Medium, as well as our programming guide. At the time of writing, there is no iOS SDK for Event Hubs so we relied on the Event Hubs REST API.

Code artifacts

Here’s a snippet of the post function from Delgado’s writeup that we used in wiring up Hykso’s iOS app to send data to Event Hubs:

class func postEvent(data: [[String : Double]], completionHandler: AlamofireCompletionHandler) {
    let url = NSURL(string: "\(eventHubName)/publishers/\(publisherId)/messages?timeout=60&api-version=2014-01")!
    let request = NSMutableURLRequest(URL: url)
    request.setValue(NetworkHelper.sasToken, forHTTPHeaderField: "Authorization")
    request.setValue("application/vnd.microsoft.servicebus.json", forHTTPHeaderField: "Content-Type") //this is needed for batch posting of events
    let body = serizalizeDataForBatchPosting(data)
    request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(body, options: NSJSONWritingOptions.init(rawValue: 0))
    request.HTTPMethod = "POST"
    Alamofire.request(request).responseData { response in
        if response.response?.statusCode == 401 {
            NetworkHelper.getSasToken({ getSasTokenResponse in
                if getSasTokenResponse.response?.statusCode == 200 {
                    EventHub.postEvent(data, completionHandler: completionHandler)
                } else {
                    print("error getting sas token")
                    completionHandler(response)
                }
            })
        } else {
            completionHandler(response)
        }
    }
}


Key learnings

We ran into some roadblocks having the iOS send data directly to Event Hubs. We discovered that the REST API expects the string “publishers” to be part of the URI. However, in much of the documentation, “publisher” is optional. Note the string “publishers” in the following line:

let url = NSURL(string: "\(eventHubName)/publishers/\(publisherId)/messages?timeout=60&api-version=2014-01")!

Solution 3: Data consumption

When the data was properly ingested and aggregated, we implemented a third solution to display the data in real time as the punches are being thrown. For this, we worked on multiple front-end implementations.

Technical details

There were two logical choices to either pull or push the data aggregated by Stream Analytics (which comprises a query that runs a 10-second SlidingWindow to calculate the data that will end up being consumed by the UI). We did both during the hackathon. The first was an Angular.JS SPA hosted as a web app that triggers an Azure Function to poll for aggregated data at a fixed time. As punches are being thrown, aggregated data shows up on the UI.



The second was an ASP.NET web app that received data directly from an event hub with the help of SignalR. An Azure WebJob used an Azure Queue storage binding to monitor for new data. The WebJob then posts the update to an ASP.NET Web API endpoint in the web app. The Web API endpoint then publishes the update to the SignalR hub, where it is then sent via web sockets to the Angular front-end.

The third was a Power BI Embedded solution that allowed for a detailed and dynamic drill-down of the punch data.

Architecture

Diagram of simplified architecture

Key learnings

The data consumption solutions of this project, which were the last that we tackled during the hackathon, will require more time to polish up. In fact, we tried a third variant using Power BI to consume and display data as well.

Conclusion and general lessons

We had the luxury of starting from the ground up for this project in terms of wiring up the critical components (data service layer, data ingestion mechanisms, and more). Deploying them as app services (using Web Apps, Azure Functions, and so on) allowed Hykso, a typical high-potential but small startup, to avoid incurring additional and expensive development cycles and to extend the capabilities of their product. None of what we have done in a very short amount of time, for example, required Hykso to hire a “back-end guy” to maintain.

Setting expectations of the hackathon goals and, at a higher level, the project itself, is critical. At the hackathon, we were obviously limited to implementing a feature or two of a product. The 3-day hackathon could be described as hacking through the “jungle” to carve out a rough path to our desired outcome. Following the hackathon, when we have more time, the task at hand is to return to the point of origin and fine-tune the path for security, performance, and more. Outside of the hackathon, at the scope of about 2 to 3 months, fleshing out and stabilizing that integration is more feasible.

A reasonable hand-off of code is an objective that must be thought through as well. While many of our frameworks and solutions solve the business or technical problem a customer has, it is good to consider what resources and bandwidth the customer has to maintain, debug, or troubleshoot what we put together.