Tuesday, November 26, 2013

Sensor design using MATLAB



Love it or hate it, the Arduino was one of the greatest things to happen in the field of electronics in the past 20 years. It made microcontrollers accessible to everyone, which in turn set off a demand for all sorts of sensors to allow these microcontrollers to take input from the world around them. Nowadays there’s a sensor for almost everything, and you can get them on a module all the required circuitry included - all you have to do is connect the power and data lines! However, just because the connection is easy doesn’t mean the implementation is as well. To design your algorithm, you will need to perform data analysis and simulations, and MATLAB is a tool that can make that much easier for you. In this guide I’ll show some ways to use MATLAB for this through an example with a magnetic vehicle detection sensor.


part 1: sending your data
99% of the time, if you’re talking to a microcontroller it’s going to be over UART. It may be in the form of an FTDI board, USB CDC mode, XBee, RS232, or Bluetooth, but it’s always good old UART. Since it’s primarily used for ASCII data (text, readable stuff), most people just go ahead and send their data over ASCII. It’s easy, but it comes with 2 big drawbacks. Lets say for my sensor, I want to transmit a value for each axis (XYZ) and the number of microseconds since the previous transmission routine. Each one of the axis values is a number between -2048 and 2048, and the microseconds value should ideally be 10000. At the worst case scenario, each line of the data would be:
-2048,-2048,-2048,10000\r\n
\r and \n are the carriage return and newline characters. the \ isn’t a separate character, it only specifies that the r and n are not regular printable characters.


That’s 25 characters, which means that you need to transmit 25 bytes of data for each sample. So what if instead of sending a textual representation of the data we send it in raw binary form? Each one of the axis values would be 2 bytes, the timestamp 3, and the \r\n characters 2. That’s only 11 bytes per sample - less than half of the original amount! Plus, this saves the overhead of converting the data to and back from text. There’s also yet another reason why you want to do this when working with Simulink - it doesn’t support strings. You either do it like this or you can’t do it at all.
Speaking of strange restrictions from Simulink, you’re going to have to frame your data rather oddly. A 16 bit integer is 2 bytes and UART sends one byte at a time, so you’d normally send the most significant byte followed by the least significant one, right? Not with Simulink! Lets pack up just the axis values into a frame (along with the newline characters) as an example.
X = -678 0xFD5A 0xFD 0x5A
Y = 1220 0x04C4 0x04 0xC4
Z = -922 0xFC66 0xFC 0x66

Framing that along with the newline characters results in the following 8 byte array
0xFD 0x5A 0x04 0xC4 0xFC 0x66 0x0D 0x0A

That won’t work with Simulink. Instead, you need to frame it like so:
0x5A 0xFD 0xC4 0x04 0x66 0xFC 0x0D 0x0A

If this is confusing, then here’s my sample Arduino code to transmit a single sample over UART.

byte outBuff[] = { 0, 0, 0, 0, 0, 0, '\r', '\n' }; void rawPrintMode() { outBuff[0] = (((sensor.rawVals)[0] & 0x00ff)); outBuff[1] = (((sensor.rawVals)[0] & 0xff00) >> 8); outBuff[2] = (((sensor.rawVals)[1] & 0x00ff)); outBuff[3] = (((sensor.rawVals)[1] & 0xff00) >> 8); outBuff[4] = (((sensor.rawVals)[2] & 0x00ff)); outBuff[5] = (((sensor.rawVals)[2] & 0xff00) >> 8); Serial.write(outBuff, 8); }

But enough about how you should frame your data, let’s go on to Simulink.


part 2: receiving the data
Okay, so we’re transmitting data from our sensor, now we need to receive it.
The model above is our “Hello World” proof of concept. It merely reads in data and displays it through a display and a scope. The configuration block is self explanatory, but the receive block is a little confusing.
Terminator: As you can see in my example Arduino code, all of my frames end with \r\n, so that’s what I selected for the terminator.
Data Type: Is it an int? A byte? A long? Is it signed or unsigned? If you’re sending your data in raw format, then you should already know what type it is.
Data Length: This isn’t the number of bytes in the payload part of the frame, it’s the number of values of the given data type. For example, even though I’m sending 6 bytes, I set the size to 3 because each number consists of 2 bytes.
Enable Blocking Mode: Check this box if you don't like real-time performance
Sample Time: MATLAB doesn't count time in seconds, so I don't have anything solid for what you should put here. Go with a very small number, one that's much smaller than your sensor's sampling rate. Also, make sure your simulation is running for inf time with a fixed step size of auto.

