element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Hats Off Design Challenge
  • Challenges & Projects
  • Design Challenges
  • Hats Off Design Challenge
  • More
  • Cancel
Hats Off Design Challenge
Blog Coachman's Navigational Top Hat: Working on the code
  • Blog
  • Forum
  • Documents
  • Files
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: synaesdav
  • Date Created: 7 Oct 2014 1:21 AM Date Created
  • Views 622 views
  • Likes 0 likes
  • Comments 1 comment
  • coachman's_navigational_top_hat
  • hats_off
  • wearable_technology
  • gemma
  • adafruit
Related
Recommended

Coachman's Navigational Top Hat: Working on the code

synaesdav
synaesdav
7 Oct 2014

So now that my hat seems well on its way, I decided it would be wise to dive back into my code. I know what I am doing on the hat construction side of things, but the electronics and coding is a bit more exploratory. Please bear with me on my journey. I have learned all I know about coding for Arduino form the Adafruit learning site and a few other online tutorials. I take a patchwork quilt mentality to coding, looking at other peoples projects, trying to understand why it works and then modifying it for my own purposes. I really hadn't done anything much with the code for this project since my proof of concept video.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

You can see in the video that the dots appear to be tracking in one direction, but then drift away a bit.

Here is the code I used with the library files that I modified. You can also find the code here at codebender. https://codebender.cc/sketch:42502

The code compiles in the Arduino IDE at 5,300 bytes out of an allowed 5,310.

Gemma ring pointer V2

//using code from  https://learn.adafruit.com/steam-punk-goggles?view=all
//idea for pointer pixel from http://www.element14.com/community/groups/arduino/blog/2013/08/27/get-closer-challenge--navigation-glove
//idea for library modification from http://www.slickstreamer.info/2014/02/led-bracelet-accelerometer-3dprinting.html
#include <TinyWireM.h>
#include "TinyLSM303_mag.h"
#include <Adafruit_NeoPixel.h>

#define Pin 1
#define numPixels 16

Adafruit_NeoPixel ring = Adafruit_NeoPixel(numPixels, Pin, NEO_GRB + NEO_KHZ800);

TinyLSM303_mag lsm;

// Pi for calculations - not the raspberry type
const float Pi = 3.14159;

void setup()
{
  ring.begin();
  ring.show();// Initialize all pixels to 'off'
  lsm.begin();// Initialize the sensors
}

void loop()
{
  // Read the magnetometer and determine the compass heading:
  lsm.read();

  // Calculate the angle of the vector y,x from magnetic North
  float heading = (atan2(lsm.magData.y,lsm.magData.x) * 180) / Pi;

  // Normalize to 0-360 for a compass heading
  if (heading < 0)
  {
    heading = 360 + heading;
  }

  byte d = map(heading,0,360,0,16);//map the heading to the size of the ring

  byte traceFront = d + 1;
  if (d == 16)
  {
    traceFront = 0;
  }

  byte traceBack = d-1;
  if (d == 0)
  {
    traceBack = 15;
  }

  //wipe the slate clean
  for(byte i=0;i<numPixels;i++)
  {
    ring.setPixelColor(i,ring.Color(0,0,0));
  }

  ring.setPixelColor(d, ring.Color(0, 80, 100)); //set the closest pixel in the ring to the heading
  ring.setPixelColor(traceFront, ring.Color(20, 30, 40));
  ring.setPixelColor(traceBack, ring.Color(20, 30, 40));

  ring.show();
}


Tiny_LSM303_mag.h

/***************************************************************************
  This is a library for the LSM303 Accelerometer and magnentometer/compass

  Designed specifically to work with the Adafruit LSM303DLHC Breakout

  These displays use I2C to communicate, 2 pins are required to interface.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
***************************************************************************/
//Modified by David Crittenden July, 2014
//removed sections of accelerometer code to fit on GEMMA

#ifndef __LSM303_H__
#define __LSM303_H__

#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "TinyWireM.h"

#define LSM303_ADDRESS_MAG            (0x3C >> 1)         // 0011110x
#define LSM303_ID                     (0b11010100)

