Thursday, August 29, 2013

wki.pe - Official Announcement







I finally have reached a point on Wki.pe that I feel comfortable announcing it to the world.


For those of you who don't know, for over a year (I think it has been closer to two), I've been tossing around the idea of creating a URL shortener for Wikipedia. The idea is that there are several social media platforms (Twitter, Facebook, Google+, SMS, etc.) where you cannot create hyperlinks with custom text. Instead, the platform recognizes URLs and formats them as links for you. Often (e.g. Twitter), you are limited by the number of characters you can have, so URL shorteners are popular. However, often these services give you a nonsense URL (e.g.bit.ly/1865N9v). If you want to link to Wikipedia, and you want it to be clear that that is where it is going, you can use Wki.pe. That way, you can use the URL inside your sentence, for example:
TIL that wki.pe/Aldous_Huxley 's brother a.wki.pe/julian was a pretty cool guy.
The link to the Wikipedia article on Aldous Huxley is simply the name of the article on Wikipedia. I didn't have to generate this on the site (although I still could), I just knew the name of the article. Also notice the second link has an "a." at the beginning. This is a link I generated on the site. I told Wki.pe I wanted a URL that said "julian" to point to the article on Julian Huxley. For the time being, I'm calling this aliasing (but I'm looking for a more clear name).

The service allows for multiple articles for the same alias (that's where the "a." part comes from). If you try to go to an article that doesn't exist, it will take you to the search results page. It also handles languages relatively well. By default, it will detect your language and send you to the right version of Wikipedia. You can also force a particular language by generating it on the site. For now only a handful of languages are supported, but soon I will add all language versions of Wikipedia (and there are hundreds).

The base functionality has been working for a while (this was my first foray into databases, well before I knew SQL). Over the last few weeks, I've really been working on the UI and learning some fancy Javascript tricks in the process. The site uses jQuery (and jQuery-UI), and uses aAjax calls to PHP pages. You can see the source code on GitHub if you are curious. There is a bug with IE<=9 I still need to fix and two more features I plan to add (and I'm sure there are some Unicode bugs to still be worked out), but I think it is ready to be used.

Please, use it! Give me the satisfaction that I made something useful. :)

Tuesday, August 13, 2013

Discrete potentiometer

My car music player is finally coming along to the point where it looks like I might actually finish it. I will do a post about the software soon, and probably a more general post on the hardware later, but right now I want to talk about a knob I will be using on the device.

Your car stereo probably has a volume knob that spins all the way around. You can keep twisting it in either direction as much as you want, and the software will determine when you've topped- or bottomed-out. You probably didn't think too much about why this was the case, but there are some nice features that can be implemented thanks to it.

For one, you can have multiple volume controls. Often there are volume buttons on your steering wheel. A knob with set start and end positions means it is tied to a particular state, but changing the software state somewhere else doesn't change the physical state of the knob. Some car stereos (I know mine does, and I bet most do now) will set the volume to 0 when the car starts, and ramp it up gradually. That way, if a friend borrowed your car and cranked the volume up really high, you won't get a nasty surprise when you get in.

While most knobs are potentiometers with a continuous resistance from 0 to X, knobs like this have discrete states (they 'click' in small increments). To make one of these, I bought a cheap rotary switch. This is a SP12T (single pole, 12-throw) switch, so one input can be connected to any one of 12 outputs. Some rotary switches will let you twist all the way around forever in either direction, but this one did not, so I had to pry the case open and carefully cut out a little piece of plastic that was blocking the gap between positions 1 and 12. This particular switch had a small ball bearing and a spring, both of which I almost lost and I struggled for a few minutes to put everything back together (fair warning).

Now that the switch can spin freely, we need it to be able to turn a setting up or down. To know if the knob is spinning clockwise or counterclockwise (or not at all), we can simply compare the current state of the switch to the previous one. There are a few ways to read the switch state. Connecting every output to its own GPIO pin on your microprocessor would take up 12 pins which is a very inefficient use of your pins. One option would be to connect each pin to the first 12 inputs of a 16-to-4 encoder, which would encode your signal into a 4 bit number, taking up 4 GPIO pins. However, there is still a better way to do this.


