Categories
Uncategorized

My day at GM’s “Makers Hustle Harder” hackathon in Tampa

Last Saturday in Tampa was sunny, cloudless, and warm, and still I chose to spend half the morning and half the afternoon indoors to participate in GM’s Makers Hustle Harder hackathon at Tampa Hackerspace. And it was worth it — I got to meet new people, hang out with friends, work on some interesting tech, and even won a prize!

GM is opening their in-car console system— called NGI, which is short for Next Generation Infotainment — to third-party app developers in a way that no other auto manufacturer has before, and they’re promoting the NGI SDK with a series of hackathons taking place this year. The first hackathon took place here in Tampa on Saturday, March 4th; there will also be hackathons in Boston and Chicago later this year.

Using the NGI SDK, developers can write apps for the in-car infotainment consoles located in many GM vehicle center dashboards, like the one pictured above. The SDK gives you access to:

  • An 800-pixel high by 390-pixel wide touchscreen to receive input from and display information to the user
  • The voice system to respond to user commands and provide spoken responses to the user
  • Data from nearly 400 sensors ranging from the state of controls (buttons and the big dial) to instrumentation (such as speed, trip odometer, orientation) to car status information (Are there passengers in the car? Are the windows open or closed?) and more.
  • The navigation system to get and set navigation directions
  • The media system to play or stream audio files
  • The file system to create, edit, and delete files on the system
  • An inter-app communication system so that apps can send messages to each other

The SDK allows you to build and test apps for GM cars on your own computer. It comes with an emulator that lets you see your apps as they would appear on the car’s display, simulate sensor readings, and debug your app with a specialized console.

If you’d like to try your hand at building apps for NGI, check out my introductory tutorial, Build your first app for GM’s Next Generation Infotainment (NGI) in-car platform.

Crown, a local auto dealer, provided 3 NGI-equipped GM vehicles: 2 Buick Lacrosse sedans (pictured above), and a GM Sierra truck:

The NGI team were there to answer our questions and help us install our apps onto the in-car console to give them some non-emulator, on-the-real-thing testing. I gave one of my apps, Shotgun, a “smoke test” early in the morning on the Sierra’s console…

…and I have to say that there’s nothing like the feeling when your code runs for the first time on a completely new-to-you platform. I also ran a smoke test for WeatherEye, an app written by my teammate Chris Woodard (whom I know from his Meetup group, Tampa Bay Cocoaheads), and it worked as well.

Tampa Hackerspace was a hive of coding activity, what with teams working on all sorts of projects. With about three hours remaining in the allotted hacking time, I came up with an idea for an app: a referee for the car game classically known as “Chinese Fire Drill”. Here’s how it works:

  • Four people get in the car, close the doors, and someone starts the app. They’ll see this screen:

  • When everyone’s ready, someone in the front presses the start button.
  • If any of the doors are open when the start button is pressed, the players will be told to close all the doors first:

  • If all the doors are closed when the start button is pressed, the game begins. The screen looks like this:

  • Players exit the car, run around it once, return to their original seat, and close their doors.
  • The game ends when all four doors are closed, at which point the time it took them to complete the drill is displayed:

Yeah, the app’s not pretty, but that’s not what hackathons are about — they’re about working code in the time allotted. If you’d like to see the code, I’ve put it in a repo on my GitHub account.

An hour later, I had working code, an available vehicle, three other people to playtest the game with me, and two camera operators to record video of a test runs. We played the game twice, and we were giggling all the time. Fitness Fire Drill (I decided to not call it Chinese Fire Drill, as the term comes from an era when “Chinese” was used as a pejorative adjective to mean sloppy, sub-par, or amateurish) was a success!

Everyone who built a project presented it at the end of the day to the panel of judges, and the organizers saved Fitness Fire Drill for the very end — it got a lot of laughs.

My wife Anitra was flying out early the following morning on business, so rather than stay for the hackathon dinner and judges’ results, I high-tailed it home to have dinner with her. Before going to bed, I noticed that Chris had sent me an email telling me that I’d won the “Judges’ Fetish” prize for Fitness Fire Drill, which was a pleasant surprise.

My thanks to GM and Tampa Hackerspace for making this event happen!

Categories
Uncategorized

While I’m looking for work, can I make a custom iOS game for you? For FREE?

Last year, I released a free game in the App Store called Aspirations Winery’s Wine Crush, or Wine Crush for short. Here’s a short video (one minute and thirty seconds) showing it in action:

The quickest way I can describe it is “Like that candy game, but with wine!”. It’s a “match 3 or more of the same item” kind of game, where you advance by earning enough points to graduate to the next level.

Here are screenshots of the first four levels:

wine crush - first 4 levels

If you’d like to play Wine Crush, go ahead — it’s free, and available right now in the App Store!
download on the app store

I wrote Wine Crush for my friends at Aspirations Winery so that they’d have a promotional tool that most wineries don’t have: their very own branded game for the iPhone and iPad. The icons in the game are wine-related, the background images are taken directly from their own wine bottle labels, and the game presents the user with lots of information about the winery. I think that they like being able to say that they have their very own branded game.

