External Accessory Framework?
The error message is ValueError: no Objective-C class named ‘EAAccessoryManger’ found . Is it because the framework is not linked to Pythonista? Is there any other way that I can make it work besides asking @omz to link the framework? I am trying to talk to a Lego Mindstorms brick using the bluetooth connection. The brick uses bluetooth 2.1 and is a MFi device. I did some research and it seems that EA framework is the way to go.
omz отредактировано omz
I’m not sure if what you’re trying to achieve is possible at all, or if the app would need special entitlements to talk to an MFi device. However to get the class name to resolve, all you need to do is load the framework, like this:
from objc_util import * NSBundle = ObjCClass('NSBundle') ea_framework = NSBundle.bundleWithPath_('/System/Library/Frameworks/ExternalAccessory.framework') ea_framework.load() # This should work now: EAAccessoryManager = ObjCClass('EAAccessoryManager') print EAAccessoryManager
ywangd отредактировано ywangd
Thanks @omz ! The above code works. I was able to chain two more method calls as follows:
print EAAccessoryManager.sharedAccessoryManager().connectedAccessories()
It returns an empty tuple as I don’t currently have the Lego brick around. I’ll do some more tests later. As I understand it, in order to talk to the Lego brick, following key/value settings are needed in the app’s info.plist file.
UISupportedExternalAccessoryProtocols COM.LEGO.MINDSTORMS.EV3
I thhink it can probably be set programmatically with following objc code?
session = [[EASession alloc] initWithAccessory:accessory forProtocol:protocolString];
which is something can be translated using objc_util . It is just my guess though. I’ll give it a try later and report the results.
omz отредактировано
As I understand it, in order to talk to the Lego brick, following key/value settings are needed in the app’s info.plist file.
That’s what I suspected. The thing is, there is absolutely no way for you to change what’s in the app’s Info.plist.
ywangd отредактировано
@omz Are you saying the protocol string MUST be declared in the plist file. Otherwise it is impossible to set the it programmatically within the app?
JonB отредактировано
have youe tried the cb module?
omz отредактировано
Are you saying the protocol string MUST be declared in the plist file. Otherwise it is impossible to set the it programmatically within the app?
Yes, that’s one of the security mechanisms of iOS. Certain things need to be declared in the Info.plist file and can’t be changed at runtime. This is also used for things like location access, background audio, push notifications, etc.
omz отредактировано omz
@JonB Using the cb module is unfortunately not an option for a Bluetooth 2.1 MFi device. It only works with Bluetooth 4 (BLE).
ywangd @JonB отредактировано
have youe tried the cb module?
cb module only works with Bluetooth LE devices (4.0). But the Lego brick is on Bluetooth v2.1 .
ywangd отредактировано
@omz I tried a bit more with the brick connected to the iPad. I was able to see the brick. But the session creation failed silently by return None . The code is as follows (after your sample code):
accessories = EAAccessoryManager.sharedAccessoryManager().connectedAccessories() accessory = accessories[0] print accessory EASession = ObjCClass('EASession') session = EASession.alloc().initWithAccessory_forProtocol_(accessory, 'COM.LEGO.MINDSTORMS.EV3') print session
The output is
< connected: YES connectionID: 27234188 name: MFI Accessory manufacturer: LEGO modelNumber: DM240411 serialNumber: firewareRevision: 1.0.0 harewareRevision: 1.0.0 protocols: ( "COM.LEGO.MINDSTORMS.EV3" ) delegate: (null) >None
Notice that the last None is the printing result of session . Is this because that the external framework setting is not in Pythonista’s info.plist ?
Gerzer отредактировано
@omz Is there a place I can find the cb module reference? It doesn’t seem to be included in the latest beta’s in-app docs.
omz отредактировано
@Gerzer Sorry, I didn’t notice that I accidentally removed that while moving some things around in the documentation – will be back in the next build.
Webmaster4o отредактировано
@ywangd if you do get pythonista to talk to a Mindstorms brick, make sure to post the code, I’d love to see what I can do with this! I haven’t touched my brick in about a year, but I wonder if I could get pythonista to control the brick’s motors, or even monitor sensor input! I’d be really cool to read something like the ultrasonic sensor input on iOS.
Webmaster4o отредактировано Webmaster4o
This guy seems to have it working http://youtu.be/tZn9DQtC0So it looks like he has code running on a Mac that talks to EV3
ywangd @Webmaster4o отредактировано
This guy seems to have it working http://youtu.be/tZn9DQtC0So it looks like he has code running on a Mac that talks to EV3
This is different from what I’d like to achieve. It used a Mac as a bridge between iOS and EV3 so that iOS was not directly talking to the EV3. I have done similar things as well. On the iOS side I created a socket client in Pythonista and talk to a server running on Mac that forwards commands from iOS to EV3. A few libraries are available to control an EV3 from a PC. The one I used is called MonoBrick. What I’d like to do now is to avoid the PC bridge and let iOS directly control the EV3. However, it does NOT seem to be possible from within Pythonista due to the info.plist setting. A communication session could NOT be created (though Pythonista was able to discover the existence of EV3). It seems that a dedicated app is needed to achieve the goal.
Saved searches
Use saved searches to filter your results more quickly
Cancel Create saved search
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
rvndios / EADemo Public
External accessory framework demo
rvndios/EADemo
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
External accessory framework test hardware
I need to write an app for a client the uses the ExternalAccessory framework to communicate with some hardware, I read in ‘External Accessory Programming Exercises’ by Norman McEntire that the Simulator includes 2 test accessories, however when I run the EADemo sample app from Apple on the Simulator it says there are no accessories connected. Does anyone know how I can simulate an accessory so I can learn the framework, or is there an inexpensive piece of hardware I can use to learn with. Thanks in advance!
asked Jan 9, 2012 at 21:27
marchinram marchinram
5,748 5 5 gold badges 47 47 silver badges 59 59 bronze badges
3 Answers 3
true, but the redpark serial cable is an accessory itself, it comes with its own sdk but you can use the eaframework and write your own code.
But its debatable how useful it is for learning the framework,
A lot of developers including me experiment with it using the arduino micro computer, which is a really cheap little microprocessor that is used for all kinds of hobbyist experiments.
there are two books you should look at.
Building iPhone OS Accessories: Use the iPhone Accessories API to Control and Monitor Devices By: Ken Maskrey Publisher: Apress Pub. Date: June 7, 2010 Print ISBN: 978-1-4302-2931-5 Web ISBN: 1-4302-2931-4
iOS Sensor Apps with Arduino By: Alasdair Allan Publisher: O’Reilly Media, Inc. Pub. Date: September 19, 2011 Print ISBN-13: 978-1-4493-0848-3
You can also look at this recent blog we authored.
External Accessory framework: What? Why? How?
E ver wondered whether you can connect any gadget to your iPhone and communicate with it ? Do you have a cool gadget which needs an iOS app to broaden it’s user base?
This article will answer all your questions!
First things first. For any gadget to communicate with an iOS device, the gadget manufacturer should enroll themselves to Apple’s MFi program.
A protocol is common set of rules that both the gadget and the iOS device should adhere to for data transmission. It is like a language or medium that is understood by both the entities.
Assuming you have all of the above handy, let’s move on.
As always, Apple has provided a framework which makes it very easy for developers to establish communication with electronic gadgets connected to the iOS device, the External Accessory Framework.
Using this framework, we can communicate with accessories connected to a device by the Apple Lightning connector, or connected wirelessly through Bluetooth.
Let’s understand the key components of this framework before we deep dive into the code.
This object helps in coordinating with the connected gadgets. We have only one instance of this object for an iOS device and is used to receive connection or disconnection notifications of all the gadgets communicating with your iOS device.
2. EAAccessory
This is the logical representation of your real world gadget in code. It contains all the information about the gadget like the serial number, model number, manufacturer, firmware version and supported communication protocols.
3. EASession
An object that helps managing communication between the gadget and your application. There should be only one session running for every gadget that we connect to.
Let’s get started with a sample project.
How to send data to the gadget?
Data is sent to the gadget by writing into the output stream. We will store the data to be sent in to an array of UInt8 values:
private var writeBuffer:[UInt8] = [0,0,0,0]
We can write this data into the output stream only if there is space available. The code looks as follows:
private func writeToStream() while self.session?.outputStream?.hasSpaceAvailable ?? false && !writeBuffer.isEmpty guard let bytesWritten = self.session?.outputStream?.write(&writeBuffer, maxLength: writeBuffer.count) else return
>
if bytesWritten == -1 return
>
else if bytesWritten > 0 writeBuffer.replaceSubrange(0.. >
>
>
We are placing the write operation in a loop so that we can constantly check for availability of space in the output stream and also write until all the bytes in the writeBuffer are written.
The output stream’s write method accepts two parameters:
- Address of the array that holds the bytes to be written
- The length of the byte array
In return, we receive an integer value that indicates the number of bytes successfully written. Thus we can eliminate those bytes from the writeBuffer array.
In case the write to the output stream fails, -1 is returned to handle the error scenario gracefully.
The write to the output stream can be initiated when we receive the event Stream.Event.hasBytesAvailable in the delegate callback method
How to accept data sent by the gadget?
Data sent by the gadget is received on the input stream of the session
We should start reading from the input stream when we receive the Stream.Event.hasBytesAvailable event in the delegate callback. The data can be fetched from the stream as follows:
private func readFromStream() -> Data? var readBuffer = [UInt8]()
let BUF_LEN = 128
var buf = [UInt8].init(repeating: 0x00, count: BUF_LEN) while (self.session?.inputStream?.hasBytesAvailable) ?? false guard let bytesRead = session?.inputStream?.read(&buf, maxLength: BUF_LEN) else <
return nil
>
if bytesRead == -1 return nil
>
else if bytesRead > 0 readBuffer.append(contentsOf: buf.prefix(bytesRead))
>
>
return Data.init(readBuffer)
>
Very similar to the write operation, the read is also iterative so that we keep reading until there are bytes available in the input stream.
You can also notice how the read operation of the stream is similar to the write operation in terms of the input parameters and return values.
The write operation accepted the address of an array of values whereas read operation accepts the address of an array that can hold the bytes that were read from the stream
These read and write operations can be called at our convenience based on the events received in the delegate callback methods
Now that we have successfully completed sending and receiving data from the gadget, we can consider closing the session established. Ignoring this would lead to battery draining as the iOS device will try to keep the connection alive with the gadget.
Closing the session involves:
- Closing the streams
- Removing the streams from the scheduled run loops
- Nullifying the delegate callbacks of the streams
- Nullifying the session instance
func closeSession() session?.inputStream?.close()
session?.inputStream?.remove(from: RunLoop.current, forMode: .default)
session?.inputStream?.delegate = nil session?.outputStream?.close()
session?.outputStream?.remove(from: RunLoop.current, forMode: .default)
session?.outputStream?.delegate = nil session = nil
>
We were able to establish a session with the gadget, send/receive data and finally terminate the connection with gadget. But the key to initiating all these activities is enroll your gadgets with Apple.
Sidenote:
Say you were able to successfully receive data from the device or have a list of user input values you wish to send to the gadget. Are you wondering how to make sense of this long list of raw bytes that you have got?
Talk to the hardware and firmware engineers involved in the development of the gadget to understand the protocol in which data has to be formatted for communication
Please refer this link for more details about the process involved and support provided by Apple to enroll into the MFi Program.
Refer to the ExternalAccessory project in my Github repo for complete source code. Please make sure to change the protocol in the Info.plist and ViewController.swift files before proceeding any further.