1

有人知道如何在 Xamarin.iOS 上使用 ExternalAccessory API 吗?

我的 Xamarin Studio 版本是 4.0.12(内部版本 3)、Xamarin.Android 版本 4.8.1、Xamarin.iOS 版本 6.4.5.0 和 Xcode 是版本 5.0 (5A1413),我尝试同时针对 6.1 和 7.0 iPad/iPhone。

我已经浏览了互联网,并且没有太多文档。甚至 MonoTouch 文档也有损坏的链接。

我想要的是,列出连接的蓝牙设备,然后按名称获取其中一个,然后连接到它,这样我就可以打开一个套接字并开始向它发送数据。它是使用串行通信的设备,是的,它具有 Apple 外部附件协议 ID。

我试过这个:

var am = EAAccessoryManager.SharedAccessoryManager;

它只是给我一个异常 InvaidCastException。

有什么线索吗?

谢谢!我真的很感激帮助。

PS:Xamarin 详细信息

Xamarin Studio
Version 4.0.12 (build 3)
Installation UUID: 7348d641-ed6d-4c8a-b59a-116674e06dfd
Runtime:
    Mono 3.2.0 ((no/7c7fcc7)
    GTK 2.24.20
    GTK# (2.12.0.0)
    Package version: 302000000

[...]

Apple Developer Tools
Xcode 5.0 (3332.25)
Build 5A1413

[...]

Xamarin.iOS
Version: 6.4.5.0 (Trial Edition)
Hash: 1336a36
Branch: 
Build date: 2013-10-09 11:14:45-0400

Build Information
Release ID: 400120003
Git revision: 593d7acb1cb78ceeeb482d5133cf1fe514467e39
Build date: 2013-08-07 20:30:53+0000
Xamarin addins: 25a0858b281923e666b09259ad4746b774e0a873

Operating System
Mac OS X 10.8.5
Darwin Gutembergs-MacBook-Pro.local 12.5.0 Darwin Kernel Version 12.5.0
    Mon Jul 29 16:33:49 PDT 2013
    root:xnu-2050.48.11~1/RELEASE_X86_64 x86_64
4

2 回答 2

5

尽管您似乎已经解决了这个问题,但我想我会展示一些显示基础知识的代码片段(在这种情况下,连接到 Sphero 并将其变为绿色):

EAAccessoryManager mgr = EAAccessoryManager.SharedAccessoryManager;
var accessories = mgr.ConnectedAccessories;
foreach(var accessory in accessories)
{
    myLabel.Text = "Got me an accessory";
    Console.WriteLine(accessory.ToString());
    Console.WriteLine(accessory.Name);
    var protocol = "com.orbotix.robotprotocol";

    if(accessory.ProtocolStrings.Where(s => s == protocol).Any())
    {
        myLabel.Text = "Got me a Sphero";

        var session = new EASession(accessory, protocol);
        var outputStream = session.OutputStream;
        outputStream.Delegate = new MyOutputStreamDelegate(myLabel);
        outputStream.Schedule(NSRunLoop.Current, "kCFRunLoopDefaultMode");
        outputStream.Open();
    }
}

public class MyOutputStreamDelegate : NSStreamDelegate
{
    UILabel label;
    bool hasWritten = false;

    public MyOutputStreamDelegate(UILabel label)
    {
        this.label = label;
    }
    public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
    {
        if(streamEvent == NSStreamEvent.HasSpaceAvailable && ! hasWritten)
        {
            //Set the color of the Sphero
            var written = ((NSOutputStream)theStream).Write(new byte[] {0xFF, 0xFF, 0x02, 0x20, 0x0e, 0x05, 0x1F, 0xFF, 0x1B, 0x00, 0x91}, 11);
            if(written == 11)
            {
                label.Text = "Sphero should be green";
            }
            hasWritten = true;
        }
    }
}
于 2013-09-20T21:14:49.597 回答
4

我知道您特别询问了有关将数据写入蓝牙设备的问题,但这只是扩展了读取数据以及 Xamarin.iOS 外部附件 API 的一般用途,因为那里没有太多文档或 Xamarin 示例。这是使用 Objective-C 完成的Apple 示例的松散转换。我的配件是 MFi 认证的微芯片阅读器。我只添加了“读取”功能,因为我的应用程序只需要它。

创建一个继承自 NSStreamDelegate 的 SessionController 类,这会做很多工作。打开、关闭会话、处理来自设备的事件并读取数据。我想你也会在这里添加你的写方法。

public class EASessionController : NSStreamDelegate
{

    NSString SessionDataReceivedNotification = (NSString)"SessionDataReceivedNotification";

    public static EAAccessory _accessory;
    public static string _protocolString;

    EASession _session;
    NSMutableData _readData;

    public static EASessionController SharedController()
    {
        EASessionController sessionController = null;

        if (sessionController == null)
        {
            sessionController = new EASessionController();
        }

        return sessionController;

    }

    public void SetupController(EAAccessory accessory, string protocolString)
    {

        _accessory = accessory;
        _protocolString = protocolString;

    }

    public bool OpenSession()
    {

        Console.WriteLine("opening new session");

        _accessory.WeakDelegate = this;

        if (_session == null)
            _session = new EASession(_accessory, _protocolString);

        // Open both input and output streams even if the device only makes use of one of them

        _session.InputStream.Delegate = this;
        _session.InputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
        _session.InputStream.Open();

        _session.OutputStream.Delegate = this;
        _session.OutputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
        _session.OutputStream.Open();

        return (_session != null);

    }

    public void CloseSession()
    {
        _session.InputStream.Unschedule(NSRunLoop.Current, NSRunLoopMode.Default);
        _session.InputStream.Delegate = null;
        _session.InputStream.Close();

        _session.OutputStream.Unschedule(NSRunLoop.Current, NSRunLoopMode.Default);
        _session.OutputStream.Delegate = null;
        _session.OutputStream.Close();

        _session = null;

    }


    /// <summary>
    /// Get Number of bytes to read into local buffer
    /// </summary>
    /// <returns></returns>
    public nuint ReadBytesAvailable()
    {
        return _readData.Length;
    }



    /// <summary>
    /// High level read method
    /// </summary>
    /// <param name="bytesToRead"></param>
    /// <returns></returns>
    public NSData ReadData(nuint bytesToRead)
    {

        NSData data = null;

        if (_readData.Length >= bytesToRead)
        {
            NSRange range = new NSRange(0, (nint)bytesToRead);
            data = _readData.Subdata(range);
            _readData.ReplaceBytes(range, IntPtr.Zero, 0);
        }

        return data;

    }

    /// <summary>
    /// Low level read method - read data while there is data and space in input buffer, then post notification to observer
    /// </summary>
    void ReadData()
    {

        nuint bufferSize = 128;
        byte[] buffer = new byte[bufferSize];

        while (_session.InputStream.HasBytesAvailable())
        {
            nint bytesRead = _session.InputStream.Read(buffer, bufferSize);

            if (_readData == null)
            {
                _readData = new NSMutableData(); 
            }
            _readData.AppendBytes(buffer, 0, bytesRead);
            Console.WriteLine(buffer);


        }

        // We now have our data from the device (stored in _readData), so post the notification for an observer to do something with the data

        NSNotificationCenter.DefaultCenter.PostNotificationName(SessionDataReceivedNotification, this);

    }


    /// <summary>
    /// Handle the events occurring with the external accessory
    /// </summary>
    /// <param name="theStream"></param>
    /// <param name="streamEvent"></param>
    public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
    {

        switch (streamEvent)
        {

            case NSStreamEvent.None:
                Console.WriteLine("StreamEventNone");
                break;
            case NSStreamEvent.HasBytesAvailable:
                Console.WriteLine("StreamEventHasBytesAvailable");
                ReadData();
                break;
            case NSStreamEvent.HasSpaceAvailable:
                Console.WriteLine("StreamEventHasSpaceAvailable");
                // Do write operations to the device here
                break;
            case NSStreamEvent.OpenCompleted:
                Console.WriteLine("StreamEventOpenCompleted");
                break;
            case NSStreamEvent.ErrorOccurred:
                Console.WriteLine("StreamEventErroOccurred");
                break;
            case NSStreamEvent.EndEncountered:
                Console.WriteLine("StreamEventEndEncountered");
                break;
            default:
                Console.WriteLine("Stream present but no event");
                break;

        }
    }

}

在将显示我刚刚从外部附件读取的数据的 ViewController 中,我们将其全部连接起来。在 ViewDidLoad 中,创建观察者,以便视图知道设备何时触发了事件。还要检查我们是否连接到正确的附件并打开一个会话。

  public EASessionController _EASessionController;
  EAAccessory[] _accessoryList;
  EAAccessory _selectedAccessory;
  NSString SessionDataReceivedNotification = (NSString)"SessionDataReceivedNotification";
  string myDeviceProtocol = "com.my-microchip-reader.1234";

  public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        NSNotificationCenter.DefaultCenter.AddObserver(EAAccessoryManager.DidConnectNotification, EADidConnect);
        NSNotificationCenter.DefaultCenter.AddObserver(EAAccessoryManager.DidDisconnectNotification, EADidDisconnect);
        NSNotificationCenter.DefaultCenter.AddObserver(SessionDataReceivedNotification, SessionDataReceived);
        EAAccessoryManager.SharedAccessoryManager.RegisterForLocalNotifications();



        _EASessionController = EASessionController.SharedController();
        _accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;

        foreach (EAAccessory acc in _accessoryList)
        {
            if (acc.ProtocolStrings.Contains(myDeviceProtocol))
            {
                // Connected to the correct accessory
                _selectedAccessory = acc;
                _EASessionController.SetupController(acc, myDeviceProtocol);
                _EASessionController.OpenSession();
                lblEAConnectionStatus.Text = acc.Name;

                Console.WriteLine("Already connected via bluetooth");

            }
            else
            {
                // Not connected
            }
        }

}