My current contract will be winding down in a couple of weeks, and I’m looking for new opportunities.

I’ve been looking at different ways to get the word out about my availability, and one of my first ideas was to create a version of Wine Crush with my own personal branding, icons and backgrounds that represented me and the kind of work I do, bundled with additional information about myself, including a resume and even a way to contact me directly from the app.

It then occurred to me that it might be more interesting if I also offered to make branded versions of the game for other people or organizations, with icons, music, background graphics, and additional information that suited them. So that’s what I’m doing — for no charge at all — for a limited number of people, for a limited time. All I ask in return is that that the app be made available for free on the App Store, with my name listed as the developer.

Would you like your own “Candy Crush”-style app, either for yourself or your organization? Are you an artist who wants a videogame platform to show off your art skills? Or would you rather simply talk to someone capable of putting together such a game who also has great communications skills and unusual promotional ideas, just to see what he can do for you? If the answer is “yes” to any of these questions, drop me a line at joey@joeydevilla.com and let’s talk!

Categories
Uncategorized

“Oblique Strategies” and Tampa iOS Meetup’s February 2017 session

This past Tuesday, I held another session of Tampa iOS Meetup, which I’ve changed to be considerably more hands-on and aimed squarely at beginning developers. This session covered building “Magic 8-Ball”- or “executive decision maker”-style apps — the kind where the user presses a button and the app presents a randomly-selected response.

For the benefit of the people who attended, for those who couldn’t make it, and for the curious, I’ll walk you through the process of coding an app called Oblique Strategies, a “press a button and get a random response” app based on Brian Eno (as in the avant-garde musician and producer) and Peter Schmidt’s (the visual artist) creativity tool.

What is Oblique Strategies?

Eno and Schmidt created Oblique Strategies in late 1974 as a tool to help artists, writers, musicians, and other creative types overcome creative blocks. The original version was a deck of cards, each with a constraint, question, or suggestion that encouraged what was then called “lateral thinking” and now called “thinking outside the box”. If you were feeling creatively blocked, you’d shuffle the deck and then draw one or more cards, each of which would say something such as:

  • Abandon one stricture.
  • Emphasize repetitions.
  • Is there something missing?
  • What would your closest friend do?
  • Use an old idea.

If you’d like to try out Oblique Strategies, there are a number of web versions: herehere, here, and here, for starters.

In this exercise, we’ll remake Oblique Strategies as an app. Here’s what the end result will look like:

The apps is pretty simple: the user taps the Get a strategy button, and a randomly-selected strategy appears below it.

Download the starter project

Rather than have you type in all the strategies and monkey around with the user interface layout, I’ve already done those things and incorporated them into a starter project that you can download and use for this coding exercise.

Click here to download the starter project.

Once you’ve downloaded the project, unzip it, which will give you a folder containing the entire project. Open that folder, then open Oblique Strategies.xcodeproj in Xcode.

Connect the view objects to the underlying view controller


The Oblique Strategies app has two user interface objects that we need to connect to the underlying code:

  1. A button, which the user presses to get a new random strategy. The button causes something to happen (that is, it executes code) in response to an event (which in this case, is the user pressing it). This requires an action connection.
  2. A label, which the app uses to display the strategy. In order to be able to change the text contents of the label, the underlying code needs to be able to refer to it using a variable. This requires an outlet connection.

Let’s start by creating the outlet connection for the label:

Do the following:

  • We need to see the main storyboard. Do this by selecting the Main.storyboard file in the project navigator.
  • The storyboard should appear. Make sure that the label that we’ll use to display the randomly-selected strategy (its default text is “Your strategy will appear here.”) is visible.
  • Turn on the assistant editor by clicking on the assistant editor button (see the screenshot above).
  • The center of your Xcode window should now be split in two, with one part still showing the main storyboard, but another part showing the underlying view controller code.
  • Control-drag from the label to the underlying code, inside the ViewController class, just below the line that reads class ViewController: UIViewController, then drop.

  • A pop-up will appear. In it:
    • Make sure that the selection in the Connection drop-down list is set to Outlet.
    • You”ll need to give the connection a name in the Name text field. Set it to strategyLabel.
    • To make the connection, click the Connect button.

  • Xcode will add a new instance variable to the class named strategyLabel. We’ll use this variable to refer to the label so that we can change its text.

Now it’s time to connect the button.

Do the following:

  • Control-drag from the button to the underlying code, inside the ViewController class, after all the other functions in the class, then drop.

A pop-up will appear. In it:

  • Make sure that the selection in the Connection drop-down list is set to Action.
  • You”ll need to give the connection a name in the Name text field. Set it to getStrategyButtonPressed.
  • You’ll need to specify the event that the connection should respond to. Select Touch Up Inside in the Event drop-down menu, which specifies that the event we want to respond to is one where the user presses down on and releases his/her finger while it’s still within the bounds of the button.
  • To make the connection, click the Connect button.

Xcode will add a new empty method to the class named getStrategyButtonPressed. We’ll use this method to carry out actions in response to the button being pressed.

We can test both the button and label connections with a single line of code. Change the contents of the getStrategyButtonPressed method so that it looks like this:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  strategyLabel.text = "You pressed the button!"
}

