LoginSignup
12
7

More than 5 years have passed since last update.

Cocos2d-xでドローン制御してみる

Posted at

概要

Cocos2d-xでParrot社MAMBOの制御を行ってみました。
このMAMBOはiOS、Android用のSDKが提供されているので、
比較的簡単に飛行制御することが出来ます。
今回は、Cocos2d-x + iOS環境で、このドローンを制御してみたいと思います。
MAMBOの接続はBlueToothを使って行います。

環境

開発環境は以下の通りです。

  • Cocos2d-x 3.13
  • XCode 8.2.1
  • iPod Touch 第6世代

インストール

ダウンロードしてきたSDKをiOSフォルダに保存してください。
proj.ios_mac
└iOS
 └Framework
  └ARSDK3_iOS_3_11_0
   ├include
   └lib

Other Linker Flagsは
-ObjCを忘れず、その他に必要なライブラリを記述していきます。
-larcommands -larcontroller -lardiscovery -larnetwork -larnetworkal -larsal -larstream -larstream2 -larmavlink -ljson -larmedia -larutils -lcurl -lardatatransfer -lmux -lpomp -lcrypto -lssl -lz

ExternalAccessory.frameworkも忘れずに追加してください。

プログラミング

ドローン検索用のViewContorollerを作成します。

DeviceListVC.h
#import <UIKit/UIKit.h>
@interface DeviceListVC : UIViewController

+(DeviceListVC*) sharedInstance;

@property BOOL isConnect;
@end
DeviceListVC.m
#import "DeviceListVC.h"
#import "DroneDiscoverer.h"
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>
#import "MiniDrone.h"

@interface DeviceListVC () <DroneDiscovererDelegate>

@property (nonatomic, strong) NSArray *dataSource;
@property (nonatomic, strong) DroneDiscoverer *droneDiscoverer;
@property (nonatomic, strong) ARService *selectedService;

@end

static DeviceListVC *deveiceListHelper;

@implementation DeviceListVC

+(DeviceListVC*) sharedInstance
{
    @synchronized(self)
    {
        if (deveiceListHelper == nil)
        {
            deveiceListHelper = [[DeviceListVC alloc] init];
        }

        return deveiceListHelper;
    }

    // to avoid compiler warning
    return nil;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    _dataSource = [NSArray array];
    _droneDiscoverer = [[DroneDiscoverer alloc] init];
    [_droneDiscoverer setDelegate:self];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [self registerNotifications];
    [_droneDiscoverer startDiscovering];
}

- (void) viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    [self unregisterNotifications];
    [_droneDiscoverer stopDiscovering];
}

#pragma mark DroneDiscovererDelegate
- (void)droneDiscoverer:(DroneDiscoverer *)droneDiscoverer didUpdateDronesList:(NSArray *)dronesList {
    if (_isConnect) return;

    _dataSource = dronesList;

    [dronesList enumerateObjectsUsingBlock:^(ARService *service, NSUInteger idx, BOOL * _Nonnull stop)
    {
        if (ARDISCOVERY_PRODUCT_MINIDRONE_DELOS3 == service.product) {
            if (!_isConnect) {
                // MAMBOドローン初期化
                [[MiniDrone sharedInstance] initWithService:service];
                [[MiniDrone sharedInstance] connect];
                _isConnect = YES;
            }
        }

    }];

}

#pragma mark notification registration
- (void)registerNotifications {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enteredBackground:) name: UIApplicationDidEnterBackgroundNotification object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterForeground:) name: UIApplicationWillEnterForegroundNotification object: nil];
}

- (void)unregisterNotifications {
    [[NSNotificationCenter defaultCenter] removeObserver:self name: UIApplicationDidEnterBackgroundNotification object: nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name: UIApplicationWillEnterForegroundNotification object: nil];
}

#pragma mark - application notifications
- (void)enterForeground:(NSNotification*)notification {
    [_droneDiscoverer startDiscovering];
}

