During the last year, several chip vendors have released low-cost WiFi boards to power the Internet of Things revolution. MediaTek’s take on this is the contribution of newly released MediaTek Linkit Connect 7681. In this tutorial we will show how you can control a GPIO of the board using a mobile application running off your smartphone, which you can customise and adapt to suit your own IoT innovations and projects.
The MediaTek LinkIt 7681 has been developed by MediaTek. The development board is based on the MediaTek MT7681 chipset which has been designed to help makers, hackers and constructors to connect anything to the internet. The board can either connect via an access point or act as one. The board also has five general input/output (GPIOs, numbered 0..4) and one universal asynchronous receiver/transmitter (UART) port. Compared to the MediaTek LinkIt ONE which we wrote about earlier, the board is a much simplfied architecture in terms of hardware. The intended use is to either build really simple connected projects or to extend an existing project with means of connectivity.
If you are curious to learn more about the LinkIt Connect 7681, there is a great developers guide from MediaTek that covers what you need to know in order to start working with the hardware.
Source Code
You can browse the source code for this tutorial in our designated Evothings GitHub repository.
What you need
To work through this tutorial; you will need the following:
- The MediaTek LinkIt Connect 7681.
- An iOS or Android smartphone.
- A computer running Microsoft Windows or Ubuntu Linux.
Step 1 – Hardware requirements
This tutorial requires no external hardware since each GPIO has a LED connected to it.
Step 2 – Embedded software
Preparation
If you already have the MediaTek LinkIt Connect 7681 SDK installed on your computer, you can skip this step.
Before you can start compiling and downloading any software to your board you need to provide your computer with the development environment. As of today there is support for Windows and Ubuntu (Linux). MediaTek provides instructions on how to configure the development environment in their developers guide which you can find on their website. From this point on in the tutorial, we assume that you have already installed the development environment and assume that you know how to open a serial connection to the board.
Got a Mac? This tutorial was written running Ubuntu 14.04 LTS within VirtualBox on a MacOS X computer, so there’s always a way.
Application Design
Enough with preparations, let’s get down to business. We are now going to configure the development board to connect to an existing WiFi network and start to listen for TCP connections on port 4538. When a client makes a connection, the software will send the current status of the five GPIOs and then idles awaiting new commands from the connected client.
In order to accomplish this we need a simple protocol to control the LEDs. In this tutorial we’ll define a simple protocol on the form: $XY#, where X (0-4) is the GPIO we want to change and Y (1/0) is what binary state we want to change it to, i.e. a 1 will turn on the LED and a 0 will turn it off. The development board will then answer and acknowledge that the changes has been made by sending a response that will be the same as the request.
If you for example would like to turn the first LED on, the request would be $01# since the first LED is connected to the GPIO 0. Then we would expect the board to answer with $01# thus verifying that the LED has been turned on.
Create a project
First step in this part of the tutorial is to create an empty project.
You do this by copying the src/ directory from the SDK to a path where you would like to store your project. Due to legal rights reasons, we are unable to provide the entire source code to this example. The source code is instead provided using a patch file. This file is created simply by comparing our project directory with the empty project in the src/ directory, the difference between the directories is then saved to a patch-file.
In order to apply the patch to your new project you need the patch application. This is usually provided with Linux distributions. If you are using Windows, you’ll need to install the patch in some way, e.g. during the installation of the MediaTek LinkIt Connect 7681 SDK you’re asked to install Cygwin, this software has in turn a bundled patch application that you are recommended to install.
The patch itself can be downloaded from our Github repository.
Start Cygwin or open a terminal and browse to the directory containing your project. Also ensure that you know the path to the Evothings-MediaTek-Connect-7681-TCP-Led.patch provided by Evothings. Execute the following command:
patch -p1 < path/to/Evothings-MediaTek-Connect-7681-TCP-Led.patch
If the command executes without any issues you should see the following printout:
patching file cust/iot_custom.c patching file cust/tcpip/iot_tcp_app.c patching file mak/MT7681/flags_sta.mk
Before you try to build and upload the firmware you’ll need to add your WiFi credentials in the source code. The following section contains instructions on how to do that.
Configure WiFi
The first step is to add the credentials of your WiFi to the source code.
Open the file cust/iot_custom.c and find the function IoT_Cust_SM_Smnt(). Change the Ssid variable to the name of your network and the Passphrase to the password of that network. When we developed this tutorial we faced an issue that prevented us from actually changing what WiFi we could connect to. We found that sending the command “AT#Default” (without quotes) to the board using the serial connection solved our issue – keep that in mind if you have any issues with the WiFi connection. We sent the command to the board each time we uploaded new firmware to the board that changed either the Ssid or Passphrase variable and faced no issues.
In the same file we have to ensure that the board listens for connections on port 4538. This is simply done by adding the function call uip_listen(HTONS(4538)) to the function iot_cust_init() – which is called once after the module initializations has finished.
Open the file cust/tcpip/iot_tcp_app.c in an text editor and then locate the function iot_tcp_appcall(). This function is executed each time a TCP event occurs, in this tutorial we will detect when a connection is made on the port 4538 and execute the function app_handle_request(). This function is the base for our implementation. When a new client is connected the following code is executed.
if(uip_connected()) { u8_t buffer[sizeof(valid_pins) * RESPONSE_LENGTH]; u8_t buffer_length; app_create_response(buffer, &buffer_length); uip_send(buffer,buffer_length); }
This code verifies the connection and responds to the connection by sending the current status of the LEDs on the board. When connecting to a board that currently has LED 1 and 3 turned on the data sent will be the following:
$00# $11# $20# $31# $40#
When a client is connected and sends new data, the data is received and parsed using the app_is_request_valid() function. If the message received is valid, it is acted upon. In this case that means that an LED is either turned on or off after which an acknowledgment is sent back to the client. Non-valid messages is simply ignored. The code that accomplishes this can bee seen below.
else if(uip_newdata()) { u16_t index = 0; while(index < uip_datalen()) { u8_t *request = (u8_t *)uip_appdata + index; if(app_is_request_valid(request)) { u8_t pin = request[REQUEST_PIN_OFFSET] - '0'; u8_t pin_value = request[REQUEST_PIN_VALUE_OFFSET] - '0'; iot_gpio_output(pin, !pin_value); u8_t polarity; u8_t response[RESPONSE_LENGTH]; iot_gpio_read((int32)pin, &pin_value, &polarity); write_response_to_buffer(response, (u8_t)pin, pin_value); uip_send(response, sizeof(response)); index += REQUEST_LENGTH; } else { index++; } } }
Now we are ready to try to build and upload the application. MediaTek provides instructions on how to do that in their Developer’s guide (see links above).
Step 3 – Mobile application
Preparation
If you already have installed a working copy of Evothings Studio you can skip this step.
First you have to install the Evothings Studio on your computer. The Evothings Studio contains two applications that interact with each other. You have to install Evothings Workbench on you computer. The software provides you with the interface in which you will perform your development. You’ll also need to install the Evothings Client (iOS, Android) on your smartphone. When started, the client can scan for a Workbench environment and then open a connection dialog – once open, simply pressing RUN for any project on the PC will push the corresponding code the mobile application — just like that.
Source code
The application source consists of three files; an index.html file which contains the user interface, an app.css which contains the style sheet information and finally the app.js which contains the application logic. In this tutorial, we’ll also make use chrome.tcp.sockets in order to open a socket to our MediaTek LinkIt Connect 7681 board. The socket is then used to send requests and to receive responses from the board according to the protocol that we defined earlier in this tutorial. And yes, the Chromium Socket low-level code is already compiled into the Evothings Client in beforehand, ready to run straight from JavaScript.
The user interface is built on three views using the jQuery Mobile framework; a startView, connectingView and controlView which can be seen above. The startView is displayed to the user as the first view that the user faces when running the application. It consists of a text input field where the user inputs the IP address of the development board and a connect button. The connectingView is displayed to the user while the application tries to open a TCP socket and finally the controlView that contains five circles, each representing a LED on the board and a disconnect button. Simple as pie, yet a worthy starting point hopefully challenging you to make great apps.
The following code is executed once the application is fully loaded and the code defines event handlers for the connectButton and disconnectButton.
$(document).ready(function() { $('#connectButton').click(function() { app.connect() }) $('#disconnectButton').click(function() { app.disconnect() }) })
As you can see in the code above the app.connect() method is executed when the connectButton is pressed. The application first hides the startView and displays the connectingView. Then it tries to open a socket to the ip address the user put in IPAddress input using the chrome.sockets.tcp API. If it succeeds in opening a socket the it hides the connectingView and displays the controlView and then registers a callback to execute when data is received. If it fails to open a socket an error message is displayed to the user and then the connectingView is hidden and startView is displayed again. In the code displayed below you can also see the callback that handles incoming data, it basically converts the data to a string and then executes the app.parseReceivedData() method.
app.connect = function() { var IPAddress = $('#IPAddress').val() console.log('Trying to connect to ' + IPAddress) $('#startView').hide() $('#connectingStatus').text('Connecting to ' + IPAddress) $('#connectingView').show() chrome.sockets.tcp.create(function(createInfo) { app.socketId = createInfo.socketId chrome.sockets.tcp.connect( createInfo.socketId, IPAddress, app.PORT, connectedCallback) }) function connectedCallback(result) { if (result === 0) { console.log('Connected to ' + app.IPAdress) $('#connectingView').hide() $('#controlView').show() chrome.sockets.tcp.onReceive.addListener(function(info) { var data = app.bufferToString(info.data) console.log('Received: ' + data) app.parseReceivedData(data) }) } else { var errorMessage = 'Failed to connect to ' + app.IPAdress console.log(errorMessage) navigator.notification.alert(errorMessage, function() {}) $('#connectingView').hide() $('#startView').show() } } }
The method app.parseReceivedData() handles the incoming handles the incoming data. In this case the method looks through the string after a part of the string that matches the pattern we defined in our protocol and then each match is sent to the method app.handleVerifiedResponse() as you can see in the code snippet below.
app.parseReceivedData = function (data) { var regExpPattern = /\$[0-4][0-1]#/gm data.match(regExpPattern).forEach(function(element,index, array) { app.handleVerifiedResponse(element) }) }
The method app.handleVerifiedResponse() contains a bit of jQuery magic. The first thing the method does is to extract the GPIO pin and the value of that pin from the response. Since the response already has been verified by the app.parseReceivedData() method no further checking is done at this point.
Each LED is represented by a <div> element belonging to the class circleBase, this class ensures that the <div> is drawn as a circle. Each <div> also has an unique id on the form ledX where X is a LED from 0 – 4. This id is then used to find the <div> representing each LED. Once the <div> is identified the color off the <div> is set to a color that corresponds to the color of the physical LED if the response states that the LED has been lit, else the color of the <div> is set to grey. This is done by adding and removing the class ledOff and ledX where X is the LED number. The different colors is defined in app.css. The code also ensures that the click event is changed to either app.ledOn() or app.ledOff() also depending on if the LED was turned on or off. The code that implements this functionality can be seen below.
app.handleVerifiedResponse = function(response) { var pin = response.charAt(1) var pin_value = parseInt(response.charAt(2)) var domId = '#led' + pin if($(domId).length == 0) { var htmlString = '<div style="display:inline-block">' + '<div id="led' + pin +'" class="circleBase ledOff"></div>' + '<p class="center">' + pin + '</p></div>' $('#ledView').append($(htmlString)) } if(pin_value == 1) { $(domId).removeClass('ledOff').addClass('ledOn') $(domId).unbind('click').click(function(){ app.ledOff(domId) }) } else { $(domId).removeClass('ledOn').addClass('ledOff') $(domId).unbind('click').click(function(){ app.ledOn(domId) }) } }
The methods app.ledOn() and app.ledOff() is both basically wrappers that embeds a request into a string that follows the defined protocol. Both methods then call the app.sendString() which also acts as a wrapper for the chrome.sockets.tcp.send() method, the implementation of app.sendString() can be seen below.
app.sendString = function(sendString) { console.log('Trying to send:' + sendString) chrome.sockets.tcp.send( app.socketId, app.stringToBuffer(sendString), function(sendInfo) { if(sendInfo.resultCode < 0) { var errorMessage = 'Failed to send data' console.log(errorMessage) navigator.notification.alert(errorMessage, function() {}) } }) }
In the code, there are two helper functions, app.stringToBuffer() and app.bufferToString() which encodes the string to be sent and decodes the received data.
Finally there is a app.disconnect() method that executes the chrome.sockets.tcp.disconnect() and hides the controlView and displays the startView. The method is called when the disconnectButton is pressed.
Summary
In this tutorial, we have demonstrated how easily you can write a cross-platform mobile application that communicates with your MediaTek LinkIt Connect 7681 using TCP sockets. The mobile application was developed using the Evothings Studio. The application should provide a great starting point for any connected project related to the MediaTek LinkIt Connect 7681.
We would love to see what you build using these products, please share your projects with @evothings.
Happy tinkering!