Run the app and press the Get a strategy button. The result should look like this:

Let’s code!

Near the top of the class, you should see a declaration that looks like this:

let strategies = [
  "Remove specifics and convert to ambiguities.",
  "Think of the radio.",
  "Don’t be frightened of clichés.",
  "Abandon one stricture.",
  "What is the reality of the situation?",
  "Simple subtraction.",
  "Are there sections? Consider transitions.",
  "Turn it upside down.",
  "Go slowly all the way round the outside.",
  "Remember: a line has two sides.",
  "Two words: Infinitesimal gradations.",
  "Make a list of everything you might do, do last item on list.",
  "Change instrument roles.",
  "Into the impossible.",
  "One word: accretion.",
  "Ask people to work against their better judgement.",
  "Disconnect from desire.",
  "Take away the elements in order of apparent non-importance.",
  "Emphasize repetitions.",
  "Don’t be afraid of things because they’re easy to do.",
  "Is there something missing?",
  "Don't be frightened to display your talents.",
  "Use unqualified people.",
  "Breathe more deeply.",
  "How would you have done it?",
  "Honor thy error as a hidden intention.",
  "Emphasize differences.",
  "Only one element of each kind.",
  "Do nothing for as long as possible.",
  "Bridges. Build? Burn?",
  "One word: water.",
  "You don’t have to be ashamed of using your own ideas.",
  "Make a sudden, destructive unpredictable action; incorporate.",
  "Tidy up.",
  "Consult other sources.",
  "Do the words need changing?",
  "Use an unacceptable color.",
  "Ask your body.",
  "Humanize something free of error.",
  "Use filters.",
  "Balance the consistency principle with the inconsistency principle.",
  "Fill every beat with something.",
  "Discard an axiom.",
  "Listen to the quiet voice.",
  "What wouldn’t you do?",
  "Is it finished?",
  "Decorate, decorate!",
  "Put in earplugs.",
  "Give the game away.",
  "Reverse.",
  "Abandon normal instruments.",
  "Trust in the you of now.",
  "Use fewer notes.",
  "What would your closest friend do?",
  "Repetition is a form of change.",
  "Two words: distorting time.",
  "Give way to your worst impulse.",
  "Make a blank valuable by putting it in an exquisite frame.",
  "The inconsistency principle.",
  "Ghost echoes.",
  "Don't break the silence.",
  "You can only make one dot at a time.",
  "Discover the recipes you are using and abandon them.",
  "Just carry on.",
  "Comrades.",
  "(Organic) machinery.",
  "Courage!",
  "What mistakes did you make last time?",
  "You are an engineer.",
  "Consider different fading systems.",
  "Remove ambiguities and convert to specifics.",
  "Mute and continue.",
  "Look at the order in which you do things.",
  "It is quite possible (after all).",
  "Go outside. Shut the door.",
  "Don't stress one thing more than another.",
  "Do we need holes?",
  "Two words: cluster analysis.",
  "Work at a different speed.",
  "Do something boring.",
  "Look closely at the most embarrassing details and amplify them.",
  "Define an area as ‘safe’ and use it as an anchor.",
  "Mechanicalize something idiosyncratic.",
  "Overtly resist change.",
  "Emphasize the flaws.",
  "Accept advice.",
  "Remember those quiet evenings.",
  "Take a break.",
  "Imagine the music as a moving chain or caterpillar.",
  "Use an old idea.",
  "Imagine the music as a set of disconnected events.",
  "Change nothing and continue with immaculate consistency.",
  "What are you really thinking about just now? Incorporate.",
  "Look at a very small object, look at its center.",
  "Not building a wall, but making a brick.",
  "The most important thing is the thing most easily forgotten.",
  "Always first steps.",
  "Question the heroic approach.",
  "Be extravagant.",
  "State the problem in words as clearly as possible.",
  "Faced with a choice, do both.",
  "Retrace your steps.",
  "Convert a melodic element into a rhythmic element.",
  "Go to an extreme, move back to a more comfortable place.",
  "Once the search is in progress, something will be found.",
  "Only a part, not the whole.",
  "From nothing to more than nothing.",
  "Be less critical more often.",
  "When is it for? Who is it for?",
  "Destroy nothing. Destroy the most important thing.",
  "Take away as much mystery as possible. What’s left?",
  "What most recently impressed you? How is it similar? What can you learn from it? What could you take from it?",
  "First work alone, then work in unusual pairs.",
  "What do you do? Now, what do you do best?",
  "Back up a few steps. What else could you have done?",
  "What were the branch points in the evolution of this entity?",
  "Try faking it.",
  "How would you explain this to your parents?",
  "Who would make this really successful?",
  "What would make this really successful?",
  "Instead of changing the thing, change the world around it.",
  "List the qualities it has. List those you'd like.",
  "What else is this like?",
  "Describe the landscape in which this belongs.",
  "Steal a solution.",
  "Assemble some of the elements in a group and treat the group.",
  "Be dirty.",
  "Lost in useless territory.",
  "Lowest common denominator.",
]

