How to have multiple connections to a Arduino webserver? - http

I have an Arduino running a webserver that is receiving data every 2 seconds. I CAN connect to its IP address by typing into the browser. I also created a web app that pulls data from this IP address every time new data comes in. The issue is that I need to access the IP address with the web app while another program is accessing it. Currently only one program can access at a time. I would like to have a Python script pulling data from the IP address constantly and still allow the web app to connect to view it live. How can I achieve this?
Arduino code with a lot of other stuff removed...
WiFiServer server(80); //server socket
WiFiClient client_1 = server.available();
void setup() {
Serial.begin(9600);
enable_WiFi(); // function to enable wifi
connect_WiFi(); // function to connect to wifi
server.begin();
}
void loop() {
client_1 = server.available();
if (client_1) {
printWEB(client_1); // this posts the data as text to the web IP address
}
delay(2000);
}
void printWEB(WiFiClient client) {
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Access-Control-Allow-Origin: *");
client.println("Access-Control-Allow-Methods: GET");
client.println("Content-type: application/json");
client.println();
moistureReading = analogRead(A1);
tmpString = dataPosted;
tmpString.replace("%moistureData%", String(moistureReading) );
client.flush();
client.print( tmpString );
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
}
else { // if you got a newline, then clear currentLine:
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}

The solution here was to take the delay off of the arduino code by simply removing the sleep function (Turns out this is not required). I then had to create a delay in all of my fetching codes. The React.js needed a sleep function and the python script needed a sleep function. This allows the two platforms to collect data from the same IP Address simultaneously. I needed a minimum of about 5-10 second sleep for both apps in order for this to work. I initially tried 1 second for each and it didn't work.

The webserver as programmed on your Arduino can only serve one request from a client at a time, not two simultaneously.
If two clients are doing requests in a loop, and are doing that fairly quickly, there will be situations where one client is blocking access for the other.
Try making the two clients much slower, i.e. retrieve information at a much lower frequency, just to see if the Arduino can keep up then.
Also, make the webserver on the Arduino as fast as possible, and call it as often as possible.
So, don't put delay()s in the loop, or anywhere else, and try to do the analogread() and the return string preparation outside of the webserver code and in the loop() every x seconds using millis(), so as little time as possible is spent in the webserver code.

Related

esp32 BLE client application - connect to device name

I've hacked apart the ESP32 BLE arduino sketches to do what I want. The server side is easy. Please see code below:
if (con == 0){
digitalWrite(LED, LOW);
}
if (con == 1){
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
con = 1;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
con = 0;
}
This works exactly how I want. It simply sits idle doing nothing, when a device connects to the BLE server then it will flash an LED.
No problems there, even though I suspect my code isn't 'that pretty.
What i'm having trouble doing however is creating an ESP32 client to connect to the BLE device.
The client has the name set as
BLEDevice::init("BOX_A1");
The example code seems to want UID for both the service and characteristic. Is there any way to just connect to the short advertised name? No data is being shared, it's just simply acting as a beacon to identify a box when connected to.
Thanks
Andrew
You can't connect to a device using the advertised name, not directly at least.
If you want to use the advertised name you have to scan for all BLE devices around you and select the one matching your name. The BLE scan example shows you how this is done. The code scans for a scanTime of 5 seconds, waits 2 seconds and starts scanning again:
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
The example just prints the amount of found devices, you want to search through them and look for the correct name. The BLEScan returns an object of type BLEScanResults. You can access the found devices using getDevice with an index. Something like this might work to print the names of all found devices:
BLEAdvertisedDevice device;
for (int i = 0; i < foundDevices.getCount(); ++i) {
device = foundDevices.getDevice(i);
Serial.println(device.getName().c_str());
}
Now you can compare the names and work with the correct device.
To my understanding,
You want the client to connect to the server with given advertised name.
After connection is success, server turns on led.
You do have notification service running on server, but your client isn't interested.
Below is the client code which only connects to server with name "BOX_A1".
First Set our callback function that checks if device name matches when scanner discovers the devices:
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice)
{
if (advertisedDevice.getName() == "BOX_A1")
{
advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
foundDevice = new BLEAdvertisedDevice(advertisedDevice);
deviceFound = true;
Serial.print("Device found: ");
Serial.println(advertisedDevice.toString().c_str());
}
}
};
Use the BLEAdvertisedDevice object i.e. foundDevice object to connect to this server.
BLEClient* connectToServer(BLEAdvertisedDevice* device) {
BLEClient* pClient = BLEDevice::createClient();
if (pClient->connect(device)){ // Connect to the remote BLE Server.
Serial.println(" - Connected to server");
return pClient;
}
else{
Serial.println("Failed to Connect to device");
return NULL;
}
}
Use following line to call this connect function, it return the client object which can be used to disconnect from server.
if(deviceFound==true){
BLEClient* myDevice = connectToServer(device);
if(myDevice!=NULL){
Serial.print("Connected to the BLE Server: ");
Serial.println(device->getName().c_str());//print name of server
//DO SOME STUFF
//disconnect the device
myDevice->disconnect();
Serial.println("Device disconnected.");
}
}
This was the client side.
At server side to set the connection status flag use the following:
//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
Serial.println("Client Connected");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
Serial.println("Client Disconnected");
deviceConnected = false;
}
};