By soldering resistors of equal magnitude between each of the positions, we've created what is essentially a discrete potentiometer. In this case, I used 100 ohm resistors, so this is a 11*100 = 1.1K ohm potentiometer (the first state is 0 for 0 ohms, so the last state is 11 for 1100 ohms), but one which can only take values 0, 100, 200, 300... Now we can connect it to an analog pin, and save our digital pins for other things.

There is still one issue here, however. The analogRead() function is going to give integer values from 0 to 1023. That means our values are going to be 0, 85, 171, 256, 341, 427, 512, 597, 683, 768, 853, 939. But these aren't exact. These resistors have a margin of error around 2% (for most resistors, it is around 5%, but I bought more accurate ones for this situation). The supply voltage can fluctuate as well. That means we need to allow for ranges, so maybe 0-42 means position 0, 85-128 means position 1, and so on.

A easy way to do this would be:
   round(12*analogRead(0)/1024);
However, this is a very inefficient command for a microprocessor. First it multiplies two integers, then it divides two integers, creating a float (microprocessors like the Ardunio are very slow at doing floating point math, particularly division). Then it converts that back to an integer. This one line translates to 4 logical steps, which translate into a whole lot of steps for the processor.

The way I implemented this was not nearly as slick, but it runs about 10 times faster. If we look at our predicted analogRead() values in binary:
0000000000
0001010101
0010101011
0100000000
0101010101
0110101011
1000000000
1001010101
1010101011
1100000000
1101010101
1110101011
Notice the first four bits (highlighted in red) are unique for each value. That means that we can get a good idea which state we are in just looking at the 4 most significant bits. If you think about it in decimal: let's say you were going to get numbers between 0 and 1000, and you wanted to quickly tell if that number fell in the first 250 number bin (0 to 249), the second (250 to 499), and so on. You don't really care what the number in the one's place is in this situation. Just by looking at the hundred's and ten's places, you can tell which bin it's in (26X is in the 2nd bin, and 8XX is in the 4th). So back to the binary above, if we trash all the lower-significance bits (which might vary slightly), we can still get the right answer.

There is one little bit of weirdness here. Four bits means 16 possible values, 0000 to 1111. But we only have 12 positions we care about. Look at the binary version of the predicted value of position 3 (0100000000 or 256). If analogRead() returned something a little high, like 275, we would be okay, since the first four bits are still 0100. But what if it returned something a little low, like 245 (binary 0011110101)? This has a smaller value for its four most significant bits. However, notice also that we skipped from 0010 for position 2 to 0100 for position 3 (right over 0011). This is because 1024 is not divisible by 12. In hindsight, getting a 8- or 16- position rotary switch would have been a better plan. If we give both 0011 and 0100 to position 3, we've solved this problem. We have to do this as well for positions 7 and 9 (thanks to the fact that gcd(12,1024)=4). The shortest margin of error that would return a wrong value is then 21 (for example, if the value for position 2 was high by 21-- 192, it would be read as position 3). That is a hair over a 2% margin of error. 

Below is the code I used for this. It is not nearly as concise or elegant as the single line from before, but it is faster by an order of magnitude, as bitwise shifts are computationally cheap. If you have a better way to do this, I would love to hear your suggestion.

   int c = analogRead(0)>>6;
   switch(c){
     case 0:
       return 0;
       break;
     case 1:
       return 1;
       break;
     case 2:
     case 3:
       return 2;
       break;
     case 4:
       return 3;
       break;
     case 5:
     case 6:
       return 4;
       break;
     case 7:
       return 5;
       break;
     case 8:
     case 9:
       return 6;
       break;
     case 10:
       return 7;
       break;
     case 11:
     case 12:
       return 8;
       break;
     case 13:
       return 9;
       break;
     case 14:
       return 10;
       break;
     case 15:
       return 11;
       break;
   }