This declaration creates an array of strings named strategies. The array will act as the “deck”, from which a “card” will be randomly selected.

Let’s try displaying the last strategy in the array when the user presses the button. Change getStrategyButtonPressed() to the following:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  let lastStrategyIndex = strategies.count
  strategyLabel.text = strategies[lastStrategyIndex]
}

If you try to run the app right now, you’ll see something like this:

We’ve just encountered what’s called an “off-by-one error”. If there are x items in an array, the index of the last item isn’t x, but x-1, since array indexes start at 0.

The error is easy to fix:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  let lastStrategyIndex = strategies.count - 1
  strategyLabel.text = strategies[lastStrategyIndex]
}

Now try running the app. It should compile this time, and if you press the Get a strategy button, the app responds with the last strategy in the array:

Of course, the app would be rather uninteresting if it displayed the same result all the time. We’ll use a random number generator’s output as a way to pick a strategy from the strategies array.

Swift comes with a number of built-in random number generator functions, but the preferred general-purpose random number generator is arc4random_uniform(). It takes a positive integer n as its sole argument, and returns a pseudo-random positive integer in the range starting at 0 and ending at n – 1.

arc4random_uniform() is a little unwieldy because the argument you’re supposed to pass it is a special kind of integer: UInt32. You’ll need to convert any value you pass to it to that type. The value that arc4random_uniform() returns is also of type UInt32, which you’ll typically have to convert back into plain old Int. If you look at Swift code that uses arc4random_uniform(), you’ll often see something like this:

result = Int(arc4random_uniform(UInt32(upperBound)))

Rather than deal with all that clunkiness, let’s write a function that wraps arc4random_uniform() and frees us from having to do all those type conversions from Int to UInt32 and back again:

func randomNumber(upToButNotIncluding upperBound: Int) -> Int {
  return Int(arc4random_uniform(UInt32(upperBound)))
}

Now update getStrategyButtonPressed() so that it makes use of our new randomNumber() function:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  let numberOfStrategies = strategies.count
  strategyLabel.text = strategies[randomNumber(upToButNotIncluding: numberOfStrategies)]
}

We can even pare it down to a single line of code and make it even more concise:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  strategyLabel.text = strategies[randomNumber(upToButNotIncluding: strategies.count)]
}

We now have a properly functioning Oblique Strategies app. Run it, press the Get a strategy button, and see what strategies come up!

Add a “fade in” animation

Let’s give our app a little polish by having the strategy “fade in”. Whenever the user presses the Get a strategy button, we will:

  • Set the strategy label’s alpha (opacity) value to 0, making it completely transparent.
  • Randomly select a strategy and use it to set the text of the label.
  • Animate the label so that its alpha value is brought up to 1 over a period of 3 seconds, making it completely opaque.

Do this by updating getStrategyButtonPressed() so that it looks like this:

@IBAction func getStrategyButtonPressed(_ sender: Any) {
  strategyLabel.alpha = 0
  strategyLabel.text = strategies[randomNumber(upToButNotIncluding: strategies.count)]
  UIView.animate(withDuration: 3.0,
                 animations: {
    self.strategyLabel.alpha = 1
  })
}

Run the app. You should see the strategy fade in when you press the Get a strategy button.

All the code

If you’ve made it to this point, the code in ViewController.swift should look like this:

import UIKit


class ViewController: UIViewController {
  
