Skip to content

Commit 4b465fe

Browse files
committed
Sync update 2017-09-10
1 parent 49eac9a commit 4b465fe

22 files changed

+647
-213
lines changed

cpp_utils/.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
/docs/
2-
/Arduino/

cpp_utils/Arduino/ESP32_BLE.zip

77.8 KB
Binary file not shown.

cpp_utils/ArduinoBLE.md

+7-28
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
# Arduino BLE Support
2-
As part of this Github project, we provide libraries for Bluetooth Low Energy (BLE) for the ESP32 Arduino environment. Support for this capability is still in the process of being cooked (as of August 2017). As such you should not build product based on these functions as changes to the API and implementation are extremely likely over the next couple of months.
2+
As part of this Github project, we provide libraries for Bluetooth Low Energy (BLE) for the ESP32 Arduino environment. Support for this capability is still in the process of being cooked (as of September 2017). As such you should not build product based on these functions as changes to the API and implementation are extremely likely over the next couple of months.
33

44
That said, we now have the ability to produce a driver you can use for testing. This will give you early access to the function while give those who choose to build and maintain the code early feedback on what works, what doesn't and what can be improved from a usage perspective.
55

66
When complete, the BLE support for ESP32 Arduino will be available as a single ZIP file. The file will be called **ESP32_BLE.zip**. It is this file that will be able to be imported into the Arduino IDE from the `Sketch -> Include Library -> Add .ZIP library`. When initial development of the library has been completed, this ZIP will be placed under some form of release control so that an ordinary Arduino IDE user can simply download this as a unit and install.
77

8-
However, today, we are choosing **not** to distribute the BLE support as a downloadable library. The reason for this is that we believe that the code base for the BLE support is too fluid and it is being actively worked upon. Each day there will be changes and that means that for any given instance of the ZIP you get, if it is more than a day old, it is likely out of date.
8+
We provide sample builds of the `ESP32_BLE.zip` file in the `Arduino` folder found relative to this directory.
99

10-
Instead of giving you a fish (a new ESP32_BLE.zip) we are going to teach you to fish so that you yourself can build the very latest ESP32_BLE.zip yourself. Hopefully you will find this quite easy to accomplish.
11-
12-
Here is the recipe.
10+
The build of the Arduino support will be current as of the date of the ZIP file however should you wish to build your own instance of the ZIP from the source, here is the recipe.
1311

1412
1. Create a new directory called `build`
1513
2. Enter that directory and run `git clone https://github.com/nkolban/esp32-snippets.git`
@@ -24,41 +22,22 @@ And here you will find the `ESP32_BLE.zip` that is build from the latest source.
2422
If you have previously installed a version of the Arduino BLE Support and need to install a new one, follow the steps above to build yourself a new instance of the `ESP32_BLE.zip` that is ready for installation. I recommend removing the old one before installing the new one. To remove the old one, find the directory where the Arduino IDE stores your libraries (on Linux this is commonly `$HOME/Arduino`). In there you will find a directory called `libraries` and, if you have previously installed the BLE support, you will find a directory called `ESP32_BLE`. Remove that directory.
2523

2624
## Switching on debugging
27-
The BLE support contains extensive internal diagnostics which can be switched on by editing the file called `sdkconfig` and finding the lines which read:
25+
The BLE support contains extensive internal diagnostics which can be switched on by editing the file called `sdkconfig.h` and finding the lines which read:
2826

2927
```
30-
#
31-
# Log output
32-
#
33-
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
34-
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
35-
# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set
36-
# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set
37-
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
38-
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
39-
CONFIG_LOG_DEFAULT_LEVEL=1
28+
#define CONFIG_LOG_DEFAULT 1
4029
```
4130

4231
Change this to:
4332

4433
```
45-
#
46-
# Log output
47-
#
48-
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
49-
# CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
50-
# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set
51-
# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set
52-
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
53-
CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y
54-
CONFIG_LOG_DEFAULT_LEVEL=5
55-
# CONFIG_LOG_COLORS is not set
34+
#define CONFIG_LOG_DEFAULT 5
5635
```
5736

5837
and rebuild/deploy your project.
5938

6039
This file can be found in your Arduino IDE installation directories at:
6140

6241
```
63-
<ArduinoIDE>/hardware/espressif/esp32/tools/sdk
42+
<ArduinoIDE>/hardware/espressif/esp32/tools/sdk/include/config
6443
```

cpp_utils/DesignNotes/WebSockets.md

+37-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ For example, imagine a WebSocket client wants to send a file of 1MByte to the ES
1313

1414
This sounds workable ... so let us now think about how we might go about creating an input stream for a WebSocket message. Each WebSocket message starts with a WebSocket frame which contains, amongst other things, the length of the payload data. This means that we know up front how much of the remaining data is payload. This becomes essential as we can't rely on an "end of file" marker in the input stream to indicate the end of the WebSocket payload. The reason for this is that the WebSocket is a TCP stream that will be used to carry multiple sequential messages.
1515

16-
Let us now invent a new class. Let us call it a SocketInputRecordStream.
16+
Let us now invent a new class. Let us call it a SocketInputRecordStreambuf.
1717
It will have a constructor of the form:
1818

1919
```
20-
SocketInputRecordStream(Socket &socket, size_t dataLength, size_t bufferSize=512)
20+
SocketInputRecordStreambuf(Socket &socket, size_t dataLength, size_t bufferSize=512)
2121
```
2222

2323
The `socket` is the TCP/IP socket that we are going to read data from. The `dataLength` is the size of the data we wish to read. The class will extend `std::streambuf`. It will internally maintain a data buffer of size `bufferSize`. Initially, the buffer will be empty. When a read is performed on the stream, a call to `underflow()` will be made (this is a `std::streambuf` virtual function).
@@ -27,4 +27,38 @@ Our rules for this class include:
2727
* We must **not** read more the `dataLength` bytes from the socket.
2828
* We must **indicate** and `EOF` once we have had `dataLength` bytes consumed by a stream reader.
2929
* The class must implement a `discard()` method that will discard further bytes from the socket such that the total number of bytes read from the socket will equal `dataLength`.
30-
* Deleting an instance of the class must invoke `discard()`.
30+
* Deleting an instance of the class must invoke `discard()`.
31+
32+
## File transfer
33+
WebSockets make a great file transfer technology. While this is more an application utilization of the technology than the design of the framework, we'll capture it here. Let us first set the scene. We have a client application that wishes to transmit a file. We will assume that the file is composed of three logical components:
34+
35+
* The file name
36+
* The file length
37+
* The content of the file
38+
39+
It would be wrong to expect the client to send the file as one continuous unit in one WebSocket message. The reason for this is that the client would have to have loaded the complete file into its memory buffers in order to send it. As such, we should assume that the client will send the files as one or more "parts" where each part represents a piece of the file.
40+
41+
We thus invent the following protocol:
42+
43+
For the first message we have:
44+
45+
```
46+
+-----------------------+-----------+----+--------------------+-----------+
47+
| transferId (32bit/LE) | file name | \0 | length (32bits/LE) | Data .... |
48+
+-----------------------+-----------+----+--------------------+-----------+
49+
```
50+
51+
For subsequent messages we have:
52+
53+
```
54+
+-----------------------+-----------+
55+
| transferId (32bit/LE) | Data .... |
56+
+-----------------------+-----------+
57+
```
58+
Let us look at these.
59+
60+
* `transferId` - An Id that is randomly generated by the client. This is used to associate multiple messages for the same file together.
61+
* `file name` - The name of the file that the client wishes to send. Can include paths. This will be used to determine where on the file system the file will be written.
62+
* `length` - The size of the file in bytes. Knowing the size will allow us to know when the whole file has been received.
63+
64+
We will create an encapsulation class called `WebSocketFileTransfer`.

cpp_utils/FATFS_VFS.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
* @param [in] partitionName The name of the partition used to store the FAT file system.
2727
*/
2828
FATFS_VFS::FATFS_VFS(std::string mountPath, std::string partitionName) {
29-
m_mountPath = mountPath;
29+
m_mountPath = mountPath;
3030
m_partitionName = partitionName;
31-
m_maxFiles = 4;
32-
m_wl_handle = WL_INVALID_HANDLE;
31+
m_maxFiles = 4;
32+
m_wl_handle = WL_INVALID_HANDLE;
3333
} // FATFS_VFS
3434

3535

cpp_utils/FATFS_VFS.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <string>
1111
extern "C" {
1212
#include <esp_vfs_fat.h>
13+
#include <wear_levelling.h>
1314
}
1415
/**
1516
* @brief Provide access to the FAT file system on %SPI flash.
@@ -42,14 +43,15 @@ class FATFS_VFS {
4243
public:
4344
FATFS_VFS(std::string mountPath, std::string partitionName);
4445
virtual ~FATFS_VFS();
46+
4547
void mount();
4648
void setMaxFiles(int maxFiles);
4749
void unmount();
4850
private:
4951
wl_handle_t m_wl_handle;
5052
std::string m_mountPath;
5153
std::string m_partitionName;
52-
int m_maxFiles;
54+
int m_maxFiles;
5355
};
5456

5557
#endif /* COMPONENTS_CPP_UTILS_FATFS_VFS_H_ */

cpp_utils/HttpRequest.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ HttpRequest::~HttpRequest() {
122122
} // ~HttpRequest
123123

124124

125-
void HttpRequest::close_cpp() {
125+
void HttpRequest::close() {
126126
m_clientSocket.close_cpp();
127127
} // close_cpp
128128

cpp_utils/HttpRequest.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "WebSocket.h"
1515
#include "HttpParser.h"
1616

17-
17+
#undef close
1818

1919
class HttpRequest {
2020
private:
@@ -51,10 +51,8 @@ class HttpRequest {
5151
static const std::string HTTP_METHOD_POST;
5252
static const std::string HTTP_METHOD_PUT;
5353

54-
55-
56-
void close_cpp();
57-
void dump();
54+
void close();
55+
void dump();
5856
std::string getBody();
5957
std::string getHeader(std::string name);
6058
std::map<std::string, std::string> getHeaders();

cpp_utils/HttpResponse.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ void HttpResponse::addHeader(const std::string name, const std::string value) {
5050
} // addHeader
5151

5252

53-
void HttpResponse::close_cpp() {
54-
m_request->close_cpp();
55-
} // close_cpp
53+
void HttpResponse::close() {
54+
m_request->close();
55+
} // close
5656

5757

5858
std::string HttpResponse::getHeader(std::string name) {
@@ -74,6 +74,7 @@ std::map<std::string, std::string> HttpResponse::getHeaders() {
7474
* @param [in] data The data to send to the partner.
7575
*/
7676
void HttpResponse::sendData(std::string data) {
77+
// If we haven't yet sent the header of the data, send that now.
7778
if (m_headerCommitted == false) {
7879
std::ostringstream oss;
7980
oss << m_request->getVersion() << " " << m_status << " " << m_statusMessage << lineTerminator;
@@ -84,6 +85,8 @@ void HttpResponse::sendData(std::string data) {
8485
m_headerCommitted = true;
8586
m_request->getSocket().send_cpp(oss.str());
8687
}
88+
89+
// Send the payload data.
8790
m_request->getSocket().send_cpp(data);
8891
} // sendData
8992

cpp_utils/HttpResponse.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class HttpResponse {
3636
virtual ~HttpResponse();
3737

3838
void addHeader(std::string name, std::string value);
39-
void close_cpp();
39+
void close();
4040
//std::string getRootPath();
4141
std::string getHeader(std::string name);
4242
std::map<std::string, std::string> getHeaders();

cpp_utils/HttpServer.cpp

+55-19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Author: kolban
66
*/
77

8+
#include <fstream>
89
#include "HttpServer.h"
910
#include "SockServ.h"
1011
#include "Task.h"
@@ -14,6 +15,7 @@
1415
#include "WebSocket.h"
1516
static const char* LOG_TAG = "HttpServer";
1617

18+
#undef close
1719
/**
1820
* Constructor for HTTP Server
1921
*/
@@ -30,13 +32,16 @@ HttpServer::~HttpServer() {
3032
*/
3133
class HttpServerTask: public Task {
3234
public:
33-
HttpServerTask(std::string name): Task(name) {};
35+
HttpServerTask(std::string name): Task(name) {
36+
m_pHttpServer = nullptr;
37+
};
3438

3539
private:
3640
HttpServer* m_pHttpServer; // Reference to the HTTP Server
3741

3842
/**
3943
* @brief Process an incoming HTTP Request
44+
*
4045
* We examine each of the path handlers to see if we have a match for the method/path pair. If we do,
4146
* we invoke the handler callback passing in both the request and response.
4247
*
@@ -45,52 +50,81 @@ class HttpServerTask: public Task {
4550
* @param [in] request The HTTP request to process.
4651
*/
4752
void processRequest(HttpRequest &request) {
48-
ESP_LOGD(LOG_TAG, ">> processRequest: Method: %s, Path: %s",
49-
request.getMethod().c_str(), request.getPath().c_str())
53+
ESP_LOGD("HttpServerTask", ">> processRequest: Method: %s, Path: %s",
54+
request.getMethod().c_str(), request.getPath().c_str());
55+
5056
for (auto it = m_pHttpServer->m_pathHandlers.begin(); it != m_pHttpServer->m_pathHandlers.end(); ++it) {
5157
if (it->match(request.getMethod(), request.getPath())) {
52-
ESP_LOGD(LOG_TAG, "Found a path handler match!!");
58+
ESP_LOGD("HttpServerTask", "Found a path handler match!!");
5359
if (request.isWebsocket()) {
54-
it->invoke(&request, nullptr);
60+
it->invokePathHandler(&request, nullptr);
5561
request.getWebSocket()->startReader();
5662
} else {
5763
HttpResponse response(&request);
58-
59-
it->invoke(&request, &response);
64+
it->invokePathHandler(&request, &response);
6065
}
6166
return;
6267
} // Path handler match
6368
} // For each path handler
6469

70+
ESP_LOGD("HttpServerTask", "No Path handler found");
71+
// If we reach here, then we did not find a handler for the request.
72+
73+
// Check to see if we have an un-handled WebSocket
74+
if (request.isWebsocket()) {
75+
request.getWebSocket()->close_cpp();
76+
return;
77+
}
78+
6579
// Serve up the content from the file on the file system ... if found ...
6680

67-
ESP_LOGD(LOG_TAG, "No Path handler found");
81+
std::ifstream ifStream;
82+
std::string fileName = m_pHttpServer->getRootPath() + request.getPath();
83+
ESP_LOGD("HttpServerTask", "Opening file: %s", fileName.c_str());
84+
ifStream.open(fileName, std::ifstream::in | std::ifstream::binary);
85+
86+
// If we failed to open the requested file, then it probably didn't exist so return a not found.
87+
if (!ifStream.is_open()) {
88+
HttpResponse response(&request);
89+
response.setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found");
90+
response.sendData("");
91+
return;
92+
}
93+
94+
// We now have an open file and want to push the content of that file through to the browser.
6895
HttpResponse response(&request);
69-
response.setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found");
70-
response.sendData("");
96+
response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK");
97+
std::stringstream ss;
98+
ss << ifStream.rdbuf();
99+
response.sendData(ss.str());
100+
ifStream.close();
71101

72102
} // processRequest
73103

74104

75105
/**
76-
* Perform the task handling for server.
106+
* @brief Perform the task handling for server.
107+
* @param [in] data A reference to the HttpServer.
77108
*/
78109
void run(void* data) {
79110
m_pHttpServer = (HttpServer*)data;
111+
112+
// Create a socket server and start it running.
80113
SockServ sockServ(m_pHttpServer->getPort());
81114
sockServ.start();
82-
ESP_LOGD(LOG_TAG, "Listening on port %d", m_pHttpServer->getPort());
115+
ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort());
116+
83117
while(1) {
84-
ESP_LOGD(LOG_TAG, "Waiting for new client");
118+
ESP_LOGD("HttpServerTask", "Waiting for new peer client");
85119
Socket clientSocket = sockServ.waitForNewClient();
86-
ESP_LOGD(LOG_TAG, "Got a new client");
120+
ESP_LOGD("HttpServerTask", "HttpServer listening on port %d received a new client connection", m_pHttpServer->getPort());
121+
87122
HttpRequest request(clientSocket);
88123
request.dump();
89124
processRequest(request);
90125
if (!request.isWebsocket()) {
91-
request.close_cpp();
126+
request.close();
92127
}
93-
94128
} // while
95129
} // run
96130
}; // HttpServerTask
@@ -132,6 +166,7 @@ uint16_t HttpServer::getPort() {
132166
return m_portNumber;
133167
} // getPort
134168

169+
135170
/**
136171
* @brief Get the current root path.
137172
* @return The current root path.
@@ -140,6 +175,7 @@ std::string HttpServer::getRootPath() {
140175
return m_rootPath;
141176
} // getRootPath
142177

178+
143179
/**
144180
* @brief Set the root path for URL file mapping.
145181
*
@@ -200,7 +236,7 @@ PathHandler::PathHandler(std::string method, std::string pathPattern,
200236
* @return True if the path matches.
201237
*/
202238
bool PathHandler::match(std::string method, std::string path) {
203-
ESP_LOGD(LOG_TAG, "match: %s with %s", m_textPattern.c_str(), path.c_str());
239+
ESP_LOGD(LOG_TAG, "matching: %s with %s", m_textPattern.c_str(), path.c_str());
204240
if (method != m_method) {
205241
return false;
206242
}
@@ -214,6 +250,6 @@ bool PathHandler::match(std::string method, std::string path) {
214250
* @param [in] response An object representing the response.
215251
* @return N/A.
216252
*/
217-
void PathHandler::invoke(HttpRequest* request, HttpResponse *response) {
253+
void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse *response) {
218254
m_requestHandler(request, response);
219-
} // invoke
255+
} // invokePathHandler

0 commit comments

Comments
 (0)