As you can see, our signal is getting routed into 4 places. First, there's a scope and a display so you can observe the data in real time. It also gets sent to the current workspace and gets logged to a file so you can then analyze and test against it. Now that you've captured your data, it's time to disconnect the sensor and start the analysis.

part 3: building the algorithm
We have 3 analog inputs and need to generate a single boolean output. The following scripts and graph demonstrate this process step by step.


vals = [x y z]; t = millis; % Find a good section and zoom in. subplot(321) plot(vals) title('1') % 8375 to 9075 seems good. t = t(8375:9075); vals = [x(8375:9075) y(8375:9075) z(8375:9075)]; subplot(322) plot(vals) title('2') % Now we need to remove each channel's offset. % We do this by taking the average of a silent segment. avgs = [mean(x(950:1150)) mean(y(950:1150)) mean(z(950:1150))] for i = 1:3; vals(:,i) = vals(:,i) - avgs(i); end subplot 323 plot(vals) title('3') % To normalize the three axes, we calculate the magnitude. mag = [] for i = 1:length(vals); mag(i,:) = sqrt(sum(vals(i,:).^2)); end subplot 324 plot(mag) title('4') subplot 325 % Finally, we run it through our parser function detected = [] lastState = 0 for i = 1:length(mag); lastState = detectObject(mag(i), lastState); detected(i) = lastState; end plot([mag,detected' .* 120]) title('5') subplot 326 % And now just the triggers detected = [] lastState = 0 for i = 1:length(mag); lastState = detectObject(mag(i), lastState); detected(i) = lastState; end plot(detected' .* 120) ylim([0,280]) title('6')

Here's the main detection function, which is a simple threshold trigger with hysteresis.

function [ output] = detectObject (magnitude, isOn) OFFpoint = 80 ONpoint = 120 if isOn ~= 0 & magnitude < OFFpoint; output = false elseif isOn == 0 & magnitude > ONpoint output = true else output = isOn end end


part 4: implementing the algorithm
From here on out, it's just a matter of porting your MATLAB functions to your platform. For this
example, the detectObject function would be translated into C and be called every time a new data 
sample is received.

Thursday, November 1, 2012

Window Tagging with Openbox

There's few things I like in this world more than CrunchBang Linux. I'd used Ubuntu, Xubuntu, Lubuntu before, but all of them felt like bloated alternatives to bloated Windows and there was little reason to make the switch. Then, I discovered CrunchBang and within a month had deleted my Windows partition.

My favorite part about CrunchBang is Openbox - a damn fine window manager with a lot of room for customization. One (of the very, very few) drawbacks about Openbox is the lack of window tagging - and I'm not about to switch to a whole new WM like i3 or awesome just for this feature. Thankfully though, it's not impossible to implement it!

Here's how my solution works - press Alt-Super-(1-9) to tag a window to that number, and then press Super-(1-9) to switch to that window. This is all done using xdotool and some text files to store window id's, but since Openbox can't do anything too fancy in its execute command we have to do the work in an external shell script. Anyways, here's the goods.

In your rc.xml file (add as many copies as you want, just make sure to change the numbers):
 <keybind key="A-W-1">  
  <action name="Execute">  
      <command>sh ~/.config/winTag/saveTag 1</command>  
  </action>  
 </keybind>  
 <keybind key="W-1">  
  <action name="Execute">  
      <command>sh ~/.config/winTag/recallTag 1</command>  
  </action>  
 </keybind>  

Then, place the following 2 files in ~/.config/winTag/

saveTag
 #!/bin/sh  
 xdotool getwindowfocus > ~/.config/wintag/save$1  



recallTag
 #!/bin/sh  
 xdotool windowactivate $(cat ~/.config/wintag/save$1)  



Enjoy your new found freedom from Alt-Tab hell!

Monday, October 17, 2011

Using a TomTom Bluetooth Remote with Android

EDIT: The TomTom Bluetooth Remote only works with Android 2.3 and below. The issue is that on Android 2.3 and below, the user first enters the pairing key for keyboard devices on the phone and then on the keyboard. From Honeycomb and above, Android generates its own random pairing key and since the TomTom remote doesn't have any real keys, it's impossible to type that code in. If anyone finds a workaround, please let me know so I can update this.
EDIT: A workaround has been found! Read Becherraecher's comment below for more details.


I was digging through my box of assorted crap and stumbled upon an old TomTom Bluetooth Remote that I got from my old job (because after all, why does a store display TomTom need a remote?). When I first got it I couldn't find a use for it, but 2 years later with a good deal of Android experience under my belt, I decided to see what I could do with it.


Now the first hurdle was pairing the device with my phone. Although it's using Bluetooth, the pairing isn't done the standard way. However, I found the following method to work most of the time. Note: Your device must support the Bluetooth HID profile for this to work.

1. Remove one of the batteries from the remote for 30 seconds. This clears any previous pairing data and gives you a fresh slate to start from.
2. Tap "Scan for devices" and quickly insert the batteries. It probably won't be found on the first try, so keep on hitting "Scan for devices" until TomTom Remote shows up. At this point, if you try to pair, it will probably fail.
3. When the blue light on the remote stops blinking, select the remote on your Android device and pair with the passcode "0000".
4. Now your Android device should show that it is trying to pair with the remote. Press the middle button on the remote and your Android device should prompt your for the passcode again. Enter "0000" again.
5. The Android device should now show that the remote is paired but not connected. Long press the remote, check the box for Connect, and go back. Now press the center button on the remote again and it should be connected to your phone!

We just got over the first major hurdle! Select a text input box and press some buttons on the remote and you'll see text being entered! However, a keyboard with a few random buttons isn't really useful, so let's remap the keys!

To do this, we'll have to create a custom keyboard profile for the remote which will map the scancodes sent by the device to keys recognized by Android. I'm going to save you the effort of finding out the scancodes since I already found them myself.

Direcrional Keys
Up 103
Down 108
Left 105
Right 106
Center 28

3 Middle Keys
A 1
B 13
C 14

Vol+ 67
Vol- 68

Next, you'll need to pull 2 files off of your phone to modify. Fire up command prompt, navigate to the platform-tools directory of the Android SDK, and enter the following commands:


adb pull /system/usr/keychars/qwerty.kcm.bin TomTom_Remote.kcm.bin

adb pull /system/usr/keylayout/qwerty.kl TomTom_Remote.kl


Open up TomTom_Remote.kl in your favorite text editor, see how the syntax works, erase everything, and create an entry for each of the 10 buttons. For example, mine looks like this:


key 106 MEDIA_NEXT
key 105 MEDIA_PREVIOUS
key 28 MEDIA_PLAY_PAUSE
key 108 1
key 1 3
key 13 4
key 14 5
key 68 VOLUME_UP
key 67 VOLUME_DOWN
key 103 POWER WAKE


You may chose to map the remote's D-Pad to Android's directional keys if you like, but since I'm using it as a music remote in my car, I used those keys. You can find a list of all the possible keys here. (If you're wondering why I mapped some key's to 1,3,4, and 5, it's because I also programmed my personal media player to preform special functions with those keys.)

Save your changes to the .kl file and enter the following commands into your command prompt:


adb push TomTom_Remote.kl /system/usr/keylayout/TomTom_Remote.kl

adb push TomTom_Remote.kcm.bin /system/usr/keychars/TomTom_Remote.kcm.kl

adb shell "chmod 644 /system/usr/keychars/TomTom_Remote.kcm.kl"

adb shell "chmod 644 /system/usr/keylayout/TomTom_Remote.kl"


Restart your phone, connect to the remote, and it should now preform the new functions!

Many thanks to this blog post for teaching me how to remap buttons.


Monday, June 14, 2010

GorgiLabs

This is the site/blog for the Android developer Norvan Gorgi.