- (void)enteredBackground:(NSNotification*)notification {
    [_droneDiscoverer stopDiscovering];
    _isConnect = NO;
}
@end 

次にドローンを見つけるための処理

DroneDiscoverer.h
#import <Foundation/Foundation.h>

@class DroneDiscoverer;

@protocol DroneDiscovererDelegate<NSObject>

/**
 * Called when the device found list is updated
 * Called on the main thread
 * @param droneDiscoverer the drone discoverer concerned
 * @param dronesList the list of found ARService
 */
- (void)droneDiscoverer:(DroneDiscoverer*)droneDiscoverer didUpdateDronesList:(NSArray*)dronesList;

@end

@interface DroneDiscoverer : NSObject

@property (nonatomic, weak) id<DroneDiscovererDelegate> delegate;

- (void)startDiscovering;
- (void)stopDiscovering;

@end
DroneDiscoverer.m
#import "DroneDiscoverer.h"
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>

@implementation DroneDiscoverer

- (void)setDelegate:(id<DroneDiscovererDelegate>)delegate {
    _delegate = delegate;

    if (_delegate && [_delegate respondsToSelector:@selector(droneDiscoverer:didUpdateDronesList:)]) {
        [_delegate droneDiscoverer:self didUpdateDronesList:[[ARDiscovery sharedInstance] getCurrentListOfDevicesServices]];
    }
}

- (void)startDiscovering {
    [self registerNotifications];
    [[ARDiscovery sharedInstance] start];
}

- (void)stopDiscovering {
    [[ARDiscovery sharedInstance] stop];
    [self unregisterNotifications];
}

#pragma mark notification registration
- (void)registerNotifications {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(discoveryDidUpdateServices:) name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}

- (void)unregisterNotifications {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}

#pragma mark ARDiscovery notification
- (void)discoveryDidUpdateServices:(NSNotification *)notification {
    // reload the data in the main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        if (_delegate && [_delegate respondsToSelector:@selector(droneDiscoverer:didUpdateDronesList:)]) {
            [_delegate droneDiscoverer:self didUpdateDronesList:[[notification userInfo] objectForKey:kARDiscoveryServicesList]];
        }
    });
}

@end

AppController.mmに先程作成したViewControllerを呼び出します。

AppController.mm
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

    // Add the view controller's view to the window and display.
    window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

    // Use RootViewController to manage CCEAGLView
    _viewController = [[RootViewController alloc]init];
    _viewController.wantsFullScreenLayout = YES;
    [_viewController.view addSubview: [DeviceListVC sharedInstance].view];

}

ドローン制御用プログラムです

MiniDrone.h
#import <Foundation/Foundation.h>
#import <libARController/ARController.h>
#import <libARCommands/ARCommands.h>
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>

@class MiniDrone;

@protocol MiniDroneDelegate <NSObject>
@required
/**
 * Called when the connection to the drone did change
 * Called on the main thread
 * @param miniDrone the drone concerned
 * @param state the state of the connection
 */
- (void)miniDrone:(MiniDrone*)miniDrone connectionDidChange:(eARCONTROLLER_DEVICE_STATE)state;

/**
 * Called when the battery charge did change
 * Called on the main thread
 * @param MiniDrone the drone concerned
 * @param batteryPercent the battery remaining (in percent)
 */
- (void)miniDrone:(MiniDrone*)miniDrone batteryDidChange:(int)batteryPercentage;

/**
 * Called when the piloting state did change
 * Called on the main thread
 * @param miniDrone the drone concerned
 * @param batteryPercent the piloting state of the drone
 */
- (void)miniDrone:(MiniDrone*)miniDrone flyingStateDidChange:(eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)state;

/**
 * Called before medias will be downloaded
 * Called on the main thread
 * @param miniDrone the drone concerned
 * @param nbMedias the number of medias that will be downloaded
 */
- (void)miniDrone:(MiniDrone*)miniDrone didFoundMatchingMedias:(NSUInteger)nbMedias;

