0

Using Flora Wearable Bluefruit LE Module to take compass and
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Using Flora Wearable Bluefruit LE Module to take compass and

by hugolennon on Mon Feb 11, 2019 5:31 pm

Hi there,

I am looking for some help on a project I am working on for university. Basically I am trying to develop a device which will be able to give you guidance, like the style of a compass, to a predetermined GPS location. I know similar things have been done on the learn section but it hasn't been done before with the modules I have. The module I am using are the following:

1x FLORA Wearable Bluefruit LE Module
1x FLORA - Wearable electronic platform: Arduino-compatible - v3
1x NeoPixel Ring - 12 x 5050 RGB LED with Integrated Drivers
1x Vibrating Mini Motor Disc
1x Adafruit DRV2605L Haptic Motor Controller

(note: I have all of the required libraries for the various components downloaded)

Right now what I have working is the following. I have got the Bluefruit LE module wired up and working with the 'Controller' example code that the library for this module has. When I connect my phone to it I can send the data I want, i.e. GPS/Magnetometer/Location/Gyro etc... and this appears in the Serial Monitor. What I don't know how to do is the next step. Making the Bluefruit LE module communicate with the NeoPixel Ring to act like a compass to guide the user to the programmed destination and when reaching the destination have the NeoPixel Ring flash and make the vibration motor vibrate (so the person knows there in the right place).

Any help with this would be amazingly appreciated!

Thanks in advance!!

hugolennon
 
Posts: 2
Joined: Wed Feb 06, 2019 8:22 pm

Re: Using Flora Wearable Bluefruit LE Module to take compass

by adafruit_support_mike on Tue Feb 12, 2019 4:02 am

The code you need will break down into functional blocks that are largely independent of each other, and are defined in terms of a 'what' and a 'how'.

One block would be 'get my current GPS coordinates', for instance. Other blocks will need to know what it does (provide a GPS coordinate), but nothing outside that block needs to know or care how that happens.. the block could get a reading from a GPS sensor, it could pull data from a BLE message generated by a phone, it could ask the user to look the value up online and punch it in with a keyboard, or it could make up values that kind of look right. As long as another block can say, "give me a GPS coordinate" and get a GPS coordinate as a reply, it will be happy.

The 'give me a GPS coordinate' part is called the block's 'interface', and the work of actually getting the coordinate is called the block's 'implementation'. The programming style called 'design to the interface' keeps the two parts mostly independent of each other.

Using a design-to-interface mindset, you can look at how the Glitter Positioning System tutorial generates control signals for a NeoPixel ring:

https://learn.adafruit.com/glitter-posi ... m/overview

You only have to ask, "what values does that piece of code need to do its job?" and can temporarily ignore the question, "how are those values generated?" Once you understand the NeoPixel control block in terms of, "these input values make those pixels light up with these colors," you can put that block aside. Then you can say, "I need a block that produces this value" and focus on how to generate that value.

If you approach the project in those terms, you'll find blocks that you can lift more or less as-is from the projects in the Learning System.

adafruit_support_mike
 
Posts: 55848
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using Flora Wearable Bluefruit LE Module to take compass

by hugolennon on Wed Feb 13, 2019 8:07 am

Thanks for your reply!

I sort of understand what you mean by breaking it all up not sections. Is there any further guidance you might be able to give me with regards to actually writing the code for the specific blocks you mentioned?

hugolennon
 
Posts: 2
Joined: Wed Feb 06, 2019 8:22 pm

Re: Using Flora Wearable Bluefruit LE Module to take compass

by adafruit_support_mike on Fri Feb 15, 2019 6:00 am

There are a couple of related concepts called 'programming in the large' and 'self-commenting code' that help organize code.

Your lowest-level code will involve specific details about how to turn information into patterns of bits.. whether you're sending messages across an I2C bus, controlling NeoPixels, or just doing math. You need those details to do those jobs, but repeating the details everywhere creates a lot of unnecessary work and confusion. To reduce work and make things easier to understand, you wrap those low-level operations in functions whose names describe what's being done, and variables whose names describe what the information is for.

I just posted this piece of code in another thread, for instance. It demonstrates meaningful names:

Code: Select all | TOGGLE FULL SIZE
uint32_t next_on = 0;
uint32_t time_on = 200;

uint32_t next_off = 0xffffffff;
uint32_t time_off = 300;

void loop () {
    uint32_t now = millis();
   
    if ( now > next_on ) {
        next_on = 0xffffffff;
        next_off = now + time_on;
        //  turn the LED on
    }
    if ( now > next_off ) {
        next_off = 0xffffffff;
        next_on = now + time_off
        // turn the LED off
    }
}
I can make the code in loop() more descriptive by adding some functions:

Code: Select all | TOGGLE FULL SIZE
void loop () {
    uint32_t now = millis();
   
    if ( time_to_turn_on( now ) ) {
        adjust_on_times();
        turn_LED_on();
    }
    if ( time_to_turn_off( now ) ) {
        adjust_off_times();
        turn_LED_off();
    }
}


