Wednesday, July 16, 2014

How to Extract RC radio receiver pulse width data (all channels) with only ONE digital microcontroller pin (Part 1)

So you're running out of gpio ports on your microcontroller and you have an 8 channel receiver you'd like to use for your super cool Quadcopter project. What do you do?


There are several ways to go about this. The radio signal comes in through the antenna, passes through a series of low pass filters and low noise amplifiers, eventually reaching a series of flip flops that basically separate each of the pulses to their respective channels (with the help of a reference xtal oscillator).


PPM signal decomposition by internal Flip Flops

It's possible, and very easy to extract the signal right before it reaches these flip flops (PPM signal) by soldering a wire to the pin that contains the PPM signal, and simply parse the data yourself through code. The other way, which I will explain in more detail doesn't require soldering or opening your precious receiver and it's silly simple by using OR gates. Yes, remember OR gates from your logic design class in college? I sure do ^_^


Just connect the channel outputs of your RC receiver to a simple OR gate circuit (shown below) in order to recreate the PPM signal. Furthermore, it is very easy to cascade multiple OR gate ICs together if your application requires more than the 5 channels shown below.

Circuit diagram with single Quad OR gate IC

The PPM signal line (above) should be connected to any digital pin of your microcontroller, which you can then parse via Pin Change Interrupts in your software. I will explain the software part on a different post since it's rather long.

Tuesday, July 8, 2014

Arduino (atmega328) Interrupt-Based Maxbotix EZ Sonar Sensor

So your girlfriend (or boyfriend) gave you an EZ0 Maxbotix Sonar Module for your birthday. It's laying around collecting dust mites and you just don't know what to do with it. Well, you've come to the right place! But seriously, here is a picture of the damn thing.


In most blogs I've come across, they only explain how to interface to the module through the Analog pin, and that's fine. However, analog reads in the Arduino take a lot of CPU cycles, and it could potentially slow down the rest of your code. There is another pin in the module, however, that allows us to get useful readings from the sensor in a faster way: the PW (Pulse width) pin.


Theory

The PW pin essentially outputs a Pulse-width modulated signal whose pulse width is proportional to the distance to the object/s being detected. By using Pin Change Interrupts, we can measure the deltas in the pulse widths, and then convert these to distances without wasting time sampling analog voltages. The following diagram is a high level representation of the interaction between the sonar sensor and our program's logic.



Circuit Diagram

The following image shows a simple circuit setup with an Arduino UNO. 


I am connecting the PW pin from the ultrasonic module to digital pin 4 (PD4) on the Arduino, but you can use any other pin as long as you setup the appropriate PCINT register values. I highly recommend going over the atmega328 datasheet for more details. It may seem confusing at first, but keep at it, and it will start making sense over time. If it doesn't, drop a comment below and I will do my best to help clarify ^_^ 

These are the registers that we need to modify in order to configure Pin Change Interrupts on PD4. You would put these two lines of code somewhere in your initialization routine, before you start attempting to take any measurements from the sensor module. 

PCICR |= (1 << PCIE2) /* Allow PCINT interrupts on port D */
PCMSK2 |= (1 << PCINT20) /* Allow PCINT on pin PD4 */

I put these definitions somewhere in the code in order to organize things a bit. 

#define MICROSECONDS_PER_CM 373.38f
#define CM_PER_MICROSECONDS (1/MICROSECONDS_PER_CM)

#define SONAR_PIN 4 /* Digital pin 4 (PD4) */
#define SONAR_PIN_MASK (1 << SONAR_PIN)

typedef struct _sonar_state
{
  volatile uint32_t t_start;            /* Start of pulse */
  volatile uint32_t dt;                 /* Puse duration in microseconds */
  volatile boolean pulseStarted;        /* Flag used to ensure correct initial conditions */
  volatile boolean sonarPin_lastState;  /* Keep track of the last logical state of PD4 (1 or 0) */
} SONAR_STATE;