class TinyLSM303_mag
{
  public:

    typedef enum
    {
      LSM303_REGISTER_MAG_CRA_REG_M             = 0x00,
      LSM303_REGISTER_MAG_CRB_REG_M             = 0x01,
      LSM303_REGISTER_MAG_MR_REG_M              = 0x02,
      LSM303_REGISTER_MAG_OUT_X_H_M             = 0x03,
      LSM303_REGISTER_MAG_OUT_X_L_M             = 0x04,
      LSM303_REGISTER_MAG_OUT_Z_H_M             = 0x05,
      LSM303_REGISTER_MAG_OUT_Z_L_M             = 0x06,
      LSM303_REGISTER_MAG_OUT_Y_H_M             = 0x07,
      LSM303_REGISTER_MAG_OUT_Y_L_M             = 0x08,
      LSM303_REGISTER_MAG_SR_REG_Mg             = 0x09,
      LSM303_REGISTER_MAG_IRA_REG_M             = 0x0A,
      LSM303_REGISTER_MAG_IRB_REG_M             = 0x0B,
      LSM303_REGISTER_MAG_IRC_REG_M             = 0x0C,
      LSM303_REGISTER_MAG_TEMP_OUT_H_M          = 0x31,
      LSM303_REGISTER_MAG_TEMP_OUT_L_M          = 0x32
    } lsm303MagRegisters_t;

    typedef enum
    {
      LSM303_MAGGAIN_1_3                        = 0x20,  // +/- 1.3
      LSM303_MAGGAIN_1_9                        = 0x40,  // +/- 1.9
      LSM303_MAGGAIN_2_5                        = 0x60,  // +/- 2.5
      LSM303_MAGGAIN_4_0                        = 0x80,  // +/- 4.0
      LSM303_MAGGAIN_4_7                        = 0xA0,  // +/- 4.7
      LSM303_MAGGAIN_5_6                        = 0xC0,  // +/- 5.6
      LSM303_MAGGAIN_8_1                        = 0xE0   // +/- 8.1
    } lsm303MagGain; 

        typedef struct lsm303MagData_s
    {
      int32_t x;
      int32_t y;
      int32_t z;
     int32_t orientation;
    } lsm303MagData;

    bool begin(void);
    void read(void);
    void setMagGain(lsm303MagGain gain);

    lsm303MagData magData;        // Last read magnetometer data will be available here

    void write8(byte address, byte reg, byte value);
    byte read8(byte address, byte reg);

  private:
};

#endif

 

Tiny_LSM303_mag.ccp

/***************************************************************************
  This is a library for the LSM303 Accelerometer and magnentometer/compass

  Designed specifically to work with the Adafruit LSM303DLHC Breakout

  These displays use I2C to communicate, 2 pins are required to interface.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
***************************************************************************/
#include <TinyLSM303_mag.h>

/***************************************************************************
CONSTRUCTOR
***************************************************************************/
bool TinyLSM303_mag::begin()
{
  TinyWireM.begin();

  // Enable the magnetometer
  write8(LSM303_ADDRESS_MAG, LSM303_REGISTER_MAG_MR_REG_M, 0x00);

  return true;
}

/***************************************************************************
PUBLIC FUNCTIONS
***************************************************************************/
void TinyLSM303_mag::read()
{
  // Read the magnetometer
  TinyWireM.beginTransmission((byte)LSM303_ADDRESS_MAG);
  TinyWireM.write(LSM303_REGISTER_MAG_OUT_X_H_M);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom((byte)LSM303_ADDRESS_MAG, (byte)6);

  // Wait around until enough data is available
  while (TinyWireM.available() < 6);

  // Note high before low (different than accel)
  uint8_t xhi = TinyWireM.read();
  uint8_t xlo = TinyWireM.read();
  uint8_t zhi = TinyWireM.read();
  uint8_t zlo = TinyWireM.read();
  uint8_t yhi = TinyWireM.read();
  uint8_t ylo = TinyWireM.read();

  // Shift values to create properly formed integer (low byte first)
  magData.x = (xlo | (xhi << 8));
  magData.y = (ylo | (yhi << 8));
  magData.z = (zlo | (zhi << 8));

  // ToDo: Calculate orientation
  magData.orientation = 0.0;
}

