I am working on raspberry pi and android application in which everytime application is get opned it search for raspberry pi in network and if raspberry pi is found then do further operation else give aknwolegement to the user. I just need IP address of raspberry pi to do further process.
Solution purposed -
Making raspberry pi IP address static - Not applicable because application distributed from play store and dont have access to router.
Searching for the raspberry pi in network - Working on this.
What i tried is used SSDP, DLNA, UPNP protocol to create a server on raspberry pi and everytime app comes online search for the raspberry pi in network.
Used resourcee
Here is what i have done
private static final String tag = "SSDP";
private static final String query = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 1\r\n" +
//"ST: urn:schemas-upnp-org:device:MediaServer:1\r\n" +
"ST: ssdp:all\r\n"+
"\r\n";
private static final int port = 1900;
String request() {
String response = "";
byte[] sendData;
byte[] receiveData = new byte[1024];
sendData = query.getBytes();
DatagramPacket sendPacket = null;
try {
sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.250"), port);
} catch (UnknownHostException e) {
Log.d(tag, "Unknown Host Exception Thrown after creating DatagramPacket to send to server");
e.printStackTrace();
}
DatagramSocket clientSocket = null;
try {
clientSocket = new DatagramSocket();
} catch (SocketException e) {
Log.d(tag, "Socket Exception thrown when creating socket to transport data");
e.printStackTrace();
}
try {
if (clientSocket != null) {
clientSocket.setSoTimeout(50000);
clientSocket.send(sendPacket);
}
} catch (IOException e) {
Log.d(tag, "IOException thrown when sending data to socket");
e.printStackTrace();
}
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
if (clientSocket != null) {
clientSocket.receive(receivePacket);
}
} catch (IOException e) {
Log.d(tag, "IOException thrown when receiving data");
e.printStackTrace();
}
//the target package should not be empty
//try three times
for (int i = 0; i < 3; i++) {
Log.d(tag, "Checking target package to see if its empty on iteration#: " + i);
response = new String(receivePacket.getData());
Log.d(tag, "Response contains: " + response);
if (response.contains("Location:")) {
break;
}
}
String adress = "";
//filter IP address from "Location"
Matcher ma = Pattern.compile("Location: (.*)").matcher(response);
if (ma.find()) {
adress += ma.group(1);
adress = adress.split("/")[2].split(":")[0];
}
return adress;
}
Using above methode and solution i was able to find out router IP address everytime but not of pi. Also gone through each of every library i can found on internet but not worked. Apart from this method if there any other way suggested will be appraciated.
Now eventually i figured out the solution i will share my answer step by step. I am using ssdp protocol to find out the Pi on network where the Pi have dynamic Ip address. So i created the server in python and using android as a client. lets start with server first -
Also you can find the server script https://github.com/ZeWaren/python-upnp-ssdp-example
from lib.ssdp import SSDPServer
from lib.upnp_http_server import UPNPHTTPServer
import uuid
import netifaces as ni
from time import sleep
import logging
NETWORK_INTERFACE = 'wlp2s0'
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def get_network_interface_ip_address(interface='eth0'):
while True:
if NETWORK_INTERFACE not in ni.interfaces():
logger.error('Could not find interface %s.' % (interface,))
exit(1)
interface = ni.ifaddresses(interface)
if (2 not in interface) or (len(interface[2]) == 0):
logger.warning('Could not find IP of interface %s. Sleeping.' % (interface,))
sleep(60)
continue
return interface[2][0]['addr']
device_uuid = uuid.uuid4()
local_ip_address = get_network_interface_ip_address(NETWORK_INTERFACE)
http_server = UPNPHTTPServer(8088,
friendly_name="Personal Home",
manufacturer="Home Personal SAS",
manufacturer_url='http://www.example.com/',
model_description='Home Appliance 3000',
model_name="Personal",
model_number="3000",
model_url="http://www.example.com/en/prducts/personal-3000/",
serial_number="PER425133",
uuid=device_uuid,
presentation_url="http://{}:5000/".format(local_ip_address))
http_server.start()
ssdp = SSDPServer()
ssdp.register('local',
'uuid:{}::upnp:rootdevice'.format(device_uuid),
'urn:schemas-upnp-org:device:MediaServer:1',
'http://{}:8088/jambon-3000.xml'.format(local_ip_address))
ssdp.run()
This will create an exuction point for the server script.
import random
import time
import socket
import logging
from email.utils import formatdate
from errno import ENOPROTOOPT
SSDP_PORT = 1900
SSDP_ADDR = '239.255.255.250'
SERVER_ID = 'Personal Home SSDP Server'
logger = logging.getLogger()
class SSDPServer:
known = {}
def __init__(self):
self.sock = None
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if hasattr(socket, "SO_REUSEPORT"):
try:
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except socket.error as le:
# RHEL6 defines SO_REUSEPORT but it doesn't work
if le.errno == ENOPROTOOPT:
pass
else:
raise
addr = socket.inet_aton(SSDP_ADDR)
interface = socket.inet_aton('0.0.0.0')
cmd = socket.IP_ADD_MEMBERSHIP
self.sock.setsockopt(socket.IPPROTO_IP, cmd, addr + interface)
self.sock.bind(('0.0.0.0', SSDP_PORT))
self.sock.settimeout(1)
while True:
try:
data, addr = self.sock.recvfrom(1024)
self.datagram_received(data, addr)
except socket.timeout:
continue
self.shutdown()
def shutdown(self):
for st in self.known:
if self.known[st]['MANIFESTATION'] == 'local':
self.do_byebye(st)
def datagram_received(self, data, host_port):
"""Handle a received multicast datagram."""
(host, port) = host_port
try:
header, payload = data.decode().split('\r\n\r\n')[:2]
except ValueError as err:
logger.error(err)
return
lines = header.split('\r\n')
cmd = lines[0].split(' ')
lines = map(lambda x: x.replace(': ', ':', 1), lines[1:])
lines = filter(lambda x: len(x) > 0, lines)
headers = [x.split(':', 1) for x in lines]
headers = dict(map(lambda x: (x[0].lower(), x[1]), headers))
logger.info('SSDP command %s %s - from %s:%d' % (cmd[0], cmd[1], host, port))
logger.debug('with headers: {}.'.format(headers))
if cmd[0] == 'M-SEARCH' and cmd[1] == '*':
# SSDP discovery
self.discovery_request(headers, (host, port))
elif cmd[0] == 'NOTIFY' and cmd[1] == '*':
# SSDP presence
logger.debug('NOTIFY *')
else:
logger.warning('Unknown SSDP command %s %s' % (cmd[0], cmd[1]))
def register(self, manifestation, usn, st, location, server=SERVER_ID, cache_control='max-age=1800', silent=False,
host=None):
"""Register a service or device that this SSDP server will
respond to."""
logging.info('Registering %s (%s)' % (st, location))
self.known[usn] = {}
self.known[usn]['USN'] = usn
self.known[usn]['LOCATION'] = location
self.known[usn]['ST'] = st
self.known[usn]['EXT'] = ''
self.known[usn]['SERVER'] = server
self.known[usn]['CACHE-CONTROL'] = cache_control
self.known[usn]['MANIFESTATION'] = manifestation
self.known[usn]['SILENT'] = silent
self.known[usn]['HOST'] = host
self.known[usn]['last-seen'] = time.time()
if manifestation == 'local' and self.sock:
self.do_notify(usn)
def unregister(self, usn):
logger.info("Un-registering %s" % usn)
del self.known[usn]
def is_known(self, usn):
return usn in self.known
def send_it(self, response, destination, delay, usn):
logger.debug('send discovery response delayed by %ds for %s to %r' % (delay, usn, destination))
try:
self.sock.sendto(response.encode(), destination)
except (AttributeError, socket.error) as msg:
logger.warning("failure sending out byebye notification: %r" % msg)
def discovery_request(self, headers, host_port):
"""Process a discovery request. The response must be sent to
the address specified by (host, port)."""
(host, port) = host_port
logger.info('Discovery request from (%s,%d) for %s' % (host, port, headers['st']))
logger.info('Discovery request for %s' % headers['st'])
# Do we know about this service?
for i in self.known.values():
if i['MANIFESTATION'] == 'remote':
continue
if headers['st'] == 'ssdp:all' and i['SILENT']:
continue
if i['ST'] == headers['st'] or headers['st'] == 'ssdp:all':
response = ['HTTP/1.1 200 OK']
usn = None
for k, v in i.items():
if k == 'USN':
usn = v
if k not in ('MANIFESTATION', 'SILENT', 'HOST'):
response.append('%s: %s' % (k, v))
if usn:
response.append('DATE: %s' % formatdate(timeval=None, localtime=False, usegmt=True))
response.extend(('', ''))
delay = random.randint(0, int(headers['mx']))
self.send_it('\r\n'.join(response), (host, port), delay, usn)
def do_notify(self, usn):
"""Do notification"""
if self.known[usn]['SILENT']:
return
logger.info('Sending alive notification for %s' % usn)
resp = [
'NOTIFY * HTTP/1.1',
'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT),
'NTS: ssdp:alive',
]
stcpy = dict(self.known[usn].items())
stcpy['NT'] = stcpy['ST']
del stcpy['ST']
del stcpy['MANIFESTATION']
del stcpy['SILENT']
del stcpy['HOST']
del stcpy['last-seen']
resp.extend(map(lambda x: ': '.join(x), stcpy.items()))
resp.extend(('', ''))
logger.debug('do_notify content', resp)
try:
self.sock.sendto('\r\n'.join(resp).encode(), (SSDP_ADDR, SSDP_PORT))
self.sock.sendto('\r\n'.join(resp).encode(), (SSDP_ADDR, SSDP_PORT))
except (AttributeError, socket.error) as msg:
logger.warning("failure sending out alive notification: %r" % msg)
def do_byebye(self, usn):
"""Do byebye"""
logger.info('Sending byebye notification for %s' % usn)
resp = [
'NOTIFY * HTTP/1.1',
'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT),
'NTS: ssdp:byebye',
]
try:
stcpy = dict(self.known[usn].items())
stcpy['NT'] = stcpy['ST']
del stcpy['ST']
del stcpy['MANIFESTATION']
del stcpy['SILENT']
del stcpy['HOST']
del stcpy['last-seen']
resp.extend(map(lambda x: ': '.join(x), stcpy.items()))
resp.extend(('', ''))
logger.debug('do_byebye content', resp)
if self.sock:
try:
self.sock.sendto('\r\n'.join(resp), (SSDP_ADDR, SSDP_PORT))
except (AttributeError, socket.error) as msg:
logger.error("failure sending out byebye notification: %r" % msg)
except KeyError as msg:
logger.error("error building byebye notification: %r" % msg)
A class implementing a SSDP server. The notify_received and searchReceived methods are called when the appropriate type of datagram is received by the server.
from http.server import BaseHTTPRequestHandler, HTTPServer
import threading
PORT_NUMBER = 8080
class UPNPHTTPServerHandler(BaseHTTPRequestHandler):
# Handler for the GET requests
def do_GET(self):
if self.path == '/boucherie_wsd.xml':
self.send_response(200)
self.send_header('Content-type', 'application/xml')
self.end_headers()
self.wfile.write(self.get_wsd_xml().encode())
return
if self.path == '/jambon-3000.xml':
self.send_response(200)
self.send_header('Content-type', 'application/xml')
self.end_headers()
self.wfile.write(self.get_device_xml().encode())
return
else:
self.send_response(404)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b"Not found.")
return
def get_device_xml(self):
"""
Get the main device descriptor xml file.
"""
xml = """<root>
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
<friendlyName>{friendly_name}</friendlyName>
<manufacturer>{manufacturer}</manufacturer>
<manufacturerURL>{manufacturer_url}</manufacturerURL>
<modelDescription>{model_description}</modelDescription>
<modelName>{model_name}</modelName>
<modelNumber>{model_number}</modelNumber>
<modelURL>{model_url}</modelURL>
<serialNumber>{serial_number}</serialNumber>
<UDN>uuid:{uuid}</UDN>
<serviceList>
<service>
<URLBase>http://xxx.yyy.zzz.aaaa:5000</URLBase>
<serviceType>urn:boucherie.example.com:service:Jambon:1</serviceType>
<serviceId>urn:boucherie.example.com:serviceId:Jambon</serviceId>
<controlURL>/jambon</controlURL>
<eventSubURL/>
<SCPDURL>/boucherie_wsd.xml</SCPDURL>
</service>
</serviceList>
<presentationURL>{presentation_url}</presentationURL>
</device>
</root>"""
return xml.format(friendly_name=self.server.friendly_name,
manufacturer=self.server.manufacturer,
manufacturer_url=self.server.manufacturer_url,
model_description=self.server.model_description,
model_name=self.server.model_name,
model_number=self.server.model_number,
model_url=self.server.model_url,
serial_number=self.server.serial_number,
uuid=self.server.uuid,
presentation_url=self.server.presentation_url)
@staticmethod
def get_wsd_xml():
"""
Get the device WSD file.
"""
return """<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
</scpd>"""
class UPNPHTTPServerBase(HTTPServer):
"""
A simple HTTP server that knows the information about a UPnP device.
"""
def __init__(self, server_address, request_handler_class):
HTTPServer.__init__(self, server_address, request_handler_class)
self.port = None
self.friendly_name = None
self.manufacturer = None
self.manufacturer_url = None
self.model_description = None
self.model_name = None
self.model_url = None
self.serial_number = None
self.uuid = None
self.presentation_url = None
class UPNPHTTPServer(threading.Thread):
"""
A thread that runs UPNPHTTPServerBase.
"""
def __init__(self, port, friendly_name, manufacturer, manufacturer_url, model_description, model_name,
model_number, model_url, serial_number, uuid, presentation_url):
threading.Thread.__init__(self, daemon=True)
self.server = UPNPHTTPServerBase(('', port), UPNPHTTPServerHandler)
self.server.port = port
self.server.friendly_name = friendly_name
self.server.manufacturer = manufacturer
self.server.manufacturer_url = manufacturer_url
self.server.model_description = model_description
self.server.model_name = model_name
self.server.model_number = model_number
self.server.model_url = model_url
self.server.serial_number = serial_number
self.server.uuid = uuid
self.server.presentation_url = presentation_url
def run(self):
self.server.serve_forever()
A HTTP handler that serves the UPnP XML files. Lets scan the Pi on network using android.
private static final String tag = "SearchPi";
private static final String query = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 1\r\n" +
"ST: urn:schemas-upnp-org:device:MediaServer:1\r\n" +
//"ST: ssdp:all\r\n" +
"\r\n";
private static final int port = 1900;
Now lets create a new asyncTask because it is network releated process and add the below code in asyncTask
String response = "";
byte[] sendData;
byte[] receiveData = new byte[1024];
sendData = query.getBytes();
DatagramPacket sendPacket = null;
try {
sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.250"), port);
} catch (UnknownHostException e) {
Log.d(tag, "Unknown Host Exception Thrown after creating DatagramPacket to send to server");
e.printStackTrace();
}
DatagramSocket clientSocket = null;
try {
clientSocket = new DatagramSocket();
} catch (SocketException e) {
Log.d(tag, "Socket Exception thrown when creating socket to transport data");
e.printStackTrace();
}
try {
if (clientSocket != null) {
clientSocket.setSoTimeout(1000);
clientSocket.send(sendPacket);
}
} catch (IOException e) {
Log.d(tag, "IOException thrown when sending data to socket");
e.printStackTrace();
}
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
if (clientSocket != null) {
clientSocket.receive(receivePacket);
}
} catch (IOException e) {
Log.d(tag, "IOException thrown when receiving data");
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
response = new String(receivePacket.getData());
Log.d(tag, "Response contains: " + response);
if (response.contains("/jambon-3000.xml")) {
Log.d("logCat", receivePacket.getAddress().toString());
break;
}
else {
Log.d("logCat", "Popz! Pi not found");
}
}
With above code you can also create media server on Pi.