Need help getting a SparkFun ESP8266 connecting to a URL

I've been trying to get a piece of code that sends a text message to a phone when an IFTTT applet site is visited, I've been following this tutorial regarding the text message itself and this one for the WiFi shield for the ability to connect to a webpage and an HTTP request.
Basically, my problem is that it will connect to any "simple" site like google.com but it can't for "longer/complex" links. I was wondering if you would have any idea how would I solve this problem and get this to work. I've tried just using the addition symbol to combine the "simple" link and the rest of my desired link but that doesn't work either.
#include <SoftwareSerial.h> // Include software serial library, ESP8266 library dependency
#include <SparkFunESP8266WiFi.h> // Include the ESP8266 AT library
void setup() {
Serial.begin(9600);
String url = "/trigger/ESP/with/key/dwSukgpyQsyampQMkXXXX";
Serial.print (url);
// put your setup code here, to run once:
if (esp8266.begin()) // Initialize the ESP8266 and check it's return status
Serial.println("ESP8266 ready to go!"); // Communication and setup successful
else
Serial.println("Unable to communicate with the ESP8266 :(");
int retVal;
retVal = esp8266.connect("network", "networkpassword");
if (retVal < 0)
{
Serial.print(F("Error connecting: "));
Serial.println(retVal);
}
IPAddress myIP = esp8266.localIP(); // Get the ESP8266's local IP
Serial.print(F("My IP is: ")); Serial.println(myIP);
ESP8266Client client; // Create a client object
retVal = client.connect("maker.ifttt.com" + url, 80); // Connect to sparkfun (HTTP port)
if (retVal > 0)
Serial.println("Successfully connected!");
client.print("GET / HTTP/1.1\nHost: maker.ifttt.com" + url + "\nConnection: close\n\n");
while (client.available()) // While there's data available
Serial.write(client.read()); // Read it and print to serial
}
void loop() {
// put your main code here, to run repeatedly:
}
Thanks, any help would be very much appreciated!
First, the connect function requires a server(name) to connect to. In your case: maker.ifttt.com. Anything after the .com will make the connection fail (because it's not a correct servername).
Second: this function needs an IP address (like 54.175.81.255) or an array of characters. You cannot concatenate.
After you've established the connection, you can send and receive data to a specific part of this website, using the print function.
Also, in this function you can't concatenate.
Luckily, there is a String class where we easily can concatenate.
So, after you've created the client object (ESP8266Client client;), this could be the code:
String url;
char host[] = "maker.ifttt.com";
retVal = client.connect(host, 80);
if (retVal > 0) {
Serial.println("Successfully connected!");
}
url = "GET / HTTP/1.1\r\nHost: ";
url += host;
url += "/trigger/ESP/with/key/dwSukgpyQsyampQMkXXXX";
url += "\nConnection: close\n\n";
client.print(url);
while (client.connected() && !client.available());
while (client.available()) {
Serial.write(client.read());
}

How can I make the WifiClient.connect work in Arduino?

I would like to know if WifiClient.connect does not allow the locally running server to be connected (e.g. http://localhost:8080). I discovered that WifiClient.connect works when the url is anything public (e.g. www.google.com). Please help if there may be something I can programmatically change in order to connect the localhost server. The code below is a portion of setup().
status = WiFi.begin(ssid, pass);
// if you're not connected, stop here:
if ( status != WL_CONNECTED) {
Serial.println("Couldn't get a wifi connection");
while(true);
}
// if you are connected, print out info about the connection:
else {
Serial.println("Connected to network");
}
// configure the uri to be called
char apiURL[] = "127.0.0.1";
uint16_t port = 8080;
if (client.connect(apiURL, port)){
Serial.println("connected to " + String(apiURL));
// it always prints this statement
}else{
Serial.println("not calling " + String(apiURL));
}
One more (and maybe silly) thing I would like to ask: would Bluetooth be a better option than Wifi when I want my Arduino to send data to server (e.g. making POST request)?

Arduino + ESP8266, How can i send Continous Get Request?

I have a code that was available on this website https://hackaday.io/project/3072/instructions . I made the code work by modifying it a little but the main problem is that it serves the GET request only once. What i want is continuous page fetch and there should be no TCP connection closing.
I have tried different methods but the connection always breaks after 1 GET request.
Moreover, if i do not send any GET request then it serves the domain's index page continuously without breaking TCP connection.
This is the original code http://dunarbin.com/esp8266/retroBrowser.ino.
And this is mine.
#define SSID "vivek"
#define PASS "bustedparamour21"
#define DEST_HOST "www.electronics2work.com"
#define DEST_IP "31.170.161.234"
#define TIMEOUT 10000 // mS
#define CONTINUE false
#define HALT true
#define ECHO_COMMANDS // Un-comment to echo AT+ commands to serial monitor
// Print error message and loop stop.
void errorHalt(String msg)
{
Serial.println(msg);
Serial.println("HALT");
while(true){};
}
// Read characters from WiFi module and echo to serial until keyword occurs or timeout.
boolean echoFind(String keyword)
{
byte current_char = 0;
byte keyword_length = keyword.length();
// Fail if the target string has not been sent by deadline.
long deadline = millis() + TIMEOUT;
while(millis() < deadline)
{
if (Serial1.available())
{
char ch = Serial1.read();
Serial.write(ch);
if (ch == keyword[current_char])
if (++current_char == keyword_length)
{
Serial.println();
return true;
}
}
}
return false; // Timed out
}
// Read and echo all available module output.
// (Used when we're indifferent to "OK" vs. "no change" responses or to get around firmware bugs.)
void echoFlush()
{while(Serial1.available()) Serial.write(Serial1.read());}
// Echo module output until 3 newlines encountered.
// (Used when we're indifferent to "OK" vs. "no change" responses.)
void echoSkip()
{
echoFind("\n"); // Search for nl at end of command echo
echoFind("\n"); // Search for 2nd nl at end of response.
echoFind("\n"); // Search for 3rd nl at end of blank line.
}
// Send a command to the module and wait for acknowledgement string
// (or flush module output if no ack specified).
// Echoes all data received to the serial monitor.
boolean echoCommand(String cmd, String ack, boolean halt_on_fail)
{
Serial1.println(cmd);
#ifdef ECHO_COMMANDS
Serial.print("--"); Serial.println(cmd);
#endif
// If no ack response specified, skip all available module output.
if (ack == "")
echoSkip();
else
// Otherwise wait for ack.
if (!echoFind(ack)) // timed out waiting for ack string
if (halt_on_fail)
errorHalt(cmd+" failed");// Critical failure halt.
else
return false; // Let the caller handle it.
return true; // ack blank or ack found
}
// Connect to the specified wireless network.
boolean connectWiFi()
{
String cmd = "AT+CWJAP=\""; cmd += SSID; cmd += "\",\""; cmd += PASS; cmd += "\"";
if (echoCommand(cmd, "OK", CONTINUE)) // Join Access Point
{
Serial.println("Connected to WiFi.");
return true;
}
else
{
Serial.println("Connection to WiFi failed.");
return false;
}
}
// ******** SETUP ********
void setup()
{
Serial.begin(9600); // Communication with PC monitor via USB
Serial1.begin(9600); // Communication with ESP8266 via 5V/3.3V level shifter
Serial1.setTimeout(TIMEOUT);
Serial.println("ESP8266 Demo");
delay(2000);
Serial.println("Module is ready.");
echoCommand("AT+GMR", "OK", CONTINUE); // Retrieves the firmware ID (version number) of the module.
echoCommand("AT+CWMODE?","OK", CONTINUE);// Get module access mode.
// echoCommand("AT+CWLAP", "OK", CONTINUE); // List available access points - DOESN't WORK FOR ME
echoCommand("AT+CWMODE=1", "", HALT); // Station mode
echoCommand("AT+CIPMUX=1", "", HALT); // Allow multiple connections (we'll only use the first).
//connect to the wifi
boolean connection_established = false;
for(int i=0;i<5;i++)
{
if(connectWiFi())
{
connection_established = true;
break;
}
}
if (!connection_established) errorHalt("Connection failed");
delay(5000);
//echoCommand("AT+CWSAP=?", "OK", CONTINUE); // Test connection
echoCommand("AT+CIFSR", "", HALT); // Echo IP address. (Firmware bug - should return "OK".)
//echoCommand("AT+CIPMUX=0", "", HALT); // Set single connection mode
}
// ******** LOOP ********
void loop()
{
// Establish TCP connection
String cmd = "AT+CIPSTART=0,\"TCP\",\""; cmd += DEST_IP; cmd += "\",80";
if (!echoCommand(cmd, "OK", CONTINUE)) return;
delay(2000);
// Get connection status
if (!echoCommand("AT+CIPSTATUS", "OK", CONTINUE))
return;
// Build HTTP request.
cmd = "GET /";
cmd +="iot/graphing.php?a=1&b=ldr&c=41 ";
cmd += "HTTP/1.1\r\nHost: ";
cmd += DEST_HOST;
cmd += ":80\r\n\r\n";
// Ready the module to receive raw data
if (!echoCommand("AT+CIPSEND=0,"+String(cmd.length()), ">", CONTINUE))
{
echoCommand("AT+CIPCLOSE", "", CONTINUE);
Serial.println("Connection timeout.");
return;
}
// Send the raw HTTP request
echoCommand(cmd, "OK",CONTINUE ); // GET
// Loop forever echoing data received from destination server.
while(true)
while (Serial1.available())
Serial.write(Serial1.read());
errorHalt("ONCE ONLY");
}
This code makes get request only once. How can i make it serve GET request Continously without closing TCP connection?
THANKS in Advance!!
You need to close your connection using AT+CIPCLOSE and then start a new connection again.
For example, if you need to make two connections everytime(like making connection with 2 websites) , you can make 1 connection, then close this connection. Now make another connection and close it.
I made 2 connections in my loop() function using above logic and it is working fine.

Trying to loop a http request using the Arduino Adafruit CC3000 Webclient

I am trying to retrieve data from a webpage to my Arduino on a looping 5 second interval. I'm building off the Adafruit CC3000 'Webclient' example.
The complete code can be seen here.
Everything works as expected, the Arduino connects to the network and then makes a single request to the specified website.
Now I'm trying to add the loop, so that the Arduino gets the refreshed data. I don't want to disconnect and then have to reconnect to the wifi network so I tried to loop the following code.
void myLoop(uint32_t ip)
{
// START LOOPING
int count = 0;
while(count == 0) {
Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
if (www.connected()) {
www.fastrprint(F("GET "));
www.fastrprint(WEBPAGE);
www.fastrprint(F(" HTTP/1.1\r\n"));
www.fastrprint(F("Host: ")); www.fastrprint(WEBSITE); www.fastrprint(F("\r\n"));
www.fastrprint(F("\r\n"));
www.println();
} else {
Serial.println(F("Connection failed"));
return;
}
/* Read data until either the connection is closed, or the idle timeout is reached. */
//unsigned long lastRead = millis();
while (www.connected()) {
while (www.available()) {
char c = www.read();
content = c;
Serial.print(c);
//lastRead = millis();
}
}
www.close();
Serial.println(F("\n\nDisconnecting"));
cc3000.disconnect();
delay(5000);
}
}
The loop runs perfectly once, but after the first iteration continually outputs "Connection Failed".
It appears that I cannot run the connectTCP function more than once but I cannot understand why
Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
I've also tried removing this from the loop and removing the www.close(); and cc3000.disconnect(); but it still fails to leave the connection open.
Any assistance would be greatly appreciated.
I'm also new using CC3000 and had a same problem with that code before.
you cannot connecting again because you put cc3000.disconnect(); inside the loop()
so just remove it, but keep the www.close(); in the loop().
I had the same issue, the cc3000 connecting only once or 3-4 http requests (Get) before crashing.
Now i have it working like this:
on the loop():
Adafruit_CC3000_Client www = cc3000.connectTCP(ip, WEBPORT);
delay(300);
if (www.connected()) {
//your http request get
//with a connection close:
www.fastrprint(F("GET "));
//inser your stuff
www.fastrprint(F("Connection: close\r\n"));
www.fastrprint(F("\r\n"));
www.println();
} else {
Serial.println(F("Connection failed"));
return;
}
//reading response
while(www.connected()){
while (www.available()) {
char c = www.read();
Serial.print(c);
}
}
www.stop();
delay(100);
www.close();
delay(200);
and stop&close the client at the end.
Hope this helps someone.. took mi so long to have this easy stuff working!

Resources