IoT: raspberry pi 3 how to blink led from iPhone

Internet of Things: raspberry pi 3 – how to blink led from iPhone?

Here’s a tech stack:
– raspberry pi 3 + led
– node-red hosted in AWS/anywhere – should be available in internet
– node js script on raspberry pi connected to node-red via web socket
– swift/ios app which makes http requests to node-red

Let’s start from node-red configuration (it’s a really cool tool for wiring together hardware devices, APIs and online services). All we need is to have “http GET” input, let specify the following path “/mmled/status” for it. and we want to wire it with Output/”Listen on” “web socket” with “/ws/mmled” path. We might also want to add “pay load” node.
So with several “debug” nodes (convenient way to debug our endpoint) it should look something like this:

nodered

At this point if we will GET https://{nore-red-host}/mmled/status?value=1 we will see the following things:
– {“value”: 1} in the debug panel
– the same message we will get in case if we are listening the “wss://{nore-red-host}/ws/mmled” socket.

Let’s check it, here is the node js “server” script which we going to start like “sudo node server” on raspberry pi:

var W3CWebSocket = require('websocket').w3cwebsocket;
var Gpio = require('onoff').Gpio;
var led = new Gpio(17, 'out');  
var client = new W3CWebSocket('wss://{node-red-host}/ws/mmled');
 
client.onerror = function() {
    console.log('Connection Error');
};
 
client.onopen = function() {
    console.log('WebSocket Client Connected');
};
 
client.onclose = function() {
    console.log('echo-protocol Client Closed');
    if (led) led.unexport();
};
 
client.onmessage = function(e) {
    if (typeof e.data === 'string') {
        console.log("Received: '" + e.data + "'");
        try {
            var data = JSON.parse(e.data);
            var newValue = parseInt(data.value);
            console.log('got new value = ' + newValue);
            led.writeSync(newValue);
        } catch (ex) {
            console.log('invalid JSON in e.data');
        }
    }
};

Ok, let’s digg into the magic, first of all what is

var Gpio = require('onoff').Gpio;
var led = new Gpio(17, 'out');

if you will google “raspberry pi 3 pins” you’ll find where “GPIO 17” – you basically need to connect it to resistor + led + GND
after that you’ll have a chance to turn led on with this line – “led.writeSync(1);” and turn it off with “led.writeSync(0);“. So the circuit is the following:
raspberry pi [DPIO 17 pin] -> resistor -> led -> raspberry pi [GND pin]

Everything else we need to connect and listen socket and to react on the value change. run the script with

sudo node server

and try to GET https://{node-red-host}/mmled/status?value=1
and and try to GET https://{node-red-host}/mmled/status?value=0 to toggle the led. It’s awesome, at this step you can enable/disable your LED from internet!

Let’s create a new “single view” xcode project and drop a “switch” UI element on the view. Go to “Assistant editor” and bind the switch’s “action” to the code. Here is the controller’s code:

import UIKit
 
class ViewController: UIViewController {
 
    let ledUrl = "https://{node-red-host}/mmled/status"
 
    func setLed(status: Bool) {
        let finalUrl = ledUrl + "?value=" + (status ? "1" : "0")
        print("final url = " + finalUrl)
        let nsUrl = NSURL(string: finalUrl)
        let task = NSURLSession.sharedSession().dataTaskWithURL(nsUrl!) {(data, response, error) in
            print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        }
 
        task.resume()
    }
 
    @IBAction func onChange(sender: UISwitch) {
        setLed(sender.on);
    }
 
    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.
    }
}

It’s just sending GET request with the value 1 or 0 on toggle/switch change. That’s it, if you’ll run the app and toggle the switch you’ll see the message in the “debug” node, and you’ll see your LED blinking which is Awesome! You might go further and replace led with relay+real lamp, but it’s a different story.

Here’s an EPIC demo video, just for fun and self-motivation.

Social Share Toolbar

WatchOS 2 tutorial: Activity Ring – WKInterfaceActivityRing

Just a quick example how to use WKInterfaceActivityRing and HKActivitySummary to show Activity Ring.

Simulator Screen Shot Jun 7, 2016, 8.58.27 PM

Go ahead and drag “Activity Ring” on your Interface Controller, use Assistant Editor to add Outlet to your Controller, e.g.

@IBOutlet var activityRing: WKInterfaceActivityRing!

All you need to set up Activity Ring values is to create and initialize HKActivitySummary, here is the sample code (for simplicity we are going to use the same values for all the rings)

let summary = HKActivitySummary()
let value: Double = 2
let goal: Double = 10
 
