How to upload large (2 blocks) orbBasic program to Sphero using Mac SDK? - sphero-api

I explore Sphero with children in the computer club. We develop simple orbBasic programs that do interesting things. I did gave up with orbBasic app on mobile devices because it is next to unusable for programming (tiny fonts, hard to edit on mobile). I found Sphero Mac SDK and we now use its orbBasicLoader to upload programs from Mac. But when our programs become larger, we found that they won't load to Sphero this way, probably because they have to split to two blocks of Sphero memory, which isn't correctly handled by Mac SDK. It is handled by orbBasic app on a device, so it is possible.
How to upload large orbBasic programs to Sphero using Mac SDK?
This is one of our programs - Snake like game for Sphero, written in orbBasic that suffers from this problem. In the space 4*4 meters (sphere is in the center of the space at program start) there is hidden 'food' and your objective is to 'eat food' by driving Sphero near the 'food' using its brightness as a guide. You end game after 5 'foods' eaten. Double shake Sphero to restart.
10 locate 0,0
20 E=0
30 X=200-rnd 400
40 Y=200-rnd 400
50 C=xpos-X
60 D=ypos-Y
70 L=sqrt(C*C+D*D)
80 if L>200 then L=200
90 O=255-L
100 RGB O,O,O
110 if L<10 then goto 140
120 delay 100
130 goto 50
140 E=E+1
150 RGB 0,E*51,0
160 delay 1000
170 if E<5 then goto 30
180 LEDC 1+rnd 7
190 delay 100
200 if dshake > 0 then goto 10
210 goto 180

