Bluetooth HID Keyboard & Mouse Bridge

Just keeping you up to date (if you want to). I’ve written a firmware for Texas Instruments CC2540 Bluetooth Low Energy IC that sends keyboard & mouse reports to the host using a few UART commands that works similarly to Arduino Keyboard’s press() and release() commands, except it will send raw HID keycodes instead of printable characters. As for the mouse, a UART command with button states, X, Y, Z bytes can be used at the same time as well.
The firmware isn’t as good as if it is programmed by professional software engineers and if IAR Workbench was free so I can spend an infinite amount of time tweaking, but it does the job. The advantage to using a CC2540 with my firmware means you can turn any cheap eBay HM-10 serial Bluetooth module into a HID Bluetooth keyboard & mouse bridge! The disadvantage would be that to write a new firmware, you would need to use IAR Embedded Workbench for 8051 which is free for 30 days or cost $3k if you buy it. However, for just flashing, IAR is not needed. So as an end-user, there are essentially no disadvantages.

The  UART commands are sent to the CC2540 via UART at 57600bps with 8 data bits and no parity. They must end with a carriage return (0x0D, ‘\r’) or a line termination character (0x0A, ‘\n’), or both.

The commands include:

  • KU(keycode): a key is released with the keycode (keycode)
  • KD(keycode): a key is pressed with the keycode (keycode)
  • M(status)(X)(Y)(Z): sending buttons and mouse coordinates
  • KUPDATE: send report to host

(___) are 8-byte characters, and you don’t send the brackets, just the
value inside the brackets.

The (keycode) used are from USB HID usage table 1.12 in the keyboard section. Since I haven’t made a full keyboard board, the module will ask the host for pin, which is 000000. There is also another firmware that advertise the device with containing a keyboard so the user must use the keyboard to enter the pin, but a test board is needed thus this has not been tested.

Here is a pretty 3D render of the board I’ve whipped up.
BLE Keyboard & Mouse v0.1
It uses a MSP430 to handle the keyboard scanning and TrackPoint polling. The entire top left portion of the board is dedicated to power management. Battery used can be of any types in the range of 3V-5V. If the battery is a rechargeable Li-Po or Li-Ion single cell, the Micro USB can be used to recharge it. There are 2 LEDs (Green and Yellow) indicating if external power is present and whether the battery is being charged.
On this board, the battery connector used is a JST-PH 2.0mm 2-pin right angle connector. It is used for many battery packs for Remote Controlled toys though I have found that there is also 1.5mm, 1.25mm and 2.5mm. It just happens that the 2.0mm is cheap and available.

UART ISR and custom printf() function with CC2540

I recently got started learning Texas Instruments CC2540 Bluetooth Low Energy chip to write a HID keyboard and mouse bridge to pair up with a MSP430 microcontroller and make my ThinkPad keyboard wireless.

Besides getting started with the CC2540 in general, I found using UART a major hurdle even though it turned out to be quite easy using the HAL driver supplied. When I wrote ‘easy’ I meant to pair it with ‘by using ISR (interrupts)’ instead of ‘DMA’. I have not had a stab at DMA yet and given that my 30-day license is expiring in a few days, I would love to finish writing the firmware as soon as possible.

So may this be a quick trot down of how I went with UART and tiny modified printf() function over UART based on oPossum’s tiny printf for the MSP430 platform.

I found it beneficial to read over Design Note DN112 as it gives a basic understanding of how UART works in the CC2530, which is quite similar to CC2540.

In general, to use UART, it can be as followed:

/*
Setting up UART
- To use UART, HAL_UART=TRUE, preferrably POWER_SAVING is not enabled (xPOWER_SAVING)
- To use interrupts, HAL_UART_ISR = (1 or 2), HAL_UART_DMA=FALSE
- To use DMA, HAL_UART_ISR = 0, HAL_UART_DMA = (1 or 2)

+ HAL_UART_ISR = 1: Use USART 0
+ HAL_UART_ISR = 2: Use USART 1

Check _hal_uart_isr.c for more information.

For keyfob, USART 0 alt. 1 is being used:
- HAL_UART_ISR = 1
For HM-10, USART 1 alt. 2 is being used:
- HAL_UART_ISR = 2

*/