void TinyLSM303_mag::setMagGain(lsm303MagGain gain)
{
  write8(LSM303_ADDRESS_MAG, LSM303_REGISTER_MAG_CRB_REG_M, (byte)gain);
}

/***************************************************************************
PRIVATE FUNCTIONS
***************************************************************************/
void TinyLSM303_mag::write8(byte address, byte reg, byte value)
{
  TinyWireM.beginTransmission(address);
  TinyWireM.write(reg);
  TinyWireM.write(value);
  TinyWireM.endTransmission();
}

byte TinyLSM303_mag::read8(byte address, byte reg)
{
  byte value;

  TinyWireM.beginTransmission(address);
  TinyWireM.write(reg);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(address, (byte)1);
  value = TinyWireM.read();
  TinyWireM.endTransmission();

  return value;
}

 

So, I reloaded that test code onto my Gemma and did some tests. I put everything neatly onto a breadboard with the NeoPixel ring directly above the magnetometer which was directly above a key chain compass. I drew a line with the x and y axis printed on the magnetometer onto a piece of cardboard, and taped my breadboard to it. Then I taped out a clock onto my work surface and set a ruler to point north according to my compass. I then recorded the position of the dot of light when I turned the cardboard to point to each hour. This is what I got:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

Not very reliable. But the results were repeatable, so something had to be off. So I went back to the learning site at Learn.Adafruit.com to see what I could learn. I already knew that my accuracy was going to be compromised because I had deactivated the accelerometer and couldn't compensate for tilt. I found a calibration sketch with the original library, so I loaded it onto my Flora, hooked up the accelerometer, and plugged it into the computer to read the serial data.

Here is the code I used:

 

LSM_303 test

#include <Wire.h>
#include <Adafruit_LSM303.h>

Adafruit_LSM303 lsm;

void setup()
{
  Serial.begin(9600);

  // Try to initialise and warn if we couldn't detect the chip
  if (!lsm.begin())
  {
    Serial.println("Oops ... unable to initialize the LSM303. Check your wiring!");
    while (1);
  }
}

void loop()
{
  lsm.read();
  Serial.print("Accel X: "); Serial.print((int)lsm.accelData.x); Serial.print(" ");
  Serial.print("Y: "); Serial.print((int)lsm.accelData.y);       Serial.print(" ");
  Serial.print("Z: "); Serial.println((int)lsm.accelData.z);     Serial.print(" ");
  Serial.print("Mag X: "); Serial.print((int)lsm.magData.x);     Serial.print(" ");
  Serial.print("Y: "); Serial.print((int)lsm.magData.y);         Serial.print(" ");
  Serial.print("Z: "); Serial.println((int)lsm.magData.z);       Serial.print(" ");
  delay(1000);
}

 

Then I took my taped down cardboard setup and recorded the max and min values for x, y, and z in four directions. The numbers were a bit jumpy, so I let them settle well before noting the min and max. I did this process three times. Then I took the average of the min and max for each time, and then the average of those three numbers to establish the average number for each direction.

image

Now I had established the range for x and y. Z seemed to be superfluous. I found in a bit of code from one of last years contestants that these numbers could be used in the by mapping them to the total sensor range.

 

int16_t x = map(lsm.magData.x, -202, 224, -1000, 1000);
int16_t y = map(lsm.magData.y, -397, -3, -1000, 1000);

 

I made these changes to my code, but had to comment out my "tracer" pixels because there wasn't enough room. Getting a functioning compass was priority one. I did the same controlled setup with much better results.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