/*
 *  only this group of functions know anything about the variables that
 *  control timing:
 */

uint32_t next_on = 0;
uint32_t time_on = 200;

uint32_t next_off = 0xffffffff;
uint32_t time_off = 300;

bool time_to_turn_on ( uint32_t now ) {
    return( ( now > next_on ) ? 1 : 0 );
}

void adjust_on_times () {
    next_on = 0xffffffff;
    next_off = now + time_on;
}

bool time_to_turn_off ( uint32_t now ) {
    return( ( now > next_off ) ? 1 : 0 );
}

void adjust_on_times () {
    next_off = 0xffffffff;
    next_on = now + time_off;
}

/*
 *  only the functions below know anything about the LED:
 */

void turn_LED_on () {
    digitalWrite( LED_pin, HIGH );
}

void turn_LED_off () {
    digitalWrite( LED_pin, LOW );
}
At which point the code in loop() is close to a human-language description of what the code does.

That's the general idea of writing self-commenting code and programming in the large: as you create functions and give variables names, you build a language that describes the problem your'e trying to solve, and the way you've chosen to solve it. Ideally, your highest level code should look like the notes you wrote for yourself while deciding how to write the program.

In practice, it's an evolutionary process. You start with an idea for how to do some piece of the overall job, usually without knowing all the details you'll need to make it happen. As you write, you'll run into a series of, "okay, I need this information" moments, and you can stub in something that's good enough to get to the end of the idea you wanted to put into code. The result will be ugly, but it will be something you can compile and test functionally.

Once you get that piece working, you can go back and tidy things up.. create functions for sub-tasks that add a bunch of distracting detail, think of better names for functions that are already there, add variables with names that make sense in terms of the rest of what you've written, and so on.

Almost every function can be started with the following skeleton:

Code: Select all | TOGGLE FULL SIZE
do_something_to ( these, nouns ) {
    make sure ( these, nouns ) are valid
   
    if ( there's a problem ) {
        fix it or generate an error
    }
    get ( additional, information )
   
    use ( these, nouns ) and ( additional, information ) to do something
}
More generally, and at every level, every piece of code does three things:

- get input from somewhere
- do something to it
- send the result somewhere

That may seem simplistic, but it's the essential building block of code structure. One of the easiest ways to tell good code from bad code is to try and break it down along those lines. In good code, you can find all three pieces easily. In bad code, they're jumbled together and complicated to the point where you can't tell them apart.

Two major sub-headings of 'do something to it' involve storing information so you can use it later, and rearranging information you have into a form that's more convenient to work with. A program's storage system is called a 'repository' or 'model', and it's useful to think of that piece being its own unit. Other blocks of code will send information to the repository or pull information from it, but having groups of functions to handle the details of all your 'save this for later', 'get this saved value', and 'give me a version most useful to me' operations make the rest of the code much easier to write and modify.

Better yet, repository and representation-translation functions are usually easy to write, and make up a large part of most programs.

As another practical point, it's usually easiest to start developing a program at the parts that create output. For your project, that would be the haptic buzzers and the NeoPixel ring.

Those parts are the easiest to test because you can see their output. Once you have the hardware working in general, you can develop code to produce the specific motor and pixel output you want in various situations. Any input values you need can be hard-coded, or better yet, wrapped in 'get this saved value' functions that look like so:

Code: Select all | TOGGLE FULL SIZE
float distance = 110.7;

float get_distance_to_waypoint () {
    float result = distance;
    distance /= 2.0;
    return( distance );
}
You can hard-code and simulate any set of values you want while you test the output code, then worry about changing get_distance_to_waypoint() so it uses values generated from the GPS data.

Along the way, you'll do two things: you'll create output-generating functions that are entirely driven by the data they get as parameters or from repository functions, and you'll learn exactly what set of values you need to generate the kind of output you want. Then you can step back to the problem of generating those values.

As you do, your code will evolve closer and closer to values you get from the input devices, Better yet, you'll have a well-tested suite of code that creates output based on the values you get from the input devices.

You can always write a program that produces every kind of output you want using nothing but hardcoded or artifically generated input values. You can always write code that reads data from input devices and stores it somewhere. Both categories of code can be elaborate, but they can always be broken down into sensible pieces. Programming in the large and self-commenting code strongly encourage you to write code in chunks that do make sense.

By the time you have all the output generation and input gathering code written and tested, the remaining job is to connect the two. That part is almost entirely 'turn this kind of value into that kind of value' code, each piece of which is fairly straighforward.. you know where it comes from, you know where it goes to, and you just have to process one to get the other.

adafruit_support_mike
 
Posts: 55848
Joined: Thu Feb 11, 2010 2:51 pm

Please be positive and constructive with your questions and comments.