SONAR_STATE sonar_state;

I'm sure you've noticed the volatile modifiers above. If you don't know what it does, don't worry. Essentially, you would declare a variable as volatile, when its value may change unexpectedly, like in an interrupt, for instance. When an interrupt handler is called, the state of all variables used within the interrupt handler are saved to the stack, and when the program returns from the interrupt routine, these variables are then restored to their previous values, ignoring the modifications that may have occurred during the interrupt. You can see how this could be a problem. 

The following code is the essence of the interrupt mechanism. Upon any signal change on PD4, the ISR routine (below) will automagically execute, allowing us to record the signal transition times. Thus, being able to calculate the distance to nearby detected objects. In theory anyways ^_^

ISR( PCINT2_vect )
{
  /* If PD4 is high, then save pulse start time (micros() returns the time in microseconds since program started.     Overflow condition is ignored for the sake of simplicity). Otherwise, save pulse end */
  if( PIND & SONAR_PIN_MASK > 0 )
  {
    sonar_state.t_start = micros();
    sonar_state.pulseStarted = 1;
  }
  else if( sonar_state.pulseStarted )
  {
    sonar_state.dt = micros() - sonar_state.t_start;

    /* Calculate distance in centimeter */
    sonar_state.distance = CM_PER_MICROSECONDS * sonar_state.dt;

    /* Clear flag */
    sonar_state.pulseStarted = 0;
  }
}

Note: if we have several pins within port D that allow Pin Change Interrupts, the above ISR will execute on ANY pin change that has been configured in the PCMSK2 register (not only PD4). This would cause erroneous time deltas to be recorded. A way to fix this is to keep track of the state of PD4 and add a check within the interrupt routine to verify that PD4 indeed changed logical states (log-to-high or high-to-low) before recording signal-change times. Just keep in mind that the code above assumes there's no other port D pin with Pin Change Interrupts enabled, other than digital pin 4 (PD4) for the sake of simplicity.

Thursday, July 3, 2014

Arduino (atmega328) Interrupt-based BMP085 (Barometric Pressure Sensor)

The BMP085 is a neat little sensor to measure atmospheric pressure, and ultimately altitude (which is the reason why I am using the damn thing in the first place).


The problem I found with the BMP085 library was that every time you want to read the temperature or pressure, you have to add a delay in your code and wait for the sensor to sample and return a valid value (the time delay will depend on the resolution you are using). The code below is part of the BMP085 library to read raw pressure from the barometer. Notice how after we request a pressure measurement, we wait for some time before the data can be read from the pressure register.

int32_t BMP085::readRawPressure(void) 
{
  uint32_t raw;

  /* Request pressure measurement from the BMP085 */
  I2C::write8( DEV_ADDR, CONTROL_REG, READPRESSURE_CMD + (oversampling << 6) );
  
  /* Wait until BMP085 finishes sampling pressure for you */
  if ( oversampling == ULTRALOWPOWER ) 
    delay(5);
  else if ( oversampling == STANDARD ) 
    delay(8);
  else if ( oversampling == HIGHRES ) 
    delay(14);
  else 
    delay(26);

  /* Read registers 0xF6, 0xF7, and 0xF8
   * UP = data(0xF6) << 16 | data(0xF7) << 8 | data(0xF8)
   */
  raw = I2C::read16( DEV_ADDR, PRESSURE_REG );
  raw <<= 8; 
  raw |= I2C::read8( DEV_ADDR, PRESSURE_REG + 2 ); 
  raw >>= (8 - oversampling);

  return raw;
}

The added delay would kill the rest of your code for that time. So, I'm sitting here thinking... interrupts anyone?

On the image above (right), the EOC pin (End of Conversion) essentially tells you when a conversion is done and the requested value is ready for you. This means that we can just request a value, continue on with our lives (but keep an eye on the EOC pin), and when that value is ready for you, then go ahead and read (no unnecessary delays). I made some modifications to the original library in order to include an interrupt-polling mechanism without the headache of timers, just good ol' pin change interrupts (PCINT).