The dot of light stays mainly between 9 and 11, so I am calling this a success for the moment. Next, I needed to see if it were possible at all to light up the rest of my planned rig. 16 pixels around the brim, a ring of 16 at the cockade, and two pixels in the feather for a total of 34 pixels. Since space on the Gemma is so tight I am going to have to get really clever. In my original code, there is a colorwipe function that blacks out the previous pixel lit so only the current sensor reading is displayed. I discovered that I could use this bit to light other pixels in my rig if I used a color instead of 0,0,0. I set the last two pixel colors manually so that my feather could be a different color from the rest of the hat. I tried to compile this with the Arduino IDE and it compiled at 5,312 bytes out of an allowable 5,310. Two bytes to big! I then loaded this same code onto codebender and tried to compile it. It compiled it at 5, 300. Ten bytes to spare! I think I am going to continue to develop my code on codebender because it seems to have a better compiler for my current needs.

Here is the revised code. Available from codebender at https://codebender.cc/sketch:52246

 

Compass_Gemma_ring_pointer_V2b

//using calibration data
//trying to light up entire unit
//using code from  https://learn.adafruit.com/steam-punk-goggles?view=all
//idea for pointer pixel from http://www.element14.com/community/groups/arduino/blog/2013/08/27/get-closer-challenge--navigation-glove
//idea for library modification from http://www.slickstreamer.info/2014/02/led-bracelet-accelerometer-3dprinting.html
#include <TinyWireM.h>
#include "TinyLSM303_mag.h"
#include <Adafruit_NeoPixel.h>

#define pin 1
#define numPixels 34

Adafruit_NeoPixel ring = Adafruit_NeoPixel(numPixels, pin, NEO_GRB + NEO_KHZ800);

TinyLSM303_mag lsm;

// Pi for calculations - not the raspberry type
const float Pi = 3.14159;

void setup()
{
  ring.begin();
  ring.show();// Initialize all pixels to 'off'
  lsm.begin();// Initialize the sensors
}

void loop()
{
  // Read the magnetometer and determine the compass heading:
  lsm.read();
  int16_t x = map(lsm.magData.x, -202, 224, -1000, 1000);
  int16_t y = map(lsm.magData.y, -397, -3, -1000, 1000);

  // Calculate the angle of the vector y,x from magnetic North
  float heading = (atan2(y,x) * 180) / Pi;

  // Normalize to 0-360 for a compass heading
  if (heading < 0)
  {
    heading = 360 + heading;
  }

  uint16_t d = map(heading, 0, 360, 0, 15);//map the heading to the size of the ring around the brim

  //wipe all the pixels with a color
  for(uint8_t i = 0; i < 32; i++)
  {
    ring.setPixelColor(i,ring.Color(0, 20, 50));
   }

  ring.setPixelColor(d, ring.Color(255, 0, 0)); //set the closest pixel in the ring to the heading
  ring.setPixelColor(32,ring.Color(0, 100, 255));
  ring.setPixelColor(33,ring.Color(0, 255, 100));
  ring.show();
}

 

So now that I had a code running a more accurate compass and able to address all the pixels in my rig, I decided to try and breadboard my entire circuit. I had a strip of 8 sewable pixels that I had soldered together previously. I connected these to a bar strip of 8 pixels. These were then connected to the pixels in my feather. I plugged everything in and it all lit up for just a moment and then crashed. My Gemma was unresponsive. I unplugged everything, reloaded the code onto the Gemma and tried it out with just the ring of 16 to make sure that it worked. I realized that I needed to power my pixels with a separate power source so it doesn't over draw current from the Gemma pin. I hooked up everything back to the breadboard, this time only powering the magnetometer from the Gemma, the pixels were powered from a separate battery of the same size. Plugged everything in and it went super bright and weirdly jumpy in the color. I got scared and pulled the plug. Gemma was unresponsive again. Reloaded Gemma and went back to 16 to check if it was working, thankfully it was. So I think I am on the right path, I just need to do some more research into separate power sources. Once I get it stabilized I will continue to refine the code as much as I can.

 

Coming up next...

Back to my comfort zone, I am going to play with some velvet.

  • Sign in to reply

Top Comments

  • DAB
    DAB over 10 years ago +1
    A nice entertaining post. DAB
  • DAB
    DAB over 10 years ago

    A nice entertaining post.

     

    DAB

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube