Digital Car Compass and Thermometer

My car doesn’t have a compass which I wish to have one. So I started making one using my JeonLab mini v1.3 (minimalist Arduino compatible board), popular 16×2 LCD panel with a back light LED, and a 3 axis magnetometer.  Here is the part list and pictures of the LCD, JeonLab mini v1.3 and the prototyping board.

  • LCD: 16×2 HD44780 LCD (white text on blue background)
  • JeonLab mini v1.3
  • Digital compass: MAG3110 (bought an assembled from ebay)
  • Temperature sensor: TMP36
  • Phototransistor: LTR-4206E
  • 7805 regulator
  • resistors and capacitors (see schematic diagram)
  • Car battery jack
  • prototyping board
  • push button switch
  • solid copper wire (1mm in diameter) for bracket
  • cable ties

parts photo 20130203_162415.jpg

The JeonLab mini v1.3 is so small that can be attached to the back of the LCD.

compass & LCD test photo 20121228_174420.jpg

First of all, the LCD, JeonLab mini and the magnetometer, MAG3110 have been assembled on a breadboard and tested. The magnetometer has 3 axis sensor, but because, fortunately, the most roads where I live and commute to work are relatively level. So I didn’t bother to use complicated equations, but decided to calculate simply the heading angle using ATAN from X and Y readings. And it really works just good enough. Take a look at the source Arduino code below.
JeonLab Car Digital Compass & Thermometer

LCD display
Digital compass (MAG3110)
Interior temperature
LCD backlight brightness automatic adjust

MAG3110 read: Original code from Sparkfun example code

#include #include <avr/eeprom.h>

#define MAG_ADDR 0x0E
int avgX, avgY;

struct settings_t
long maxX, minX, maxY, minY;

LiquidCrystal lcd(2,3,6,7,8,9);

void setup()
config(); //configuration of the magnetometer, MAG3110
pinMode(10,INPUT); //temperature sensor
pinMode(5,OUTPUT); //LCD backlight LED
analogWrite(5, 100);

//read previously stored calibration data from EEPROM
eeprom_read_block((void*)&settings, (void*)0, sizeof(settings));

lcd.begin(16, 2);

void loop()
//read the magnetometer and calculate heading
float heading = (atan2(readx()-avgX, ready()-avgY))*180/PI;

//read and calculate the interior temperature in celcius
float temp = (analogRead(0)/1024.0*5000.0-500)/10;

//read ambient brightness from the photo-transistor
int brightness = map(analogRead(1), 900, 1023, 50, 255);
analogWrite(5,brightness); //LCD backlight LED voltage control by PWM

//diaplay compass bearing
lcd.print(” “);
if (abs(heading) <= 22.5) lcd.print(“N “); if (abs(heading) >= 157.5) lcd.print(“S “);
if (heading >= 67.5 && heading <= 112.5) lcd.print(“E “);
if (heading <= -67.5 && heading >= -112.5) lcd.print(“W “);
if (heading > 22.5 && heading < 67.5) lcd.print(“NE”);
if (heading < -22.5 && heading > -67.5) lcd.print(“NW”);
if (heading > 112.5 && heading < 157.5) lcd.print(“SE”);
if (heading < -112.5 && heading > -157.5) lcd.print(“SW”);

if (heading < 0) heading += 360.0; //display heading lcd.setCursor(3,0); lcd.print(int(heading)); lcd.setCursor(7,0); lcd.print(int(temp)); lcd.print(“\337C”); lcd.setCursor(0,1); lcd.print(” “); lcd.setCursor(0,1); lcd.print(“Brightness: “); //show PWM value for a reference lcd.print(brightness); if (digitalRead(10) == HIGH) //monitor calibration button status { analogWrite(5,200); lcd.setCursor(0,1); lcd.print(“calibrating…..”); delay(1000); calXY(); } delay(1000); } void calXY() //magnetometer calibration: finding max and min of X, Y axis { int tempXmax, tempXmin, tempYmax, tempYmin, newX, newY; lcd.setCursor(0,1); lcd.print(“Rotate the car “); delay(1000); lcd.setCursor(0,1); lcd.print(“and press button”); delay(1000); lcd.setCursor(0,1); lcd.print(“Now, begin! “); delay(500); tempXmax = tempXmin = readx(); tempYmax = tempYmin = ready(); while(digitalRead(10) == LOW) { newX = readx(); newY = ready(); if (newX > tempXmax) tempXmax = newX;
if (newX < tempXmin) tempXmin = newX; if (newY > tempYmax) tempYmax = newY;
if (newY < tempYmin) tempYmin = newY;
settings.maxX = tempXmax;
settings.minX = tempXmin;
settings.maxY = tempYmax;
settings.minY = tempYmin;

//store new X, Y values in the EEPROM
eeprom_write_block((const void*)&settings, (void*)0, sizeof(settings));


lcd.print(“Calibration done”);
lcd.print(” “);


void config(void) //MAG3110 config taken from Sparkfun example
Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x10); // cntrl register1
Wire.write(0); // send 0x00, standby mode
Wire.endTransmission(); // stop transmitting


Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x11); // cntrl register2
Wire.write(0x80); // send 0x80, enable auto resets
Wire.endTransmission(); // stop transmitting


Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x10); // cntrl register1
Wire.write(0x19); // send 0x01, active mode
Wire.endTransmission(); // stop transmitting


void print_values(void)

int readx(void) //MAG3110 read X value taken from Sparkfun example
int xl, xh; //define the MSB and LSB

Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x01); // x MSB reg
Wire.endTransmission(); // stop transmitting

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.requestFrom(MAG_ADDR, 1); // request 1 byte
while(Wire.available()) // slave may send less than requested
xh =; // receive the byte

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x02); // x LSB reg
Wire.endTransmission(); // stop transmitting

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.requestFrom(MAG_ADDR, 1); // request 1 byte
while(Wire.available()) // slave may send less than requested
xl =; // receive the byte

int xout = (xl|(xh << 8)); //concatenate the MSB and LSB
return abs(xout);

int ready(void) //MAG3110 read Y value taken from Sparkfun example
int yl, yh; //define the MSB and LSB

Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x03); // y MSB reg
Wire.endTransmission(); // stop transmitting

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.requestFrom(MAG_ADDR, 1); // request 1 byte
while(Wire.available()) // slave may send less than requested
yh =; // receive the byte

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
Wire.write(0x04); // y LSB reg
Wire.endTransmission(); // stop transmitting

delayMicroseconds(2); //needs at least 1.3us free time between start and stop

Wire.requestFrom(MAG_ADDR, 1); // request 1 byte
while(Wire.available()) // slave may send less than requested
yl =; // receive the byte

int yout = (yl|(yh << 8)); //concatenate the MSB and LSB
return abs(yout);

And the schematic diagram is below.
Schematic photo JeonLabCarDigitalCompassThermometer-1.png

I have introduced a step-by-step assembling procedure on Instructable, and here are some pictures of assembling and mounting on top of the interior mirror of my car.

assembling JeonLab mini v1.3 photo 20130203_165903.jpg

Note that there is no LED attached. A 6-pin header is attached upwards for the FTDI USB interface and 3 single header pins are attached at the bottom of the JeonLab mini to support on the prototyping board which will be attached to the back of the LCD.

JeonLab mini on the back of the LCD photo 20130209_122723.jpg

 photo 20130217_124514.jpg
The temperature sensor (TO-92 package), a voltage regulator, and a calibration switch are assembled.

finished assembly photo 20130317_163954.jpg
Next to the temperature sensor, the phototransistor (looks like a 3mm LED in black) is added to adjust the LED backlight of the LCD automatically.