For my project, I am using the CC2540 keyfob and am using USART 0 Alt. 1:

//UART test variable
uint8 *rxBuffer;
uint8 rxBufferIndex = 0;

static void setupUART(void) {
 HalUARTInit();

 halUARTCfg_t uartConfig;

 // configure UART
 uartConfig.configured = TRUE;
 uartConfig.baudRate = HAL_UART_BR_57600;
 uartConfig.flowControl = HAL_UART_FLOW_OFF;
 uartConfig.flowControlThreshold = 0;
 uartConfig.rx.maxBufSize = 128;
 uartConfig.tx.maxBufSize = 128;
 uartConfig.idleTimeout = 1; //1ms timeout
 uartConfig.intEnable = TRUE;
 uartConfig.callBackFunc = (halUARTCBack_t)uartCallback;

 //start UART
 //assumes no issues with starting UART
 (void)HalUARTOpen(HAL_UART_PORT_0, &uartConfig);

 rxBuffer = osal_mem_alloc(128); //assumes there is no problem with getting this block of memory
}

static void uartCallback(uint8 port, uint8 event) {
 uint16 len;
 uint8 buf[8];
 uint8 i;

 switch(event) {
 case HAL_UART_RX_FULL:
 case HAL_UART_RX_ABOUT_FULL:
 case HAL_UART_RX_TIMEOUT:
 len = Hal_UART_RxBufLen(HAL_UART_PORT_0);
 HalUARTRead(HAL_UART_PORT_0, buf, len);
 for(i = 0; i < len; i++) {
 //in this application, all lines sent to CC2540 ends with a carriage return (0x0D)
 if(buf[i] != 0x0D) rxBuffer[rxBufferIndex++] = buf[i];
 else {
 processBuffer(); //do stuff with rxBuffer using rxBufferIndex as the length of the rxBuffer
 break;
 }
 }

 break;
 }
}

setupUART() is placed in the application’s Init() function after all the other initializations. For example:

#if defined( CC2540_MINIDK )
   //some initializations specific to the keyfob
#endif

//UART init
setupUART();

//setup a delayed profile startup...

The callback function is called anytime there is an event related to UART is fired. Through printing text onto UART with HalUARTWrite(), I found that only HAL_UART_RX_TIMEOUT matters for receiving data. I’m not sure if this is the correct approach but so far it seems to work fine.

Next up, based on oPossum’s tiny printf(),  one can be implemeted for CC2540 to send data easily over UART: Printf.c:

#include "stdarg.h"
#include "hal_uart.h"
#include <string.h>
#include "hal_types.h"

static void sendByte(unsigned char byte) {
 HalUARTWrite(HAL_UART_PORT_0, &byte, 1); //change port to suit your needs
}

static void putc(unsigned char c) {
 sendByte(c);
}

static void puts(uint8 *str) {
 HalUARTWrite(HAL_UART_PORT_0, str, strlen((const char*)str)); //change port to suit your needs
}

static const unsigned long dv[] = {
 // 4294967296 // 32 bit unsigned max
 1000000000,// +0
 100000000, // +1
 10000000, // +2
 1000000, // +3
 100000, // +4
 // 65535 // 16 bit unsigned max
 10000, // +5
 1000, // +6
 100, // +7
 10, // +8
 1, // +9
};

static void xtoa(unsigned long x, const unsigned long *dp) {
 char c;
 unsigned long d;
 if (x) {
 while (x < *dp)
 ++dp;
 do {
 d = *dp++;
 c = '0';
 while (x >= d)
 ++c, x -= d;
 putc(c);
 } while (!(d & 1));
 } else
 putc('0');
}

static void puth(unsigned n) {
 static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
 '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 putc(hex[n & 15]);
}

