シリアル TX ポート経由で基本的な文字列を送信する Arduino Duemilanove セットアップがあります。もう一方の端では、ドックコネクタのシリアルポートを介してメッセージを受信するように iPhone をセットアップしています。
次のコマンドを実行するとcat /dev/tty.iap 9600
、Arduino が送信しているメッセージがターミナル ウィンドウに表示されます。メッセージはHello iPhone
、画面全体にメッセージを繰り返し表示する while ループを介して送信されています。
そのため、開発中の iPhone アプリでメッセージを読み取ろうとすると、問題が発生します。アプリ内の基本的なシリアル コンソール用のシンプルな GUI をまとめたところ、次のような文字が表示されます。
物理的なホームボタンが機能しないため、ボタン付きのオーバーレイがあります。
このチュートリアルから見つけたコードを使用してシリアルポートを開いています
シリアル インターフェイス ファイルは次のようになります。
JailbrokenSerial.h
#import <Foundation/Foundation.h>
#include <termios.h> /* POSIX terminal control definitions */
@protocol JailbrokenSerialDelegate
- (void) JailbrokenSerialReceived:(char)ch;
@end
@interface JailbrokenSerial : NSObject {
NSThread *thread;
__unsafe_unretained id<JailbrokenSerialDelegate> _receiver; // added "__unsafe_unretained" because of SO thread
int fd;
struct termios gOriginalTTYAttrs;
BOOL debug; // will print debugging message if YES
BOOL nonBlock; // will act as nonblocking mode. delegate 'receiver' required
char receivedCh;
}
@property (nonatomic, assign) id<JailbrokenSerialDelegate> receiver;
@property (nonatomic, assign) BOOL debug;
@property (nonatomic, assign) BOOL nonBlock;
- (id)initWithReceiver:(id<JailbrokenSerialDelegate>) receiver;
- (BOOL)open:(int)baudRate;
- (BOOL)isOpened;
- (void)close;
- (ssize_t)read:(void *)buffer length:(size_t)len;
- (void)write:(const char *)message length:(int)len;
- (void)write:(NSString *)message;
@end
JailbrokenSerial.m
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <sys/ioctl.h>
#import "JailbrokenSerial.h"
@interface JailbrokenSerial ()
- (void)readPoller;
@property (nonatomic, retain) NSThread *thread;
@end
@implementation JailbrokenSerial
//@synthesize receiver = _receiver;
@synthesize debug, nonBlock;
@synthesize thread;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
fd = -1;
debug = false;
nonBlock = false;
self.thread = nil;
}
return self;
}
- (id)initWithReceiver:(id<JailbrokenSerialDelegate>) receiver {
self = [self init];
if(self) {
[self setReceiver:receiver];
}
return self;
}
- (void)dealloc {
[thread cancel];
//[thread release];
// [super dealloc];
}
- (void)readPoller {
// // Top-level pool
char ch;
int buf_len;
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
while(![[NSThread currentThread] isCancelled]) {
buf_len = read(fd,&ch,1); // Read 1 byte over serial. This will block
// NSLog(@"Loop");
// If something can be read
if(buf_len == 1) {
receivedCh = ch;
[self performSelectorOnMainThread:@selector(sendReadMessage) withObject:nil waitUntilDone:YES];
}
else if(buf_len == -1) {
[NSThread sleepForTimeInterval:0.1]; // set the wait time for the read loop - IMPORTANT
}
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
//[pool release];
}
- (void)sendReadMessage {
[self.receiver JailbrokenSerialReceived:receivedCh];
}
- (ssize_t)read:(void *)buffer length:(size_t)len {
return read(fd, buffer, len);
}
- (BOOL)open:(int)baudRate {
struct termios options;
int fileDescriptor = open("/dev/tty.iap", O_RDWR | O_NOCTTY | O_NONBLOCK);
// int fileDescriptor = open("/dev/tty.iap", O_RDWR | O_NOCTTY | O_NDELAY);
if (fileDescriptor == -1)
{
if(debug) NSLog(@"Error opening serial port %@ - %s(%d).", @"/dev/tty.iap", strerror(errno), errno);
close(fileDescriptor);
return false;
}
if(ioctl(fileDescriptor, TIOCEXCL) == -1) {
if(debug) NSLog(@"Error setting TIOCEXCL on %@ - %s(%d).", @"/dev/tty.iap", strerror(errno), errno);
close(fileDescriptor);
return false;
}
if(nonBlock) {
if(fcntl(fileDescriptor, F_SETFL, O_NONBLOCK) == -1) {
if(debug) NSLog(@"Error setting O_NONBLOCK on %@ - %s(%d).", @"/dev/tty.iap", strerror(errno), errno);
close(fileDescriptor);
return false;
}
}
else {
if(fcntl(fileDescriptor, F_SETFL, 0) == -1) {
if(debug) NSLog(@"Error clearing O_NONBLOCK on %@ - %s(%d).", @"/dev/tty.iap", strerror(errno), errno);
close(fileDescriptor);
return false;
}
}
tcgetattr(fileDescriptor, &gOriginalTTYAttrs);
options = gOriginalTTYAttrs;
if(debug) {
NSLog(@"Current input baud rate is %d\n", (int) cfgetispeed(&options));
NSLog(@"Current output baud rate is %d\n", (int) cfgetospeed(&options));
}
// Set raw input (non-canonical) mode, with reads blocking until either a single character
// has been received or a one second timeout expires.
// See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") for details.
cfmakeraw(&options);
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 10;
// The baud rate, word length, and handshake options can be set as follows:
cfsetspeed(&options, baudRate); // Set 19200 baud
options.c_cflag &= ~PARENB; // Set as 8-1-N
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
if(debug) {
NSLog(@"Input baud rate changed to %d\n", (int) cfgetispeed(&options));
NSLog(@"Output baud rate changed to %d\n", (int) cfgetospeed(&options));
}
tcsetattr(fileDescriptor, TCSANOW, &options);
fd = fileDescriptor;
if(nonBlock) {
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(readPoller) object:nil];
[thread start];
}
return true;
}
- (BOOL)isOpened {
if(fd==-1)
return false;
else
return true;
}
- (void)close{
if(fd != -1) {
close(fd);
fd = -1;
[thread cancel];
self.thread = nil;
if(debug)
NSLog(@"Closed");
}
}
- (void)write:(const char *)message length:(int)len {
if(fd != -1) {
write(fd, message, len);
}
if(debug) {
NSLog(@"%d bytes wrote", len);
}
}
- (void)write:(NSString *)message {
[self write:[message UTF8String] length:[message length]];
}
@end
シリアル コンソール ビュー コントローラー内のシリアル ポートを次のように開いています。
ViewControllerSerialConsole.h
#import <UIKit/UIKit.h>
// #import "Serial.c"
#import "JailbrokenSerial.h"
#define BUFFER_LEN 1024
@interface ViewControllerSerialConsole : UIViewController <JailbrokenSerialDelegate> {
JailbrokenSerial *serial;
UInt8 rxBuffer[BUFFER_LEN];
UInt8 txBuffer[BUFFER_LEN];
}
@property (weak, nonatomic) IBOutlet UITextField *textEntry;
@property (weak, nonatomic) IBOutlet UIButton *btnSend;
@property (weak, nonatomic) IBOutlet UITextView *serialView;
/*
@property (weak, nonatomic) IBOutlet UIToolbar *btnOpen;
@property (weak, nonatomic) IBOutlet UIToolbar *btnClose;
@property (weak, nonatomic) IBOutlet UIToolbar *btnDone;
*/
- (IBAction)donePressed:(id)sender;
- (IBAction)sendString:(id)sender;
- (IBAction)openSerial:(id)sender;
- (IBAction)closeSerial:(id)sender;
@end
ViewControllerSerialConsole.m
#import "ViewControllerSerialConsole.h"
@interface ViewControllerSerialConsole ()
@end
@implementation ViewControllerSerialConsole
@synthesize textEntry = _textEntry;
@synthesize btnSend = _btnSend;
@synthesize serialView = _serialView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setTextEntry:nil];
[self setBtnSend:nil];
[self setSerialView:nil];
/*
[self setBtnOpen:nil];
[self setBtnClose:nil];
[self setBtnDone:nil];
*/
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)donePressed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)sendString:(id)sender {
// dismiss keyboard
[self.textEntry resignFirstResponder];
}
- (IBAction)openSerial:(id)sender
{
serial.debug = YES; // debug messaages will be printed out using NSLog() if the flag is set to YES
[serial open:B9600];
NSLog(@"%c", [serial isOpened]);
serial.nonBlock = true;
serial.receiver = self;
char buffer[12];
[serial read:buffer length:12]; // will be blocked until read 5 characters.
// print line to textview
_serialView.text = [NSString stringWithFormat:@"%s",buffer];
}
- (IBAction)closeSerial:(id)sender
{
[serial close];
}
@end