wrapping heat shrink tube photo 20130409_193307.jpg
A thick solid copper wire (appx. 1mm in diameter) is used to form a simple bracket on top of the interior mirror.

finalizing bracket photo 20130409_194316.jpg
A heat shrink tube is used to insulate the copper wire bracket where it touches the LCD PCB.

attched to the mirror photo 20130409_194552.jpg
Two short cable ties are used to fix the assembled LCD and bracket on top of the mirror.

magnetometer fix photo 20130413_153144.jpg
The magnetometer is held by a small suction cup on the wind shield at the back of the mirror.

in my car photo 20130410_174041.jpg
Finally, it works!

Posted in Arduino, Electronics, JeonLab mini

Sunny day afternoon at Jackson park

Posted in Photograph

Cheap and simple digital TV antenna

It’s been a long time since I posted the last one.  I got a new job and had to move to a new place in Windsor, Ontario and have been really busy with everything. Now I think I can resume this blogging with exciting projects.

Since I moved to this brand new house, I made a temporary TV antenna as shown below.

I found this TV antenna design from internet some time ago and it really works quite well. A matching transformer in the middle in this picture is missing since I have already removed it to a new design. This one works fine but I don’t want to put this one on top of the roof because I don’t want to take any risk of thunder or even climbing up to the roof, nor to hang it on the living room wall because it is not that small (about 1m high) and doesn’t look pretty. So I decided to test very simple antenna that is cheap, small enough to be hidden behind a picture frame, and indoor.

The aluminum foil in the kitchen is always handy. I needed a support to attach two pieces of aluminum foil and found a piece of cardboard box. The size of each side of the aluminum foil is 250mm x 215mm. I didn’t test other than this size but it works good enough. You may want to try different size and shape of similar design for your TV. I could get over 20 channels with this.


Since it is a brand new house, all the cable outlets on the walls in the living room (over the fireplace), in the master bedroom, and in the basement remain unconnected and exposed in front of the switch panel (power distribution panel) in the basement.


I wanted to put the TV in the basement and wanted to put the antenna as high as possible and hopefully to be able to hide the antenna such as behind a picture frame (I don’t have it yet though.) above the fireplace. So I tested all the cables using my multimeter and identified which one was which and connected the two cables, one from the living room and one from the basement, and connected together as shown pictures below.





Two pieces of the aluminum foil are connected to the TV antenna matching transformer and attached between the foil by using a hot glue.



A short TV cable connects the antenna matching transformer and the outlet on the wall like this.



Auto scanning of the DTV (digital TV) channels gave me 23 channels!

Now I need a picture frame to hide the antenna.

Tagged with:
Posted in Electronics, Tips and tricks

Albert Einstein Quotes

You may have heard a lot of quotes Albert Einstein said as I have. Here are my favorites.

– Most people say that is it is the intellect which makes a great scientist. They are wrong: it is character.

– Imagination is more important than knowledge.

– Gravitation is not responsible for people falling in love.

– Peace cannot be kept by force; it can only be achieved by understanding.

– The hardest thing in the world to understand is the income tax.(I totally agree.)

– Science without religion is lame. Religion without science is blind.

– Anyone who has never made a mistake has never tried anything new.
If you can’t explain it simply, you don’t understand it well enough.

– Information is not knowledge.

– Learn from yesterday, live for today, hope for tomorrow. The important thing is not to stop questioning.

– The world is a dangerous place to live; not because of the people who are evil, but because of the people who don’t do anything about it.

– If the facts don’t fit the theory, change the facts. (I disagree this one. One can not change or modify facts.)

Tagged with: ,
Posted in Else

Wii Nunchuck hack: making an electronic level with Arduino compatible JeonLab mini v1.3

I have posted a project on using the accelerometer chip in the broken Wii Nunchuck. It was broken: the joystick did’t work. However, the accelerometer chip in the Nunchuck was intact and it also has the I2C interface which can be easily communicate with Arduino (or compatible board) with the Wire.h library.

I have seen those applications on iPod touch or Android tablets that can be used as a level using their built-in accelerometer sensor chip with a nice graphic showing bubble level.  I thought I could make one like that with the Wii Nunchuck and started building one.  I had bought a Wii Nunchuck from ebay to replace my kids’ broken Nunchuck for less than $10 (I don’t remember exactly) and I thought it was much cheaper to buy them than to buy the accelerometer with I2C interface or breakout boards so I bought a couple more at that time.

The joystick and the PCB look like this.
Wii Nunchuck PCB-Joystick

I don’t need the (broken) joystick part, so I cut off that part.
Accelerometer part of the Wii Nunchuck PCB top
The accelerometer chip (A7260) is shown.

The I2C converter is at the bottom of the PCB.
Accelerometer part of the Wii Nunchuck PCB bottom

Now I need a case for this project and found perfect size clear acrylic case from my junk box. I don’t even remember where I got it.
Clear case
Clear case width
Clear case height

The width inside of the case was almost perfect for the JeonLab mini v1.3. I had to file off about half mm of the PCB. I also thought to use a 3.0V CR123A battery for this project.
JeonLab mini v1.3 in clear case with CR123A

But later I changed the power supply to a 3.3V regulated with a 12V A23 battery for better stability.

The cut PCB from the Nunchuck was glued on top of the ATmega328P chip on the JeonLab mini v1.3.
Wii Nunchuck PCB on JeonLab mini v1.3 in case

The I2C wires, clock and data, are connected to the analog pin 5 and 4 on the JeonLab mini v1.3, respectively.
I2C wiring between Wii Nunchuck PCB and JeonLab mini v1.3

I also connected the power for the Nunchuck board to the JeonLab mini v1.3 board as shown below, but this was my mistake.
Power sharing between Wii and JeonLab mini

The power supply I thought was 3.0V battery, so I thought sharing the power should be fine. BUT I forgot the program upload through the FTDI. The accelerometer chip and the I2C interface need 3.3V (3.0-3.6V) and the ATmega328 on the JeonLab mini v1.3 (and other Arduino compatible boards as well) can work 3-5V. The Nunchuck data reading header, nunchuck_funcs.h (from WiiChuckDemo by Tod E. Kurt) provides the settings for utilizing the analog pins 3 and 2 as power source for the Nunchuck board but this provides 5V, not 3.3V. The problem is that 5V supply to the Nunchuck board could damage the chip(s) either the accelerometer or the I2C chip or both. Actually, the first one I used had been unstable and noisy, so it had to be replaced with a new one. That’s when I decided to change the power source from the 3V battery to 12V battery with a 3.3V regulator and added a Schottky diode (1N5819) to protect the Nunchuck board from FTDI 5V supply.

Now, let me explain the design. Using the accelerometer sensor on the Nunchuck board, it detects which side is tilted and send the 3 axis data to the Arduino program loaded JeonLab mini board through the I2C protocol. The program compares the current data to the stored (in EEPROM of the ATmega328P) calibration data. There are three stages of displaying: 1) If the value is within certain range, it will turn on the central red LED connected to the digital pin 7; 2) If the value is greater than the range (sens in the program) and less than 2 times of the range (sens), it will turn on the red LED and one green LED at the opposite (in order to simulate the bubble direction) side of the tilt; 3) If the value is greater than 2 times of the range (sens), then only the green LED is turned on.

The calibration values are the neutral value of each axis reading when it is leveled on that axis. For example, the Nunchuck data reading is between 60-70 (these values are different from sensor to sensor) at -g (upside down) on that axis and is over 170 at g (up right). So the neutral (leveled) value of each axis is about 120-130. The calibration begins when the pin 10 goes HIGH by connected to V+ with a small push button switch pressed. One the calibration process begins, in order to give the user some time to put the device down on a flat surface, it waits until the central red LED blinks a few times. The actual calibration is done really quickly and followed by a few quicker blinks.

Here is the whole program.

 * Wii_Nunchuck_Level
 * Feb-Mar 2012, Jinseok Jeon
 * Wii Nunchuck data read:
 *  nunchuck_funcs.h from WiiChuckDemo by Tod E. Kurt, 

#include "nunchuck_funcs.h"

byte accl[3]; //accelerometer readings for x, y, z axis
int calPin = 10; //calibration pin
int sens = 1; //sensitivity
int orient;

void setup()
  for (int i=5;i&lt;10;i++) { 
    pinMode(i, OUTPUT);
  } //9 left, 8 up, 7 center, 6 down, 5 right
  pinMode(calPin, INPUT);

void loop()
  //if the calibration pin is pressed, jump to funcion calibrate()
  if (digitalRead(calPin) == HIGH) calibrate();

  if (orient == 1 || orient == 2) {
    if (abs(accl[0] + orient*10))  sens) digitalWrite(5, HIGH);
    if ( + orient*10)-accl[0] &gt; sens) digitalWrite(9, HIGH);
  if (orient == 3 || orient == 4) {
    if (abs(accl[1] + orient*10))  sens) digitalWrite(8, HIGH);
    if ( + orient*10)-accl[1] &gt; sens) digitalWrite(6, HIGH);
  if (orient == 5 || orient == 6) {
    if (abs(accl[0] + orient*10)) &lt;= 2*sens &amp;&amp; abs(accl[1] + orient*10))  sens) digitalWrite(5, HIGH);
    if ( + orient*10)-accl[0] &gt; sens) digitalWrite(9, HIGH);
    if (accl[1] + orient*10) &gt; sens) digitalWrite(8, HIGH);
    if ( + orient*10)-accl[1] &gt; sens) digitalWrite(6, HIGH);

  for (int i=5;i&lt;10;i++) { //turn off all LEDs
    digitalWrite(i, LOW);

void getData()
  accl[0] = nunchuck_accelx();
  accl[1] = nunchuck_accely();
  accl[2] = nunchuck_accelz();

  orient = orientation(); //get orientation

void calibrate()
  for (int i=0;i&lt;3;i++) {
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW); 


  for (int i=0;i&lt;3;i++) {
    EEPROM.write(i + orient*10, accl[i]);

  for (int i=0;i 125 &amp;&amp; accl[0]  110 &amp;&amp; accl[2]  170) orient = 1; //bottom on floor
    else if (accl[1]  110 &amp;&amp; accl[1]  110 &amp;&amp; accl[2]  180) orient = 3; //left on floor
    else if (accl[0]  110 &amp;&amp; accl[1]  125 &amp;&amp; accl[0]  170) orient = 5; //back on floor
    else if (accl[2] &lt; 80) orient = 6; //front on floor
  return orient;

Some pictures below show the assembling procedure of the LEDs and the tiny switch from a broken camera. Here I want to give all of you a note: don’t throw away any broken electronics. Because they don’t work doesn’t mean they are garbage. There are a lot of good parts you can use for other projects.

Bubble LEDs
assembled bubble LEDs and resistors
assembled JeonLabmini v1.3 - Wii Nunchuck - bubble LEDs

The tiny switch found from a broken camera.
micro switch for calibration

A 10k resistor is used to pull down the pin 10 and this tiny switch is connected to the pin 10 and V+ to trigger the calibration.
Calibration switch connection

Now these are the parts for the power supply. There are a piece of prototype board, a Schottky diode (from a broken power adapter), 0.1uF ceramic capacitor, a 10uF electrolyte capacitor, 12V A23 battery, 3.3V regulator LD33V, and battery contacts also found from the broken camera.
Power supply parts
Battery contacts from broken camera

The battery contacts were modified a little bit and soldered on the board. The regulator doesn’t need the big heat sink so the metal part was cut on top and soldered on the board and it also form a perfect battery holder.
battery holder with 3.3V regulator

This is assembled power supply in the case.
power supply with rearragned caps

Fully assembled Wii Nunchuck Level:
Completed Wii Nunchuck Level top

When it is leveled:
Wii Nunchuck Level leveled

When it is tiled to left:
Wii Nunchuck Level tilted left

When it is tilted to right:
Wii Nunchuck Level tilted right

And here is the Youtube video showing how it works.

Posted in Arduino, JeonLab mini, Modification

Earth Hour 2012: Mar 31, 8:30pm

Earth Hour_8:30pm Mar 31, 2012

Turn off lights for just 1 hour and save the energy, save the Earth!

Posted in Energy Saving

TV-B-Gone_JeonLab power source

As same as Adafruit’s original TV-B-Gone kit, my modified TV-B-Gone_JeonLab accepts any DC power source within 3-5V. The micro-controller, ATtiny85V, runs within 1.8-5.5V, but the IR LEDs consumes more power than the micro-controller, you need to keep the source voltage at least 3V.

Adafruit includes 2 AA battery holder in their kit, but I will not include any battery holder or connector in my kit in order to give more flexibility of choice of power source.  As long as the voltage of the source is within 3-5.5V, any battery or supercap can be used.  For example, 2-3 AA or AAA alkaline batteries, 3-4 rechargeable batteries like NiCd or NiMH, Li-ion (or Li-PO) rechargeable batteries or 2 of 2.5V (0r 2.7V) supercap (at least 10F).

As I mentioned a few times before, I like supercaps. Even though they can not hold much charge compared to batteries, they can be charged quickly (less than a minute) through USB or 5V wall adapter and small and light.  So I decided to attach two supercaps to my TV-B-Gone_JeonLab, but I wanted to compare the sizes of 4 and 2 AA battery holder and AAA batteries.

TV-B-Gone_JeonLab size_4AA

TV-B-Gone_JeonLab size_2AA

TV-B-Gone_JeonLab size 3AAA & 1A

The actual size of my modified PCB is slightly smaller than Adafruit’s which fits nicely at the back of 2 AA battery holder. Anyway, I figured someone would be interested in having supercaps as a power source and here is how I did.

First thing I did was to attach a male header a guide (plastic piece on one side) for assuring correct polarity of connection.

connector male header

But this one was too tall compared to the other components.
connector male header before cut

So I cut the plastic guide and pins a little bit.
connector male header after cut

Using a small piece of double sided tap with foam two supercaps (2.5V, 10F) were attached to the bottom of the PCB and soldered in series together.
TV-B-Gone_JeonLab supercap soldered

TV-B-Gone_JeonLab sideview with supercap

In order for ensuring the supercaps in place securely, a thin cable tie was used.
TV-B-Gone_JeonLab top view cable tied

TV-B-Gone_JeonLab cable tied bottom

Now it’s time to make the USB cable connector. I cut one of those USB cable found from my junk box.
USB cable wires

And attached female connector to the red (+) and black (-) wires of the USB cable.
USB cable power wires red and black

female connector before finishing

When you insert those spring pins in the connector housing, make sure the stopper to be inserted all the way in until it makes click sound (blue arrows in the picture below).
female connector assembled_click

Finished with a heat shrink tube.
female connector shrink tubed

Charging from my laptop computer.
TV-B-Gone_JeonLab with supercap USB charging

Look how small it is in my hand.
TV-B-Gone_JeonLab size in hand

And it works!!
TV-B-Gone_JeonLab_supercap in hand

With one full charge (about one minute), I could turn one of my TVs on and off more than 40 times from about 6.5m (21 feet) away. Of course I used STOP button right after it turned on (or off) the TV. It does save power!

If you want to buy the kit (without supercap or battery holder), PCB, or firmware loaded ATtiny85V or any combination, email me for a quote.

Posted in ATtiny, Modification, TV-B-Gone
May 2017
« Jan    

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 10 other followers