I wanted to connect my photon to an android app i was writing via WiFi, without using the particle cloud or anything else for varying reasons. I saw plenty of things for connecting to a PC, but nothing for Android. So, once i figured it out, i decided to share it here!
The particle side is reasonably simple: It more or less copies the example code. My version is below: It basically starts the Udp connection and spits packets out (no receiving - i only want to send data to the phone)
// UDP Port used for two way communication
unsigned int localPort = 10000;
// A UDP instance to let us send and receive packets over UDP
UDP Udp;
void setup() {
// start the UDP
Udp.begin(localPort);
// Print your device IP Address via serial
Serial.begin(9600);
Serial.println(WiFi.localIP()); //this has been returning 192.168.43.xxx when connected to a mobile hotspot
}
unsigned char msg_count = 1;
void loop()
{
IPAddress remoteIP(192, 168, 43, xxx); //IP adress i am sedning to - in this case, the phone IP
Udp.beginPacket(remoteIP, 10000);
Udp.write(msg_count++); //add an incrementing message number for sync
if (msg_count > 100){msg_count = 0;}
Udp.write("Hi!");
Udp.endPacket();
}
It should be noted that Udp does not grantee packets arrive or are correct, hence the message number for the most basic form of “which packets did i miss”
The android side is more complicated. I will assume a basic competency in Android programming (i.e. the first 5 ish tutorials form the Android website). First and foremost, the following permissions are required in the manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
The xml layout file for the main page of the app is as below. It basically contains 2 buttons, one to trigger a receive message command, and one to refresh the status box to show the message (if any)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.ucc.simon.wifi_test.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="receive message"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:id="@+id/button_begin_graph"
android:onClick="sendMessage"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Refresh status"
android:layout_centerHorizontal="true"
android:layout_below="@id/button_begin_graph"
android:id="@+id/button_refresh_status"
android:onClick="refreshStatus"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="Status: Unconnected"
android:textSize="12dp"
android:id="@+id/status"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_above="@id/status"
android:text="no message"
android:textSize="12dp"
android:id="@+id/message"
/>
</RelativeLayout>
Finally the main Java file. This is a bit of a mess, but its the only working android Udp example i know of.
package com.company.name.wifi_test;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class MainActivity extends AppCompatActivity {
WifiManager w;
TextView status;
InetAddress server_ip;
int server_port = 10000;
String statusText;
private AsyncTask<Void, Void, Void> async_udp;
private boolean Server_Active = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
status = (TextView) findViewById(R.id.status);
w = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if (!w.isWifiEnabled()) {
status.setText("switching ON wifi ");
w.setWifiEnabled(true);
} else {
status.setText("Its already ON ");
}
}
public void sendMessage(View view) //function bound to message button
{
runUdpServer();
}
public void refreshStatus(View view)
{
status.setText(statusText);
}
public void runUdpServer()
{
int x;
WifiInfo info = w.getConnectionInfo();
status.setText(" ");
status.append("\n\nWiFi Status: " + info.toString());
x = info.getIpAddress();
String str1 = info.getMacAddress();
status.append("\n\nmac address===" + str1 + " ,ip===" + x);
try {
server_ip = InetAddress.getByName("192.168.43.xxx"); // ip of THE OTHER DEVICE - NOT THE PHONE
} catch (UnknownHostException e) {
status.append("Error at fetching inetAddress");
}
async_udp = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
String str2 = "TEST MESSAGE !!!";
byte b1[];
b1 = new byte[100];
b1 = str2.getBytes();
//DatagramPacket p1 = new DatagramPacket(b1, b1.length, server_ip, server_port);
try {
//DatagramSocket s = new DatagramSocket(server_port, server_ip);
DatagramSocket s = new DatagramSocket(server_port);
s.connect(server_ip, server_port);
//DatagramPacket p0 = new DatagramPacket(b1, b1.length, InetAddress.getByName("192.168.43.xxx"), server_port);
//s.send(p0);
//The above two line can be used to send a packet - the other code is only to recieve
DatagramPacket p1 = new DatagramPacket(b1,b1.length);
s.receive(p1);
s.close();
b1=p1.getData();
String str = new String( b1);
server_port = p1.getPort();
server_ip=p1.getAddress();
String str_msg = "RECEIVED FROM CLIENT IP =" + server_ip + " port=" + server_port + " message no = " + b1[0] +
" data=" + str.substring(1); //first character is message number
//WARNING: b1 bytes display as signed but are sent as signed characters!
//status.setText(str_msg);
statusText = str_msg;
} catch (SocketException e)
{
//status.append("Error creating socket");
statusText.concat(" Error creating socket"); //this doesnt work!
} catch (IOException e)
{
//status.append("Error recieving packet");
statusText.concat(" Error recieving packet"); //this doesnt work!
}
return null;
}
};
if (Build.VERSION.SDK_INT >= 11)
{
async_udp.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else
{
async_udp.execute();
}
status.setText(statusText); //need to set out here, as above is in an async thread
}
}
The complicated part is the "run_udp function - the networking part of the wifi must be run in a different thread to the UI thread, which is the default one. In this case im using an asynchronous thread, so i have a private "async_udp = new AsyncTask<Void, Void, Void>() " class to start this thread, contained in a function in the mian class to implement this (the thread starting is handled by android with this class type). Finally, the execute line runs the class. To continually recieve packets, put the internal parts of the class in a while(true) loop, to run forever!
That should be all - be careful the ports match, and you have the right IP addresses in!