/**
 * Called each time the progress of a download changes
 * Called on the main thread
 * @param miniDrone the drone concerned
 * @param mediaName the name of the media
 * @param progress the progress of its download (from 0 to 100)
 */
- (void)miniDrone:(MiniDrone*)miniDrone media:(NSString*)mediaName downloadDidProgress:(int)progress;

/**
 * Called when a media download has ended
 * Called on the main thread
 * @param miniDrone the drone concerned
 * @param mediaName the name of the media
 */
- (void)miniDrone:(MiniDrone*)miniDrone mediaDownloadDidFinish:(NSString*)mediaName;

@end

@interface MiniDrone : NSObject

@property (nonatomic, weak) id<MiniDroneDelegate>delegate;

+ (MiniDrone*) sharedInstance;

- (id)initWithService:(ARService*)service;
- (eARCONTROLLER_DEVICE_STATE)connectionState;
- (eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)flyingState;
- (uint8_t)batteryLevel;

- (BOOL)takeOff;
- (BOOL)land;
- (void)setPitch:(uint8_t)pitch;
- (void)setRoll:(uint8_t)roll;
- (void)setYaw:(uint8_t)yaw;
- (void)setGaz:(uint8_t)gaz;
- (void)setFlag:(uint8_t)flag;
- (ARService*)getService;

@end
MiniDrone.m
@interface MiniDrone ()<MiniDroneDelegate>

@property (nonatomic, assign) ARCONTROLLER_Device_t *deviceController;
@property (nonatomic, assign) ARCOMMANDS_Decoder_t *commandsDecoder;
@property (nonatomic, strong) ARService *service;
@property (nonatomic, assign) eARCONTROLLER_DEVICE_STATE connectionState;
@property (nonatomic, assign) eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState;
@property (nonatomic, assign) uint8_t batteryLevel;

@end

@implementation MiniDrone

+ (MiniDrone *)sharedInstance
{
    static MiniDrone *drone  = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^ {
        drone = [[MiniDrone alloc] init];
    });
    return drone;
}

-(id)initWithService:(ARService *)service
{
    self = [super init];
    if (self) {
        _service = service;
        _flyingState = ARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED;

    }
    return self;
}

- (void)dealloc
{
    if (_deviceController) {
        ARCONTROLLER_Device_Delete(&_deviceController);
    }
}

- (void)connect {

    if (!_deviceController) {
        // call createDeviceControllerWithService in background
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // if the product type of the service matches with the supported types
            eARDISCOVERY_PRODUCT product = _service.product;
            eARDISCOVERY_PRODUCT_FAMILY family = ARDISCOVERY_getProductFamily(product);
            if (family == ARDISCOVERY_PRODUCT_FAMILY_MINIDRONE) {
                // create the device controller
                [self createDeviceControllerWithService:_service];
            }
        });
    } else {
        ARCONTROLLER_Device_Start (_deviceController);
    }
}

- (void)disconnect {
    ARCONTROLLER_Device_Stop (_deviceController);
}

- (eARCONTROLLER_DEVICE_STATE)connectionState {
    return _connectionState;
}

- (eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)flyingState {
    return _flyingState;
}

- (uint8_t)batteryLevel
{
    return _batteryLevel;
}