summary.activeEnergyBurned = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: value)
summary.activeEnergyBurnedGoal = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: goal)
 
summary.appleExerciseTime = HKQuantity(unit: HKUnit.minuteUnit(), doubleValue: value)
summary.appleExerciseTimeGoal = HKQuantity(unit: HKUnit.minuteUnit(), doubleValue: goal)
 
summary.appleStandHours = HKQuantity(unit: HKUnit.countUnit(), doubleValue: value)
summary.appleStandHoursGoal = HKQuantity(unit: HKUnit.countUnit(), doubleValue: goal)
 
activityRing.setActivitySummary(summary, animated: true)

That’s pretty much it.

Social Share Toolbar

Watch OS 2: Refreshing Apple Watch complication

To make WCSession work fine with Complications we have to use transferCurrentComplicationUserInfo on the iPhone side.
So here’s how to send updated dictionary from iPhone

// update complication
if session.watchAppInstalled {
    session.transferCurrentComplicationUserInfo(dataDictionary)
}

Let’s get back to Apple Watch Extension, here’s what should be in the InterfaceController which uses WCSession to subscribe on updates:

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
    if let dateString = userInfo["dataKey"] as? String {
         // save new value to user defaults
         let defaults = NSUserDefaults.standardUserDefaults()
         defaults.setObject(dateString, forKey: "dataKey")
 
         // reload complication data
         reloadComplications()
     }
 }
 
func reloadComplications() {
    let server = CLKComplicationServer.sharedInstance()
    guard let complications = server.activeComplications where complications.count > 0 else {
        return
    }
 
    for complication in complications  {
        server.reloadTimelineForComplication(complication)
    }
}

That’s basically it, now we can get these values from the Cache in the Complication itself.

Social Share Toolbar

watchOS 2: Writing a WatchKit Complication

How to create custom WatchKit Complication which could be easily added to Apple Watch face.
This is one of the extremely exciting new additions to the WatchKit Framework in watchOS 2 – ability to add custom complications to the clock faces provided by Apple.

First let’s add Complication Data Source

import ClockKit
 
class Complication: NSObject, CLKComplicationDataSource {
 
    func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
        handler(nil)      
    }
 
    func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
        handler(nil)
    }
 
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.ShowOnLockScreen)
    }
 
    func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler([])
    }
 
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler([CLKComplicationTimeTravelDirections.None])        
    }
 
    func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
 
    func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
}

Let’s register it in the info.plist, make sure, that the following properties are set correctly
CLKComplicationPrincipalClass set to e.g. “$(PRODUCT_MODULE_NAME).Complication”
CLKComplicationSupportedFamilies has at least one value – CLKComplicationFamilyCircularSmall
RemoteInterfacePrincipalClass set to e.g. $(PRODUCT_MODULE_NAME).InterfaceController

Let’s update our data source template to provide some percentage like data, let say we have goal of 10 something and right now our progress is 8/10 – let’s render it (let’s make data static not to overcomplicate the example):

import ClockKit
 
class Complication: NSObject, CLKComplicationDataSource {
 
    func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
        // Update hourly
        handler(NSDate(timeIntervalSinceNow: 60*60))
    }
 
    func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
        var template: CLKComplicationTemplate? = nil
        switch complication.family {
        case .ModularSmall:
            template = nil
        case .ModularLarge:
            template = nil
        case .UtilitarianSmall:
            template = nil
        case .UtilitarianLarge:
            template = nil
        case .CircularSmall:
            let modularTemplate = CLKComplicationTemplateCircularSmallRingText()
            modularTemplate.textProvider = CLKSimpleTextProvider(text: "--")
            modularTemplate.fillFraction = 0.7
            modularTemplate.ringStyle = CLKComplicationRingStyle.Closed
            template = modularTemplate
        }
        handler(template)
    }
 
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.ShowOnLockScreen)
    }
 
    func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {
        if complication.family == .CircularSmall {
            // these values should be fetched from somewhere...
            let progress = 8
            let goal = 10
 
            let template = CLKComplicationTemplateCircularSmallRingText()
            template.textProvider = CLKSimpleTextProvider(text: "\(progress)")
            template.fillFraction = Float(progress) / Float(goal)
            template.ringStyle = CLKComplicationRingStyle.Closed
 
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        } else {
            handler(nil)
        }
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler([])
    }
 
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler([CLKComplicationTimeTravelDirections.None])
    }
 
    func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
 
    func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
}

Feel free to use transferCurrentComplicationUserInfo for getting actual data, or simply get the data from NSCache().

Social Share Toolbar