For the past few years I have been attending music festivals. One of my biggest gripes at these festivals is trying to find my friends. Typically there are thousands of people that attend these music festivals and with the proliferation of smart phones and social media applications the cellular network at the festival is overloaded leaving me communication-less. After a few of these music festivals I have brainstormed ideas to find my friends but have never acted through on it till now thanks to the Hexiwear platform. The main draw of Hexiwear is that it is a very flexible prototyping platform that is compact and looks ready for market launch.
The idea I ended up settling on was to make a wearable with the Hexiwear and a ISM band radio transceiver. The vibrant OLED display of the Hexiwear and the buttons with haptic feedback will allow me to create a rich and simple easy to use interface. The ISM band radio transceiver would allow users to send messages without a license! (Phew! No getting in trouble with the law!) The radio transceiver will need a power amplifier (PA) and a low noise amplifier (LNA) allowing for amazing range. Hexiwear does have a 2.4 GHz transceiver but it would be unable to get more than 50m of range with its PCB antenna and the lack of a PA and a LNA. I will use a transceiver in the 2.4 GHz band despite a few drawbacks since it will allow for global compliance with Spectrum Licensing Authorities meaning I can use this device in any Music Festival worldwide! Also, since people from all around world follow hackster.io it means they will be unlikely to get in trouble when they follow through with this tutorial! Typically a sub-GHz RF transceiver will get better range in these scenarios but the 915 MHz band I would use would be non-compliant in the rest of the world (except for South America).
TLDR: Well that has been a lot of talking! I will let this wonderful video explain everything! Enjoy!
I hope you enjoyed the video and continue scrolling to see the process I went through for the creating the HexiComm prototype. In the current prototype form, HexiComm is about the size of a walkie-talkie. With more time and resources I will be able to design a smaller PCB docking board with an on-chip antenna, PA and LNA allowing the innards of Hexiwear to be enclosed in a slightly large case that would make it truly become a wearable.
HardwarenRF24L01 RF TransceiverThe nRF24L01 RF transceiver with added Power Amplifier (PA) and Low Noise Amplifier (LNA) was chosen for this project as it has been widely used by the maker community with numerous amounts of resources generated for it. This transceiver operates at 2.4 GHz and features a PA and LNA allowing for great range reception. This module's form factor will need to be adjusted to fit the click module but before that a quick fix needs to be done.
During the usage of the nRF24L01 with the Hexiwear, the transceiver could transmit but it would have issues with receiving messages. The problem was attributed to the noisiness of the Hexiwear's 3.3V supply which is generated through the use of a DC-DC regulator which are typically noisier than linear regulators. The fix that was employed was to solder a 10 μF capacitor between VCC and GND. The cheap construction of these modules make it very susceptible to noise, the capacitor will help to filter out noise from the power supply. I chose to use a surface mount type (1206) as it would make the final product look a bit cleaner than with a large thru-hole capacitor hanging on the side.
The PROTO click module was used to interface with the nRF24L01 board. Although MikroElektronika had a click module with the nRF24L01 chip it however did not have the variant with PA, LNA and a SMA connector for an external antenna. Connecting everything up was pretty fun...Yay! tiny wires.... The nrF24L01 interfaces with the PROTO click through the use of a 4x2 Female Header that would allow swapping for different modules in the event one fails. The GND,CE,SCK, MISO, CS, MOSI and IRQ pins of the nRF24L01 were connected to the male headers that interface with the Hexiwear Docking Station Expansion Port 1 as shown in the schematic below. VCC for the nRF24L01 module could be supplied by the Hexiwear 3.3V supply but due to the high current draw (100mA) of the PA and LNA I opted to use a linear 3.3V regulator with a 400mAh battery. (The nRF24L01 module variant without PA and LNA can be used with the Hexiwear 3.3V supply with the 10 μF capacitor modification. I still recommend using the regulator as there will be even less noise which means better performance!)
SchematicUsing the schematic I made the neccessary modifications on the PROTO Click board as shown below. Sadly I did something which is not a very good practice...using similar colored wires for different pins. But hey...who has that many different colored wires at this thinness. Hopefully you will have all the colors for this or have a PCB made.
Now that all of the hardware is done! We can focus on the fun stuff! I chose to use ARM's mBed online compiler as it would be the easiest way to quickly program the Hexiwear and leverage all the driver libraries already written for mBed. The online compiler lets you write and compile code for a target device. The result of the compilation is a binary file which you download. The binary file can then be copied and pasted to an mbed compatible target device which will instantiate itself as a flash drive. To become more familiar with mBed online compile I recommend this tutorial. This section will assume familiarity with the mBed interface.
The hardware drivers which will be required for development are
- Buttons (KW40Z)
- 2.4 GHz RF Transceiver (nRF24L01)
- OLED Screen (SSD1351)
The button interface was a bit unique as but luckily a custom mBed driver library was written by Cristian Cotiga to interface with the buttons of the Hexiwear. The software library can be found here. I followed this example to understand how to utilize the library.
In the code snippet below, the KW40Z class is instantiated and callbacks are registered to functions. in the code below, if the up/down button is pressed the ButtonUp/ButtonDown function will be called.
#include "Hexi_KW40Z.h"
/* Instantiate the Hexi KW40Z Driver (UART TX, UART RX) */
KW40Z kw40z_device(PTE24, PTE25);
int main()
{
/* Register callbacks to application functions */
kw40z_device.attach_buttonUp(&ButtonUp);
kw40z_device.attach_buttonDown(&ButtonDown);
while (true) {
Thread::wait(500);
}
}
The following are the functions which run when up or down button is pressed.
void ButtonUp(void)
{
StartHaptic();
redLed = LED_ON;
greenLed = LED_OFF;
blueLed = LED_OFF;
}
void ButtonDown(void)
{
StartHaptic();
redLed = LED_OFF;
greenLed = LED_ON;
blueLed = LED_OFF;
}
2.4 GHz RF Transceiver (nRF24L01) LibraryThe nRF24L01 is very common and I was able to leverage an existing mBed driver library written by Owen Edwards. To understand how to utilize this library I followed its companion example application and took down a few key code snippets.
The following code is the main initialization needed for using the nRF24L01.
#include "nRF24L01P.h"
/*Instantiate USB Serial Class */
Serial pc(USBTX, USBRX); // tx, rx
/*Instantiate nRF24LO1+ Class */
nRF24L01P my_nrf24l01p(p5, p6, p7, p8, p9, p10); // mosi, miso, sck, csn, ce, irq
#define TRANSFER_SIZE 4
int main() {
char txData[TRANSFER_SIZE], rxData[TRANSFER_SIZE];
int txDataCnt = 0;
int rxDataCnt = 0;
/* Init Sequence */
my_nrf24l01p.powerUp();
my_nrf24l01p.setTransferSize( TRANSFER_SIZE );
my_nrf24l01p.setReceiveMode();
my_nrf24l01p.enable();
The following code shows how to transmit and receive.
while (1) {
// If we've received anything over the host serial link...
if ( pc.readable() ) {
// ...add it to the transmit buffer
txData[txDataCnt++] = pc.getc();
// If the transmit buffer is full
if ( txDataCnt >= sizeof( txData ) ) {
// Send the transmitbuffer via the nRF24L01+
my_nrf24l01p.write( NRF24L01P_PIPE_P0, txData, txDataCnt );
txDataCnt = 0;
}
}
// If we've received anything in the nRF24L01+...
if ( my_nrf24l01p.readable() ) {
// ...read the data into the receive buffer
rxDataCnt = my_nrf24l01p.read( NRF24L01P_PIPE_P0, rxData, sizeof(rxData ));
// Display the receive buffer contents via the host serial link
for ( int i = 0; rxDataCnt > 0; rxDataCnt--, i++ ) {
pc.putc( rxData[i] );
}
}
}
Slight modification to the nRF24L01P library. I added a snippet of code to the init function which flushes the transmit and receive buffer.
void nRF24L01P::init(void)
{
<original code>
nCS_ = 0;
spi_.write(_NRF24L01P_SPI_CMD_FLUSH_TX);
spi_.write(_NRF24L01P_SPI_CMD_FLUSH_RX);
nCS_ = 1;
}
OLED Screen (SSD1351) LibraryI was able to find an existing software library that interfaced with the SSD1351 OLED screen but it had a few flaws so I ported the OLED driver written by MikroElektronica for the Kinetis Design Studio project. My OLED driver library can be found here. This example which I also wrote shows how to draw images and write text on the OLED screen.
The following snippet showcases the init sequence for the library.
#include "Hexi_OLED_SSD1351.h"
#include "string.h"
#include "images.h"
/* Instantiate the SSD1351 OLED Driver */
SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15); /* (MOSI,SCLK,POWER,CS,RST,DC)*/
/* Get OLED Class Default Text Properties */
oled_text_properties_t textProperties = {0};
oled.GetTextProperties(&textProperties);
/* Fills the screen with solid black */
oled.FillScreen(COLOR_BLACK);
The following snippet showcases how to write text using label and textbox function. It also shows how to change the font attributes.
/* Change font color to blue */
textProperties.fontColor = COLOR_BLUE;
oled.SetTextProperties(&textProperties);
/* Display text at (x=5,y=40) */
strcpy(text,"Timer(s):");
oled.Label((uint8_t *)text,5,40);
/* Set text properties to white and right aligned for the dynamic text */
textProperties.fontColor = COLOR_WHITE;
textProperties.alignParam = OLED_TEXT_ALIGN_RIGHT;
oled.SetTextProperties(&textProperties);
/* Display time reading in 35px by 15px textbox at(x=55, y=40) */
oled.TextBox((uint8_t *)text,55,40,35,15);
The following snippet showcases how to add images to a particular position on the OLED screen.
/* Adding Banner Image to bottom of screen */
/* Setting pointer location of the 96 by 32 pixel bitmap */
const uint8_t *image = NXP_banner_bmp;
/* Draws the image on the Screen starting at (x=0,y=64) */
oled.DrawImage(image,0,64);
NXP_banner_bmp is a bitmap array located in the images.c in the project.
To add your own images, bitmaps need to be formatted as 16bppRgb565, flipped vertically and a 6 byte header needs to be appended at the start of the array. This tool by MikroElektronica can be used to create the correct arrays for images. It takes an image and outputs the array the software library expects. It handles the flipping and the 6 byte header. The image needs to be converted outside the tool to fit the screen (96px by 96px) as it will not scale down to the correct size for the OLED screen.
User InterfaceThe user interface will feature the usage of the four buttons with haptic feedback. All screen will have the blue HEXICOMM bmp and the home button bmp. The send button BMP will be in all screens except the received messages screen. Also for each screen there is a total of 3 lines dedicated for text. It will be primarily the text within these three lines changing for the different screens.
The four buttons will be Home, Send, Toggle and Stage. The home button brings the user back to the home screen. The Send Button sends the current message on the screen. The Stage button advances the user to the stage screens and allows changing stage number. The Toggle button works only in the stage screens and allows the user to toggle the text in line 2. For a better idea on the interface consult the photos below.
When messages are received the green LED will bet lit, the haptic motor will vibrate and one of the messages below will be shown on the screen. The received message screen utilizes Line 1 to show the initials of who sent the message. The initials are set in bytes 3 and 4 of the packet.
The examples have only shown two stages for ease of visibility but this interface can be easily expanded to show more stages.
HexiComm Software BuildFrom the driver examples I collected snippets of code that would aid me in creating my HexiComm project. I have split my main.cpp into multiple sections for ease of understanding.
Initialization
- OLED, Buttons and RF transceiver driver classes are instantiated.
- Haptic Feedback and LEDs instantiated.
- Function Prototypes for helper functions.
- Flags,message, UI variables initialized.
- Bitmap pointers assigned to correct images.
- Wait 6.5 seconds before running any code to allow device to be reset and placed into mini docking station which has no reset button.
- Run all of the nRF24L01+ startup sequences.
- Button callbacks registered with functions.
- Text home screen of HexiComm device displayed.
- Images for Hexicomm displayed.
- #define NAME is for your initials to uniquely identify your HexiComm during messaging.
#include "mbed.h"
#include "Hexi_KW40Z.h"
#include "Hexi_OLED_SSD1351.h"
#include "OLED_types.h"
#include "OpenSans_Font.h"
#include "nRF24L01P.h"
#include "string.h"
#include "images.h"
#define NAME "KH"
#define LED_ON 0
#define LED_OFF 1
#define NUM_OF_SCREENS 6
#define TRANSFER_SIZE 4
void StartHaptic(void);
void StopHaptic(void const *n);
void displayHome();
void screenHandler(uint8_t stageNum,uint8_t header);
DigitalOut redLed(LED1,1);
DigitalOut greenLed(LED2,1);
DigitalOut blueLed(LED3,1);
DigitalOut haptic(PTB9);
/* Define timer for haptic feedback */
RtosTimer hapticTimer(StopHaptic, osTimerOnce);
/* Instantiate the Hexi KW40Z Driver (UART TX, UART RX) */
KW40Z kw40z_device(PTE24, PTE25);
/* Instantiate the SSD1351 OLED Driver */
SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15); /* (MOSI,SCLK,POWER,CS,RST,DC) */
/* Instantiate the nRF24L01P Driver */
nRF24L01P my_nrf24l01p(PTC6,PTC7,PTC5,PTC4,PTB2,NC); // mosi, miso, sck, csn, ce, irq
/* Text Buffer */
char text[20];
oled_text_properties_t textProperties = {0};
uint8_t screenNum=0;
bool prefix=0;
bool sentMessageDisplayedFlag=0;
char rxData[TRANSFER_SIZE];
char txData[TRANSFER_SIZE];
/* Pointer for the image to be displayed */
const uint8_t *homeBMP = HEXIWEAR_HOME_bmp;
const uint8_t *sendBMP = HEXIWEAR_SEND_bmp;
const uint8_t *bannerBMP = hexicomm_bmp;
int main()
{
/* Wait Sequence in the beginning for board to be reset then placed in mini d ocking station*/
Thread::wait(6000);
blueLed=0;
Thread::wait(500);
blueLed=1;
/* NRF24l0p Setup */
my_nrf24l01p.init();
my_nrf24l01p.powerUp();
my_nrf24l01p.setAirDataRate(NRF24L01P_DATARATE_250_KBPS);
my_nrf24l01p.setRfOutputPower(NRF24L01P_TX_PWR_ZERO_DB);
my_nrf24l01p.setRxAddress(0xE7E7E7E7E8);
my_nrf24l01p.setTxAddress(0xE7E7E7E7E8);
my_nrf24l01p.setTransferSize( TRANSFER_SIZE );
my_nrf24l01p.setReceiveMode();
my_nrf24l01p.enable();
/* Get OLED Class Default Text Properties */
oled.GetTextProperties(&textProperties);
/* Fills the screen with solid black */
oled.FillScreen(COLOR_BLACK);
/* Register callbacks to application functions */
kw40z_device.attach_buttonLeft(&ButtonLeft);
kw40z_device.attach_buttonRight(&ButtonRight);
kw40z_device.attach_buttonUp(&ButtonUp);
kw40z_device.attach_buttonDown(&ButtonDown);
/* Change font color to white */
textProperties.fontColor = COLOR_WHITE;
textProperties.alignParam = OLED_TEXT_ALIGN_CENTER;
oled.SetTextProperties(&textProperties);
/*Displays the Home Screen*/
displayHome();
/*Draw Home Button and Send Button*/
oled.DrawImage(homeBMP,0,81);
oled.DrawImage(sendBMP,53,81);
oled.DrawImage(hexicomm_bmp,0,0);
Main LoopThe main loop of the program checks to see if there are any packets received and processes them. If received packets are received the following occurs
- Green LED is turned on and the Hexiwear vibrates to indicate a message has been received.
- It also sets a flag to indicate that a message has been received.
- With this flag set, the send button will be deactivated and the send button on the screen removed to create the illusion of a new screen.
- The packet is processed and counting on the content of the packet a series of messages is displayed.
- Line 1 displays the initials of the sender sent in byte[2:3] of the packet.
- Line 2 displays either "Meet" or a blank which is an implicit "I am" depending on what is in byte[0] of the packet.
- Line 3 displays either "Where Yall" or "@Stage #" depending on the number in byte[1] of the packet.
- A wait of 50 ms is added at the end of the loop for a delay.
while (true)
{
// If we've received anything in the nRF24L01+...
if ( my_nrf24l01p.readable() ) {
// ...read the data into the receive buffer
my_nrf24l01p.read( NRF24L01P_PIPE_P0, rxData, sizeof(rxData));
//Set a flag that a message has been received
sentMessageDisplayedFlag=1;
//Turn on Green LED to indicate received message
greenLed = !sentMessageDisplayedFlag;
//Turn area black to get rid of Send Button
oled.DrawBox (53,81,43,15,COLOR_BLACK);
char name[7];
name[0] = rxData[2];
name[1] = rxData[3];
name[2] = ' ';
name[3] = 's';
name[4] = 'e';
name[5] = 'n';
name[6] = 't';
oled.TextBox((uint8_t *)name,0,20,95,18);
switch (rxData[0])
{
case 'M':
{
oled.TextBox("Meet",0,35,95,18);
break;
}
case 'I':
{
oled.TextBox(" ",0,35,95,18);
break;
}
default: {break;}
}
switch (rxData[1])
{
case '0':
{
oled.TextBox("Where Yall?",0,50,95,18);
break;
}
case '1':
{
oled.TextBox("@ Stage 1",0,50,95,18);
break;
}
case '2':
{
oled.TextBox("@ Stage 2",0,50,95,18);
break;
}
case '3':
{
oled.TextBox("@ Stage 3",0,50,95,18);
break;
}
case '4':
{
oled.TextBox("@ Stage 4",0,50,95,18);
break;
}
case '5':
{
oled.TextBox("@ Stage 5",0,50,95,18);
break;
}
default:{break;}
}
StartHaptic();
}
Thread::wait(50);
}
}
Callback FunctionsThe callback functions define the behavior of the buttons.
- The right button is assigned to be the send button. It sends a message based on what screen is being shown. If a received message is being shown the user is unable to send any messages since it is not in an acceptable screen.
- The left button is assigned to be the home button. It will send the user back to the home screen which has the message "Where Yall at?"
- The up button is assigned to let the user toggle between the words "I am @" and "Meet @" on Line 2 in all screens except the home screen.
- The down button is assigned to advance the stage number for the Screen. Starting from the home screen, it will advance to "Stage 1", "Stage 2" till it reaches the maximum number of stages The text is displayed on Line 3.
- Pressing any button except the right button will invoke the screenHandler.
- Pressing any button except the right button when the user is in the received message screen will close that screen and turn the Green Notification LED off.
- Haptic Feedback is felt by the user when pressing buttons in screen in which they are classified as active.
/*Send Button */
void ButtonRight(void)
{
if (!sentMessageDisplayedFlag)
{
StartHaptic();
// Send the transmitbuffer via the nRF24L01+
my_nrf24l01p.write( NRF24L01P_PIPE_P0, txData, 4 );
}
}
/*Home Button */
void ButtonLeft(void)
{
StartHaptic();
screenNum = 0;
/*Turn off Green LED */
sentMessageDisplayedFlag=0;
greenLed = !sentMessageDisplayedFlag;
/*Redraw Send Button*/
oled.DrawImage(sendBMP,53,81);
screenHandler(screenNum,prefix);
}
/*Toggles Between I am @ and Meet @ */
void ButtonUp(void)
{
if (screenNum !=0)
{
StartHaptic();
/*Turn off Green LED */
sentMessageDisplayedFlag=0;
greenLed = !sentMessageDisplayedFlag;
/*Redraw Send Button*/
oled.DrawImage(sendBMP,53,81);
prefix = !prefix;
screenHandler(screenNum,prefix);
}
}
/*Advances Stage Number */
void ButtonDown(void)
{
StartHaptic();
/*Turn off Green LED */
sentMessageDisplayedFlag=0;
greenLed = !sentMessageDisplayedFlag;
/*Redraw Send Button*/
oled.DrawImage(sendBMP,53,81);
if (screenNum < NUM_OF_SCREENS -1) {
screenNum++;
}
else
{
screenNum = 0;
}
screenHandler(screenNum,prefix);
}
Helper Functions- StartHaptic() and StopHaptic() functions used to provide Haptic Feedback for button touches and for a vibration alert for messages received.
- displayHome() function displays the Text Home Screen consisting of three lines of text and it also prepares a Transmit Packet should the user choose to press the word send.
- screenHandler() function displays the correct screen based on the button presses registered. It also prepares the corresponding transmit packet for that screen.
void StartHaptic(void) {
hapticTimer.start(50);
haptic = 1;
}
void StopHaptic(void const *n) {
haptic = 0;
hapticTimer.stop();
}
void displayHome(void)
{
oled.TextBox(" ",0,20,95,18); //Line 1
oled.TextBox("Where",0,35,95,18); //Line 2
oled.TextBox("Yall At?",0,50,95,18); //Line 3
strcpy(txData,"I"); //Packet[0]
strcat(txData,"0"); //Packet[1]
strcat(txData,NAME); //Packet[2:3]
}
void screenHandler(uint8_t stageNum,uint8_t header)
{
//Text for Line 1
oled.TextBox(" ",0,20,95,18);
//Text for Line 2
switch(header)
{
case 0:
{
//Packet Encoding for I am @
strcpy(txData,"I");
oled.TextBox("I am",0,35,95,18);
break;
}
case 1:
{
//Packet Encoding for Meet @
strcpy(txData,"M");
oled.TextBox("Meet",0,35,95,18);
break;
}
default:
{
break;
}
}
//Text for Line 3
switch (stageNum)
{
case 0:
{
displayHome();
break;
}
case 1:
{
//Packet Encoding for Stage 1
strcat(txData,"1");
oled.TextBox("@ Stage 1",0,50,95,18);
break;
}
case 2:
{
//Packet Encoding for Stage 2
strcat(txData,"2");
oled.TextBox("@ Stage 2",0,50,95,18);
break;
}
case 3:
{
//Packet Encoding for Stage 3
strcat(txData,"3");
oled.TextBox("@ Stage 3",0,50,95,18);
break;
}
case 4:
{
//Packet Encoding for Stage 4
strcat(txData,"4");
oled.TextBox("@ Stage 4",0,50,95,18);
break;
}
case 5:
{
//Packet Encoding for Stage 5
strcat(txData,"5");
oled.TextBox("@ Stage 5",0,50,95,18);
break;
}
default:
{
break;
}
}
//Append Initials to txData[2:3].
strcat(txData,NAME);
}
The repository for this project with all the required files can be found here or in the repository listed at the bottom of the page.
CADI used Autodesk Fusion 360 for the first time in this project. The only real experience I had with CAD was using SketchUp, I must say that the interface is fairly intuitive. Since my experience with CAD is a bit lacking the case I prototyped for HexiComm is a bit simplistic. Sadly I was unable to get access to a 3D Printer to print this case. Check this link out for the 3D model.
Here is a video showing how the user interface works.
Final ThoughtsThis project was a tremendous amounts of fun as I basically made my own product from beginning to end. I even got to do a bit of marketing! Hopefully this project will be easy to follow and duplicate as I have spent countless numbers of hours debugging hardware issues specifically the noise issue on the transceiver. It has also been awesome to see that other hackster.io members have been using my OLED software library to create their own projects! (The power of mBed and sharing!)
As for future work I would hope to add Bluetooth functionality such that a smartphone could be used as the input interface to send messages through HexiComm. (or maybe this tutorial has been amazing and you feel inspired to do it for me!) I would also like to attempt a subGHz version to see how much greater the range could be. As I get more free time I would likely create a new PCB which would mate to the innards of the Hexiwear through the existing docking connector that would allow me to add the extra hardware for extended RF range in a smaller form factor. As I get more experience with CAD I will make a new case for the Hexiwear which would truly make HexiComm a wearable.
Comments