void printf(char *format, ...)
{
 char c;
 int i;
 long n;

 va_list a;
 va_start(a, format);
 while(c = *format++) {
 if(c == '%') {
 switch(c = *format++) {
 case 's': // String
 puts(va_arg(a, char*));
 break;
 case 'c':// Char
 putc(va_arg(a, char));
 break;
 case 'i':// 16 bit Integer
 case 'u':// 16 bit Unsigned
 i = va_arg(a, int);
 if(c == 'i' && i < 0) i = -i, putc('-');
 xtoa((unsigned)i, dv + 5);
 break;
 case 'l':// 32 bit Long
 case 'n':// 32 bit uNsigned loNg
 n = va_arg(a, long);
 if(c == 'l' && n < 0) n = -n, putc('-');
 xtoa((unsigned long)n, dv);
 break;
 case 'x':// 16 bit heXadecimal
 i = va_arg(a, int);
 puth(i >> 12);
 puth(i >> 8);
 puth(i >> 4);
 puth(i);
 break;
 case 0: return;
 default: goto bad_fmt;
 }
 } else
 bad_fmt: putc(c);
 }
 va_end(a);
}

With printf() declared in <Application>.h (so there’s no need to make a Printf.h)

extern void printf(char *format, ...);

Here’s a screenshot of the CC2540 sending a number counting up at 5ms apart (through use of a timer based on SimplePeripheral project):

CC2540 printf()

Staying away from QFNs, my first Android app plus some Bluetooth

So, exams are finally over, the results are out and I scraped through. No supplements, none failed.

Over the past 2 weeks, I have learnt a valuable lesson: I cannot solder QFN packages. Out of 10 chips, I burnt 9. It’s horrible. It might be that I’m not using enough flux or maybe not enough solder but the solder will not melt below the many of the pins. One of my F550x break-out board arrived yesterday. Double sided, it fits perfectly onto a breadboard, but sadly, the QFN that is on the bottom is burnt and the board doesn’t work. I was playing with my phone’s camera software and applied a sketch art effect. Unfortunately, there was no undo.

F550x break-out

 

So I shall be staying away from QFNs in general from now on. The leaded packages may be much more expensive than non-leaded, but the convenience is definitely worth it, for me.

A few weeks ago, my dad asked me to write an application that lets him send his current GPS location to his colleagues using SMS and the like. Of course, there are plenty or maybe hundreds of apps that do this already. To name a few: Viber, Whatsapp, Google Maps, Tango… Location messaging is integrated into the apps. But what if you don’t have internet and you haven’t found this function in your chatting map, or maybe, you just don’t have time to search for it. Introducing GPS Share.

7inch_2

 

With its simplistic design, with a few seconds, you can send your current GPS coordinates via SMS. With internet connection, the application finds out the address of your location and it lets you share this information through e-mails. It also lets you copy the location information to the clipboard so you may paste it into any application you want. The time it takes from opening the app to sending a SMS packed with your current location is 4 seconds. I tried it out today after parking the car. It’s great. Link to Play Store.

And lastly, Bluetooth. This time, I’m taking a break from MSP430 and learn some C2000 and Bluetooth. I’ve got CC2540 mini dev-kit and I made a break-out board for the BC417. Unfortunately, the PIO is the wrong way around. but the code works well enough. The programmer is a clone I got from goodluckbuy. Free shipping. It works beautifully. Before receiving my programmer, I used TIVA C Launchpad and flashed it with CsrUsbSpiDeviceRE TIVA by Richard Aplin. It worked like a charm. But since I’ve got a programmer, I may just use it and reserve the TIVA for future learning.

BC417-BO board 1

 

So far, I’ve made a Blinky program and a Hello World program. Just the basics and following CSR’s very limited tutorials, but at least there is no time limit on the IDE, unlike the 30-day IAR Embedded Workbench for 8051 for TI CC2540.

ThinkPad USB Keyboard Project – It’s over

2014-06-07 06.33.07

This is the final PCB run of my ThinkPad USB Keyboard adapter project. It has a BSL button to upload firmware via USB using TI’s MSP430 Firmware update software, saving end-user lots of money from having to invest on a programmer.

Originally, I planned to write a custom USB flashing software on the PC that allows users to create their own matrices and keyboard combinations. However, due to lack of time, it is dropped.

Firmware wise, the keyboard scanning is pretty fast. All the keycodes are supported. Num Lock works, remote wake-up supported. It works just like a normal keyboard, except this one you can customize the matrix and add key combinations, at least I can.

Of course, the aesthetic of the PCB can be improved. Via holes can be smaller and not covering text, something like this:

Altium_Final_v0.6.2

But for functionality, all is well. Anyway, that’s it. No more ThinkPad keyboard adapters for me.  Here’s quick video of the board in action:

I have made the left-over boards avaiable for purchase from Tindie. Two flavours are available. Have your pick:

https://www.tindie.com/products/rampadc/thinkpad-usb-keyboard-adapter-v062-pcbreceptacle/

https://www.tindie.com/products/rampadc/thinkpad-usb-keyboard-adapter-v061-rev-11-pcbreceptacle/

Soldered up and… a pin isn’t soldered

So last week I got my v0.6.1 PCBs from DirtyPCBs and I decided to solder one of the boards and see how it improves upon version 0.6.0. This is my second time working with 0603 and QFNs. The first time didn’t work so well that this time I had to get a stencil and solder  paste. It turned out to be a pretty good experience. A quick swipe of paste onto the board, placed a few components, just hot air the boards and it was done in less than an hour! Shiny and everything.

Got it hooked to the computer, BSLed fine, loaded the firmware, keyboard detected, every keys works, except for F1, 2, W, CapsLock, S and X :(. Well that sucked. Turns out a pin wasn’t soldered under the QFN chip, This has become a bit annoying. Maybe the change to QFN to save $2.50 on cost isn’t exactly worth it.

2014-05-24 22.24.47

PCBs from DirtyPCB.com

Back in April, or March, Dangerous Prototypes (DP) opened a PCB service, that is just like Elecrow – free colour, except DP has nothing to do with the manufacturing, they just send your Gerber to some Chinese board house, where your boards would get manufactured, if your design meets their capabilities.

For one of my ThinkPad keyboard PCB revisions, I decided to try these guys out. At 14USD, they offer free colour and free shipping. However, I’m sure shipping is already included in that 14 bucks. Elecrow shipping was $6.5 on top of their $9.90 per 10 boards price, works out to be around $16. So DirtyPCB wins, in terms of pricing.

Both services take about a month and a few more days to account for non-business weekend days. So in term of shipping, they’re both the same. Thus, DirtyPCB still wins because it’s cheaper.

Now, this revision of mine is consisted of 8mil (thou) traces, 0.3mm via drills, 0.5mm fine pitch SMD components, 0603 passive components and a QFN with tiny plated through holes on its pad for better solder transfer.

Let’s have a look at one of the PCBs that arrived today (Look at that superb layout!):

IMG_20140519_134703

Except for that serial number at top left, everything else looks great! Connections wise, no shorts that I can find so far. All that’s left is to start soldering with a lovely stencil I got from OSHStencils, load the firmware and see how it goes!

ThinkPad keyboard project – A quick update

With the firmware stable and fast, this keyboard project of mine is slowly coming to a close. It supports all the basics functionality of the keyboard: TrackPoint, FN combinations, Num Lock, remote wake-up and supports all the keyboard’s key codes that I can test on a Windows machine.

This USB device currently have 3 interfaces: HID Mouse (TrackPoint), HID keyboard and HID Datapipe, which is a generic TI MSP430′s HID format that allows ‘simple’ data transfer, similar to CDC Datapipe.

For this draft PCB run, I’m resorting back to 0805 resistors and capacitors as they are much easier to solder than 0603. I’m only worried about the BSL button pad spacing, it was hard to find the datasheet to those eBay SMD tactile buttons since the sellers never include datasheets.

Altium_Draft_Final

 

There are 3 things left to do:

  • Designing an enclosure, such that it clamps onto any laptop bodies – essentially overlaying existing keyboards (This is where the ThinkClamp name came from)
  • Write a script or find a way to disable laptop’s internal keyboard
  • Write a PC software that allows end-user to easily find their keyboard’s matrix and flash the microcontroller on-board with that matrix using the HID datapipe, as well as using MSP430 Bootstrap Loader (BSL) for flashing the device.

For the enclosure, I’m thinking of using wood/aluminium, maybe coat the final surface with some rubber paint for that awesome ThinkPad laptop surface feel. For the enclosure’s clamp, something like extension springs that run under the keyboard would work nicely.

I have yet to find a way to disable my laptop’s internal keyboard as well as unable to write any PC software as I lack the skills to do so. Even though I have the skills to program in C, C++, Windows API is still very difficult to work with. Even Jan Axelson recommends using Visual C# .NET for writing HID software. So that is something I need to do. If anyone wants to help me in this aspect, feel free to leave a comment.