mbed+サーボ+AquesTalk

June 05, 2016

前回mbedでAquesTalkをI2Cで制御する回路を作ってみました。さらに今回はサーボモータを追加してみることにしました。おしゃべりロボットを作るためです。

できるだけ消費電力を抑えたかったので、モーターの電源はmbedの電源ではなく、専用の電源を追加し、待機時は、リレー(上の写真の右上の方にある黒い四角い物体)でモーターの電源だけ切り離すようにしています。AquesTalkも待機時はスリープさせるようにして極力mbedだけ動作させるようにしました。

ちなみにサーボモータはアマゾンで購入したSG90というラジコン用のサーボモータ(5個入りで2,300円くらい)を使ってます。

しかし、例のごとくまたはまってしまいました。今回、サーボモータを制御するためにPwmOutというモジュール(正確にはそのラッパーであるServoモジュール)を使っているのですが、I2Cと組み合わせて使うとモーターが動かない、という不具合に悩まされてしまいました。いろいろ調べましたが、理由は結局わからずです。多分PWMのパルス幅が何らかの原因で期待とは異なる幅になっていたんだと思います。結局、I2Cを使うのはやめて、UART(Serialモジュールを使用)で通信するように変更したところ、正常に動くようになりました。

さらに、BLEで遠隔操作できるようにソースを変更しました。できればBLEのドングルをつけたLinuxサーバからコントロールしたいのですが、まだちょっとうまくいっていません。ひょっとしたらドングルが対応していない可能性ありです。その場合はiPodを中継させるかもしれません。


#include "mbed.h"
#include "BLE.h"
#include "AT3011/AT3011.h"
#include "Servo.h"
#include "RoboController.h"

#define SG90_RANGE_S (0.0008)
#define SG90_DEGREE (90.0)
#define SG90_CENTER_WIDTH_S (0.00152)
#define SG90_PERIOD_S (0.020)

BLEDevice ble;
DigitalOut sw(P0_29);
Servo myservo(P0_28);
AT3011 atp(TX_PIN_NUMBER, RX_PIN_NUMBER, P0_30);

/* RoboController Service */
static const uint16_t RoboController_service_uuid = 0xFFF0;
static const uint16_t RoboController_Characteristic_uuid = 0xFFF1;
static const uint16_t uuid16_list[] = { RoboController_service_uuid };
uint8_t RoboControllerPayload[10] = {0,};

GattCharacteristic ControllerChar (RoboController_Characteristic_uuid,RoboControllerPayload,10, 10,
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
GattCharacteristic *ControllerChars[] = {&ControllerChar};
GattService RoboControllerService(RoboController_service_uuid, ControllerChars, sizeof(ControllerChars) / sizeof(GattCharacteristic *));

RoboController controller;

/* servo */
void move()
{
  for(int i=0; i<100; i++) {
    wait(0.1);
    myservo = 0.01 * i;
  }
}

/* AquesTalk */
void speak(const char *msg)
{
  atp.speak(msg);
}

/* Robot Action */
void do_action()
{
  /* まだ途中 */
  char cmd[] = "konnichiwa";
  sw = 1;
  wait(0.8);
  move();
  speak(cmd);
  sw = 0;
  wait(5);
}

/* BLE */

void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params) //Mod
{

}

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) // Mod
{
  ble.startAdvertising();
}

// GattEvent
void DataWrittenCallback(const GattWriteCallbackParams *params)
{
  memcpy( &controller.data[0], RoboControllerPayload, sizeof(controller));
  do_action();
}
/* initialize */
void init_robot_devices()
{
  sw = 1;
  myservo.calibrate( SG90_RANGE_S, SG90_DEGREE);
  myservo = 0.5;
  wait(1);
  atp.reset();
  sw = 0;
  wait(1);
}

void init_ble()
{
  ble.init();
  ble.onDisconnection(disconnectionCallback);
  ble.onConnection(onConnectionCallback);
  ble.onDataWritten(DataWrittenCallback);

  /* setup advertising */
  ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
  ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
  ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
  (const uint8_t *)"mbed HRM1017", sizeof("mbed HRM1017") - 1);
  ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
  (uint8_t *)uuid16_list, sizeof(uuid16_list));

  ble.setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
  ble.startAdvertising();

  ble.addService(RoboControllerService);
}

void init()
{
  init_ble();
  init_robot_devices();
}

int main(void)
{
  init();

  while (true) {
    ble.waitForEvent();
  }
}