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
Hey there,
ReplyDeleteGreat article! I'm pretty much doing your setup the only difference is I'm doing this using an iPhone :-)
Anyhow, I'm having some trouble setting up zeroconf on my banana pi running raspbian - I'm hoping you can clarify a couple of things:
- I suppose from your article you don't have a dhcp server running that is assigning a network IP address - can you confirm?
My issue:
- if I have my pi hardwired to my network my service is running and my iPhone app can see the pi using something like pi.local (I have updated the host file)
- if I unplug the RJ45 my service is advertised (I can see it from my iPhone app) but the domain resolution fails
I have a feeling my network conf file is not correct for wlan0 but can you confirm you got this to work with using dhcp and let me know if I missed anything obvious?
Thanks in advance for any help/advice you may have,
X
fyi my /etc/network/interfaces :
auto lo
iface lo inet loopback
iface eth0 inet dhcp
#auto wlan0
#allow-hotplug wlan0
#iface wlan0 inet manual
allow-hotplug wlan0
auto wlan0
iface wlan0 inte ipv4ll
iface default inet dhcp
Hi Xav, thanks for the feedback! I'll try and help. To confirm I originally hardcoded my RPi IP address (i.e. used a static IP) before setting up Zeroconf, but after, yes, I reverted back to using DHCP to get an IP address for the RPi and it worked fine resolving the name on Windows and Android. I was always running on WiFi (wlan0) because my RPi is located far from my home's ADSL modem/router. I will dig out my /etc/network/interfaces file for you also, if that helps. Just give me a few hours to get around to it since I'm at work right now.
Deletethat would be great because right now I'm stuck and I have no clue what I'm missing. I'm starting to wonder if this is supposed to work when the wifi needs a pwd (maybe I need to specify ssid and psk?) this would kinda make this not very useful but anyhow, lmk what your setup is... thanks!
DeleteI have a feeling though you may be using a static or dhcp address - from your post it seems you're not using avahi-autoipd...anyhow I'll wait now for your reply :-)
DeleteHi Xav,
DeleteI went back to look at the notes I made when setting up the RPi. It seems I never used a static IP ... I was always using DHCP with WiFi.
Also I am running avahi-daemon, which I installed with this command ...
sudo apt-get install avahi-daemon
Here's the full set of notes I made regarding setting up the WiFi, hopefully this helps ...
;
; Installing the Edimax USB WiFi dongle on the RPi
; Ref: "Raspberry Pi - Installing the Edimax EW-7811Un USB WiFi Adapter (WiFiPi)"
; http://www.savagehomeautomation.com/projects/raspberry-pi-installing-the-edimax-ew-7811un-usb-wifi-adapte.html
; Boot the RPi with the only the dongle and the keyboard attached
; Check the device is detected
lsusb # Ensure present: Edimax Technology Co., Ltd EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]
; list the loaded kernel modules
lsmod # Ensure present: 8192cu
; Check the WiFi config
iwconfig # Ensure present: wlan0
; Configure the WiFi as shown on the page above
sudo nano /etc/network/interfaces
; CONTENTS SET TO --->
auto lo
iface lo inet loopback
iface eth0 inet dhcp
auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp
<----
; Additional ref: http://www.raspberrypi.org/phpBB3/viewtopic.php?t=50312&p=391044
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
; CONTENTS SET TO --->
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid="YOURSSIDHERE"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP TKIP
group=CCMP TKIP
psk="PASSWORDHERE"
}
<----
; restart the RPi
sudo shutdown -r now
; pull out the ethernet cable
; check the status and IP address of the WLAN
ifconfig wlan0
; check the status and IP address of the LAN
ifconfig eth0
Another small comment - you are correct, I am not running avahi-autoipd.
DeleteThis comment has been removed by the author.
ReplyDeleteThank you for such a nice post , I have been trying to find solution for same problem , did not know it has to be _workstation._tcp. ...Thank you so much
ReplyDeleteThank you for such a nice post , I have been trying to find solution for same problem , did not know it has to be _workstation._tcp. ...Thank you so much
ReplyDeleteThank you so much for your tutorial. I was stuck in this task for about two weeks. Now I can access my Raspberry from my phone :).
ReplyDeleteHi,
ReplyDeleteI installed "sudo apt-get install avahi-daemon" in my RPi and I can access my website from my laptop (windows 10), ipad via http://raspberrypi.local/mywebsite . However, When I use my phone browser (android), my website can't display via http://raspberrypi.local/mywebsite. is there anyway to resolve this issue? Thanks.
I tried but app is crashing need help..
ReplyDeleteAdd to your Manifest.
Delete