- (void)createDeviceControllerWithService:(ARService*)service {
    // first get a discovery device
    ARDISCOVERY_Device_t *discoveryDevice = [self createDiscoveryDeviceWithService:service];

    if (discoveryDevice != NULL) {
        eARCONTROLLER_ERROR error = ARCONTROLLER_OK;
        eARCOMMANDS_DECODER_ERROR errorDecoder = ARCOMMANDS_DECODER_OK;

        // create the device controller
        _deviceController = ARCONTROLLER_Device_New (discoveryDevice, &error);
        _commandsDecoder = ARCOMMANDS_Decoder_NewDecoder(&errorDecoder);

        // add the state change callback to be informed when the device controller starts, stops...
        if (error == ARCONTROLLER_OK) {
            error = ARCONTROLLER_Device_AddStateChangedCallback(_deviceController, stateChanged, (__bridge void *)(self));
        }

        // add the command received callback to be informed when a command has been received from the device
        if (error == ARCONTROLLER_OK) {
            error = ARCONTROLLER_Device_AddCommandReceivedCallback(_deviceController, onCommandReceived, (__bridge void *)(self));
        }

        // start the device controller (the callback stateChanged should be called soon)
        if (error == ARCONTROLLER_OK) {
            error = ARCONTROLLER_Device_Start (_deviceController);
        }

        // we don't need the discovery device anymore
        ARDISCOVERY_Device_Delete (&discoveryDevice);

        // if an error occured, inform the delegate that the state is stopped
        if (error != ARCONTROLLER_OK) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [_delegate miniDrone:self connectionDidChange:ARCONTROLLER_DEVICE_STATE_STOPPED];
            });
        }
    } else {
        // if an error occured, inform the delegate that the state is stopped
        dispatch_async(dispatch_get_main_queue(), ^{
            [_delegate miniDrone:self connectionDidChange:ARCONTROLLER_DEVICE_STATE_STOPPED];
        });
    }
}

- (ARDISCOVERY_Device_t *)createDiscoveryDeviceWithService:(ARService*)service {
    ARDISCOVERY_Device_t *device = NULL;
    eARDISCOVERY_ERROR errorDiscovery = ARDISCOVERY_OK;

    device = ARDISCOVERY_Device_New (&errorDiscovery);

    if (errorDiscovery == ARDISCOVERY_OK) {
        // get the ble service from the ARService
        ARBLEService* bleService = service.service;

        // create a BLE discovery device
        errorDiscovery = ARDISCOVERY_Device_InitBLE (device, service.product, (__bridge ARNETWORKAL_BLEDeviceManager_t)(bleService.centralManager), (__bridge ARNETWORKAL_BLEDevice_t)(bleService.peripheral));
    }

    if (errorDiscovery != ARDISCOVERY_OK) {
        NSLog(@"Discovery error :%s", ARDISCOVERY_Error_ToString(errorDiscovery));
        ARDISCOVERY_Device_Delete(&device);
    }

    return device;
}

- (ARCOMMANDS_Decoder_t*) createCommandsDecoder {
    return nil;
}

- (void)createSDCardModule {
}

#pragma mark commands
// 離陸
- (BOOL)takeOff {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->sendPilotingTakeOff(_deviceController->miniDrone);
        return YES;
    }
    return NO;
}

// 着陸
- (BOOL)land {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->sendPilotingLanding(_deviceController->miniDrone);
        return YES;
    }
    return NO;
}

- (void)setPitch:(uint8_t)pitch {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->setPilotingPCMDPitch(_deviceController->miniDrone, pitch);
    }
}

- (void)setRoll:(uint8_t)roll {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->setPilotingPCMDRoll(_deviceController->miniDrone, roll);
    }
}

- (void)setYaw:(uint8_t)yaw {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->setPilotingPCMDYaw(_deviceController->miniDrone, yaw);
    }
}

- (void)setGaz:(uint8_t)gaz {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->setPilotingPCMDGaz(_deviceController->miniDrone, gaz);
    }
}

- (void)setFlag:(uint8_t)flag {
    if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
        _deviceController->miniDrone->setPilotingPCMDFlag(_deviceController->miniDrone, flag);
    }
}

#pragma mark Device controller callbacks
// called when the state of the device controller has changed
static void stateChanged (eARCONTROLLER_DEVICE_STATE newState, eARCONTROLLER_ERROR error, void *customData) {
    MiniDrone *miniDrone = (__bridge MiniDrone*)customData;
    if (miniDrone != nil) {
        switch (newState) {
            case ARCONTROLLER_DEVICE_STATE_RUNNING:
                break;
            case ARCONTROLLER_DEVICE_STATE_STOPPED:
                break;
            default:
                break;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            miniDrone.connectionState = newState;
            [miniDrone.delegate miniDrone:miniDrone connectionDidChange:newState];
        });
    }
}

// ドローンから送られてくる情報を取得します
static void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData) {
    MiniDrone *miniDrone = (__bridge MiniDrone*)customData;

    NSLog(@"onCommandReceived=%u %u", commandKey, ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_USBACCESSORYSTATE_GUNSTATE);

    // バッテリー残量
    if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED) &&
        (elementDictionary != NULL)) {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL) {
            HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT, arg);
            if (arg != NULL) {
                uint8_t battery = arg->value.U8;
                dispatch_async(dispatch_get_main_queue(), ^{
                    [miniDrone.delegate miniDrone:miniDrone batteryDidChange:battery];
                    miniDrone.batteryLevel = battery;
                });
            }
        }
    }
    // 飛行状態
    else if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED) &&
        (elementDictionary != NULL)) {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL) {
            HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);
            if (arg != NULL) {
                miniDrone.flyingState = arg->value.I32;
                dispatch_async(dispatch_get_main_queue(), ^{
                    [miniDrone.delegate miniDrone:miniDrone flyingStateDidChange:miniDrone.flyingState];
                });
            }
        }
    }

}

- (ARService*)getService
{
    return _service;
}

static void onChargeRate(eARCOMMANDS_COMMON_CHARGERSTATE_CURRENTCHARGESTATECHANGED_STATUS status, eARCOMMANDS_COMMON_CHARGERSTATE_CURRENTCHARGESTATECHANGED_PHASE phase, void *custom) {
    NSLog(@"onChargeRate=%d", status);
}

@end

呼び出し

Cocos2d-xから呼び出す為のブリッジ処理です。

MiniDroneBridge.h
#include "cocos2d.h"

using namespace cocos2d;

class MiniDroneBridge;

using MiniDroneBridgeCompletionHandler = std::function<void (bool isSuccess)>;
using MiniDroneBridgeBatteryLevelHandler = std::function<void (int level)>;
using MiniDroneBridgeStatHandler = std::function<void (int stat)>;
using MiniDroneBridgeValueHandler = std::function<void (int val)>;

class MiniDroneBridge
{
public :
    static void takeOff(const MiniDroneBridgeCompletionHandler handler);
    static void land(const MiniDroneBridgeCompletionHandler handler);
    static void setGazUD(int val);
    static void setYawLR(int val);
    static void setPitchFB(int val);
    static void setRollLR(int val);
    static void connect();
    static void disconnect();

    static void dispBatteryLevel(const MiniDroneBridgeBatteryLevelHandler handler);
    static void dispConnectStat(const MiniDroneBridgeStatHandler handler);
    static void dispFlyingStat(const MiniDroneBridgeStatHandler handler);
};

MiniDroneBridge.mm
#import "MiniDrone.h"
#import "MiniDroneBridge.h"

// 離陸
void MiniDroneBridge::takeOff(const MiniDroneBridgeCompletionHandler handler)
{
    if ([[MiniDrone sharedInstance] takeOff])
    {
        handler(true);
    } else {
        handler(false);
    }

}

// 着陸
void MiniDroneBridge::land(const MiniDroneBridgeCompletionHandler handler)
{
    if ([[MiniDrone sharedInstance] land])
    {
        handler(true);
    } else {
        handler(false);
    }

}

// 上昇/下降
void MiniDroneBridge::setGazUD(int val)
{
    [[MiniDrone sharedInstance] setGaz:val];
}

// ドローンの向きを左右に変える
void MiniDroneBridge::setYawLR(int val)
{
    [[MiniDrone sharedInstance] setYaw:val];
}

void MiniDroneBridge::setPitchFB(int val)
{
    // 前進/後退
    if (val == 0) {
        [[MiniDrone sharedInstance] setFlag:0];
    } else {
        [[MiniDrone sharedInstance] setFlag:1];
    }
    [[MiniDrone sharedInstance] setPitch:val];

}

