This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2540 iBeacon

Other Parts Discussed in Thread: CC2540, CC2541

Hi. 

I have been looking for a CC2540 iBeacon example, and not found one so ended up creating one. It works with apples AirLocate and simple sends the correct advert. 

http://embeddedc.co.uk/cc2540-ibeacon/

Has anyone seen/know of any further details at this stage? 

James

  • Last week I was trying to decipher Apple's protocol. I did not get it working yet, but I only spent a couple of hours on this. Do you have any more information about the advertising data? All I figured was that they use a manufacturer-specific payload in the advert data, with company identifier 0xFC00 (Apple's ID), followed by a length byte, and then the 128bit UUID, two 16 bit major and minor numbers, and then the byte 0xC5 (don't know what it is). I replicated this on a CC2541 chip but my iPhone app was not getting any callbacks. There are multiple points of failure here, which I did not investigate much, but am quite interested in the topic.

  • Hi 

    The 0xC5 is a  measured power value, i think is you are broadcasting a 4dbm then this should be 0x04 etc. 0xc5 is -59 the example used in AirLocate. 

    Without seeing all of your raw advert bytes its hard to say why its not working. Heres some free code, see if this helps you:

    static uint8 advertData[] =
    {

    0x02, // length of this data
    GAP_ADTYPE_FLAGS,
    GAP_ADTYPE_FLAGS_GENERAL|GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,


    0x1A, // length of this data (26 Bytes )
    GAP_ADTYPE_MANUFACTURER_SPECIFIC,
    /*Apple Pre-Amble*/
    0x4C,
    0x00,
    0x02,
    0x15,

    /*Device UUID (16 Bytes)*/
    0x74, 0x27, 0x8B, 0xDA, 0xB6, 0x44, 0x45,0x20, 0x8f, 0x0c, 0x72, 0x0e, 0xaf, 0x05, 0x99, 0x35,

    /*Major Value (2 Bytes)*/
    0x00, 0x01,

    /*Minor Value (2 Bytes)*/
    0x01,0x02,

    /*Measured Power*/
    0x07
    };

  • Ah, of course my code wasn't working because I had a typo in there! :D

    Somehow I typed 0xFC00 instead of 0x4C00 for apple's company identifier. I'll try it again, I'm pretty sure it'll work now.

  • Hi there. 

    Just wondering if you plan to release the sample project code here? Want to try the project with a few beacons so I need the ability to change the UUID.

    Thanks.

  • Can you please provide instructions on how this code can be used to create a bin file for CC2541 ?

    Thanks a lot.

  • You can just open SimpleBLEPeripheral or SimpleBLEBroadcaster project, navigate to the SimpleBLEPeripheral.c (or SimpleBLEBroadcaster.c) file, and change the advertisement data there. All you need to do then is compile and load it.

  • I wish i did some more research before trying to develop for this SoC that requires a $4K compiler in order to flash 5-6 custom variables to the chip :(

    what a waste of time !

  • Thanks Prashant,

    Want to ask, did you have any luck with getting the ranging function to work with iBeacon? I can now get the UUID to work, but the ranging isn't working at the moment and the device is either in range or out of range.

    Edward

  • Unfortunately I did not try ranging, so I can't comment. I played around with the AirLocate sample app for a few minutes and calibration worked, but I don't remember if the actual ranging worked or not. My device had a 1 sec advertising time and I did notice even in/out notifications took as much as 15 sec to trigger. Apple itself broadcasts the avert packet every 30 ms (!).

  • I just tried to set the value of DEFAULT_ADVERTISING_INTERVAL to 80, but the beacon somehow stopped working.

  • Hi 

    From memory the minimum interval is 160 but it would be poor form from the stack if setting an out of range value stopped it. 

    In terms of ranging, i have this working to some extent (accurate to 1 meter-ish) but this was all done by software on the iPhone. 

  • James,

    I am trying test with the board that came with the developers kit. I compiled and flashed the board with the code above. I can get the Monitoring function working with Airlocate, but when I try using it with the Ranging feature in Airlocate, the beacon info will just flash every second or so, but it won't stay on the list. For the Calibrate function, the beacon won't even show up.

    Which hardware are you using? Dev Kit? Or vtag?

    Edward 

  • Hi Edward 

    I have both hardware working identically. 

    I needed to modify the airlocate app. Basically there is a callback that is called every second (so the code comment says) that is called way more and reloads the tables on the range view and the calibration view. 

    However this is been called and table reloaded even with empty arrays. All I did is stopped the table reload when the array is empty. This gives you time to do the calibration and then see the ranging working. 

    sorry can't be any more detailed not got the code with me. 

  • In the original airlocate code it reloads the table everytime (with or without data). Hence the flashing... Change the airlocate code like this for instance

     if ([_beacons count]) {

            // beacon data present

            //NSLog(@"_beacons  %@",_beacons);

            [self.tableView reloadData];

        }

  • Thanks all. It is all working now. I had to fiddle with the Measured Power value to get the final calibration working. But it is now working smoothly.

    A bit off topic, but can anyone suggest how I can find someone to help me to write a more complete firmware for this chip/board?

    Basically, it has been a nice science project for me so far to compile a simple firmware to just broadcast the advertisement. But ideally, I want to get a fully working firmware so people can pair with the device with an Admin App to update the iBeacon Ad message from the app and also do the auto calibration, etc.

    Any suggestion? What is a reasonable price for some work like this?

  • Embedded technologies www.embeddedc.co.uk can help you there. That's exactly what we do. 

  • Thank you for very informative posts. 

    I keep getting rssi 0 when I do NsLog ... is the cc2540 failing to send rssi data?

    "CLBeacon (uuid:<__NSConcreteUUID 0x176963b0> 74278BDA-B644-4520-8F0C-720EAF059935, major:1, minor:257, proximity:0 +/- -1.00m, rssi:0)",

     It is the same for several beacons. I am not sure why, I guess this is the reason why calibration crashes when I click on one of the beacons

    'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'

  • I have no answer for the "rssi 0"problem.

    But the crash can be prevented by checking the array for content. For instance..

    If ([arrayWhatsItsName count]>0]) {

      // do something

    else {

      // do nothing 

    }

  • I tried in the ALCalibrationBegin ... 

    added this so the beacons stick ... instead of just flashing for a moment without being able to clicked on

        if ([_beacons count]) {

            NSLog(@"_beacons  %@",_beacons);

            [self.tableView reloadData];    }

       
    Then added

        if([_beacons count]){ 

    before

        NSNumber *sectionKey = [[_beacons allKeys] objectAtIndex:indexPath.section];

    since it was causing the crash... but now there is no calibration. Just nothing happens when I click on the beacon in the Calibration view.

    I think this is somehow related to RSSI .... I am using the .hex code provided but thread starter and tried some of my own compilations with no luck yet.

     

     

  • Hi Andrey 

    There is a new version of the firmware available here: 

    http://embeddedc.co.uk/buy-licence/ its provided as a free demo with a tool provided to change the parameters.

    with regards to the RSSI it has to be down to the phone in this case 

    1. The beacon is non-connectable its just sending undirected broadcasts.

    2. RSSI is the received signal strength which again suggests its the phone. 

    There are a few things around the whole iBeacon framework which seem a bit patchy at the moment but im sure once apple release everything they will sort out the framework also.

    Hope this helps.

  • Sorry, i was thinking of an other project. The RSS calibration should work (we're talking about airlocate right?)

    You should modify ALCalibrationBeginViewController.m like so. Search for the //fk comments 

    #import "ALCalibrationBeginViewController.h"

    #import "ALCalibrationEndViewController.h"

    #import "ALCalibrationCalculator.h"

    #import "ALDefaults.h"

    @interface ALCalibrationBeginViewController()

    - (void)startRangingAllRegions;

    - (void)stopRangingAllRegions;

    @end

    @implementation ALCalibrationBeginViewController

    {

        NSMutableDictionary *_beacons;

        NSMutableDictionary *_tmpBeacons;   //fk modified

        CLLocationManager *_locationManager;

        NSMutableArray *_rangedRegions;

        

        UIProgressView *_progressBar;

        BOOL _inProgress;

        

        ALCalibrationCalculator *_calculator;

        ALCalibrationEndViewController *_endViewController;

    }

    - (id)initWithStyle:(UITableViewStyle)style

    {

    self = [super initWithStyle:style];

    if(self)

    {

            _beacons = [[NSMutableDictionary alloc] init];

            

            // This location manager will be used to display beacons available for calibration.

            _locationManager = [[CLLocationManager alloc] init];

            _locationManager.delegate = self;

            _inProgress = NO;

    }

    return self;

    }

    - (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region

    {    

        // CoreLocation will call this delegate method at 1 Hz with updated range information.

        // Beacons will be categorized and displayed by proximity.

        [_beacons removeAllObjects];

        NSArray *unknownBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"proximity = %d", CLProximityUnknown]];

        if([unknownBeacons count])

            [_beacons setObject:unknownBeacons forKey:[NSNumber numberWithInt:CLProximityUnknown]];

        

        NSArray *immediateBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"proximity = %d", CLProximityImmediate]];

        if([immediateBeacons count])

            [_beacons setObject:immediateBeacons forKey:[NSNumber numberWithInt:CLProximityImmediate]];

        

        NSArray *nearBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"proximity = %d", CLProximityNear]];

        if([nearBeacons count])

            [_beacons setObject:nearBeacons forKey:[NSNumber numberWithInt:CLProximityNear]];

        

        NSArray *farBeacons = [beacons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"proximity = %d", CLProximityFar]];

        if([farBeacons count])

            [_beacons setObject:farBeacons forKey:[NSNumber numberWithInt:CLProximityFar]];

        

        if ([_beacons count]) {

            // beacon gevuld, fk modified

            NSLog(@"_beacons  %@",_beacons);

            _tmpBeacons=[_beacons mutableCopy];

            [self.tableView reloadData];

        }

        //[self.tableView reloadData];

    }

    - (void)viewDidAppear:(BOOL)animated

    {

        // Start ranging to show the beacons available for calibration.

        [self startRangingAllRegions];

    }

    - (void)viewDidDisappear:(BOOL)animated

    {

        // Cancel calibration (if it was started) and stop ranging when the view goes away.

        [_calculator cancelCalibration];

        [self stopRangingAllRegions];

    }

    - (void)viewDidLoad

    {

        [super viewDidLoad];

        

        self.title = @"Calibration";

        

        _progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];

        _progressBar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

        

        // Populate the regions for the beacons we're interested in calibrating.

        _rangedRegions = [NSMutableArray array];

        [[ALDefaults sharedDefaults].supportedProximityUUIDs enumerateObjectsUsingBlock:^(id uuidObj, NSUInteger uuidIdx, BOOL *uuidStop) {

            NSUUID *uuid = (NSUUID *)uuidObj;

            CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:[uuid UUIDString]];

            [_rangedRegions addObject:region];

        }];

        

        _endViewController = [[ALCalibrationEndViewController alloc] init];

    }

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    {

        // A special indicator appears if calibration is in progress.

        // This is handled throughout the table view controller delegate methods.

        NSInteger i = _inProgress ? _beacons.count + 1 : _beacons.count;

        

        return i;

    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    {

        NSInteger i = section;

        if(_inProgress)

        {

            if(i == 0)

            {

                return 1;

            }

            else

            {

                i--;

            }

        }

        

        NSArray *sectionValues = [_beacons allValues];

        return [[sectionValues objectAtIndex:i] count];

    }

    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

    {

        NSInteger i = section;

        if(_inProgress)

        {

            if(i == 0)

            {

                return nil;

            }

            else

            {

                i--;

            }

        }

        

        NSString *title = nil;

        NSArray *sectionKeys = [_beacons allKeys];

        

        NSNumber *sectionKey = [sectionKeys objectAtIndex:i];

        switch([sectionKey integerValue])

        {

            case CLProximityImmediate:

                title = @"Immediate";

                break;

                

            case CLProximityNear:

                title = @"Near";

                break;

                

            case CLProximityFar:

                title = @"Far";

                break;

                

            default:

                title = @"Unknown";

                break;

        }

        

        return title;

    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    {

        static NSString *beaconCellIdentifier = @"BeaconCell";

        static NSString *progressCellIdentifier = @"ProgressCell";

        

        NSInteger i = indexPath.section;

        NSString *identifier = _inProgress && i == 0 ? progressCellIdentifier : beaconCellIdentifier;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    if (cell == nil)

    {        

            if(identifier == progressCellIdentifier)

            {

                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];

                cell.selectionStyle = UITableViewCellSelectionStyleNone;

                

                _progressBar.center = CGPointMake(cell.center.x, 17.0f);

                [cell.contentView addSubview:_progressBar];

                

                // Show the indicator that denotes calibration is in progress.

                UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 300.0f, 15.0f)];

                label.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

                label.backgroundColor = [UIColor clearColor];

                label.center = CGPointMake(cell.center.x, 30.0f);

                label.font = [UIFont systemFontOfSize:11.0f];

                label.text = @"Wave device side-to-side 1m away from beacon";

                label.textAlignment = NSTextAlignmentCenter;

                label.textColor = [UIColor darkGrayColor];

                [cell.contentView addSubview:label];

            }

            else

            {

                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];

            }

    }

        

        if(identifier == progressCellIdentifier)

        {

            return cell;

        }

        else if(_inProgress)

        {

            i--;

        }

        

        NSNumber *sectionKey = [[_beacons allKeys] objectAtIndex:i];

        CLBeacon *beacon = [[_beacons objectForKey:sectionKey] objectAtIndex:indexPath.row];

        cell.textLabel.text = [beacon.proximityUUID UUIDString];

        cell.detailTextLabel.text = [NSString stringWithFormat:@"Major: %@, Minor: %@, Acc: %.2fm", beacon.major, beacon.minor, beacon.accuracy];

        return cell;

    }

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    {

        

        

       

        if ([_tmpBeacons count]) {

            // beacon gevuld, fk modified

             NSLog(@"beacons :%@",_beacons);

            NSNumber *sectionKey = [[_tmpBeacons allKeys] objectAtIndex:indexPath.section];

            CLBeacon *beacon = [[_tmpBeacons objectForKey:sectionKey] objectAtIndex:indexPath.row];

        

       

        

        if(!_inProgress)

        {

            CLBeaconRegion *region = nil;

            if(beacon.proximityUUID && beacon.major && beacon.minor)

            {

                region = [[CLBeaconRegion alloc] initWithProximityUUID:beacon.proximityUUID major:[beacon.major shortValue] minor:[beacon.minor shortValue] identifier:@"com.apple.AirLocate"];

            }

            else if(beacon.proximityUUID && beacon.major)

            {

                region = [[CLBeaconRegion alloc] initWithProximityUUID:beacon.proximityUUID major:[beacon.major shortValue] identifier:@"com.apple.AirLocate"];

            }

            else if(beacon.proximityUUID)

            {

                region = [[CLBeaconRegion alloc] initWithProximityUUID:beacon.proximityUUID identifier:@"com.apple.AirLocate"];

            }

            

            if(region)

            {

                // We can stop ranging to display beacons available for calibration.

                [self stopRangingAllRegions];

                

                // And we'll start the calibration process.

                _calculator = [[ALCalibrationCalculator alloc] initWithRegion:region completionHandler:^(NSInteger measuredPower, NSError *error) {

                    if(error)

                    {

                        // Only display if the view is showing.

                        if(self.view.window)

                        {

                            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unable to calibrate device" message:[error.userInfo objectForKey:NSLocalizedDescriptionKey] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

                            [alert show];

                            

                            // Resume displaying beacons available for calibration if the calibration process failed.

                            [self startRangingAllRegions];

                        }

                    }

                    else

                    {                    

                        _endViewController.measuredPower = measuredPower;

                        [self.navigationController pushViewController:_endViewController animated:YES];

                    }

                    

                    _inProgress = NO;

                    _calculator = nil;

                    

                    [self.tableView reloadData];

                }];

                

                [_calculator performCalibrationWithProgressHandler:^(float percentComplete) {

                    [_progressBar setProgress:percentComplete animated:YES];

                }];

                

                _progressBar.progress = 0.0f;

                _inProgress = YES;

                

                [self.tableView reloadData];

            }

        }

        } else {

            

           NSLog(@"beacons empty");

        }

    }

    - (void)startRangingAllRegions

    {

        [_rangedRegions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

            CLBeaconRegion *region = obj;

            [_locationManager startRangingBeaconsInRegion:region];

        }];

    }

    - (void)stopRangingAllRegions

    {

        [_rangedRegions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

            CLBeaconRegion *region = obj;

            [_locationManager stopRangingBeaconsInRegion:region];

        }];

    }

    @end

  • Andrey,

    If the previous answer/post doesn't help i could mail you my complete/modified airlocate project. Let me know if you need it...

  • Thanks but for some reason your firmware will not allow Airlocate to see RSSI, I am 100% its the firmware. 

  • Thank you for the help, I really appreciate it but I believe there is issues with firmware that is why there needs to be tons of patching of apple code. In my experience they don't release something that broken.

  • Attached is a screen shot of the AirLocate App with the firmware previously mentioned. All i have done is add the RSSI to the Output. 

    Make sure you have a sensible value in the measured power field of the firmware. If this is wildly incorrect then i have seen RSSI 0 but with it at -56 (Granted on my hardware) the distance and RSSI always come through.

  • @jHough

    Hi,

    I used the "old" fw on your site. That worked (with exception of the RSSI part).

    I'm now trying/testing the new demo fw you mentioned. But when i flash the device (with uuid 74278BDAB64445208F0C720EAF050000) it does not show up anymore. What value should i use in the dbm field? 

    n.b. when i flash it back with the older fw (iBeacon_CC2541.hex)  it shows up again. 

  • Hi 

    So you used the tool to set the UUID to 74278BDAB64445208F0C720EAF050000? This will not work straight away with AirLocate, you would need to set it to: 74278BDAB64445208F0C720EAF059935 

    the dbm field depends on your hardware, a reasonable starting point is -57dbm you can use this value to improve the ranging. 

    Are you also not seeing RSSI values? what hardware are you guys using? (dev kit etc)


    James



  • Thanks James!

    It works now. With the RSSI value! I'm working with the sensortag dev-kit by the way.

    I don't know why i used that uuid... Big typo ;)

    @Andrej. If you are using the sensortag it should work with you as well...

  • @James

    The previous (free) firmware "lost-connection" after a couple of minutes. I couldn't figure out what caused it. I blamed it on some kind of bug. And just removed/inserted the battery to re-establish the connection and continue testing.

    Was there also some kind of timer on the "old" firmware?

    What i would like to know (before i buy the prototype) is, will the connection last with the new firmware?

  • Hi Mauritz

    How long before the beacon stopped?

    With the prototype licence bugs are fixed but there will never be any new features. So if you do come across a bug we will fix it.

    James 

  • Don't know. A couple of minutes (not more than 10 minutes). I could test with that fw again and let you know.

    I understand you can't give any guaranties at this point. But i'm still in "testing-mode" so buying software which is relatively new is a risk. 

    n.b. I saw the ETL iBeacons on your site and mailed my interest in buying one of those (for testing). Maybe that's the way to go...

    Frits (or Mauritz)

  • Ill see where your mails got. right now we are giving the beacons away as samples, ill see if i can get you one (Demands been very high as you would expect). 

  • That would be great! Thanks for your trouble anyway...

  • Hi everyone,

    I just have question about power parameter from iBeacon tag, like it is declared via static uint8 advertData[] =

    and for example 0xc5 it is -59. Does it need to be fix or what to do with that?

    Darko

  • Dear Mauritz,

    I'd like to do some experiments programming the iBeacon framework.
    I have the cc2540 mini developer kit.

    May I ask You, for sending me the source code for your project?

    Thank you for your help in advance,

    Gabor
    gabort@programgyar.hu

  • @James

    Hi, thanks for you post, can make a working iBeacon firmware for cc2541 now. I have one question: I made the iBeacon-firmware based on SimpleBLEBroadcaster project, but the distance of sensorTag (flashed with this firmware) I detected from my app is over 100000meters, i dont understand how this happens? and is there any parameters related to this "distance" ? 

    Thanks,

    Kaj

  • Thanks for the details and samples, but if the advertData has only one UUID, how to differentiate between multiple iBeacons?

    Who can I get 2 or more different results using the same HW from the same manufacturer? example in the demo where they place an iBeacon behind the "Mona Lisa", how you differentiate it from another piece of art if both UUIDs are the same?

    Any chance you can point me to the official iBeacon specification?

  • At any one time, you will only be able to broadcast 1 UUID out. I guess you can write the firmware so that it automatically update the UUID and broadcast again, but there will be impact to the battery life.

    At the app level, the software can detect the UUID, Major, Minor and the Power settings. So if you decide to use the same UUID for all the beacons, you can then use the combination of Major and Minor to differentiate between multiple beacons.

    Unless I have missed something, I don't think Apple has published the official profile yet. But you can read more here. http://stackoverflow.com/questions/18906988/what-is-the-ibeacon-bluetooth-profile

  • Hi Uri 

    The way you should treat the situation is to think of the UUID as an application identifier. (i.e. an app will look for a specific uuid). Then you should use the Major and Minor values to identify specific items or regions. So in your example the museum app would look for the UUID (say 0x00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ) and the beacons in the museum would be as follows:

    Mona Lisa - Major 0x0001 Minor 0x0001

    The Scream - Major 0x0001 Minor 0x0002

    and so on. You could use the Major to group specific item types 0x0001 for painting 0x0002 for Sculptures .... but this is purely up to you.

    So to summarise the UUID is not really used with the logic of your app just for identifying the type of beacon. Hope this makes sense.

     

     

  • Thanks Edward, I will differentiate beacons using the 4 bytes available.

    Do you know if iOS when observing for AdvertData will detect a change in the minor/major bytes? In other words, If I want to have dynamic content for these 4 bytes, does the iOS device hear the change, or if it is filtering on UUID only.

  • Yes, iOS will be able to pick up the major and minor as well as the measured power (rssi) field.

    Take a look at Airlocate sample project from Apple.

    Let me know if you need any help with it. (I am making some hardware beacons and will be ready in about 2 weeks, if you want to find out more, email me directly.)

    Edward