The image below shows my configuration for the circuit diagram.



I am using Digital pin 4 (PD4) on my arduino to monitor the EOC state via Pin Change Interrupts. When we submit a measurement request, EOC goes LOW, and when the measurement is ready for us, EOC goes HIGH. We are interested in capturing RISING signal changes on PD4, then simply read from the BMP085 whatever value we had requested. The image below shows a high level representation of the interrupt-polling mechanism.




To allow pin change interrupts on PD4, we have to configure some registers. By looking at the atmega328 datasheet, setting PCIE2 bit in the PCICR register, effectively allows PortD to have pin change interrupts. However, we need to mask which pin within PortD we want to enable PCINT in. Digital Pin 4 is mapped to PCINT20. By setting this bit in the PCMSK2 register, we fully enable pin change interrupts on pin 4 (see below).

PCICR |= (1 << PCIE2 ); /* Allow PortD to have Pin Change Interrupts */
PCMSK2 |= (1 << PCINT20 ); /* Enable Pin Change Interrupts on PD4 */

The following routine is the interrupt handler, which is where we advance to the next stage in the polling process.

ISR( PCINT2_vect )
{
  ... In here we process the EOC event everytime there is a LOW-to-HIGH transition.
}

The following code snippet shows the pressure request we send to the BMP085. We keep track of the request through a status structure. This way, when we receive an EOC event, we can read the appropriate register from the barometer. We also set a busy flag to 1, since we don't want to send another request while we are waiting for a reply. This is valid for all requests.

void BMP085::readRawTempReq( void )
{
  if( !status.busy )
  {
    status.dataState = GET_RAW_TEMP_STATE;
    status.busy = 1;
    I2C::write8( DEV_ADDR, CONTROL_REG, READTEMP_CMD );
  }
}

The following code snippet shows the temperature request we send to the BMP085.

void BMP085::readRawPressureReq( void )
{
  if( !status.busy )
  {
    status.dataState = GET_RAW_PRESSURE_STATE;
    status.busy = 1;
    I2C::write8( DEV_ADDR, CONTROL_REG, READPRESSURE_CMD + (oversampling << 6) );
 }
}

This is the data structure used to keep track of the state of the barometer.
typedef struct _bmp_status
{
  uint8_t dataState;

  boolean busy;
  boolean eocReceived;
  
  int32_t rawPressure;
  int32_t compensatedPressure;
  uint16_t rawTemperature;
} BMP085_STATUS;

When we get the EOC event, we have to process it accordingly. The code below shows the eoc handler.

/* After read request was submitted, End Of Conversion event will trigger */
void BMP085::eocEvent( void )
{
  int32_t val;
  int32_t p;

  switch( status.dataState )
  {
    case GET_RAW_TEMP_STATE:
      status.rawTemperature = I2C::read16( DEV_ADDR, TEMPERATURE_REG );
      break;
    case GET_RAW_PRESSURE_STATE:
      val = I2C::read16( DEV_ADDR, PRESSURE_REG );
      val <<= 8;
      val |= I2C::read8( DEV_ADDR, PRESSURE_REG + 2 );
      val >>= (8 - oversampling);
      status.rawPressure = val;

      /* Calculate compensated pressure */
      status.compensatedPressure = calculateRealPressure( status.rawTemperature, status.rawPressure );
      break;
  }

  /* Have to clear eoc flag */
  status.eocReceived = 0;
  status.busy = 0;
}

Click HERE for the code on Github

Monday, September 2, 2013

Wise teachings from a Chinese man

Something happened to me today that made me have one of those “strange” but yet insightful realizations, and it goes like this.