void MiniDroneBridge::setRollLR(int val)
{
    // 左移動、右移動
    if (val == 0) {
        [[MiniDrone sharedInstance] setFlag:0];
    } else {
        [[MiniDrone sharedInstance] setFlag:1];
    }
    [[MiniDrone sharedInstance] setRoll:val];
}

// バッテリー残数取得
void MiniDroneBridge::dispBatteryLevel(const MiniDroneBridgeBatteryLevelHandler handler)
{
    handler([[MiniDrone sharedInstance] batteryLevel]);
}

// 接続状態取得
void MiniDroneBridge::dispConnectStat(const MiniDroneBridgeStatHandler handler)
{
    handler([[MiniDrone sharedInstance] connectionState]);
}

// 飛行状態取得
void MiniDroneBridge::dispFlyingStat(const MiniDroneBridgeStatHandler handler)
{
    handler([[MiniDrone sharedInstance] flyingState]);
}

void MiniDroneBridge::connect()
{
    [[MiniDrone sharedInstance] connect];
}

void MiniDroneBridge::disconnect()
{
    [[MiniDrone sharedInstance] disconnect];
}

離陸/着陸処理

離陸と着陸を行うために、画面にボタンを設置します。

HelloWorldScene.h
#include "cocos2d.h"

#define WINSIZE Director::getInstance()->getWinSize()
#define WINCENTER Point(WINSIZE.width*0.5, WINSIZE.height*0.5)

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();
    static HelloWorld* sharedScene(void);

    virtual bool init();
    CREATE_FUNC(HelloWorld);

private:
    CC_SYNTHESIZE(MenuItemSprite*, _btnTakeOFF, BtnTakeOFF);
    CC_SYNTHESIZE(MenuItemSprite*, _btnLand, BtnLand);

    CC_SYNTHESIZE(Label*, _lblBattery, LblBattery);
    CC_SYNTHESIZE(bool, _connectStat, ConnectStat);

    void onEnterTransitionDidFinish();
    void update(float dt);

    void takeOffTapped(MenuItemSprite* sender);
    void landTapped(MenuItemSprite* sender);
};

HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "MiniDroneBridge.h"

USING_NS_CC;

static HelloWorld *g_Scene;

HelloWorld* HelloWorld::sharedScene() {
    return g_Scene;
}

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();

    // 'layer' is an autorelease object
    g_Scene = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(g_Scene);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    _connectStat = false;

    // TakeOFF
    Sprite* btnTakeOff_ON = Sprite::create();
    btnTakeOff_ON->setTextureRect(Rect(0, 0, 200, 100));
    btnTakeOff_ON->setColor(Color3B::GRAY);

    Sprite* btnTakeOff_OFF = Sprite::create();
    btnTakeOff_OFF->setTextureRect(Rect(0, 0, 200, 100));
    btnTakeOff_OFF->setColor(Color3B(0,0,200));

    _btnTakeOFF = MenuItemSprite::create(btnTakeOff_OFF,
                                         btnTakeOff_ON,
                                         [this](Ref* sender) {this->takeOffTapped((MenuItemSprite*)sender);});
    Label* lblTakeOff = Label::createWithSystemFont("離陸", "arial", 26);
    lblTakeOff->enableOutline(Color4B::BLACK, 1);
    lblTakeOff->setPosition(Vec2(_btnTakeOFF->getContentSize().width*0.5, _btnTakeOFF->getContentSize().height*0.5));
    _btnTakeOFF->addChild(lblTakeOff);
    _btnTakeOFF->setPosition(Vec2(WINCENTER.x, _btnTakeOFF->getContentSize().height*0.5));
    _btnTakeOFF->setVisible(false);

    // Land
    Sprite* btnLand_ON = Sprite::create();
    btnLand_ON->setTextureRect(Rect(0, 0, 200, 100));
    btnLand_ON->setColor(Color3B::GRAY);

    Sprite* btnLand_OFF = Sprite::create();
    btnLand_OFF->setTextureRect(Rect(0, 0, 200, 100));
    btnLand_OFF->setColor(Color3B(0,0,200));

    _btnLand = MenuItemSprite::create(btnLand_OFF,
                                      btnLand_ON,
                                      [this](Ref* sender) {this->landTapped((MenuItemSprite*)sender);});
    Label* lblLand = Label::createWithSystemFont("着陸", "arial", 26);
    lblLand->enableOutline(Color4B::BLACK, 1);
    lblLand->setPosition(Vec2(_btnLand->getContentSize().width*0.5, _btnLand->getContentSize().height*0.5));
    _btnLand->addChild(lblLand);
    _btnLand->setPosition(Vec2(WINCENTER.x, _btnLand->getContentSize().height*0.5));
    _btnLand->setVisible(false);

    Menu* menu = Menu::create();
    menu->setPosition(this->getPosition());
    menu->setContentSize(this->getContentSize());
    this->addChild(menu);
    menu->addChild(_btnTakeOFF);
    menu->addChild(_btnLand);

    _lblBattery = Label::createWithSystemFont("Battery 0%", "Arial", 26);
    _lblBattery->setAnchorPoint(Vec2(0, 1));
    _lblBattery->setAlignment(TextHAlignment::LEFT);
    _lblBattery->setPosition(Vec2(10, WINSIZE.height));
    this->addChild(_lblBattery);

    return true;
}

