Related posts:
Introduction to Bluetooth Low Energy Light Development Kit - CC2540TDK-LIGHT
Android Support for TI's BLE LED Control
MSP430FR4133 Launchpad - Magnetic North Visualisation
Illuminated Tricopter Upgrade - Adding an Addressable LED Ring
MSP430FR4133 BoosterPack for Illuminated Tricopter
Bookshelf Lights Using TI's CC2450 BLE LED Modules
MSP430FR4133 Launchpad with Custom BoosterPack in Action
MSP430FR4133 Launchpad with Custom BoosterPack in Action - appendix
MSP430FR4133 Launchpad with Custom BoosterPack update
Adding Remotely Controlled Headlights to Tricopter
Development Tools
In my previous post I mentioned that some progress with MSP430FR4133 Launchpad was made, specifically, I managed to read out the magnetometer sensor output, calculate the heading value and display it on the built-in LCD. It wasn't completely straight-forward to do this but after a little bit of research I managed to complete this part of my project.
Initial plan was to use Code Composer Studio (CCS) and DriverLib on a Linux Virtual Machine (I'm running it on a OSX host) so I prepared the virtual machine and connected the Launchpad. To be able to connect the Launchpad I had to set up a USB filter in Virtual Box (to pass through the USB device to guest OS) and I think this is where I made a mistake. I only made a specific filter for Launchpad as it was presented on first plug-in. I created a minimal project and hit debug. CSS reported that I need to update the ezFET on my Launchpad so I chose "Upgrade" I suggest not to run this upgrade from a virtual machine!
I'm not sure, but it looks like that during the upgrade procedure VID:PID values were also changed (at least temporarily) and my USB filter was valid only through the first part of upgrade - so it failed. Launchpad become inaccessible from CCS so I was unable to continue using it with CCS. However, there was a workaround that I used for some time until I reached the final solution, I used CSS to build the hex file and then I used mspdebug with tilib driver to flash it (tilib still worked). This also enabled debug but it's much more inconvenient than using CSS for debug.
element14 hosts the TI Launchpads section of site (I discovered that recently) with a lot of useful info including example projects. For this LaunchPad Development Kit for MSP430FR4133 Mixed-Signal Microcontroller, this is where I found the OutOfTheBox (msp-exp430fr4133 software examples (.zip) ) example that shows how to use the built-in LCD. I tried to modify this example and use the I2C helpers from DriverLib but I finally gave up and switched to Energia as it's much faster to prototype. Using DriverLib wasn't so trivial since I'm not really proficient with MSP430, I was unable to find the correct order of calls for I2C master read to work.
If anyone knows how to read this magnetometer using DriverLib, please do share that, I would really like to see how it should be done properly.
My Launchpad was finally upgraded with success when I found out that there's a beta version of CCS for OSX. When I tried to debug a demo project with this CCS version I was asked to upgrade ezFET again and this time the upgrade process finished properly since there was no VM in the way. CCS was now able to flash the MCU and it was even stopping at breakpoints as it should but I switched to Energia anyway...
Energia sketch
For LCD control I used the LCD_Launchpad library and for WS2812b LED strip I found the WS2811Driver library very useful.
WS2811Driver didn't work out of the box because Energia boards.txt was set to use 8MHz for this Launchpad. I changed F_CPU setting to 16MHz and that was it - LED strip became fully functional. Seems like this broke the Serial interface but I can live with that - especially since I have a working LCD for debug output.
Internally, WS2811Driver library uses assembly code to manage the strict timing required by WS2812Bs and it relies on the fact that single clock cycle takes 62.5ns to execute.
If you are interested in exact timings please check the WS2812B datasheet (http://www.seeedstudio.com/document/pdf/WS2812B%20Datasheet.pdf)
I didn't use external library for the HMC5883L magnetometer, it was simple enough to just use Wire object.
My calculations for heading value are good as long as magnetometer is positioned horizontally, I'm OK with that.
So, my Energia sketch looks like this at the moment.
#include <ws2811.h> #include <WS2811Driver.h> #include "Wire.h" #include <stdarg.h> #include "LCD_Launchpad.h" // Variables LCD_LAUNCHPAD myLCD; WS2811Driver ledStrip; uint8_t leds[180] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; uint16_t unHeading; bool bBatteryLow = false; bool bLeft = false; void SetupMagnetometer() { Wire.beginTransmission(0x1E); Wire.write(0x00); Wire.write(0x70); Wire.endTransmission(); Wire.beginTransmission(0x1E); Wire.write(0x01); Wire.write(0xA0); Wire.endTransmission(); Wire.beginTransmission(0x1E); Wire.write(0x02); Wire.write(0x00); Wire.endTransmission(); } void ReadMagnetometer() { // Request 6 bytes Wire.requestFrom(0x1E, 6); int value = 0; bool secondByte = false; int currentAxis = 0; float x, y, z; // Read 6 bytes while(Wire.available()) { char c = Wire.read(); if(!secondByte) { value = int(c) << 8; secondByte = true; } else { secondByte = false; value |= c; if(currentAxis == 0) { Serial.print("x: "); x = value; Serial.print(x); } else if(currentAxis == 1) { Serial.print(" y: "); y = value; Serial.print(y); } else { Serial.print( "z: "); z = value; Serial.print(z); } // Serial.print(value); Serial.println(" "); currentAxis++; } } Wire.endTransmission(); float heading = 0.0; // Calculate heading from magnetic field vector // This could be more compact but I found it easier to read // when 4 quadrants are processed separately if(x > 0 && z > 0) { heading = atan(z / x) * 180.0 / 3.141529; } else if (x < 0 && z > 0) { heading = 180 + atan(z / x) * 180.0 / 3.141529; } else if (x < 0 && z < 0) { heading = 180 + atan(z / x) * 180.0 / 3.141529; } else if(x > 0 && z < 0) { heading = 360 + atan(z / x) * 180.0 / 3.141529; } else if(x == 0) { if(z > 0) { heading = 90.0; } else { heading = 270.0; } } else if(z == 0 && x < 0) { heading = 180.0; } unHeading = heading; // Prepare for next sensor read cycle Wire.beginTransmission(0x1E); Wire.write(0x03); Wire.endTransmission(); } void UpdateStrip() { // Show magmetometer reading int led = unHeading / 6; leds[led * 3] = (char)0xFF; leds[led * 3 + 1] = (char)0xFF; leds[led * 3 + 2] = (char)0xFF; // Battery low if(bBatteryLow) { int i = 0; for(; i < sizeof(leds) / 3; i++) { leds[i * 3 + 1] = 0x33; } } // Controls if(bLeft) { int i = 0; int nOffset = 25; for(; i < sizeof(leds) / 36; i++) { leds[(i + nOffset) * 3] = 30 / abs(i - 2.5); } } // Temporarily disable interrupts to make sure writing to LED strip is not interrupted noInterrupts(); ledStrip.write(leds, sizeof(leds)); interrupts(); memset(leds, 0, sizeof(leds)); } void UpdateLCD() { myLCD.clear(); myLCD.println(unHeading); } void CheckBattery() { if((unHeading < 60 && unHeading > 20) || (unHeading > 80 && unHeading < 200)) { bBatteryLow = true; } else { bBatteryLow = false; } } void CheckCommands() { if(unHeading > 80 && unHeading < 200) { bLeft = true; } else { bLeft = false; } } void setup() { Serial.begin(9600); Wire.begin(); SetupMagnetometer(); myLCD.init(); ledStrip.setLEDCount(60); ledStrip.begin(); } void loop() { ReadMagnetometer(); CheckBattery(); CheckCommands(); UpdateStrip(); UpdateLCD(); delay(10); }
If you check the code you will notice that I plan to add more features so I'm turning some LEDs on depending on the heading value as a test but later I will use the real inputs like:
- Low battery warning (all WS2812B modules turn the red LED on)
- Transmitter stick positions (green LED turn one on the corresponding tricopter side when pitch and roll sticks are moved away from middle position)
Demonstration
And finally, here's the fun part - video demonstration of my LED compass:
Thanks for going through my project log!
Dragan
Top Comments