While at work, it just so happened that my stomach started feeling a bit “uncomfortable”. I thought… poop.  Anyhow, I go to the bathroom and soon realize that there is no toilet paper. I soon devise to begin the quest for the elusive paper of the toilet, elsewhere. To be brief, I finally found something that seemed like it had gone through severe usage. There was barely about 3 “butt-wipe” attempts left to it. I thought, this will do; but I had to be very efficient. So I return to the men’s bathroom triumphant, while holding the toilet paper with my right hand (the left hand was free). After carefully wiping off pee residues from the seat with enough toilet paper for a single but wipe (now I only had enough paper for 2 remaining butt wipes), and carefully laying down that peaceful white mantel  (that comes in package of 250 at a time, and that is only found in “fancier” bathrooms, like those of a hotel, or an office building) that prevents your butt from touching the cold, harsh plastic seat. I finally sat down and took a moment to appreciate the solitude, the silence and tranquility of what it meant to do number 2 in an empty bathroom, without people smelling what comes out of you, and without you smelling what comes out of other people. I was truly happy. About 30 seconds had passed until someone had a similar “stomach problem” I guess, and decided to take action.

A man had walked into the now “not peaceful” bathroom which I had happily claimed previously. Let me first describe the bathroom’s layout before continuing. It is a very brief bathroom. There is a urinal to the left of the sinks. To the left of the urinal, that is, to the back of the bathroom, there are two of those private toilets; for the more shy ones. One of these pooping cubicles is what you would call “middle class” private, while the other is “upper class”. The latter, being much bigger than the formed and with an unnecessarily more informative view of its vicinity.

Ok, let’s continue. As the unaware intruder carefully laid out his seat in order to perform his business, I noticed that I recognized his shoes; since they’re very unique. It was the Chinese man next to my work cubicle. Asian people, especially Chinese always have the most unique shoes. Anyhow, I guess I am kind of self-conscious about doing number 2 when I can clearly see other people, but for some reason, I could not relax my intestines as well as I had anticipated, in the presence of this new visitor. After a couple of frustrated attempts, I decided to just let him finish, and continue onward once he was done.  Quite logically, I could not just sit there for the sake of sitting there. It would have seemed suspicious; like one of those James Bond movies, that the guy is waiting quietly on the other bathroom cubicle, for you to drop down your pants, and then make his move and kill you from behind while you are trying very hard to loose a quick half pound. I had to find something inconspicuous to do as he finished.

After 10 minutes of playing with the flushing handle that makes the “whooooshhhhhhh” sound, I decided that this challenge was greater than I had anticipated. The man was simply sitting there unaware and happy.  I could not tell if he was succeeding in this progress or not, since I could not hear any “bloop” sound. Either, he must have had light liquidy food the night before, or a very constricted colon.  In a moment of anger, and frustration, I decided to go back to my cubicle and re-attempt the entire procedure later, once the Chinese man had finished with his enduring ritual.

As I sat down in my chair and debated whether I should write about my experience or not, my stomach made it very difficult to concentrate.  30 more minutes had passed, so I decided that “he” had already had 45 minutes to accomplish his task; I mean, how much longer could it possibly take him?! So I ventured again to the bathroom, still, holding on to the paper remnants that I had scavenged in my previous trip. I went quietly this time, slowly opened the door, and took a peek. What I saw next was unbelievable, inconceivable, out of the realms of imagination. The Chinese man was still there, unfinished. I returned to my cubicle once again, defeated and with poop waiting to explode out of my anus impatient. Sorry for my poor use of words, I was angry, you must understand. To summarize a very long and defeating experience, I returned a couple more times after, resulting in continuous moments of anguish and disappointment, induced yet again, simply, by a guy and his poop.

