Skip to content

WiFiClientStream added #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 28 additions & 35 deletions examples/StandardFirmataWiFi/StandardFirmataWiFi.ino
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
// the minimum interval for sampling analog input
#define MINIMUM_SAMPLING_INTERVAL 1

#define WIFI_MAX_CONN_ATTEMPTS 3
#define MAX_CONN_ATTEMPTS 20 // [500 ms] -> 10 s

/*==============================================================================
* GLOBAL VARIABLES
Expand All @@ -130,8 +130,8 @@ IPAddress subnet(SUBNET_MASK);
IPAddress gateway(GATEWAY_IP_ADDRESS);
#endif

int wifiConnectionAttemptCounter = 0;
int wifiStatus = WL_IDLE_STATUS;
int connectionAttempts = 0;
bool streamConnected = false;

/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
Expand Down Expand Up @@ -308,6 +308,12 @@ void checkDigitalInputs(void)
if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}

// -----------------------------------------------------------------------------
// function forward declarations
void enableI2CPins();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which compiler needs these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Xtensa Compiler for the ESP8266 can't do without forward declarations if you want to call a function before it is defined. I havn't checked the dependencies. If there are no circular references reordering the functions can avoid this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reordering the functions in the sketch the forward definitions can be removed. But there is also the question of context, of keeping certain functions in a textual proximity or order to each other.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange I have not run into any compiler errors when using the ESP8266 Arduino core and I have successfully compiled each module that library supports (except ThaiEasyElec's ESPino which fails). At least not as of version 2.1.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While compiling I see the version 1.20.0-26 for the Xtensa and a lot of compiler options that are handled by the Arduino IDE. Maybe one of them makes the difference (e.g. for Firmata.cpp):

-U__STRICT_ANSI__ -c  -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections -DF_CPU=80000000L   -DARDUINO=10607 -DARDUINO_ESP8266_ESP12 -DARDUINO_ARCH_ESP8266  -DESP8266

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, you are compiling via the command line rather than using the Arduino IDE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I just changed to verbosity via IDE settings to see what is really happening.

void disableI2CPins();
void reportAnalogCallback(byte analogPin, int value);

// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
* two bit-arrays that track Digital I/O and PWM status
Expand Down Expand Up @@ -826,38 +832,26 @@ void systemResetCallback()
}

void printWifiStatus() {
#if defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)
if ( WiFi.status() != WL_CONNECTED )
{
DEBUG_PRINT( "WiFi connection failed. Status value: " );
DEBUG_PRINTLN( WiFi.status() );
}
else
#endif //defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)
{
// print the SSID of the network you're attached to:
DEBUG_PRINT( "SSID: " );

#if defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)
DEBUG_PRINTLN( WiFi.SSID() );
#endif //defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)

// print your WiFi shield's IP address:
DEBUG_PRINT( "IP Address: " );

#if defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)
IPAddress ip = WiFi.localIP();
DEBUG_PRINTLN( ip );
#endif //defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)

// print the received signal strength:
DEBUG_PRINT( "signal strength (RSSI): " );

#if defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)
long rssi = WiFi.RSSI();
DEBUG_PRINT( rssi );
#endif //defined(ARDUINO_WIFI_SHIELD) || defined(WIFI_101) || defined(ESP8266_WIFI)

DEBUG_PRINTLN( " dBm" );
}
}
Expand Down Expand Up @@ -890,7 +884,7 @@ void setup()
#ifdef STATIC_IP_ADDRESS
DEBUG_PRINT( "Using static IP: " );
DEBUG_PRINTLN( local_ip );
#ifdef ESP8266_WIFI
#if defined(ESP8266_WIFI) || (defined(SUBNET_MASK) && defined(GATEWAY_IP_ADDRESS))
stream.config( local_ip , gateway, subnet );
#else
// you can also provide a static IP in the begin() functions, but this simplifies
Expand All @@ -902,37 +896,36 @@ void setup()
#endif

/*
* Configure WiFi security
* Configure WiFi security and initiate WiFi connection
*/
#if defined(WIFI_WEP_SECURITY)
while (wifiStatus != WL_CONNECTED) {
DEBUG_PRINT( "Attempting to connect to WEP SSID: " );
DEBUG_PRINTLN(ssid);
wifiStatus = stream.begin( ssid, wep_index, wep_key, SERVER_PORT );
delay(5000); // TODO - determine minimum delay
if (++wifiConnectionAttemptCounter > WIFI_MAX_CONN_ATTEMPTS) break;
}

stream.begin(ssid, wep_index, wep_key);
#elif defined(WIFI_WPA_SECURITY)
while (wifiStatus != WL_CONNECTED) {
DEBUG_PRINT( "Attempting to connect to WPA SSID: " );
DEBUG_PRINTLN(ssid);
wifiStatus = stream.begin(ssid, wpa_passphrase, SERVER_PORT);
delay(5000); // TODO - determine minimum delay
if (++wifiConnectionAttemptCounter > WIFI_MAX_CONN_ATTEMPTS) break;
}

stream.begin(ssid, wpa_passphrase);
#else //OPEN network
while (wifiStatus != WL_CONNECTED) {
DEBUG_PRINTLN( "Attempting to connect to open SSID: " );
DEBUG_PRINTLN(ssid);
wifiStatus = stream.begin(ssid, SERVER_PORT);
delay(5000); // TODO - determine minimum delay
if (++wifiConnectionAttemptCounter > WIFI_MAX_CONN_ATTEMPTS) break;
}
stream.begin(ssid);
#endif //defined(WIFI_WEP_SECURITY)

DEBUG_PRINTLN( "WiFi setup done" );

/*
* Wait for TCP connection to be established
*/
while (!streamConnected && ++connectionAttempts <= MAX_CONN_ATTEMPTS) {
delay(500);
DEBUG_PRINT(".");
streamConnected = stream.maintain();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add: DEBUG_PRINT("."); after the delay as a progress indicator

}
if (streamConnected) {
DEBUG_PRINTLN( "TCP connection established" );
} else {
DEBUG_PRINTLN( "failed to establish TCP connection" );
Copy link
Member

@soundanalogous soundanalogous Apr 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also reported in printWiFiStatus(). Do we really need it in both places?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also misleading currently as I get the failed message if I am not fast enough in starting up my host application. Maybe the while loop should continue until a connection is made and/or use a much longer timeout). Previously the while loop only waited until a connection was made with the router.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't have a looked at the debug output so far.

Waiting until connected is a drawback if you add active logic that runs independently of the host. That is one reason why I wanted to introduce a hostConnectedCallback.

Regarding the timeout, I reduced it by about 30% because an ESP8266 with static IP typically connects in less than 1.5 seconds. Raising the timeout should help somewhat.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#define MAX_CONN_ATTEMPTS           20  // [500 ms] -> 10 s

should be long enough for a WiFi connect + DHCP + TCP connect in most cases, but I know there are situations where it needs more time, especially if the signal strength is weak. I am still not sure if this is reason enough to use a higher timeout. An easy way to cope with different requirements would be to move the timeout value into the config file and assign a default.

}
printWifiStatus();

/*
Expand Down
35 changes: 25 additions & 10 deletions examples/StandardFirmataWiFi/wifiConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@
*/
//#define WIFI_101

//do not modify the following 10 lines
//do not modify the following 11 lines
#if defined(ARDUINO_SAMD_MKR1000) && !defined(WIFI_101)
// automatically include if compiling for MRK1000
#define WIFI_101
#endif
#ifdef WIFI_101
#include <WiFi101.h>
#include "utility/WiFiStream.h"
WiFiStream stream;
#define WIFI_LIB_INCLUDED
#include "utility/WiFiClientStream.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the memory overhead of including both client and server streams simultaneously?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Commenting out one include and looking at the build size should tell. A good linker should notice that the code is not used and skip it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the Xtensa compiler there is no difference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified there is also no difference when compiling for MKR1000 or Arduino Zero (with WiFi 101 shield)

#include "utility/WiFiServerStream.h"
#define WIFI_LIB_INCLUDED
#endif

/*
Expand All @@ -57,8 +57,8 @@ WiFiStream stream;
//do not modify the following 10 lines
#ifdef ARDUINO_WIFI_SHIELD
#include <WiFi.h>
#include "utility/WiFiStream.h"
WiFiStream stream;
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
#ifdef WIFI_LIB_INCLUDED
#define MULTIPLE_WIFI_LIB_INCLUDES
#else
Expand All @@ -85,8 +85,8 @@ WiFiStream stream;
#endif
#ifdef ESP8266_WIFI
#include <ESP8266WiFi.h>
#include "utility/WiFiStream.h"
WiFiStream stream;
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
#ifdef WIFI_LIB_INCLUDED
#define MULTIPLE_WIFI_LIB_INCLUDES
#else
Expand Down Expand Up @@ -122,12 +122,17 @@ char ssid[] = "your_network_name";
//#define GATEWAY_IP_ADDRESS 0,0,0,0 // REQUIRED for ESP8266_WIFI, optional for others


// STEP 4 [REQUIRED for all boards and shields]
// STEP 4 [OPTIONAL for all boards and shields]
// uncomment and replace with the IP address of your server if the Arduino is the TCP client
//#define SERVER_IP 10, 0, 0, 15


// STEP 5 [REQUIRED for all boards and shields]
// define your port number here, you will need this to open a TCP connection to your Arduino
#define SERVER_PORT 3030


// STEP 5 [REQUIRED for all boards and shields]
// STEP 6 [REQUIRED for all boards and shields]
// determine your network security type (OPTION A, B, or C). Option A is the most common, and the
// default.

Expand Down Expand Up @@ -200,6 +205,16 @@ char wep_key[] = "your_wep_key";
#error "you must choose between WIFI_NO_SECURITY and WIFI_WPA_SECURITY"
#endif

/*==============================================================================
* WIFI STREAM (don't change anything here)
*============================================================================*/

#ifdef SERVER_IP
WiFiClientStream stream(IPAddress(SERVER_IP), SERVER_PORT);
#else
WiFiServerStream stream(SERVER_PORT);
#endif

/*==============================================================================
* PIN IGNORE MACROS (don't change anything here)
*============================================================================*/
Expand Down
106 changes: 106 additions & 0 deletions utility/WiFiClientStream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
WiFiClientStream.h

An Arduino Stream that wraps an instance of a WiFiClient. For use
with legacy Arduino WiFi shield and other boards and shields that
are compatible with the Arduino WiFi library.

Copyright (C) 2016 Jens B. All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

See file LICENSE.txt for further informations on licensing terms.

Parts of this class are based on

- EthernetClientStream - Copyright (C) 2013 Norbert Truchsess. All rights reserved.

published under the same license.

Last updated April 17th, 2016
*/

#ifndef WIFI_CLIENT_STREAM_H
#define WIFI_CLIENT_STREAM_H

#include "WiFiStream.h"

#define MILLIS_RECONNECT 5000

class WiFiClientStream : public WiFiStream
{
protected:
uint32_t _time_connect = 0;

/**
* check if TCP client is connected
* @return true if connected
*/
virtual inline bool connect_client()
{
if( _client && _client.connected() ) return true;

if( _connected )
{
stop();
}

// active TCP connect
if( WiFi.status() == WL_CONNECTED )
{
// if the client is disconnected, try to reconnect every 5 seconds
if( millis() - _time_connect >= MILLIS_RECONNECT )
{
_connected = _client.connect( _remote_ip, _port );
if( !_connected )
{
_time_connect = millis();
}
else if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_CONNECTED);
}
}
}

return _connected;
}

public:
/**
* create a WiFi stream with a TCP client
*/
WiFiClientStream(IPAddress server_ip, uint16_t server_port) : WiFiStream(server_ip, server_port) {}

/**
* maintain WiFi and TCP connection
* @return true if WiFi and TCP connection are established
*/
virtual inline bool maintain()
{
return connect_client();
}

/**
* stop client connection
*/
virtual inline void stop()
{
if( _client)
{
_client.stop();
if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_DISCONNECTED);
}
}
_connected = false;
_time_connect = millis();
}

};

#endif //WIFI_CLIENT_STREAM_H
Loading