创建 DidConnect、DidDisconnect 和 SessionDataReceived 方法。连接/断开连接时,设备名称仅在某些标签上更新,我在文本字段中显示数据。

void EADidConnect(NSNotification notification)
    {
        EAAccessory connectedAccessory = (EAAccessory)notification.UserInfo.ObjectForKey((NSString)"EAAccessoryKey");
        Console.WriteLine("I did connect!!");
        _accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;

        // Reconnect and open the session in case the device was disconnected
        foreach (EAAccessory acc in _accessoryList)
        {
            if (acc.ProtocolStrings.Contains(myDeviceProtocol))
            {
                // Connected to the correct accessory
                _selectedAccessory = acc;
                Console.WriteLine(_selectedAccessory.ProtocolStrings);

                _EASessionController.SetupController(acc, myDeviceProtocol);
                _EASessionController.OpenSession();

            }
            else
            {
                // Not connected
            }
        }

        Console.WriteLine(connectedAccessory.Name);

        // Update a label to show it's connected
        lblEAConnectionStatus.Text = connectedAccessory.Name;

    }

    void EADidDisconnect(NSNotification notification)
    {

        Console.WriteLine("Accessory disconnected");
        _EASessionController.CloseSession();
        lblEAConnectionStatus.Text = string.Empty;

    }


    /// <summary>
    /// Data receieved from accessory
    /// </summary>
    /// <param name="notification"></param>
    void SessionDataReceived(NSNotification notification)
    {

        EASessionController sessionController = (EASessionController)notification.Object;

        nuint bytesAvailable = 0;


        while ((bytesAvailable = sessionController.ReadBytesAvailable()) > 0)
        {

            // read the data as a string

            NSData data = sessionController.ReadData(bytesAvailable);
            NSString chipNumber = new NSString(data, NSStringEncoding.UTF8);

           // Displaying the data
            txtMircochipNumber.Text = chipNumber;
           }


        }
于 2017-07-13T00:45:56.890 回答