The last drop had spilled the glass! The last drop of poop, filling a glass full of shit. It was enough! By the fifth time I decided that I didn't care anymore if another man would smell my poop, or if I would have to smell the poop of another man. My destiny was laid clear before me. I took solemn steps onto the bathroom. Slowly, but surely, I opened the door, self assured. The Chinese man was still there, obviously, I was not surprised. I felt sorry for him, for a moment. I thought of all the troubles this man had had in his life that made him poop ever so slowly. I walked into the other private cubicle and closed the door behind me, carefully trying not to rub my legs against the pee-sprayed surroundings, as I pirouetted my way through onto the toilet. I unbuttoned my pants, hid my ID, and sat down. I managed, very uncomfortably to gain strength and summon the desire to eject the inner poop. After a couple of agonizing minutes, I heard what seemed to be the sound of flushing coming from somewhere near. This could not be, I thought to myself. The Chinese man was finished!!?, and he was on his way to wiping his butt!!? He was done in less than 5 seconds, and quietly left, taking with him my sanity and sense of reason. After I had gathered my strengths, lost all fear, and convinced my vowels to relax in front of people, he just left. Quickly afterwards, and after all that trouble and commotion, defeated and unpleasant, but with a sense of accomplishment, I went number 2... and a half. Sadly, I could not prove to the Chinese man that I could poop in front of him. Nevertheless, I was now at his same level. We were now equal in a battle of character. We were both level 20, self-accomplished, confident poopers.

So, as I write these anecdotes, I realize that life is like that Chinese man, putting obstacles in our way, not to prevent us from pooping, but to make us find the strength within ourselves to poop. Once we gather the force, and the will, and we lose the fear in things, the obstacles just seem to disappear by themselves; leaving us with a slight bitter taste in the mouth, but with a slowly emerging sense of accomplishment. I realize now that I went out of my way, making my own life so much more difficult than necessary, only to avoid confronting a very simple situation. So next time you are in a bathroom, and there is a Chinese man taking a dump next to you, don’t avoid it and just poop.

^_^

Microsoft Girl

Imagine, that in a near future, science and technology would allow us to create humanoid robots that could socially interact with people. The following dialog is a representation of a date between a typical (technology-savvy) guy, and said humanoid female running Microsoft Windows Vista as her main Operating System.

And... it begins like this.

Guy: Hey!
Girl: …
Guy: eeerrr… so, how have you b…
Girl: *loud* BOOTING UP!...
Guy: Woah! Ok...
Girl: … Oh! Hi!!! *pause and suspicious stare* I don’t recognize you as a peripheral device. Would you happen to have your drivers with you?
Guy: What? Drivers?
Girl: It’s ok if you don’t.  I’ll just have to look ‘em up mah-self.
Girl: … *blank stare*
Girl: It’ll only take a minute…
Guy: Uhhh… ok… I’ll just wa…
Girl: *loud* WINDOWS IS LOOKING FOR NECESSARY DRIVERS!…

20 minutes later

Girl: Yeah… I couldn’t find them. Would you like me to connect to "duh interwebs" and download them from there?
Guy: Uhhhh…
Girl: Ok *giggles* just a second…

20 minutes later

Girl: Yeah… I couldn't find them there…
Girl: It seems we’re just gonna have to... *BA-BAM!!!* run in compatibility mode!!!*…


Guy: So… you wanna go?
Girl: … Oh my!!!… are those shoes?!


Guy: So tell me a bit about yourself…
Girl: Well let’s see… I’m really cool… *sexy voice* Very… user friendly, and I’m compatible with most devices!
Guy:  *sarcastic* Wow, that is pretty cool.
Girl: Did I mention how cool, user friendly, and highly compatible with most devices I was?
Guy: Yes… indeed you ment…
Girl: *loud* Oh my god!!! Oh! My! Gawd!
Guy: *annoyed* What!?
Girl: *high-pitch-voice*  I have an update!
Girl:  I!... Have!... An!... Up!... Date!!!!
Guy: Woah… ok. What’s the update about?
Girl: …
Guy: Are you ok?  *sigh*
Girl: Would you like me to just download the update, or go ahead and automatically install it after I download it?
Guy: Uhhh…
Girl: *loud* DOWNLOADING UPADTE!!!… 0%
Guy: Uhhh… Do you need anything…?
Girl:  …