  let strategies = [
    "Remove specifics and convert to ambiguities.",
    "Think of the radio.",
    "Don’t be frightened of clichés.",
    "Abandon one stricture.",
    "What is the reality of the situation?",
    "Simple subtraction.",
    "Are there sections? Consider transitions.",
    "Turn it upside down.",
    "Go slowly all the way round the outside.",
    "Remember: a line has two sides.",
    "Two words: Infinitesimal gradations.",
    "Make a list of everything you might do, do last item on list.",
    "Change instrument roles.",
    "Into the impossible.",
    "One word: accretion.",
    "Ask people to work against their better judgement.",
    "Disconnect from desire.",
    "Take away the elements in order of apparent non-importance.",
    "Emphasize repetitions.",
    "Don’t be afraid of things because they’re easy to do.",
    "Is there something missing?",
    "Don't be frightened to display your talents.",
    "Use unqualified people.",
    "Breathe more deeply.",
    "How would you have done it?",
    "Honor thy error as a hidden intention.",
    "Emphasize differences.",
    "Only one element of each kind.",
    "Do nothing for as long as possible.",
    "Bridges. Build? Burn?",
    "One word: water.",
    "You don’t have to be ashamed of using your own ideas.",
    "Make a sudden, destructive unpredictable action; incorporate.",
    "Tidy up.",
    "Consult other sources.",
    "Do the words need changing?",
    "Use an unacceptable color.",
    "Ask your body.",
    "Humanize something free of error.",
    "Use filters.",
    "Balance the consistency principle with the inconsistency principle.",
    "Fill every beat with something.",
    "Discard an axiom.",
    "Listen to the quiet voice.",
    "What wouldn’t you do?",
    "Is it finished?",
    "Decorate, decorate!",
    "Put in earplugs.",
    "Give the game away.",
    "Reverse.",
    "Abandon normal instruments.",
    "Trust in the you of now.",
    "Use fewer notes.",
    "What would your closest friend do?",
    "Repetition is a form of change.",
    "Two words: distorting time.",
    "Give way to your worst impulse.",
    "Make a blank valuable by putting it in an exquisite frame.",
    "The inconsistency principle.",
    "Ghost echoes.",
    "Don't break the silence.",
    "You can only make one dot at a time.",
    "Discover the recipes you are using and abandon them.",
    "Just carry on.",
    "Comrades.",
    "(Organic) machinery.",
    "Courage!",
    "What mistakes did you make last time?",
    "You are an engineer.",
    "Consider different fading systems.",
    "Remove ambiguities and convert to specifics.",
    "Mute and continue.",
    "Look at the order in which you do things.",
    "It is quite possible (after all).",
    "Go outside. Shut the door.",
    "Don't stress one thing more than another.",
    "Do we need holes?",
    "Two words: cluster analysis.",
    "Work at a different speed.",
    "Do something boring.",
    "Look closely at the most embarrassing details and amplify them.",
    "Define an area as ‘safe’ and use it as an anchor.",
    "Mechanicalize something idiosyncratic.",
    "Overtly resist change.",
    "Emphasize the flaws.",
    "Accept advice.",
    "Remember those quiet evenings.",
    "Take a break.",
    "Imagine the music as a moving chain or caterpillar.",
    "Use an old idea.",
    "Imagine the music as a set of disconnected events.",
    "Change nothing and continue with immaculate consistency.",
    "What are you really thinking about just now? Incorporate.",
    "Look at a very small object, look at its center.",
    "Not building a wall, but making a brick.",
    "The most important thing is the thing most easily forgotten.",
    "Always first steps.",
    "Question the heroic approach.",
    "Be extravagant.",
    "State the problem in words as clearly as possible.",
    "Faced with a choice, do both.",
    "Retrace your steps.",
    "Convert a melodic element into a rhythmic element.",
    "Go to an extreme, move back to a more comfortable place.",
    "Once the search is in progress, something will be found.",
    "Only a part, not the whole.",
    "From nothing to more than nothing.",
    "Be less critical more often.",
    "When is it for? Who is it for?",
    "Destroy nothing. Destroy the most important thing.",
    "Take away as much mystery as possible. What’s left?",
    "What most recently impressed you? How is it similar? What can you learn from it? What could you take from it?",
    "First work alone, then work in unusual pairs.",
    "What do you do? Now, what do you do best?",
    "Back up a few steps. What else could you have done?",
    "What were the branch points in the evolution of this entity?",
    "Try faking it.",
    "How would you explain this to your parents?",
    "Who would make this really successful?",
    "What would make this really successful?",
    "Instead of changing the thing, change the world around it.",
    "List the qualities it has. List those you'd like.",
    "What else is this like?",
    "Describe the landscape in which this belongs.",
    "Steal a solution.",
    "Assemble some of the elements in a group and treat the group.",
    "Be dirty.",
    "Lost in useless territory.",
    "Lowest common denominator.",
  ]
  
  @IBOutlet weak var strategyLabel: UILabel!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  @IBAction func getStrategyButtonPressed(_ sender: Any) {
    strategyLabel.alpha = 0
    strategyLabel.text = strategies[randomNumber(upToButNotIncluding: strategies.count)]
    UIView.animate(withDuration: 3.0,
                   animations: {
      self.strategyLabel.alpha = 1
    })
  }
  
  func randomNumber(upToButNotIncluding upperBound: Int) -> Int {
    return Int(arc4random_uniform(UInt32(upperBound)))
  }
  
}

In addition to the starter project, I’ve also posted a finished project, which you can download:

Click here to download the starter project.

You can also find this project on my GitHub.

Suggested features and further reading

Come learn iOS development at Tampa iOS Meetup!

If you’re in the Tampa area and you’ve always wanted to learn iOS development but needed some help getting started, Tampa iOS Meetup is for you! It’s a regular gathering aimed at people new to iOS development or software development in general where we cover all sorts of programming topics as we build applications together in a casual, fun, hands-on setting. Find out more at the Tampa iOS Meetup page.

 

Categories
Uncategorized

You’d be crazy to miss out on T-Mobile’s crazy (and limited-time) “free line” deal

If you have at least two active T-Mobile “lines”, you’re eligible for an additional FREE line that remains yours and free of charge for as long as you’re a T-Mobile customer. This is a limited-time offer, so I’m scurrying down to my nearby T-Mobile shop as soon as I’ve finished posting this article.

T-Mobile know that people get into all sorts of shenanigans when getting stuff for free is involved, so there are some limitations. You can’t have canceled an existing line in 2017 and then apply for the free line.

I’m off to get a line for my Moto G4 (the Android developer’s best bang for the buck), my backup and Android development phone, just so I no longer have to keep moving my SIM card between it and my iPhone.

As Chris Mills put it in BGR:

“If you’re eligible, you should sign up for the extra line just in case you ever want to add a tablet, a friend, or just leave a mobile hotspot burning data for no good reason other than that you can.”

