Can Particle (Photon) provide a WiFi network!?

Hi,
I know the main idea of the photon is to connect the real world to the internet.

My main question is: can the Particle also provide an own WiFi network?

What I intend to do:

  • My Photon should measure a voltage and represent it as a website (web server).
  • I want to give the device away to someone (users).
  • If the user wants to see the voltage measurement he should select on his PC the provided Particle WiFi network.
  • He is then disconnected from his internet WiFi, but he can then open his browser enter the IP/URL to the Particle device web server device can see the actual voltage measurement.

This approach allows me to give a device to someone who needs not to install an SW to read USB port information or to make a bluetooth connection.

It would be interesting if this idea is technically with a Particle device possible or not.

Thanks in advance!

1 Like

Technically, yes, though I don’t know for sure if it’s already officially supported. You’ll want to have a look at SoftAp in the docs, and perhaps the latest firmware releases on github. 0.6.0 should contain some interesting bits if I’m not mistaken.

@goehte, you could also run a webserver on the Photon along with an MDNS service which can be discovered with any PC running Bonjour services (think any Mac or PC running iTunes). You could run SoftAP to allow the user to setup the wifi via a web page on the Photon and once it connects to the local network, can be discovered. I believe you can find an example of that in the forum.

I had never tried this before, so I gave it a shot and it works. Quite well, actually, even with just the basic SoftAP support in 0.5.1!

Here’s my test circuit, just a potentiometer connected to A0. You can’t tell from the picture, but it’s in blinking blue (listening mode).

Here’s a screenshot of the web browser. Note that it’s connecting to http://192.168.0.1/index.html. The little box updates 10 times per second as you adjust the potentiometer!

And here’s the code:

#include "Particle.h"
#include "softap_http.h"

// Hardware connections for demo:
// Potentiometer: A0 to center. Outsides to GND and 3V3.

SYSTEM_THREAD(ENABLED);

const int VALUE_PIN = A0;



// [start d1872940-586d-44a9-b719-4a67e8a29213]
// name=/index.html contentType=text/html size=700 modified=2016-08-26 07:30:43
const char fileData0[] = 
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!DOCTYPE html>\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
"<head>\n"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
"\n"
"<title>SoftAP Sample 1</title>\n"
"\n"
"<script src=\"https://code.jquery.com/jquery-3.1.0.min.js\"   integrity=\"sha256-cCueBR6CsyA4/9szpP"
"frX3s49M9vUU5BgtiJj06wt/s=\"   crossorigin=\"anonymous\"></script>\n"
"<script type=\"text/javascript\" src=\"main.js\"></script>   \n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"main.css\" />\n"
"\n"
"</head>\n"
"<body>\n"
"\n"
"<div id=\"main\">\n"
"\n"
"<h2>SoftAP Sample 1</h2>\n"
"\n"
"<form>\n"
"\n"
"<p>Current value: <input type=\"text\" id=\"valueText\" size=\"6\" value=\"0\" disabled=\"disabled\" /></p"
">\n"
"\n"
"</form>\n"
"\n"
"</div> <!-- main -->\n"
"\n"
"</body>\n"
"</html>\n"
"";

// name=/main.css contentType=text/css size=222 modified=2016-08-26 07:20:50
const char fileData1[] = 
"@CHARSET \"UTF-8\";\n"
"\n"
"body\n"
"{\n"
"background-color:#ffffff;\n"
"max-width:1000px;\n"
"}\n"
"\n"
"p,h1,h2,h3,li,td,div\n"
"{ \n"
"font-family: helvetica, sans-serif;\n"
"color:#000000;\n"
"font-size: small; \n"
"}\n"
"\n"
"p\n"
"{\n"
"max-width:1000px;\n"
"}\n"
"\n"
".hidden \n"
"{\n"
"display:none;\n"
"}\n"
"";

// name=/main.js contentType=application/javascript size=527 modified=2016-08-26 07:39:44
const char fileData2[] = 
"\n"
"var timerId;\n"
"\n"
"$(document).ready(function() {\n"
"\ttimerId = window.setInterval(timerCallback, 100);\n"
"});\n"
"\n"
"\n"
"function timerCallback() {\n"
"\t$.ajax({\n"
"\t\ttype : 'GET',\n"
"\t\turl : '/value',\n"
"\t\tdataType : 'json',\n"
"\t\tsuccess : getValueCallback,\n"
"\t\terror : dataError\n"
"\t});\n"
"\n"
"}\n"
"\n"
"function getValueCallback(values) {\t\n"
"\t$('#valueText').val(values.value.toString());\n"
"}\n"
"\n"
"\n"
"function dataError(jqXHR, textStatus, errorThrown) {\n"
"\tconsole.log(\"dataError \" + textStatus);\n"
"\tif (timerId != undefined) {\n"
"\t\twindow.clearTimeout(timerId);\n"
"\t\ttimerId = undefined;\n"
"\t}\n"
"}\n"
"\n"
"\n"
"";

typedef struct {
	const char *name;
	const char *mimeType;
	const uint8_t *data;
	size_t dataSize;
	unsigned long modified;
	bool isBinary;
} FileInfo;

const FileInfo fileInfo[] = {
	{"/index.html", "text/html", (const uint8_t *)fileData0, sizeof(fileData0) - 1, 1472211043, FALSE},
	{"/main.css", "text/css", (const uint8_t *)fileData1, sizeof(fileData1) - 1, 1472210450, FALSE},
	{"/main.js", "application/javascript", (const uint8_t *)fileData2, sizeof(fileData2) - 1, 1472211584, FALSE},
	{NULL, NULL, 0, 0, FALSE}
};
// [end d1872940-586d-44a9-b719-4a67e8a29213]

void pageHandler(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved); // forward declaration
void valuePageHandler(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved); // forward declaration

STARTUP(softap_set_application_page_handler(pageHandler, nullptr));


void setup() {
	Serial.begin(9600);
}

void loop() {

}

void pageHandler(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved) {

	if (strcmp(url, "/value") == 0) {
		valuePageHandler(url, cb, cbArg, body, result, reserved);
		return;
	}
    if (strcmp(url,"/index") == 0) {
        Serial.println("sending redirect to index.html");
        Header h("Location: /index.html\r\n");
        cb(cbArg, 0, 301, "text/plain", &h);
        return;
    }


	int index = -1;
	for(size_t ii = 0; fileInfo[ii].name != NULL; ii++) {
		if (strcmp(fileInfo[ii].name, url) == 0) {
			index = (int) ii;
			break;
		}
	}
	if (index >= 0) {
		cb(cbArg, 0, 200, fileInfo[index].mimeType, nullptr);
		result->write(fileInfo[index].data, fileInfo[index].dataSize);
		Serial.printlnf("returned url=%s dataSize=%d mimeType=%s", url, fileInfo[index].dataSize, fileInfo[index].mimeType);
	}
	else {
	    Serial.printlnf("404 url=%s", url);
		cb(cbArg, 0, 404, nullptr, nullptr);
	}
}

void valuePageHandler(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved) {
	cb(cbArg, 0, 200, "text/json", nullptr);

	int value = (int) analogRead(VALUE_PIN);

	char buf[64];
	snprintf(buf, sizeof(buf), "{\"value\":%d}", value);

	Serial.printlnf("returning /value: %s", buf);

	result->write(buf);
}


6 Likes

Do you not need to put the device into Listening Mode for that?

Yes, it’s in listening mode.

I know it was 2 years ago, but when I try your code I only get 0 in the small box and page is not updated. When I try 192.168.0.1/value.html I get the AnalogRead value.