Sunday, September 18, 2016

Lightweight IoT Command and Control

Many hobbyist IoT projects running on the Raspberry Pi (RPi) require a webserver running on the RPi which receives requests from a browser, where the client using the browser is connected either to the local WiFi network, or possibly (via a port forwarding setup), connected to the public Internet.

An alternative that is easy to setup, lightweight and low cost is one that makes use of Amazon's SQS (Simple Queue Service) to setup two queues. One queue is for commands - from a mobile application to the RPi. Another queue is for responses - from the RPi back to the mobile application. It results in a simple and easy to secure solution available over the public Internet. It requires no port forwarding - both endpoints make use of a client connection to Amazon AWS. Given the small amount of messages needed for typical hobbyist project, the SQS component will be covered by the AWS free tier.

Here's the high level design of the system ...

Lightweight IoT Command and Control
On the Raspberry Pi, a lightweight application is running that makes use of the excellent Python boto3 AWS SDK library. It is always waiting for a command to arrive in the command queue. As soon as it receives a command, it processes it and adds a response to the response queue.

On the mobile device an Android application is running that makes use of the AWS Mobile SDK for Android. When the user presses a command button, such as 'Open Garage Door', it adds a command to the command queue, and asynchronously waits for a response to arrive in the response queue. When it receives a response (e.g. the command's success or failure status), it updates the app's user interface.

Saturday, January 2, 2016

Custom domain for Azure Web Apps using FreeDNS

The below steps go through setting up a custom domain name on your Azure Web App, using the FreeDNS service from NameCheap. Both the www subdomain www.yourdomainhere.com and the naked yourdomainhere.com will be setup.

I have an Azure S1 Small Instance App Service Plan on which I'm running 4 web apps. Originally I had planned to use Azure DNS, to keep everything within the same cloud service, but after some initial attempts formed the opinion that Azure DNS isn't mature enough yet to host production web sites. Azure DNS can currently only be administered from within Azure Powershell - that in itself was enough to put me off using it. If you need to make a quick change to a DNS record, you don't want to be having to run Powershell to do it. Microsoft are working on adding Azure DNS to the Azure Portal, but at the moment it's not available.

I have a couple of domains through NameCheap and for those and a couple of others (with CrazyDomains) I decided to use NameCheap's DNS service.  Even when your domain is not registered through NameCheap, they still offer a free DNS service, called FreeDNS.

Note that in the below steps, yourwebappname and yourdomainhere.com should of course be replaced with your specific names.


Step 1. Reduce the TTLs to 5 minutes

Note: you only need to perform this step if you're porting from an existing DNS provider. No need to do this if you're setting up a new web app on a new domain.

Within your current DNS service, setup all the TTL settings (Time To Live) on the DNS records to the minimum (typically 300 seconds, or 5 minutes). This may help in having the DNS changes propagate faster.

After this is done, try to get a full DNS zone file listing. When you port the DNS records to FreeDNS it will help ensure that you don't miss anything. My previous VPS and DNS provider was Linode and I was porting a few web apps from Linode over to Azure web apps.


Step 2. Ensure your Azure web app is running

Ensure your web app is published and running ok at http://yourwebappname.azurewebsites.net


Step 3. Find the IPv4 address of your Azure web app

From a windows cmd.exe prompt ...

C:\> nslookup yourwebappname.azurewebsites.net

Server:  UnKnown
Address:  192.168.1.1

Non-authoritative answer:
Name:    waws-prod-xxx-xxx.cloudapp.net
Address:  1.2.3.4
Aliases: 
yourwebappname.azurewebsites.net
          waws-prod-xxx-xxx.vip.azurewebsites.windows.net


The nslookup command will provide you with your web app's IPv4 address. This is shown in the second Address field above, e.g. 1.2.3.4. I am running an Azure S1 Small Instance App Service Plan. I believe this method still applies to other plans including shared instance plans.

Note that if your web app is restarted or stopped this may result in it being assigned a different IP address. Be aware of this when using naked domain URLs (without the www prefix) since these rely on the A record being configured for the correct IP address of your web app (as per step 6).


Step 4. Setup FreeDNS

On the NameCheap FreeDNS page, enter yourdomainhere.com and click 'Get DNS'. If its eligible, then click 'Add to Cart', then click 'Set up DNS'.

You get this message from NameCheap :-

N.B. don't go ahead and set the nameservers just yet, that step will come later.

    yourdomainhere.com
    Congratulations! Your domain/ sub-domain is added to our DNS service.
    Please set the nameservers of your domain/ sub-domain to

        freedns1.registrar-servers.com
        freedns2.registrar-servers.com
        freedns3.registrar-servers.com
        freedns4.registrar-servers.com
        freedns5.registrar-servers.com

    Our system will periodically monitor your domain's DNS setting and will activate your domain once it is pointing to our servers.

   

Step 5. Verify ownership of your domain

In NameCheap, on the Domain List page, once the yourdomainhere.com is listed as active, then need to click on "Authorize DNS" link...

Select the required email, such as admin@yourdomainhere.com to use as the auth email.

On the domain page, the redirect domain setting will show "Your FreeDNS domain is waiting for Authorization or Verification by domain owner."

In the received email, click on the embedded hyperlink and then on the webpage, click the "I AUTHORIZE" hyperlink.

A message should display "Host has been successfully activated".


Step 6. Setup the FreeDNS records

At this step you'll setup all of the DNS records for your domain.
For all of these I left them with the default TTL setting of 'Automatic'.
In NameCheap, on the Domain List page, click the MANAGE button next to your domain, then click on the Advanced DNS tab.

In the Host records, add the A record with the IP that was returned in step 3.

Type  Host  Value            TTL
A     @     1.2.3.4          Automatic


Add any required TXT records (often used for verification).

Add the 3 CNAME records required to verify ownership of the domain to Azure.

1. Set HOST to awverify
    Set Target to awverify.yourwebappname.azurewebsites.net

2. Set HOST to awverify.www
    Set Target to awverify.yourwebappname.azurewebsites.net

3. Set HOST to www
    Set Target to yourwebappname.azurewebsites.net
  
If required, set the mail setting to Custom MX and add the MX server records...
For example if you have Gmail enabled on the domain you'd use these MX records...

Set Mail to Custom MX

Host        Type Priority      Mail server name
@            MX     20         ALT1.ASPMX.L.GOOGLE.COM.
@            MX     10         ASPMX.L.GOOGLE.COM.
@            MX     20         ALT2.ASPMX.L.GOOGLE.COM.
@            MX     30         ASPMX2.GOOGLEMAIL.COM.
@            MX     30         ASPMX3.GOOGLEMAIL.COM.

  
  
Step 7. Change the DNS nameservers using your registrar

For the case when your registrar is not NameCheap, you'll need to point the nameservers for the domain to the FreeDNS nameservers.

Log into your registrar's website and set the following nameservers, removing the existing nameservers if required ...

        freedns1.registrar-servers.com
        freedns2.registrar-servers.com
        freedns3.registrar-servers.com
        freedns4.registrar-servers.com
        freedns5.registrar-servers.com



Step 8. Wait for the DNS nameserver change to propagate

You can check whether the changes have propagated yet using nslookup from the windows command line ...

C:\> nslookup yourdomainhere.com
C:\> nslookup www.
yourdomainhere.com
C:\> nslookup awverify.
yourdomainhere.com
C:\> nslookup awverify.www.
yourdomainhere.com

Use nslookup in interactive mode for MX and TXT record lookups ...


C:\> nslookup

set q=mx

yourdomainhere.com

set q=txt

yourdomainhere.com

exit


Or you can also use these great online tools for checking the DNS propagation ...

    http://www.whatsmydns.net
    https://www.ultratools.com/tools/dnsLookup

Or on Linux I believe the command is: dig yourdomainhere.com

   
Step 9. Bring the domains into your web app using the Azure Portal

In the Azure portal, go to yourwebappname Web App -> Settings -> Custom Domains and SSL -> Bring External Domains

Enter yourdomainhere.com into the field, and then press tab. Wait for it to verify your CNAME records.

In the next field, enter www.yourdomainhere.com and press tab again. Wait for it to verify your CNAME records.

It should succeed as you tab out of each field, and after you click the save button at the top, it should report "Updating hostname bindings".


Step 10. Test in a browser

Open both www.yourdomainhere.com and yourdomainhere.com in a browser and confirm that they load ok.

Turn on the browser's web developer tools (Network tab) to check that all the components of the web app are loading with HTTP 200 OK status, and are resolving to the new IP address.

Its normal to experience some issues for the first several hours after changing nameservers, for example you may see the domain resolve to the old name server settings or flipping back and forth from new to old. Usually this will settle down after a few hours. It can be caused by the browser caching DNS results, something I've noticed particularly bad in Firefox sometimes. You may also need to flush the DNS cache on the computer you are using.


Flush the DNS cache in windows via:

C:\> ipconfig /flushdns


Other references ...

There is some additional Azure related information in the below post about this subject:
https://azure.microsoft.com/en-us/documentation/articles/web-sites-custom-domain-name/



Follow @dodgy_coder


Subscribe to posts via RSS

Sunday, December 13, 2015

Low cost airlines online pricing hacks

Low cost airlines are the masters of dynamic pricing, bundling and pricing psychology.

Every customer who makes an online booking presents them with some new data to plug into their dynamic pricing algorithm. They also analyse the best way to present the various upgrades available to purchase before the final checkout, to maximise the customer's spend.

I've just taken a look at budget carrier JetStar's booking experience and its use of various pricing techniques and psychology.

After you've searched and selected your flights you'll be presented with a choice. Look at how unattractive they make the "Starter fare" default option ...


The "Plus bundle" at $106 is the one JetStar wants you to pick. The "Max bundle" at $753.49 is just there as an anchor price, to give you the impression that the middle one is good value. The "Starter fare" is the one they want you to avoid. They do this through the selective use of negative and positive language and the colours of grey, green and blue.

The Starter fare

Comfortable leather seat
This is patronising ... is there so little going for this bundle option that they have to mention that you actually get a seat, as opposed to standing in the aisle?

7kg carry on baggage
Strict size and weight limits apply
Negative tone. 7kg is not enough for most people's needs.

Checked baggage not included
Repeated, negative tone.

Starter fares are non-refundable
Negative tone.

... compare this to the most popular option, the option JetStar wants you to choose ...

The Plus bundle

No change fees
Positive.

2,225 Qantas points
Free money. Only $30 credit off your next fare, but its better than nothing.

Free standard seat selection
The power of free.

Meal
More included stuff.

20kg checked baggage
Notice they don't mention that a strict size and weight limit also applies to the 20kg baggage.


People are scared of change fees

Most people when seeing the starter fare option will get the impression that if they need to change their flight for any reason, perhaps due to a flight delay, they are in trouble. In fact, JetStar covers you for flight delays and will make the necessary arrangements and additional bookings free of charge. But they only mention this in the fine print.

Most people want checked baggage

You also get the impression that you have to choose the plus bundle or max bundle if you want checked baggage. Actually, you can choose the starter fare, and then later on in the process you can optionally pay for only the specific amount of checked baggage that you need, at a rate of $46 for 15kg, $50 for 20kg and $62 for 25kg. Most customers likely don't know that these cheaper options are available, and will just choose the plus bundle, paying $106, when all they needed was checked baggage. You can select the Starter fare, then pay separately for 20kg checked baggage ($50), a meal ($15) and a reserved seat ($6) and the total will be just $71, a saving of $35 on the Plus bundle.

Noone wants to be randomly assigned a seat

When it comes to selecting a seat, its a bit disingenuous for JetStar to claim this ...

"You have not selected a seat. If you do not wish to pre-select a seat one will be assigned for you randomly from what is still available at check-in."

When you check in at many airports, assuming you get to the airport early and the plane isn't sold out, you'll still be able to select what type of seat you want - the check-in staff are usually more than helpful and should be able to provide your choice of a window or an aisle seat.

If you're a family with children, the airline will try to assign an adult family member to be sitting close to each child in your family, even when you didn't pay to reserve a seat. If you can't be accommodated at the point of check in, you would likely be able to work something out after boarding by speaking to the cabin crew.

Also, remember this inconspicuous bit of writing at the bottom ...

"Jetstar will attempt to accommodate your seat preference, however due to operational considerations cannot guarantee that your seat allocation will be as your selected preference."

So in other words, they ask you to pay for a seat preference, but cannot guarantee it.


Follow @dodgy_coder

Subscribe to posts via RSS

Monday, June 15, 2015

The Default Files Folder Pattern

Software installation best practices

When you've got critical settings files that absolutely should never be overwritten or cleared back to defaults, this pattern has always worked well for me. It can apply to both Windows, Linux and Mac based software.

The installation method can either be a simple file copy utility/archive or something more complex like a Windows installer.

An example target structure might look something like this:

/MyApplication
    /bin           => Application binaries.
    /defaults      => A default version of all
settings files.
    /docs          => Documents or manuals.
    /settings      => Critical settings files.

             
The settings folder contains the critical settings files and is not part of the installer. The installer includes only the bin, defaults and docs folders. So installing the software only overwrites bin, defaults and docs, but never the settings. This applies equally to Windows installer packages - for this case, just don't include the settings folder in the Windows installer package. The defaults folder contains the default version of all of the the files expected in the settings folder.

Whenever the software accesses a particular file in settings the following lazy initialisation logic is used ...

- Does the settings folder exist?
    - Yes: Continue
    - No:  Create the folder and continue
   
- Does the file exist?
    - Yes: Open it
    - No:  Copy the file from the
defaults folder into the
           settings folder, then open it

By following the above logic, you ensure that an existing critical settings file is never overwritten.

From reading the latest report on the deadly Airbus A400M crash, its claimed that the cause was that their software installation procedure did the equivalent of overwriting a settings file or clearing it back to defaults. In this case, the settings file contained calibration parameters that were critical to the operation of 3 out of the 4 propeller engines.

Follow @dodgy_coder

Subscribe to posts via RSS

Saturday, May 2, 2015

Taking control of a 36 year old NASA spacecraft using GNU radio

In 2014, Dennis Wingo and Keith Cowing formed the ISEE-3 Reboot Project, a crowdfunded effort to attempt to gain control of the decommissioned spacecraft for the benefit of citizen science. The team raised almost $160,000 in funding and assembled top space experts for the cause. In May, 2014, they began communicating with the spacecraft in advance of its August 10th 2014 lunar fly by.

Here's an excerpt of a brilliant podcast interview between software defined radio (SDR) specialist, Balint Seeber, and Infosec journalist, Patrick Gray, as recorded on April 24, 2015. Its the best anecdote about technology and hacking I've heard recently.

Below transcript starts at the [47m:43s] mark of the Risky Business podcast #363
.

Balint: It was also to set a precedent for citizen scientists from the public to essentially take over missions that ... all the missions that NASA may not want to spend its resources, its precious, you know resources and budget on. So this was supposed to be a good first example of how they could do that hand-off to a public group, and there were initial negotiations there and a Space Act agreement was signed so it was all above board and legitimate.

We realised the reason they got in touch with Ettus Research and then with me and my former colleague, John Malsbury, who funnily enough now works at SpaceX, so he's still in the space game, but he and I met with the guys and they basically told us the reason why we need your expertise is because NASA has thrown out all of the old equipment they used to speak to the space probe. So they put the space probe in this graveyard orbit, because the mission had finished and they ran out of funding, but there was no way for the NASA deep space network to actually send it commands to wake it back up again.

And so, having done their techno-archeology, as they like to put it, retrieving all these old NASA documents, we could see the various protocols that would be necessary to re-implement, using software defined radio, in this case GNU radio, to achieve this, you know, recreate the modems to talk to it. And so we did that and we were fortunate enough to be able to go to the Arecibo radio telescope in Puerto Rico, hook our software defined radios up to their big, big telescope, and then send these commands out to the probe, which at the time I think was 15.5 million kilometres away, travelling towards the earth at about four kilometres a second ...

Patrick: That's a long distance call...

Balint: Its a very long distance, and I call it "not your average radio link budget". But we managed after some initial attempts to have some success there and send the commands out to turn the telemetry on. We could then assess the health of the space probe and ...

Patrick: And you did your happy dance of course when the response came back?

Balint: That's true, yeah, we had our unmodulated carrier suddenly become modulated telemetry and it was the first real major milestone of the project. All that preparation that we'd done, all the interpretation of the documents and taking into account the various permutations of parameters and ...

Patrick: And the plan of course was to fire the thrusters in a way to get it into a stable orbit and they you guys could use the various sensors and things on it to quote "Do Science", uh, but it didn't quite work out did it?

Balint: No, unfortunately the grand idealist game was to reactivate all of the operational science instruments on board and actually bring it back and do proper, public science with it, and, you know, have it available for STEM and so on, but unfortunately when we tried to fire the thrusters, in all the different configurations, because there were lots of redundancies in the propulsion system, we never observed any impulse being registered on the accelerometer and the accelerometers data was being transmitted back on the telemetry and it would always sort of flat line. And this was a big disappointment for us, no matter what we tried we just couldn't get the thing to move, and one of the running theories is that the (fuel) tanks were actually pressurised with nitrogen so that it would force the fuel out, back into the rest of the propulsion system, as in the valves and the thrusters, and you could imagine that over the three decades there might have been a very, very slow leak and that nitrogen pressure, it might have just leaked out over time and unfortunately now its left us with an inoperable propulsion system.

Patrick: What, no backup fuel pump? (sarcastic)

Balint: Can you believe it?

Patrick: Bloody American engineering.

Balint: What were those NASA boffins thinking? (laughing)

Patrick: But you were able to actually fire up the sensors and do some science with it. What was the total budget of this project, that was crowdfunded in the end?

Balint: They used Rockethub and I think it was on the order of $150,000, so it was quite a nice little collection there, and yeah, some of the science instruments were reactivated and we actually for a short period got some good science data out of it, which was great, and it was quite funny, when I was at DEFCON last time, I got the call that one of the science instruments needed to be rebooted and I had been waiting for quite a long time in line to get a good seat and there were a number of back to back presentations that I was ...

Patrick: Excuse me I'll be back I've just got to go give the three finger salute to a satellite

Balint: Well that's the thing though, I didn't want to leave and so, unfortunately I didn't have any cell reception so I couldn't tether to my phone, you don't get on the WiFi at DEFCON obviously because then you open yourself up to being hacked. So there's a guy next to me that I started to talk to and we're talking about software defined radio and I sort of sized him up, he was from Norway and he seemed like a good bloke and I asked whether I could tether to his phone. He said yes because he had a prepaid account with some Internet credit on there. And so I ended up tethering to his phone, SSH-ing from the third row of DEFCON into the laptop at Arecibo to send commands to the space probe that was about to fly past the moon, to reboot the science instrument on board, and then I could continue watching the presentation, so it was quite fun.

Patrick: Its a pretty amazing time isn't it...

Balint: Well it just goes to show you how interconnected the entire world is, I mean you've got the WiFi from my laptop to the phone, LTE from the phone to the cellular network, and then the data connection obviously across that and then down to Arecibo and then our custom link to the space probe. So it was a good testament both to SDR and commercially deployed wireless standards.


Futher information ...
Communicating with a space probe using Software Defined Radio

... a video of a presentation Balint gave to the Manly Warringah Radio Society ...


Follow @dodgy_coder

Subscribe to posts via RSS

Saturday, February 28, 2015

Setting up a Bonjour (Zeroconf) service name for your Raspberry Pi and accessing it from an Android App

I have written an Android app that communicates to my Raspberry Pi over SSH (using JSch) but the issue is that in the App the IP address of my Raspberry Pi has been hardcoded at 192.168.1.109 - ideally it would be good to be able to give it a local name, something like garagedoor.local - something that won't change when the DHCP assigns a different IP address.

It turns out that the way to do this isn't difficult on the Raspberry Pi and most of work is in changes to the Android App needed to make it happen. I've numbered the steps 1-4.

1. Configuration of your Raspberry Pi.

# Get superuser privileges
sudo su

# Change the host name of your Raspberry Pi from "raspberrypi" to "garagedoor"
# Reference: http://www.howtogeek.com/167195/how-to-change-your-raspberry-pi-or-other-linux-devices-hostname/

sudo nano /etc/hosts            # <== Replace raspberrypi with garagedoor
sudo nano /etc/hostname         # <== Replace raspberrypi with garagedoor

sudo /etc/init.d/hostname.sh    # Commit the changes.
sudo reboot                     # Reboot

# Install the mDNS implementation - avahi
# Reference: http://www.howtogeek.com/167190/how-and-why-to-assign-the-.local-domain-to-your-raspberry-pi/
# Get superuser privileges
sudo su

# Update the package sources
apt-get update

# Ugrade the packages
apt-get upgrade

# Install the mDNS implementation - avahi
sudo apt-get install avahi-daemon



That's the end of the changes needed on your Raspberry Pi.

2. Test on either Windows, Linux or Mac OSX that the Raspberry Pi is visible on the network with its new name of "garagedoor.local".

On Windows, you will need to have Bonjour Networking Services installed, which comes bundled with iTunes.
Run a cmd.exe prompt and type the command ping garagedoor.local -- you should get a response from your Raspberry Pi.

On Mac OSX, Bonjour Networking Services are installed by default.
In a bash terminal window, type the command ping garagedoor.local -- you should get a response from your Raspberry Pi.

On Linux, first ensure you have installed the avahi-daemon package.
In a bash terminal window, type the command ping garagedoor.local -- you should get a response from your Raspberry Pi.

3. Using NSD (Network Service Discovery) on Android to resolve the Raspberry Pi's service name.

Browsers and terminal programs on Android won't be able to resolve the garagedoor.local since to do this requires use of either the official Network Service Discovery library (supported by API Level 16 up) or the open source Java Zeroconf library named jMDNS.

In my case, I am using a custom app I wrote to communicate to the Raspberry Pi, so I have decided just to use the Network Service Discovery library.

4. Source code changes needed in the Android App to support NSD (Network Service Discovery)

Notes:
 (1) Below is just how I decided to implement it, you may want to separate the NSD related code out of the Activity into its own class, but for me that wasn't a problem.
 (2) I probably need to do something related to NSD in the Application lifecycle events of onPause(), onResume(), onDestroy() and onTeardown(). I haven't done that here yet.


4.A. Add some members to the Activity

I added this to my MainActivity but my app is small and so that's all I needed to do.

// Network Service Discovery related members
// This allows the app to discover the garagedoor.local
// "service" on the local network.
// Reference: http://developer.android.com/training/connect-devices-wirelessly/nsd.html
private NsdManager mNsdManager;
private NsdManager.DiscoveryListener mDiscoveryListener;
private NsdManager.ResolveListener mResolveListener;
private NsdServiceInfo mServiceInfo;
public String mRPiAddress;

// The NSD service type that the RPi exposes.
private static final String SERVICE_TYPE = "_workstation._tcp.";

   
4.B. Add some init code to the bottom of the Activity's onCreate() method.

mRPiAddress = "";
mNsdManager = (NsdManager)(getApplicationContext().getSystemService(Context.NSD_SERVICE));

initializeResolveListener();
initializeDiscoveryListener();
mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

   
4.C. Add the following two new methods to your Activity.

 private void initializeDiscoveryListener() {

     // Instantiate a new DiscoveryListener
     mDiscoveryListener = new NsdManager.DiscoveryListener() {

         //  Called as soon as service discovery begins.
         @Override
         public void onDiscoveryStarted(String regType) {
         }

         @Override
         public void onServiceFound(NsdServiceInfo service) {
             // A service was found!  Do something with it.
             String name = service.getServiceName();
             String type = service.getServiceType();
             Log.d("NSD", "Service Name=" + name);
             Log.d("NSD", "Service Type=" + type);
             if (type.equals(SERVICE_TYPE) && name.contains("garagedoor")) {
                 Log.d("NSD", "Service Found @ '" + name + "'");
                 mNsdManager.resolveService(service, mResolveListener);
             }
         }

         @Override
         public void onServiceLost(NsdServiceInfo service) {
             // When the network service is no longer available.
             // Internal bookkeeping code goes here.
         }

         @Override
         public void onDiscoveryStopped(String serviceType) {
         }

         @Override
         public void onStartDiscoveryFailed(String serviceType, int errorCode) {
             mNsdManager.stopServiceDiscovery(this);
         }

         @Override
         public void onStopDiscoveryFailed(String serviceType, int errorCode) {
             mNsdManager.stopServiceDiscovery(this);
         }
     };
 }

 private void initializeResolveListener() {
     mResolveListener = new NsdManager.ResolveListener() {

         @Override
         public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
             // Called when the resolve fails.  Use the error code to debug.
             Log.e("NSD", "Resolve failed" + errorCode);
         }

         @Override
         public void onServiceResolved(NsdServiceInfo serviceInfo) {
             mServiceInfo = serviceInfo;

             // Port is being returned as 9. Not needed.
             //int port = mServiceInfo.getPort();

             InetAddress host = mServiceInfo.getHost();
             String address = host.getHostAddress();
             Log.d("NSD", "Resolved address = " + address);
             mRPiAddress = address;
         }
     };
 }


4.D. Make use of the mRPiAddress member where previously you used a hardcoded IP address.

Check that its not empty before using it. If its empty, it means the RPi's name couldn't be resolved.


Follow @dodgy_coder

Subscribe to posts via RSS




Saturday, February 21, 2015

Anders Hejlsberg: C# stands for C++++

Just came across this today. Previously had heard the association with the musical sharp symbol (#) to mean a pitch higher in tone, but this was the first time I'd heard that the # symbol can also be seen as a matrix of four small + symbols, hence C# stands for C++++, i.e. an increment to C++.

Source: http://www.computerworld.com.au/article/261958/a-z_programming_languages_c_/?pp=2

Follow @dodgy_coder

Subscribe to posts via RSS