Categories
Uncategorized

Build your first app for GM’s Next Generation Infotainment (NGI) in-car platform

When we say “internet of things,” we’re really talking about the combination of computing power and sensor technologies in places where they haven’t traditionally been, including that most traditional of everyday technologies, the car. While cars have been “hackable” for decades, only now are they getting computing platforms that third-party developers can build on: Android Auto, Apple CarPlay, and now, GM’s Next Generation Infotainment, or NGI for short.

NGI gives developers the ability to write apps for the in-car infotainment consoles located in the center dashboard of a number of GM vehicles, like the one pictured above. Using the NGI SDK, developers have access to:

  • An 800-pixel high by 390-pixel wide touchscreen to receive input from and display information to the user
  • The voice system to respond to user commands and provide spoken responses to the user
  • Data from nearly 400 sensors ranging from the state of controls (buttons and the big dial) to instrumentation (such as speed, trip odometer, orientation) to car status information (Are there passengers in the car? Are the windows open or closed?) and more.
  • The navigation system to get and set navigation directions
  • The media system to play or stream audio files
  • The file system to create, edit, and delete files on the system
  • An inter-app communication system so that apps can send messages to each other

The SDK allows you to build and test apps for GM cars on your own computer. It comes with an emulator that lets you see your apps as they would appear on the car’s display, simulate sensor readings, and debug your app with a specialized console.

Best of all, you probably already have the skills to build apps with the NGI SDK: you write apps using HTML5, CSS, and JavaScript.

Getting and installing GM’s NGI SDK

First: if you don’t already have Node.js installed on your computer, do it now. The SDK uses it (as does just about every web development framework that matters these days, so you might as well install it).

To start developing apps for GM’s new cars, go to the NGI SDK site — developer.gm.com/ngi — and register:

Once you’ve registered, log into the site, and download the latest version of the SDK install package from the downloads page:

The download will be a compressed .tgz file. Uncompress it; it will uncompress into a directory named package.

Open a terminal and install the SDK by entering the following on the command line:

npm install -g <PATH_TO_INSTALL_PACKAGE>

(…and no, don’t enter <PATH_TO_INSTALL_PACKAGE> literally — replace that with the full directory path where you downloaded the SDK install package. In my case, that full directory path was /Users/joey/Downloads/package, so I typed npm install -g/Users/joey/Downloads/package on my command line.)

Once the installer has done its dance, you can confirm that it installed successfully by entering this on the command line:

ngi --help

…which should result in the following being displayed on your terminal window:

Now you’re ready to start building NGI apps!

Getting started building NGI apps

If you’re like me, you’re probably itching to see the docs. You can fire up a local copy of the NGI documentation via the command line by entering the following:

ngi docs

This will open a new browser window that will look like this:

Starting a new project is pretty simple and not all that different from the way that many web development frameworks do it. You create a new directory, navigate to that directory, and run ngi init:

mkdir hello-gm
cd hello-gm
ngi init

This will appear in your terminal:

Answer the short list of questions that appear. You can either type in your own response, or just press Return to accept the default value (displayed in parentheses in gray text):

When you reach the “What type of network connectivity is required?” question, use the arrow keys to select the “None (all app data is local)” option. This is a simple example, after all:

When you reach the “What category of app is this?” question, use the arrow keys to select the “General” option:

After this step, you’ll have an empty project, ready for coding. You should see something like this in your terminal:

You now have a working project. You can run it in the emulator by staying in your project directory and entering this on the command line:

ngi serve

An emulator named Electron will appear. It has this icon:

and the emulator should look like this:

You’ll find the HTML source for this screen in the /src directory — it’s the index.html file. Here are its contents:

<!DOCTYPE html>
<html>
  <head>
    <title>My First App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <!-- Base css, but you'll likely want to keep them -->
    <link rel="stylesheet" href="css/reset.css" type="text/css">

    <!-- Your custom files -->
    <link rel="stylesheet" href="css/app.css" type="text/css">

    <script src="GMLIB/system.js"></script>
    <script src="GMLIB/info.js"></script>
    <!-- Uncomment libraries as you need them: -->
    <!-- <script src="GMLIB/comm.js"></script> -->
    <!-- <script src="GMLIB/io.js"></script> -->
    <!-- <script src="GMLIB/media.js"></script> -->
    <!-- <script src="GMLIB/monitor.js"></script> -->
    <!-- <script src="GMLIB/nav.js"></script> -->
    <!-- <script src="GMLIB/phone.js"></script> -->
    <!-- <script src="GMLIB/ui.js"></script> -->
    <!-- <script src="GMLIB/util.js"></script> -->
    <!-- <script src="GMLIB/voice.js"></script> -->

  </head>
  <body>
    <div id="wrapper">
      <div id="close"><img src="images/close.png" onclick="gm.system.closeApp()" alt="close"></div>
      <div id="main">

        <!-- Remove all code inside #main and add your own! -->
        <h1>My First App</h1>
        <p>
          Your VIN is: <span id="vin"></span>
        </p>

      </div>
    </div>

    <!-- Your app code: -->
    <script src="js/app.js"></script>
  </body>