void HelloWorld::onEnterTransitionDidFinish()
{
    scheduleUpdate();


    // 0.1秒毎に呼び出す
    this->runAction(Repeat::create(Sequence::create(DelayTime::create(0.1), CallFunc::create([=]{
        MiniDroneBridge::dispBatteryLevel([=](int level)
        {                  
            // バッテリー数値表示
            _lblBattery->setString(StringUtils::format("Battery %d%%", level));
        });

        MiniDroneBridge::dispConnectStat([=](int stat)
        {
             // 接続状態かどうか
             if (stat == 2) {
                 _connectStat = true;
                 _btnTakeOFF->setVisible(true);                     
             } else {
                 _connectStat = false;
                 _btnTakeOFF->setVisible(false);                     
             }                 
        });

        // 飛行状態
        MiniDroneBridge::dispFlyingStat([=](int stat)
        {
            if (_connectStat) {
                if (stat == 0) {
                    // 着陸
                    _btnTakeOFF->setVisible(true);
                    _btnLand->setVisible(false);                        
                } else if (stat == 4) {
                    // 着陸中
                    _btnTakeOFF->setVisible(false);
                    _btnLand->setVisible(false);                        
                } else if (stat >= 2 && stat <= 3) {
                    // 離陸
                    _btnTakeOFF->setVisible(false);
                    _btnLand->setVisible(true);
                }
           }                                        
        });

    }) , NULL), -1));
}
// 離陸処理
void HelloWorld::takeOffTapped(cocos2d::MenuItemSprite *sender)
{
    MiniDroneBridge::takeOff([=](bool isSuccess)
    {
        log("takeoff=%d", isSuccess);
    });

}

// 着陸処理
void HelloWorld::landTapped(cocos2d::MenuItemSprite *sender)
{
    MiniDroneBridge::land([](bool isSuccess)
    {
          log("land=%d", isSuccess);
    });
}

まとめ

基本的なドローンの制御はMiniDrone.mに記述しています。
それをCocos2d-xから呼び出してあげるだけで、簡単に制御することが出来ました。
ドローン自体に超音波センサーが内蔵されているので、自立飛行も自動的にしてくれます。

MiniDroneBridge::dispConnectStatMiniDroneBridge::dispFlyingStatで、
ドローンの接続状態と飛行状態を取得しています。
リアルタイムで状態を判断して、適切なボタンを表示してあげると良いです。

このMAMBOドローンは、BB弾が発射出来たり、4gまでの物をつかむためのグラバーが付属品としてあります。
もちろん、プログラムで制御も可能なので、そちらも是非試してみてください!

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7