Introduction: A Tiny Compass With ATtiny85
This is our first project with ATtiny85; a simple pocket digital compass (in collaboration with J. Arturo Espejel Báez).
ATtiny85 is a high performance and low power microcontroller. It has 8 Kbytes of programmable flash memory. Due to this, the challenge in this project was to reduce the size of the program, since the circuit is very simple, thanks to the I2C protocol.
Supplies
For the Compass:
- ATtiny85
- HMC5883L Magnetometer
- SSD1306 I2c 0.96" 128x64 OLED Display
- Self-locking square button switch
- 3.7V 300mAh Lipo Li-polymer Battery
- 3D printed case (2 parts, please find the STL links)
For the Charger:
- Two pieces of PCB; 17x10mm and 13x18mm
- 3D printed case (2 parts, please find the STL links)
- Micro USB 5V 1A TP4056 Lithium battery charger module
Step 1: The Program
It is necessary to load the program AB.ino into the ATtiny85 before wiring it in the circuit. For this, you can follow any of the tutorials on the internet, such as https://www.instructables.com/id/DIY-Attiny-Progr...
To compile the program, you need to install the library ssd1306 by Alexey Dynda, available in https://platformio.org/lib/show/1904/ssd1306
Attachments
Step 2: The Circuit
Step 3: Wiring the ATtiny85
It is convenient to cut the unused pins of the ATtiny before soldering.
Prepare two 10-cm pairs of wire by stripping two 2-mm sections halfway and separated by about 5 mm from each other, as shown in the 1st and 2nd photos. Solder one section of the first pair of cables (A) to SDA (pin 5) and the other section to SCL (pin 7) as shown in the 3rd picture. With the other pair of wires (B), solder one cable to GND (pin 4) and the other one to +V (pin 8), like in the 4th photo.
Step 4: Wiring the OLED Display
Solder the four wires of one side of the ATtiny (SDA, SCL, +V, and GND) to the corresponding contacts of the OLED display and glue it to the case. Protect the display board with insulating tape.
Step 5: Place the Charger Contacts
Take two wires from a male header pin connector. Fold each one forming a hook as in the first photo. Insert one in the lateral side of the display case, and the other in the bottom lid as shown.
Step 6: Wiring the HMC5883L
Glue the HMC5883L magnetometer to the bottom lid as shown. Solder the SCL and SDA wires from the ATtiny to the corresponding contacts of the magnetometer, fold the charger contact wire and solder to the GND contact. Solder the +V and GND wires from the ATtiny to the corresponding contacts. Protect the magnetometer board with insulating tape.
Step 7: Wiring the Battery
Solder the negative pole of the battery to pin 4 of the ATtiny, and the positive to the charger contact in the side of the case. Add a wire from this contact to the switch (see next step).
Step 8: Wiring the Switch
Solder the wire from the lateral charger contact to one contact of the switch, and then another one to the +V contact of the magnetometer. Now you can test the Compass and glue the bottom lid.
Step 9: Calibrating
The program AB.ino has an automatic calibrating algorithm. You only have to turn on and rotate the compass 360º as shown in the video.
ATTENTION!
Never connect both external contacts as this would short-circuit the battery.
Step 10: Charger I
Cut two pieces of PCB of 17 mm x 10 mm and 13 mm x18 mm. Drill a hole in the small piece that matches with the hole in the round 3D printed part, pass a wire through and solder it. Glue the PCB as shown in the photo.
Step 11: Charger II
Solder a wire in the 17x10mm PCB piece and pass it throw the slot in the 3D printed part. Glue it as shown.
Step 12: Charger III
Fit and glue the 3D printed parts as shown and solder the wires to the battery charger module. The wire soldered in the bottom part is the negative. Now you can charge the compass' battery with a mini USB cable.