</html>

As you can see, all visible markup goes in the main div.

Let’s build a simple speedometer app. It will receive speed data and display it as shown below:

We’ll need to make changes in 3 files: HTML, CSS, and JavaScript.

Leave the emulator running, and let’s start with the HTML file, /src/index.html. Change its contents to the following:

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <!-- Base css, but you'll likely want to keep them -->
    <link rel="stylesheet" href="css/reset.css" type="text/css">

    <!-- Your custom files -->
    <link rel="stylesheet" href="css/app.css" type="text/css">

    <script src="GMLIB/system.js"></script>
    <script src="GMLIB/info.js"></script>
    <!-- Uncomment libraries as you need them: -->
    <!-- <script src="GMLIB/comm.js"></script> -->
    <!-- <script src="GMLIB/io.js"></script> -->
    <!-- <script src="GMLIB/media.js"></script> -->
    <!-- <script src="GMLIB/monitor.js"></script> -->
    <!-- <script src="GMLIB/nav.js"></script> -->
    <!-- <script src="GMLIB/phone.js"></script> -->
    <!-- <script src="GMLIB/ui.js"></script> -->
    <!-- <script src="GMLIB/util.js"></script> -->
    <!-- <script src="GMLIB/voice.js"></script> -->

  </head>
  <body>
    <div id="wrapper">
      <div id="close"><img src="images/close.png" onclick="gm.system.closeApp()" alt="close"></div>
      <div id="main">

        <h1>speed</h1>
        <div class="instruments">
          <div id="speed">??</div>
          <div id="units">---</div>
        </div>

        <span id="mph">mph</span>
        <label class="switch">
          <input id="unitSwitch" type="checkbox" onclick='changeUnits(this);'>
          <div class="slider round"></div>
        </label>
        <span id="kmh">km/h</span>

      </div>
    </div>

    <!-- Your app code: -->
    <script src="js/app.js"></script>
  </body>
</html>

Save the file. The emulator should now look like this:

Let’s now edit the app’s CSS file, /src/css/app.css. Change its contents to the following:

#main {
  text-align: center;
}

#close {
  position: absolute;
  top: 90px;
  right: 0;
  width: 64px;
  height: 64px;
  overflow: hidden;
  z-index: 1000;
}
#close img {
  width: 100%;
}

@media (min-width: 801px) {
  #close {
    top: 104px;
  }
}

#speed {
 font-size: 8em;
 line-height: .8em;
}

#mph {
  font-size: 2em;
}

#kmh {
  font-size: 2em;
}

/* The switch - the box around the slider */
.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

/* Hide default HTML checkbox */
.switch input {display:none;}

/* The slider */
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: green;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: #2196F3;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}

Save the file. The emulator should now look like this:

And finally, let’s edit the app’s Javascript, located in /src/js/app.js. Change its contents to the following:

var useMetricUnits = true
changeUnits(document.getElementById('unitSwitch'))

function showSpeed(data) {
  console.log("showSpeed - metric? " + useMetricUnits)
  var speed = data.average_speed
  if ( speed !== undefined ) {
    var speedText = document.getElementById('speed')
    speed = useMetricUnits ? speed : Math.round(speed * 0.621)
    speedText.innerHTML = speed
  }
}

function changeUnits(checkbox) {
  console.log("Changed!" + checkbox.checked)
  useMetricUnits = checkbox.checked
  gm.info.getVehicleData(showSpeed, ['average_speed'])
}

gm.info.watchVehicleData(showSpeed, ['average_speed'])

Save the file. The emulator should now look like this (the speed reported my be different):

It’s time to simulate some car sensor data. You can do this by using the Signal Panel, which you access by clicking on this icon, located in the row of icons at the bottom of the emulator:

This window will appear:

This panel will let you simulate the nearly 400 data signals available to NGI. The one we want is called average_speed. Search for it by typing average_speed into the Find a vehicle Signal… text box:

…and click on the average_speed selection when it appears. An average_speed panel will appear, which will let you enter a speed reading via a slider or text box:

Note that average_speed reports its speeds in kilometers/hour. I’ve done the metric math for you and provided a CSS toggle switch that lets you change the display between miles and kilometers per hour.

Play around with the average_speed values, and see how that affects what you see on the screen.

If you’ve done even the smallest bit of JavaScript coding, everything in /src/js/app.js should be familiar to you except for two function calls, namely…

1. gm.info.getVehicleData(success [, failure], signals)

This method does a one-time query of the car’s systems for one or more “signal values” — that is, readings from the nearly 400 readings that the car can provide, including speed. It takes these arguments:

  • success: The method to call if the signal values can be retrieved. This method should accept a single parameter: a JavaScript object containing the retrieved values.
  • failure (optional): The method to call if the signal values cannot be retrieved.
  • signals: An array of strings containing the names of the signal values to be retrieved.

2. gm.info.watchVehicleData(success [, failure][, signals][, options])