I figured it out!
You have to manually upload fragments to Sphero and fragment should not be near the limit of 253. Max size of fragment of 200 works well. Is is very simple.
Here is the code of separating fragments from program:
-(void)setProgram:(NSString *)inProgram {
NSArray *lines = [inProgram componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
if( fragments != nil ) {
[fragments release];
}
char buffer[1024]= {0};
fragments = [[NSMutableArray alloc] initWithCapacity:2];
NSMutableData *data = [[NSMutableData alloc] init];
for( int i = 0; i<[lines count]; i++ ) {
NSString *currentLine = [lines objectAtIndex:i];
//strip comments - lines starting with apostrophe
if( [currentLine hasPrefix:#"'"] ) continue;
if( (data.length + [currentLine lengthOfBytesUsingEncoding:NSASCIIStringEncoding]) > 200 ) {
[fragments addObject:data];
[data release];
data = [[NSMutableData alloc] init];
}
if( [currentLine getCString:buffer maxLength:253 encoding:NSASCIIStringEncoding] ) {
[data appendBytes:buffer length:strlen(buffer)];
[data appendBytes:"\r" length:1];
}
}
[fragments addObject:data];
[data release];
}
Here is sending code:
Function must be called each time Response success received from Sphero until loaded is set to YES.
If you try to send all the fragments at once - response code -2 will be returned and program won't load.
-(void)loadToSphero {
if( loaded == YES ) {
[self abort];
[self erase];
curFragment = 0;
}
if( curFragment < [fragments count] ) {
RKOrbBasicAppendFragmentCommand *appendCmd = [[RKOrbBasicAppendFragmentCommand alloc] initWithStorageType:RKOrbBasicStorageTypeTemporary fragment:[fragments objectAtIndex:curFragment]];
[appendCmd sendCommand];
[appendCmd release];
curFragment++;
}
else {
loaded = YES;
}
}
and here is other program commands:
-(void)execute {
if( loaded == YES ) {
RKOrbBasicExecuteCommand *execCmd = [[RKOrbBasicExecuteCommand alloc] initWithStorageArea:RKOrbBasicStorageTypeTemporary startLine:10];
[execCmd sendCommand];
[execCmd release];
}
}
-(void)erase {
RKOrbBasicEraseStorageCommand *eraseCmd = [[RKOrbBasicEraseStorageCommand alloc] initWithStorageType:RKOrbBasicStorageTypeTemporary];
[eraseCmd sendCommand];
[eraseCmd release];
loaded=NO;
}
-(void)abort {
RKOrbBasicAbortCommand *abortCmd = [[RKOrbBasicAbortCommand alloc] init];
[abortCmd sendCommand];
[abortCmd release];
}

Related

best way of handling varying iDevice widths?

I'm wondering if anyone can provide some insight as to how to handle varying device sizes when designing in storyboard.
Do you have to check for device frame size before drawing views then?
Thanks.
There are two ways you can go about it. If you insist on using frames, you would want to check the frame size before drawing your views. One way you could go about it is writing a method in your utils file that will check for the device, something like this:
+ (NSString *)getHardwareModel {
AppDelegate_iPhone *appDelegate_iPhone = (AppDelegate_iPhone *) [[UIApplication sharedApplication] delegate];
size_t size;
// get the size of the returned device name
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
// allocate the space to store name
char *machine = (char*)malloc(size);
// get the device name
sysctlbyname("hw.machine", machine, &size, NULL, 0);
// place the name into a NSString
NSString *platform = [NSString stringWithCString:machine encoding: NSUTF8StringEncoding];
free(machine);
appDelegate_iPhone.hardwareModel = platform;
return platform;
}
Once we get back what device we are using, we can then set the frame accordingly. So if I wanted to check for say the iPhone6 device, I would do something like this in my setFrameSize method:
NSString *hardwareVersion = [Utils getHardwareModel];
NSString *target=#"x86_64";
NSString *deviceTarget=#"iPhone7,1";
NSRange range =[hardwareVersion rangeOfString:target ];
NSRange deviceRange =[hardwareVersion rangeOfString:deviceTarget ];
NSLog(#"device=%#",hardwareVersion);
// Setting the frame size for the progress bar
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
if (range.location!=NSNotFound ||deviceRange.location!=NSNotFound) {
float frameSize = self.view.frame.size.width;
NSLog(#"Frame width===%f", frameSize);
self.view.frame = CGRectMake(0, 0, 480, 85);
}
Another way that would avoid all of this is to use autolayout and set constraints based on the different screen sizes. https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html

AVSpeechSynthesizer - How to detect dot for milliseconds pause (iPHONE - iPAD / iOS7 xcode)

I want to read a very long text:
NSString *text =#"Long test with dot. I want speaching pause when read dot. How can I do?";
AVSpeechSynthesizer *synthesizer = [AVSpeechSynthesizer new];
[synthesizer setDelegate:self];
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:text];
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:[self.defaults objectForKey:LANGUAGE]];
utterance.rate = 0.28;
[synthesizer speakUtterance:utterance];
I want synthesizer detect dot and pause for 100 millisecond.
Is it possible? How can I do ??
I mean a dot (.)
but maybe I found a solution also fot char ',' :
NSString *text = #"Line one. LineTwo aftre a pause. Line three, pause here again.";
text = [text stringByReplacingOccurrencesOfString:#"," withString:#"."];
NSArray *a = [text componentsSeparatedByString:#"."];
for(NSString *line in a)
{
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:line];
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:[self.defaults objectForKey:LINGUA]];
utterance.rate = 0.28;
utterance.**postUtteranceDelay** = 0.3;
[self.synthesizer speakUtterance:utterance];
}

CADisplaylink code fires even when display doesn't refresh

Note: as of iOS7 this problem may only manifest in the simulator -- still testing.
I have an implementation of CADisplayLink, and I need the code to run if and only if the display actually refreshes
This doesn't happen.
Here's a very simple test program:
I start the display link running; in the first frame aLabel should display "WordFlash"; for the next 19 frames it should display "--------" and for the next 100 it should be blank; then the cycle should repeat.
Occasionally (say 1 in 8 times), and unpredictably, the screen won't refresh to display "WordFlash" though the code has indeed fired (as the counter has advanced). I need the counter to advance only if "WordFlash" has successfully displayed for exactly 1 frame.
Any ideas? I am totally stumped.
Note: This display-refresh-skipping seems to happen without correlation to the time it takes the device to execute the simple code (as in the NSLogged time-to-execute the code can be identical in two different cycles while only one cycle has successfully flashed the word.
#import "HomePwnerViewController.h"
#interface HomePwnerViewController ()
#end
#implementation HomePwnerViewController {
int counter;
UILabel *aLabel;
}
- (void)viewDidLoad
{
[super viewDidLoad];
aLabel = [[UILabel alloc] initWithFrame:self.view.frame];
[self.view addSubview:aLabel];
aLabel.alpha = 1;
aLabel.text = #"";
aLabel.textAlignment = NSTextAlignmentCenter;
[aLabel setFont:[UIFont fontWithName:#"Courier" size:50]];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(testWordFlashMethod) userInfo:nil repeats:NO];
}
- (void) displaySensitiveProcedure: (CADisplayLink *) sender {
CFTimeInterval startTime = CACurrentMediaTime();
if (counter == 0)
aLabel.text = #"FlashWord";
else if (counter < 20)
aLabel.text = #"---------";
else
aLabel.text = #"";
CFTimeInterval endTime = CACurrentMediaTime();
if (counter == 0)
NSLog(#"frame time %f frame length %f ratio %f", endTime - startTime, sender.duration, (endTime - startTime)/sender.duration);
counter++;
if (counter == 120)
counter = 0;
}
- (void) testWordFlashMethod {
CADisplayLink *DL = [CADisplayLink displayLinkWithTarget:self selector:#selector(displaySensitiveProcedure:)];
DL.frameInterval = 1;
counter = 0;
[DL addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Much obliged,
b
If I were to guess, I'd say that animating a UILabel on screen refresh is not the best way to do things. In my experience changing the text on them can take a bit of time to render. If you want to draw a dash across the screen, you might be better served by using Core Graphics API's, or a CALayer based drawing approach.
Your displaySensitiveProcedure is probably taking more than a single frame (16.7ms) to update the screen, which makes it skip a frame. If you consistently take more than that (say 20ms), you can have the DisplayLink fire less frequently to get smooth animation at a slower framerate - see the frameInterval property. I would suggest timing your callback by doing something like this:
- (void) displaySensitiveProcedure: (CADisplayLink *) sender {
CFTimeInterval startTime = CACurrentMediaTime();
... <code> ...
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(#"frame time: %f", endTime - startTime);
}

Truncate the last line of multi-line NSTextField

I'm trying to create a text field similar to Finder's file labels. I would like the last (second) line to be truncated in the middle.
I started with a multi-line NSTextField.
However, calling [self.cell setLineBreakMode:NSLineBreakByTruncatingMiddle]; results in a the text field showing only a single truncated line (no line breaks anymore).
Here is what it looks like in Finder:
If you want to wrap text like finder labels, using two labels doesn't do you any good since you need to know what the maximum breakable amount of text is on the first line. Plus, if you're building something that will display a lot of items two labels will overburden the GUI needlessly.
Set your NSTextField.cell like this:
[captionLabel.cell setLineBreakMode: NSLineBreakByCharWrapping];
Then find the code for "NS(Attributed)String+Geometrics" (Google it, it's out there). You must #import "NS(Attributed)String+Geometrics.h"
to measure text. It monkey patches NSString and NSAttributedString
I include the following code to wrap text exactly how Finder does in its captions. Using one label below the icon it assumes that, like Finder, there will be two lines of caption.
First this is how you will call the following code in your code:
NSString *caption = self.textInput.stringValue;
CGFloat w = self.captionLabel.bounds.size.width;
NSString *wrappedCaption = [self wrappedCaptionText:self.captionLabel.font caption:caption width:w];
self.captionLabel.stringValue = wrappedCaption ? [self middleTruncatedCaption:wrappedCaption withFont:self.captionLabel.font width:w] : caption;
Now for the main code:
#define SINGLE_LINE_HEIGHT 21
/*
This is the way finder captions work -
1) see if the string needs wrapping at all
2) if so find the maximum amount that will fit on the first line of the caption
3) See if there is a (word)break character somewhere between the maximum that would fit on the first line and the begining of the string
4) If there is a break character (working backwards) on the first line- insert a line break then return a string so that the truncation function can trunc the second line
*/
-(NSString *) wrappedCaptionText:(NSFont*) aFont caption:(NSString*)caption width:(CGFloat)captionWidth
{
NSString *wrappedCaption = nil;
//get the width for the text as if it was in a single line
CGFloat widthOfText = [caption widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
//1) nothing to wrap
if ( widthOfText <= captionWidth )
return nil;
//2) find the maximum amount that fits on the first line
NSRange firstLineRange = [self getMaximumLengthOfFirstLineWithFont:aFont caption:caption width:captionWidth];
//3) find the first breakable character on the first line looking backwards
NSCharacterSet *notAlphaNums = [NSCharacterSet alphanumericCharacterSet].invertedSet;
NSCharacterSet *whites = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSRange range = [caption rangeOfCharacterFromSet:notAlphaNums options:NSBackwardsSearch range:firstLineRange];
NSUInteger splitPos;
if ( (range.length == 0) || (range.location < firstLineRange.length * 2 / 3) ) {
// no break found or break is too (less than two thirds) far to the start of the text
splitPos = firstLineRange.length;
} else {
splitPos = range.location+range.length;
}
//4) put a line break at the logical end of the first line
wrappedCaption = [NSString stringWithFormat:#"%#\n%#",
[[caption substringToIndex:splitPos] stringByTrimmingCharactersInSet:whites],
[[caption substringFromIndex:splitPos] stringByTrimmingCharactersInSet:whites]];
return wrappedCaption;
}
/*
Binary search is great..but when we split the caption in half, we dont have far to go usually
Depends on the average length of text you are trying to wrap filenames are not usually that long
compared to the captions that hold them...
*/
-(NSRange) getMaximumLengthOfFirstLineWithFont:(NSFont *)aFont caption:(NSString*)caption width:(CGFloat)captionWidth
{
BOOL fits = NO;
NSString *firstLine = nil;
NSRange range;
range.length = caption.length /2;
range.location = 0;
NSUInteger lastFailedLength = caption.length;
NSUInteger lastSuccessLength = 0;
int testCount = 0;
NSUInteger initialLength = range.length;
NSUInteger actualDistance = 0;
while (!fits) {
firstLine = [caption substringWithRange:range];
fits = [firstLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont] < captionWidth;
testCount++;
if ( !fits ) {
lastFailedLength = range.length;
range.length-= (lastFailedLength - lastSuccessLength) == 1? 1 : (lastFailedLength - lastSuccessLength)/2;
continue;
} else {
if ( range.length == lastFailedLength -1 ) {
actualDistance = range.length - initialLength;
#ifdef DEBUG
NSLog(#"# of tests:%d actualDistance:%lu iteration better? %#", testCount, (unsigned long)actualDistance, testCount > actualDistance ? #"YES" :#"NO");
#endif
break;
} else {
lastSuccessLength = range.length;
range.length += (lastFailedLength-range.length) / 2;
fits = NO;
continue;
}
}
}
return range;
}
-(NSString *)middleTruncatedCaption:(NSString*)aCaption withFont:(NSFont*)aFont width:(CGFloat)captionWidth
{
NSArray *components = [aCaption componentsSeparatedByString:#"\n"];
NSString *secondLine = [components objectAtIndex:1];
NSString *newCaption = aCaption;
CGFloat widthOfText = [secondLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
if ( widthOfText > captionWidth ) {
//ignore the fact that the length might be an odd/even number "..." will always truncate at least one character
int middleChar = ((int)secondLine.length-1) / 2;
NSString *newSecondLine = nil;
NSString *leftSide = secondLine;
NSString *rightSide = secondLine;
for (int i=1; i <= middleChar; i++) {
leftSide = [secondLine substringToIndex:middleChar-i];
rightSide = [secondLine substringFromIndex:middleChar+i];
newSecondLine = [NSString stringWithFormat:#"%#…%#", leftSide, rightSide];
widthOfText = [newSecondLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
if ( widthOfText <= captionWidth ) {
newCaption = [NSString stringWithFormat:#"%#\n%#", [components objectAtIndex:0], newSecondLine];
break;
}
}
}
return newCaption;
}
Cheers!
PS Tested in prototype works great probably has bugs...find them
I suspect there are two labels there. The top one contains the first 20 characters of a file name, and the second contains any overflow, truncated.
The length of the first label is probably restricted based on the user's font settings.

Making an NSMutableString transformation without leaking memory?

I have this function within an iPhone project Objective C class.
While it's correct in terms of the desired functionality, after a few calls, it crashes into the debugger.
So I think it's a case of bad memory management, but I'm not sure where.
- (NSString *)stripHtml:(NSString *)originalText {
// remove all html tags (<.*>) from the originalText string
NSMutableString *strippedText = [[NSMutableString alloc] init];
BOOL appendFlag = YES;
for( int i=0; i<[originalText length]; i++ ) {
NSString *current = [originalText substringWithRange:NSMakeRange(i, 1)];
if( [current isEqualTo:#"<"] )
appendFlag = NO;
if( appendFlag )
[strippedText appendString:current];
if( [current isEqualTo:#">"] )
appendFlag = YES;
}
NSString *newText = [NSString stringWithString:strippedText];
[strippedText release];
return newText;
}
Every time you iterate over your for loop, you're allocating a new NSString. While these NSStrings are autoreleased, they won't actually be released until after all the processing of your last input is finished. In the meantime, you'll allocate a potentially infinite amount of memory. The solution is to create your own autorelease pool and drain it every trip through the for loop. It'll look something like this:
BOOL appendFlag = YES;
for( int i=0; i<[originalText length]; i++ ) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// rest of for loop body
[pool drain];
}
That'll free up the memory used by your current pointer right away.

Resources