Second Prize in the
Maps Challenge
44 Comments
Question 1 year ago on Step 1
I followed your instructions on uploading AB.ino onto an ATTiny85 exactly. Unfortunately, no matter what I do, I get an "Error Compiling for Board ATTiny25/45/85." I've tried uploading the ATTiny on both a Mac and a Windows computer. I keep getting the same message. The good news... I got it to compile for an Arduino Uno. Unfortunately, the Uno is a bit big for this project. Is there a trick getting this sketch onto an ATTiny? Is there something else I can do to make this upload?
Keith
Answer 1 year ago
Hi! Please try with the .hex precompiled file.
3 years ago
Hello,
very Nice compass, i have received from Amazon a qmc5883 ans not a hmc5883 ,as i understand thé code si not thé same . What i havé to change in the program?
best regards
jean louis
Reply 3 years ago
Hello
It seems to me that there is no need to make any changes.
https://surtrtech.com/2018/02/01/interfacing-hmc8553l-qmc5883-digital-compass-with-arduino/
Reply 3 years ago
Hello,
i havé received my oled, it s not working with the qmc 5883 (DB5883 write on the chip),i make some changes And it is working now but the heading is not very stable With this chip???
best regards
Reply 3 years ago
Please, make sure that once powered on, rotate it 360° degrees to calibrate it. Please let me know if you still have any problems!
Reply 3 years ago
Hello,
after some trials, i confirm the qms5883 found on amazon is not working well even with an uno.
j have to found an hms
best regards
jlouis
Reply 2 years ago
Hi Jobtn, I had a similar problem but got lucky. I had receive both versions of the chip as I order a spare.
It seems it is a flip of a coin as to which chip you might get.
I am surprised that something that is sold as an identical board can be so different in operation.
I see you changed the I2c address (0x1E to 0x0D) for the different chip which is a good start but apparently the data register addresses/order are different. Have you seen this https://github.com/nodemcu/nodemcu-firmware/issues/2187
Reply 2 years ago
Hi lodestone1
you are genius, it s working very nice
thank you
best regards
jean louis
Reply 2 years ago
No just an insomniac, I couldn’t sleep last night so I read through the data sheets.
You are welcome.
Tim
Reply 2 years ago
Hi raul7321
Having found that I had both the QMC and HMC chips on the two identical (not) boards I had received from china, I decided to try and learn something about the differences.
My understanding of C++ is pitiful but building upon what Jobtn has done so far to try and resolve the issue I ended up with this.
I have annotated what I think is happening.
Crucially there is an addition reset register.
This is stable and calibrates fine.
#include <Wire.h>
#include "ssd1306.h"
#define address 0x0D // QMC5883
const int k92=92,k31=31,k26=26;
int xx,yy,zz;
float xxx,yyy,MaxX=-5000,MinX=5000,MaxY=-5000,MinY=5000;
const uint8_t G[4] = {
0B00111100,
0B01100110,
0B01100110,
0B00111100
};
const uint8_t PROGMEM N[8] = {
0B11111111,
0B11111111,
0B11111111,
0B00011110,
0B01111100,
0B11111111,
0B11111111,
0B11111111
};
const uint8_t PROGMEM S[8] = {
0B01001110,
0B11011111,
0B11011111,
0B11011011,
0B11011011,
0B11111011,
0B11111011,
0B01110010
};
const uint8_t PROGMEM E[8] = {
0B11111111,
0B11111111,
0B11111111,
0B11011011,
0B11011011,
0B11011011,
0B11000011,
0B11000011
};
const uint8_t PROGMEM W[8] = {
0B00111111,
0B01111111,
0B11111111,
0B01100000,
0B11111100,
0B01100000,
0B11111111,
0B01111111,
};
SPRITE sN,sS,sE,sW,sG;
void setup(){
Wire.begin();
Wire.beginTransmission(address);
Wire.write(0x0B); //Define set rest period
Wire.write(0x01); //as recomended on the data sheet
Wire.endTransmission();
Wire.begin();
Wire.beginTransmission(address);
Wire.write(0x09); //mode register opened
Wire.write(0x1D); //mode set continuous
Wire.endTransmission();
ssd1306_128x64_i2c_init();
ssd1306_fillScreen(0x00);
ssd1306_setFixedFont(ssd1306xled_font8x16);
sN = ssd1306_createSprite( 0, 0, 8, N);
sS = ssd1306_createSprite( 0, 0, 8, S);
sE = ssd1306_createSprite( 0, 0, 8, E);
sW = ssd1306_createSprite( 0, 0, 8, W);
sG = ssd1306_createSprite(34, 47, 4, G);
}
void loop(){
int b;
float a;
Wire.beginTransmission(address);
Wire.write(0x00); //start address of data registers , six consecutive
Wire.endTransmission();
Wire.requestFrom(address, 6); // read the six consecutive data registers the order is completly different from HMC chips !
if(6<=Wire.available()){
xx = Wire.read(); // X LSB on QMC
xx |= Wire.read()<<8; // X MSB on QMC
yy = Wire.read(); // y LSB on QMC
yy |= Wire.read()<<8; // y MSB on QMC
zz = Wire.read(); // z LSB on QMC
zz |= Wire.read()<<8; // z MSB on QMC
}
if (xx>=MaxX) MaxX=xx;
if (xx<=MinX) MinX=xx;
if (yy>=MaxY) MaxY=yy;
if (yy<=MinY) MinY=yy;
xxx = -50+100*(xx-MinX)/(MaxX-MinX);
yyy = -50+100*(yy-MinY)/(MaxY-MinY);
a = atan2(yyy,-xxx);
b = a*180/PI-90;
if (b<0) b+=360;
if (b>180) b-=360;
sN.x=k92+k26*cos(a+PI); sN.y=k31+k26*sin(a+PI);
sS.x=k92+k26*cos(a); sS.y=k31+k26*sin(a);
sW.x=k92+k26*cos(a+PI/2); sW.y=k31+k26*sin(a+PI/2);
sE.x=k92+k26*cos(a-PI/2); sE.y=k31+k26*sin(a-PI/2);
sN.eraseTrace();sS.eraseTrace();sW.eraseTrace();sE.eraseTrace();
sN.draw();sS.draw();sW.draw();sE.draw();sG.draw();
printGrados(b);
}
void printGrados(int g){
char gStr[5] = "0000";
gStr[0] = (g<0) ? '-':' ';
g=abs(g);
gStr[1] = '0' + g / 100;
gStr[2] = '0' + (g /10)%10;
gStr[3] = '0' + (g % 100) % 10;
ssd1306_printFixed(0,50, gStr, STYLE_NORMAL);
}
Reply 2 years ago
Hi Jobtn
I have experimented further and now have the QMC working and stable.
The additional changes I made from your code are the reset register (without this being set I found it was totally unstable) and the order of the LSB/MSB and the X,Y,Z registers.
I have put the code in another comment to the author. As I say I'm only just getting to grips with coding so don't laugh.
3 years ago
I got error messages when verifying. ???
Arduino: 1.8.10 (Windows 10), TD: 1.48, Board: "ATtiny25/45/85, Disabled, CPU, ATtiny85, 8 MHz (internal), B.O.D. Disabled"
C:\Users\ronald\Documents\Arduino\hardware\ATTinyCore-master\avr\libraries\Wire\Wire.cpp: In member function 'void TwoWire::setClock(uint32_t)':
C:\Users\ronald\Documents\Arduino\hardware\ATTinyCore-master\avr\libraries\Wire\Wire.cpp:85:3: error: 'TWBR' was not declared in this scope
TWBR = ((F_CPU / frequency) - 16) / 2;
^~~~
C:\Users\ronald\Documents\Arduino\hardware\ATTinyCore-master\avr\libraries\Wire\Wire.cpp:85:3: note: suggested alternative: 'TIFR'
TWBR = ((F_CPU / frequency) - 16) / 2;
^~~~
TIFR
Multiple libraries were found for "ssd1306.h"
Used: C:\Users\ronald\Documents\Arduino\libraries\ssd1306
Multiple libraries were found for "SPI.h"
Used: C:\Users\ronald\Documents\Arduino\hardware\ATTinyCore-master\avr\libraries\SPI
Multiple libraries were found for "Wire.h"
Used: C:\Users\ronald\Documents\Arduino\hardware\ATTinyCore-master\avr\libraries\Wire
Not used: C:\Users\ronald\Documents\Arduino\libraries\Wire
exit status 1
Error compiling for board ATtiny25/45/85.
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
Reply 3 years ago
It seems like a conflict with the ssd1306 library. Apparently you have several libraries installed with the same name. I recommend making sure that it is being compiled with the correct library (the one that corresponds to the link).
You can locate duplicate libraries and rename them temporarily.
Let me know if you need more help.
Reply 2 years ago
Hello! I have the same problem. The SSD1306 library is what you need. I would be very grateful to you if you put the HEX file here.
Reply 2 years ago
Hello!
Here is the link to the library which I used:
https://www.sugarsync.com/pf/D6813265_08911372_841...
Reply 2 years ago
Many thanks for this I wish I'd seen it sooner. Thanks also to ronw5 for asking the question as I was not originally using "Spence Konde's ATTiny Core" which seems to have things compiling nicely.
Now to see it working.
Thanks again raul7321 for posting this instructable.
Reply 2 years ago
Thank you, raul7321!
Reply 2 years ago
Thank you! But the installation of this library gave nothing, the errors are the same as before:
In file included from d:\program\arduino-nightly\hardware\tools\avr\avr\include\avr\io.h:99:0,
from D:\PROGRAM\arduino-nightly\hardware\arduino\avr\libraries\Wire\utility\twi.c:25:
D:\PROGRAM\arduino-nightly\hardware\arduino\avr\libraries\Wire\utility\twi.c:390:15: error: 'TWINT' undeclared (first use in this function)
Dear author, please provide a link to your hex file. I am very limited in time and I do not have the opportunity to debug the AB program.
Reply 2 years ago
Hi. I don't have much time either, but I uploaded the .hex file to the instructable, in step 1