Where gm.info.getVehicleData() does a one-time query of the car’s systems for signal values, gm.info.watchVehicleData() makes a continuous query. You can think of it as setting up a method as a “listener” for signal values. It takes these arguments:

  • success: The method to call if the signal values can be retrieved. This method should accept a single parameter: a JavaScript object containing the retrieved values.
  • failure (optional): The method to call if the signal values cannot be retrieved.
  • signals: An array of strings containing the names of the signal values to be retrieved.
  • options: An instance of the vehicleDataOptions object. One property of vehicleDataOptions is wait, which specifies the number of milliseconds between success callbacks. The default value for wait is 2000.

gm.info.getVehicleData() returns an integer value that identifies the “watch” operation that it was used to initiate. You can use that ID to cancel the “watch” operation with the gm.info.clearVehicleData(watchID) method, where watchID is the ID of the “watch” operation that you want to cancel.

And there you have it — your first NGI app!

And in case you were wondering: of course I set up a GitHub repo for this project! It’s called hello-gm and it lives on my GitHub.

Join us at the Makers Hustle Harder Hackathon in Tampa, February 27 – March 4, 2017!

If you’d like to find out more about the NGI SDK, building apps for GM’s in-car infotainment systems, and win prizes, come to the Makers Hustle Harder hackathon, which takes place in Tampa on the week of February 27th, 2017!

Tampa is the first of 3 U.S. cities where GM will be hosting Makers Hustle Harder, and it’s a chance for you to learn more about GM’s in-car IoT platform and see what you can do with it. There’ll be a kickoff meeting tonight at Tampa Hackerspace, remote work all week, and a final all-day session at Tampa Hackerspace on Saturday, March 4.

For more details, see my previous post, or visit the Tampa Hackerspace Meetup page for this event. I’ll be there — will you?

Categories
Current Events Tampa Bay Uncategorized

Try out GM’s in-car infotainment API at the “Makers Hustle Harder” hackathon in Tampa this week!

General Motors is hosting “Makers Hustle Harder” hackathon events in just three cities in the U.S., Tampa is one of them, and it’s happening this week! This is Tampa Bay developers’ chance to try out GM’s NGI (Next Generation Infotainment) API, which lets you build infotainment applications for the touchscreen interfaces on GM vehicles, with access to real-time data from over 350 data sources.

Makers Hustle Harder is an all-week event that starts with a kickoff meeting on Monday, February 27 at 6:00 p.m. at Tampa Hackerspace. That’s when teams (2 to 4 developers per team) will be finalized and participants will get an introduction to the hackathon, as well as NGI.

From Tuesday, February 28th through Friday, March 3rd, teams will work remotely on the their projects. Participants will be able to get live support from the GM teams from 6:00 p.m. through 9:00 p.m. on those days.

The final day of the hackathon will be an in-person event at Tampa Hackerspace on Saturday, March 4th from 9:00 a.m. through 6:00 p.m., with people putting the finishing touches on their projects and making final pitches at 4:00 p.m..

The grand prize will be a trip to GM headquarters in Detroit for all the members of the winning team. There will also be prizes for runners-up.

GM’s NGI SDK in action. Click the photo to read TechCrunch’s story on it.

Apps written using the NGI SDK are written on Node.js using HTML5, CSS, and JavaScript, and run on 8-inch (diagonal) touchscreen in GM vehicles. GM’s native APIs give developers access to all sorts of car info, including:

  • Instrument panel measurements, such as trip odometer, orientation, and vehicle speed
  • GPS and navigation data
  • Audio playback and streaming
  • Status information, such as presence of passengers or if the windows are open or closed
  • Vehicle features, such as radio or backup camera
  • Performance and maintenance data, such as oil life and tire pressure
  • Warning indicators, such as a burnt-out lightbulb or low washer fluid
  • Internet data via OnStar’s 4G LTE

The NGI SDK also has a system that simulates real vehicle data so that you can test your apps on your development machine.

GM’s Director of Application Ecosystem and Development Ed Wrenbeck says that the NGI SDK makes it possible for developers to create apps ready for testing in as little time as a week. He also says that the API opens up a world of possibilities: “If you were somebody like a map provider, for example, you could actually read the suspension data coming off the vehicle and use it to determine where potholes were at in the street, for example. Just one example of some of the unusual ways that you can use data that GM provides uniquely, that other OEMs just don’t provide via their infotainment systems.”

Heavy Metal Racing, an NGI-based racing videogame that uses the Corvette’s steering wheel as a controller.

Here’s a video showing highlights from an earlier NGI hackathon:

How to participate

  1. Make sure you register for the hackathon at the official registration page.
  2. It would also help with planning if you RSVP at the Meetup.com pages for Monday’s kickoff meeting and Saturday’s full-day event.
  3. Get GM’s NGI SDK and documentation from their developer site.
  4. Assemble a team beforehand or find a team that needs developers at Monday’s kickoff. Each team must have at least one representative present at the kickoff.
Categories
Uncategorized

How software projects are managed

The earliest instance of this that I can find is from Mikko Leskelä’s Twitter feed.

I’ve heard a lot of jokes about building software, but this one’s new to me! And yes, it’s funny because it’s true — we’ve all seen projects where the one thing that seems to be set in stone is the delivery date.