5 minutes later

Girl: ... 1%

*French accent* 1 hoeur lateur

Girl: Update complete.  *loud* Yay!!!
Guy:  *Sigh* Oook… that was excit….
Girl: I need to reboot! Like, I don’t really have to… but it’d be cool if I would. Do you what I mean? Because if I rebooted, then like, the update would be completely and awesomely installed!  
Girl: I guess I’m not your average plug-n-play kinda girl. I’m a bit more serious, if you know what I mean… do you know what I mean?
Guy: Sure… go ahead… and… reboot
Girl: Awww thank you!!! You the greatest…
Girl: Rebooting... Terminating network connections…  beep. Closing GUI…  beep. Installing updates… This may take a couple of minutes…

10 minutes later

Girl: ... Progress... 1%
Guy: Gah!!

10 minutes later

Girl: *Really loud* Beep! Shutting down…
Guy:  Jesus!
Girl: Booting… beep, beeeeeeeep, bip bip.


Girl: Error code 0x0BEEF69... System Fatal Error. The system could not start because file is missing. Please reinstall all missing components.
Guy: Wait… what!? What is this?
Girl: Please reinstall all missing components. Note: Missing components can be found in the OS’s installation CD.
Guy: CD… Where’s that stupid CD that came with this…?


Guy: I guess I’m just gonna have to download an illegal copy of the OS I guess.
Guy: Ok… www.
Guy: What was the name of that website? Oh yea…
Guy: torrents.com…  *hits enter*
Browser: Website not found…
Guy: Wasn’t it with a “z” at the end?...
Guy: www.torrentz.com...  *Enter*!



Girl: Booting… beep. beep. Msg: It seems that windows did not properly shutdown last time. Would you like to:

a) Boot in safe mode?
b) Normally start the Operating System?

Guy: I guess I’ll just normally start the OS
Girl: Beeeeeeeeeeep. Bip.
Guy: Oh god…
Girl: Starting OS…
Guy:  *relief* ….


Girl:  Welcome to Microsoft Windows Vista!!! Yay!!!


Girl: Sorry about that. The last update was kinda iffy, if you know what I mean… *snorty laugh*
Guy: …
Girl: I feel kinda funny…
Guy: You do? Well you’re OS just crashed… so it’s normal that y…
Girl: *Vomits*
Guy: Ahhh!!!!  
Girl: Ohhh…. My head hurts. I feel dizzy…
Guy: Oh crap, you have a virus! Quickly! Open Norton…
Girl: Norton…? Who is… Norton…? Want me to… *vomits again*… show you my internal RAM…?
Guy: No I do not want to “see” your RAM right now, *annoyed*…
Girl: *cries* Am I… *hiccups* not… good… enough for you?!
Girl: *sobs* Is that… *hiccups* it?!
Guy: Nooo…. No no no… is not that… Of course you’re… pretty good… in general…
Girl: You… *hiccups* think so?...
Guy: Yeah… Tell you what… what don’t I get you a free copy of AVG and Adaware for you so that you feel better? How about it?
Girl: La la la laaaaaa… *makes laser gun sound* piu pi piu!... airplanes are so cute!!
Guy: Ummm… are you… ok?
Girl: I don’t have a belly button… *stares blankly while turning blue*
Guy: Ok… I’ll go ahead and get you the… uhhh… Wow you actually don’t have a belly button…
Guy: I’ll get you the updates you need, ok?... Whoa! you just turned blue!
Girl: *shouts*  110 110 1!!!!! 001 01001!!! Baaaaa!!!!
Guy: I don’t really underst… you know what, ok… that does it. I’m shutting you down… Now where’s the mouse on this thing…?


To be continued…