1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-29 05:21:37 +03:00

Merge branch 'master' into feature/issue-2246-multi-wifi-hidden

This commit is contained in:
Erriez
2021-01-19 16:12:54 +01:00
committed by GitHub
354 changed files with 21094 additions and 34937 deletions

View File

@ -359,8 +359,12 @@ uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) {
0);
}
void ESP8266AVRISP::flash_read_page(int length) {
bool ESP8266AVRISP::flash_read_page(int length) {
uint8_t *data = (uint8_t *) malloc(length + 1);
if (!data)
{
return false;
}
for (int x = 0; x < length; x += 2) {
*(data + x) = flash_read(LOW, here);
*(data + x + 1) = flash_read(HIGH, here);
@ -369,12 +373,16 @@ void ESP8266AVRISP::flash_read_page(int length) {
*(data + length) = Resp_STK_OK;
_client.write((const uint8_t *)data, (size_t)(length + 1));
free(data);
return;
return true;
}
void ESP8266AVRISP::eeprom_read_page(int length) {
bool ESP8266AVRISP::eeprom_read_page(int length) {
// here again we have a word address
uint8_t *data = (uint8_t *) malloc(length + 1);
if (!data)
{
return false;
}
int start = here * 2;
for (int x = 0; x < length; x++) {
int addr = start + x;
@ -384,7 +392,7 @@ void ESP8266AVRISP::eeprom_read_page(int length) {
*(data + length) = Resp_STK_OK;
_client.write((const uint8_t *)data, (size_t)(length + 1));
free(data);
return;
return true;
}
void ESP8266AVRISP::read_page() {

View File

@ -89,8 +89,8 @@ protected:
void commit(int addr);
void program_page();
uint8_t flash_read(uint8_t hilo, int addr);
void flash_read_page(int length);
void eeprom_read_page(int length);
bool flash_read_page(int length);
bool eeprom_read_page(int length);
void read_page();
void read_signature();

View File

@ -725,7 +725,7 @@ int HTTPClient::writeToStream(Stream * stream)
int ret = 0;
if(_transferEncoding == HTTPC_TE_IDENTITY) {
if(len > 0) {
if(len > 0 || len == -1) {
ret = writeToStreamDataBlock(stream, len);
// have we an error?
@ -1125,18 +1125,18 @@ int HTTPClient::handleHeaderResponse()
if(transferEncoding.equalsIgnoreCase(F("chunked"))) {
_transferEncoding = HTTPC_TE_CHUNKED;
} else {
return HTTPC_ERROR_ENCODING;
_returnCode = HTTPC_ERROR_ENCODING;
return _returnCode;
}
} else {
_transferEncoding = HTTPC_TE_IDENTITY;
}
if(_returnCode) {
return _returnCode;
} else {
if(_returnCode <= 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Remote host is not an HTTP Server!");
return HTTPC_ERROR_NO_HTTP_SERVER;
_returnCode = HTTPC_ERROR_NO_HTTP_SERVER;
}
return _returnCode;
}
} else {
@ -1184,6 +1184,12 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
if(readBytes > buff_size) {
readBytes = buff_size;
}
// len == -1 or len > what is available, read only what is available
int av = _client->available();
if (readBytes < 0 || readBytes > av) {
readBytes = av;
}
// read data
int bytesRead = _client->readBytes(buff, readBytes);

View File

@ -13,6 +13,8 @@
#include <WiFiClient.h>
#include <ESP8266WebServerSecure.h>
#include <ESP8266mDNS.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
#ifndef STASSID
#define STASSID "your-ssid"
@ -23,6 +25,7 @@ const char* ssid = STASSID;
const char* password = STAPSK;
BearSSL::ESP8266WebServerSecure server(443);
BearSSL::ServerSessions serverCache(5);
static const char serverCert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
@ -130,6 +133,9 @@ void setup(void){
server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
// Cache SSL sessions to accelerate the TLS handshake.
server.getServer().setCache(&serverCache);
server.on("/", handleRoot);
server.on("/inline", [](){
@ -142,7 +148,68 @@ void setup(void){
Serial.println("HTTPS server started");
}
extern "C" void stack_thunk_dump_stack();
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 'd': {
HeapSelectDram ephemeral;
umm_info(NULL, true);
break;
}
case 'i': {
HeapSelectIram ephemeral;
umm_info(NULL, true);
break;
}
case 'h': {
{
HeapSelectIram ephemeral;
Serial.printf(PSTR("IRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap());
}
{
HeapSelectDram ephemeral;
Serial.printf(PSTR("DRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap());
}
break;
}
case 'P':
out.println(F("Calling stack_thunk_dump_stack();"));
stack_thunk_dump_stack();
break;
case 'R':
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
ESP.restart();
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" h - Free Heap Report;"));
out.println(F(" i - iRAM umm_info(null, true);"));
out.println(F(" d - dRAM umm_info(null, true);"));
out.println(F(" p - call stack_thunk_dump_stack();"));
out.println(F(" R - Restart, ESP.restart();"));
out.println(F(" ? - Print Help"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
processKey(out, '?');
break;
}
}
void loop(void){
server.handleClient();
MDNS.update();
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}

View File

@ -26,6 +26,7 @@
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
#include "FS.h"
#include "base64.h"
#include "detail/RequestHandlersImpl.h"
static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization";
@ -33,6 +34,7 @@ static const char qop_auth[] PROGMEM = "qop=auth";
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate";
static const char Content_Length[] PROGMEM = "Content-Length";
static const char ETAG_HEADER[] PROGMEM = "If-None-Match";
namespace esp8266webserver {
@ -98,21 +100,19 @@ bool ESP8266WebServerTemplate<ServerType>::authenticate(const char * username, c
authReq = "";
return false;
}
char *encoded = new (std::nothrow) char[base64_encode_expected_len(toencodeLen)+1];
if(encoded == NULL){
sprintf(toencode, "%s:%s", username, password);
String encoded = base64::encode((uint8_t *)toencode, toencodeLen, false);
if(!encoded){
authReq = "";
delete[] toencode;
return false;
}
sprintf(toencode, "%s:%s", username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) {
if(authReq.equalsConstantTime(encoded)) {
authReq = "";
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
} else if(authReq.startsWith(F("Digest"))) {
String _realm = _extractParam(authReq, F("realm=\""));
String _H1 = credentialHash((String)username,_realm,(String)password);
@ -255,7 +255,18 @@ void ESP8266WebServerTemplate<ServerType>::_addRequestHandler(RequestHandlerType
template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler<ServerType>(fs, path, uri, cache_header));
bool is_file = false;
if (fs.exists(path)) {
File file = fs.open(path, "r");
is_file = file && file.isFile();
file.close();
}
if(is_file)
_addRequestHandler(new StaticFileRequestHandler<ServerType>(fs, path, uri, cache_header));
else
_addRequestHandler(new StaticDirectoryRequestHandler<ServerType>(fs, path, uri, cache_header));
}
template <typename ServerType>
@ -607,15 +618,18 @@ const String& ESP8266WebServerTemplate<ServerType>::header(const String& name) c
return emptyString;
}
template <typename ServerType>
template<typename ServerType>
void ESP8266WebServerTemplate<ServerType>::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
_headerKeysCount = headerKeysCount + 1;
if (_currentHeaders)
delete[]_currentHeaders;
_headerKeysCount = headerKeysCount + 2;
if (_currentHeaders){
delete[] _currentHeaders;
}
_currentHeaders = new RequestArgument[_headerKeysCount];
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER);
for (int i = 1; i < _headerKeysCount; i++){
_currentHeaders[i].key = headerKeys[i-1];
_currentHeaders[1].key = FPSTR(ETAG_HEADER);
for (int i = 2; i < _headerKeysCount; i++){
_currentHeaders[i].key = headerKeys[i-2];
}
}

View File

@ -246,7 +246,7 @@ protected:
bool _parseForm(ClientType& client, const String& boundary, uint32_t len);
bool _parseFormUploadAborted();
void _uploadWriteByte(uint8_t b);
uint8_t _uploadReadByte(ClientType& client);
int _uploadReadByte(ClientType& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue);
@ -308,5 +308,4 @@ protected:
using ESP8266WebServer = esp8266webserver::ESP8266WebServerTemplate<WiFiServer>;
using RequestHandler = esp8266webserver::RequestHandler<WiFiServer>;
#endif //ESP8266WEBSERVER_H
#endif //ESP8266WEBSERVER_H

View File

@ -347,16 +347,17 @@ void ESP8266WebServerTemplate<ServerType>::_uploadWriteByte(uint8_t b){
}
template <typename ServerType>
uint8_t ESP8266WebServerTemplate<ServerType>::_uploadReadByte(ClientType& client){
int ESP8266WebServerTemplate<ServerType>::_uploadReadByte(ClientType& client){
int res = client.read();
if(res == -1){
while(!client.available() && client.connected())
yield();
res = client.read();
}
return (uint8_t)res;
return res;
}
template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const String& boundary, uint32_t len){
(void) len;
@ -442,74 +443,59 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->status = UPLOAD_FILE_WRITE;
uint8_t argByte = _uploadReadByte(client);
readfile:
while(argByte != 0x0D){
if (!client.connected()) return _parseFormUploadAborted();
_uploadWriteByte(argByte);
argByte = _uploadReadByte(client);
}
argByte = _uploadReadByte(client);
int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */;
char fastBoundary[ fastBoundaryLen ];
snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str());
int boundaryPtr = 0;
while ( true ) {
int ret = _uploadReadByte(client);
if (ret < 0) {
// Unexpected, we should have had data available per above
return _parseFormUploadAborted();
}
char in = (char) ret;
if (in == fastBoundary[ boundaryPtr ]) {
// The input matched the current expected character, advance and possibly exit this file
boundaryPtr++;
if (boundaryPtr == fastBoundaryLen - 1) {
// We read the whole boundary line, we're done here!
break;
}
} else {
// The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start
for (int i = 0; i < boundaryPtr; i++) {
_uploadWriteByte( fastBoundary[ i ] );
}
if (in == fastBoundary[ 0 ]) {
// This could be the start of the real end, mark it so and don't emit/skip it
boundaryPtr = 1;
} else {
// Not the 1st char of our pattern, so emit and ignore
_uploadWriteByte( in );
boundaryPtr = 0;
}
}
}
// Found the boundary string, finish processing this file upload
if (_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->totalSize += _currentUpload->currentSize;
_currentUpload->status = UPLOAD_FILE_END;
if (_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
DBGWS("End File: %s Type: %s Size: %d\n",
_currentUpload->filename.c_str(),
_currentUpload->type.c_str(),
(int)_currentUpload->totalSize);
if (!client.connected()) return _parseFormUploadAborted();
if (argByte == 0x0A){
argByte = _uploadReadByte(client);
if (!client.connected()) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
goto readfile;
} else {
argByte = _uploadReadByte(client);
if (!client.connected()) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
client.readBytes(endBuf, boundary.length());
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
_currentUpload->totalSize += _currentUpload->currentSize;
_currentUpload->status = UPLOAD_FILE_END;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
DBGWS("End File: %s Type: %s Size: %d\n",
_currentUpload->filename.c_str(),
_currentUpload->type.c_str(),
(int)_currentUpload->totalSize);
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
if (line == "--"){
DBGWS("Done Parsing POST\n");
break;
}
continue;
} else {
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
_uploadWriteByte((uint8_t)('-'));
uint32_t i = 0;
while(i < boundary.length()){
_uploadWriteByte(endBuf[i++]);
}
argByte = _uploadReadByte(client);
goto readfile;
}
} else {
_uploadWriteByte(0x0D);
goto readfile;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line == "--") { // extra two dashes mean we reached the end of all form fields
DBGWS("Done Parsing POST\n");
break;
}
break;
continue;
}
}
}

View File

@ -72,83 +72,11 @@ public:
, _path(path)
, _cache_header(cache_header)
{
if (fs.exists(path)) {
File file = fs.open(path, "r");
_isFile = file && file.isFile();
file.close();
}
else {
_isFile = false;
}
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header == __null ? "" : cache_header);
_baseUriLength = _uri.length();
DEBUGV("StaticRequestHandler: path=%s uri=%s, cache_header=%s\r\n", path, uri, cache_header == __null ? "" : cache_header);
}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
if ((requestMethod != HTTP_GET) && (requestMethod != HTTP_HEAD))
return false;
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
return false;
return true;
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
String path;
path.reserve(_path.length() + requestUri.length() + 32);
path = _path;
if (!_isFile) {
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (path.endsWith("/"))
path += F("index.htm");
// If neither <blah> nor <blah>.gz exist, and <blah> is a file.htm, try it with file.html instead
// For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz
if (!_fs.exists(path) && !_fs.exists(path + ".gz") && path.endsWith(".htm")) {
path += 'l';
}
}
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = mime::getContentType(path);
using namespace mime;
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = _fs.open(path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
server.streamFile(f, contentType, requestMethod);
return true;
bool validMethod(HTTPMethod requestMethod){
return (requestMethod == HTTP_GET) || (requestMethod == HTTP_HEAD);
}
/* Deprecated version. Please use mime::getContentType instead */
@ -161,10 +89,144 @@ protected:
String _uri;
String _path;
String _cache_header;
bool _isFile;
};
template<typename ServerType>
class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
using SRH = StaticRequestHandler<ServerType>;
using WebServerType = ESP8266WebServerTemplate<ServerType>;
public:
StaticDirectoryRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
:
SRH(fs, path, uri, cache_header),
_baseUriLength{SRH::_uri.length()}
{}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
return SRH::validMethod(requestMethod) && requestUri.startsWith(SRH::_uri);
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str());
String path;
path.reserve(SRH::_path.length() + requestUri.length() + 32);
path = SRH::_path;
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (path.endsWith("/"))
path += F("index.htm");
// If neither <blah> nor <blah>.gz exist, and <blah> is a file.htm, try it with file.html instead
// For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz
if (!SRH::_fs.exists(path) && !SRH::_fs.exists(path + ".gz") && path.endsWith(".htm")) {
path += 'l';
}
DEBUGV("DirectoryRequestHandler::handle: path=%s\r\n", path.c_str());
String contentType = mime::getContentType(path);
using namespace mime;
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !SRH::_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(SRH::_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = SRH::_fs.open(path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (SRH::_cache_header.length() != 0)
server.sendHeader("Cache-Control", SRH::_cache_header);
server.streamFile(f, contentType, requestMethod);
return true;
}
protected:
size_t _baseUriLength;
};
template<typename ServerType>
class StaticFileRequestHandler
:
public StaticRequestHandler<ServerType> {
using SRH = StaticRequestHandler<ServerType>;
using WebServerType = ESP8266WebServerTemplate<ServerType>;
public:
StaticFileRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
:
StaticRequestHandler<ServerType>{fs, path, uri, cache_header}
{
File f = SRH::_fs.open(path, "r");
MD5Builder calcMD5;
calcMD5.begin();
calcMD5.addStream(f, f.size());
calcMD5.calculate();
calcMD5.getBytes(_ETag_md5);
f.close();
}
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
return SRH::validMethod(requestMethod) && requestUri == SRH::_uri;
}
bool handle(WebServerType& server, HTTPMethod requestMethod, const String & requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\"";
if(server.header("If-None-Match") == etag){
server.send(304);
return true;
}
File f = SRH::_fs.open(SRH::_path, "r");
if (!f)
return false;
if (!f.isFile()) {
f.close();
return false;
}
if (SRH::_cache_header.length() != 0)
server.sendHeader("Cache-Control", SRH::_cache_header);
server.sendHeader("ETag", etag);
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod);
return true;
}
protected:
uint8_t _ETag_md5[16];
};
} // namespace
#endif //REQUESTHANDLERSIMPL_H
#endif //REQUESTHANDLERSIMPL_H

View File

@ -138,6 +138,21 @@ GBEnkz4KpKv7TkHoW+j7F5EMcLcSrUIpyw==
#endif
#define CACHE_SIZE 5 // Number of sessions to cache.
#define USE_CACHE // Enable SSL session caching.
// Caching SSL sessions shortens the length of the SSL handshake.
// You can see the performance improvement by looking at the
// Network tab of the developper tools of your browser.
//#define DYNAMIC_CACHE // Whether to dynamically allocate the cache.
#if defined(USE_CACHE) && defined(DYNAMIC_CACHE)
// Dynamically allocated cache.
BearSSL::ServerSessions serverCache(CACHE_SIZE);
#elif defined(USE_CACHE)
// Statically allocated cache.
ServerSession store[CACHE_SIZE];
BearSSL::ServerSessions serverCache(store, CACHE_SIZE);
#endif
void setup() {
Serial.begin(115200);
@ -169,6 +184,11 @@ void setup() {
server.setECCert(serverCertList, BR_KEYTYPE_KEYX|BR_KEYTYPE_SIGN, serverPrivKey);
#endif
// Set the server's cache
#if defined(USE_CACHE)
server.setCache(&serverCache);
#endif
// Actually start accepting connections
server.begin();
}

View File

@ -13,7 +13,7 @@
#include <ESP8266WiFi.h>
#include <lwip/napt.h>
#include <lwip/dns.h>
#include <dhcpserver.h>
#include <LwipDhcpServer.h>
#define NAPT 1000
#define NAPT_PORT 10
@ -57,8 +57,8 @@ void setup() {
WiFi.dnsIP(1).toString().c_str());
// give DNS servers to AP side
dhcps_set_dns(0, WiFi.dnsIP(0));
dhcps_set_dns(1, WiFi.dnsIP(1));
dhcpSoftAP.dhcps_set_dns(0, WiFi.dnsIP(0));
dhcpSoftAP.dhcps_set_dns(1, WiFi.dnsIP(1));
WiFi.softAPConfig( // enable AP, with android-compatible google domain
IPAddress(172, 217, 28, 254),

View File

@ -4,6 +4,7 @@
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <LwipDhcpServer.h>
/* Set these to your desired credentials. */
const char *ssid = "ESPap";
@ -75,8 +76,8 @@ void setup() {
...
any client not listed will use next IP address available from the range (here 192.168.0.102 and more)
*/
wifi_softap_add_dhcps_lease(mac_CAM); // always 192.168.0.100
wifi_softap_add_dhcps_lease(mac_PC); // always 192.168.0.101
dhcpSoftAP.add_dhcps_lease(mac_CAM); // always 192.168.0.100
dhcpSoftAP.add_dhcps_lease(mac_PC); // always 192.168.0.101
/* Start Access Point. You can remove the password parameter if you want the AP to be open. */
WiFi.softAP(ssid, password);
Serial.print("AP IP address: ");

View File

@ -3,43 +3,57 @@
The API is almost the same as with the WiFi Shield library,
the most obvious difference being the different file you need to include:
*/
#include "ESP8266WiFi.h"
#include <ESP8266WiFi.h>
void setup() {
Serial.begin(115200);
Serial.println(F("\nESP8266 WiFi scan example"));
// Set WiFi to station mode and disconnect from an AP if it was previously connected
// Set WiFi to station mode
WiFi.mode(WIFI_STA);
// Disconnect from an AP if it was previously connected
WiFi.disconnect();
delay(100);
Serial.println("Setup done");
}
void loop() {
Serial.println("scan start");
String ssid;
int32_t rssi;
uint8_t encryptionType;
uint8_t* bssid;
int32_t channel;
bool hidden;
int scanResult;
// WiFi.scanNetworks will return the number of networks found
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
} else {
Serial.print(n);
Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*");
delay(10);
Serial.println(F("Starting WiFi scan..."));
scanResult = WiFi.scanNetworks(/*async=*/false, /*hidden=*/true);
if (scanResult == 0) {
Serial.println(F("No networks found"));
} else if (scanResult > 0) {
Serial.printf(PSTR("%d networks found:\n"), scanResult);
// Print unsorted scan results
for (int8_t i = 0; i < scanResult; i++) {
WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden);
Serial.printf(PSTR(" %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %s\n"),
i,
channel,
bssid[0], bssid[1], bssid[2],
bssid[3], bssid[4], bssid[5],
rssi,
(encryptionType == ENC_TYPE_NONE) ? ' ' : '*',
hidden ? 'H' : 'V',
ssid.c_str());
delay(0);
}
} else {
Serial.printf(PSTR("WiFi scan error %d"), scanResult);
}
Serial.println("");
// Wait a bit before scanning again
delay(5000);

View File

@ -173,10 +173,10 @@ void loop() {
// determine maximum output size "fair TCP use"
// client.availableForWrite() returns 0 when !client.connected()
size_t maxToTcp = 0;
int maxToTcp = 0;
for (int i = 0; i < MAX_SRV_CLIENTS; i++)
if (serverClients[i]) {
size_t afw = serverClients[i].availableForWrite();
int afw = serverClients[i].availableForWrite();
if (afw) {
if (!maxToTcp) {
maxToTcp = afw;
@ -190,11 +190,11 @@ void loop() {
}
//check UART for data
size_t len = std::min((size_t)Serial.available(), maxToTcp);
size_t len = std::min(Serial.available(), maxToTcp);
len = std::min(len, (size_t)STACK_PROTECTOR);
if (len) {
uint8_t sbuf[len];
size_t serial_got = Serial.readBytes(sbuf, len);
int serial_got = Serial.readBytes(sbuf, len);
// push UART data to all connected telnet clients
for (int i = 0; i < MAX_SRV_CLIENTS; i++)
// if client.availableForWrite() was 0 (congested)

View File

@ -24,6 +24,8 @@ X509List KEYWORD1
PrivateKey KEYWORD1
PublicKey KEYWORD1
Session KEYWORD1
ServerSession KEYWORD1
ServerSessions KEYWORD1
ESP8266WiFiGratuitous KEYWORD1
@ -191,10 +193,14 @@ getMFLNStatus KEYWORD2
setRSACert KEYWORD2
setECCert KEYWORD2
setClientTrustAnchor KEYWORD2
setCache KEYWORD2
#CertStoreBearSSL
initCertStore KEYWORD2
#ServerSessions
size KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -526,6 +526,10 @@ namespace brssl {
case BR_KEYTYPE_EC:
ek = br_skey_decoder_get_ec(dc.get());
sk = (private_key*)malloc(sizeof * sk);
if (!sk)
{
return nullptr;
}
sk->key_type = BR_KEYTYPE_EC;
sk->key.ec.curve = ek->curve;
sk->key.ec.x = (uint8_t*)malloc(ek->xlen);
@ -626,6 +630,17 @@ namespace brssl {
return pk;
}
static uint8_t *loadStream(Stream& stream, size_t size) {
uint8_t *dest = (uint8_t *)malloc(size);
if (!dest) {
return nullptr; // OOM error
}
if (size != stream.readBytes(dest, size)) {
free(dest); // Error during read
return nullptr;
}
return dest;
}
};
@ -648,6 +663,15 @@ PublicKey::PublicKey(const uint8_t *derKey, size_t derLen) {
parse(derKey, derLen);
}
PublicKey::PublicKey(Stream &stream, size_t size) {
_key = nullptr;
auto buff = brssl::loadStream(stream, size);
if (buff) {
parse(buff, size);
free(buff);
}
}
PublicKey::~PublicKey() {
if (_key) {
brssl::free_public_key(_key);
@ -711,6 +735,15 @@ PrivateKey::PrivateKey(const uint8_t *derKey, size_t derLen) {
parse(derKey, derLen);
}
PrivateKey::PrivateKey(Stream &stream, size_t size) {
_key = nullptr;
auto buff = brssl::loadStream(stream, size);
if (buff) {
parse(buff, size);
free(buff);
}
}
PrivateKey::~PrivateKey() {
if (_key) {
brssl::free_private_key(_key);
@ -781,6 +814,17 @@ X509List::X509List(const uint8_t *derCert, size_t derLen) {
append(derCert, derLen);
}
X509List::X509List(Stream &stream, size_t size) {
_count = 0;
_cert = nullptr;
_ta = nullptr;
auto buff = brssl::loadStream(stream, size);
if (buff) {
append(buff, size);
free(buff);
}
}
X509List::~X509List() {
brssl::free_certificates(_cert, _count); // also frees cert
for (size_t i = 0; i < _count; i++) {
@ -832,6 +876,22 @@ bool X509List::append(const uint8_t *derCert, size_t derLen) {
return true;
}
ServerSessions::~ServerSessions() {
if (_isDynamic && _store != nullptr)
delete _store;
}
ServerSessions::ServerSessions(ServerSession *sessions, uint32_t size, bool isDynamic) :
_size(sessions != nullptr ? size : 0),
_store(sessions), _isDynamic(isDynamic) {
if (_size > 0)
br_ssl_session_cache_lru_init(&_cache, (uint8_t*)_store, size * sizeof(ServerSession));
}
const br_ssl_session_cache_class **ServerSessions::getCache() {
return _size > 0 ? &_cache.vtable : nullptr;
}
// SHA256 hash for updater
void HashSHA256::begin() {
br_sha256_init( &_cc );

View File

@ -43,6 +43,8 @@ class PublicKey {
PublicKey();
PublicKey(const char *pemKey);
PublicKey(const uint8_t *derKey, size_t derLen);
PublicKey(Stream& stream, size_t size);
PublicKey(Stream& stream) : PublicKey(stream, stream.available()) { };
~PublicKey();
bool parse(const char *pemKey);
@ -69,6 +71,8 @@ class PrivateKey {
PrivateKey();
PrivateKey(const char *pemKey);
PrivateKey(const uint8_t *derKey, size_t derLen);
PrivateKey(Stream& stream, size_t size);
PrivateKey(Stream& stream) : PrivateKey(stream, stream.available()) { };
~PrivateKey();
bool parse(const char *pemKey);
@ -98,6 +102,8 @@ class X509List {
X509List();
X509List(const char *pemCert);
X509List(const uint8_t *derCert, size_t derLen);
X509List(Stream& stream, size_t size);
X509List(Stream& stream) : X509List(stream, stream.available()) { };
~X509List();
bool append(const char *pemCert);
@ -127,17 +133,61 @@ class X509List {
// significantly faster. Completely optional.
class WiFiClientSecure;
// Cache for a TLS session with a server
// Use with BearSSL::WiFiClientSecure::setSession
// to accelerate the TLS handshake
class Session {
friend class WiFiClientSecure;
friend class WiFiClientSecureCtx;
public:
Session() { memset(&_session, 0, sizeof(_session)); }
private:
br_ssl_session_parameters *getSession() { return &_session; }
// The actual BearSSL ession information
// The actual BearSSL session information
br_ssl_session_parameters _session;
};
// Represents a single server session.
// Use with BearSSL::ServerSessions.
typedef uint8_t ServerSession[100];
// Cache for the TLS sessions of multiple clients.
// Use with BearSSL::WiFiServerSecure::setCache
class ServerSessions {
friend class WiFiClientSecureCtx;
public:
// Uses the given buffer to cache the given number of sessions and initializes it.
ServerSessions(ServerSession *sessions, uint32_t size) : ServerSessions(sessions, size, false) {}
// Dynamically allocates a cache for the given number of sessions and initializes it.
// If the allocation of the buffer wasn't successfull, the value
// returned by size() will be 0.
ServerSessions(uint32_t size) : ServerSessions(size > 0 ? new ServerSession[size] : nullptr, size, true) {}
~ServerSessions();
// Returns the number of sessions the cache can hold.
uint32_t size() { return _size; }
private:
ServerSessions(ServerSession *sessions, uint32_t size, bool isDynamic);
// Returns the cache's vtable or null if the cache has no capacity.
const br_ssl_session_cache_class **getCache();
// Size of the store in sessions.
uint32_t _size;
// Store where the informations for the sessions are stored.
ServerSession *_store;
// Whether the store is dynamically allocated.
// If this is true, the store needs to be freed in the destructor.
bool _isDynamic;
// Cache of the server using the _store.
br_ssl_session_cache_lru _cache;
};
// Updater SHA256 hash and signature verification
class HashSHA256 : public UpdaterHashClass {
public:
@ -164,7 +214,7 @@ class SigningVerifier : public UpdaterVerifyClass {
private:
PublicKey *_pubKey;
};
// Stack thunked versions of calls
extern "C" {
extern unsigned char *thunk_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);

View File

@ -80,7 +80,7 @@ CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset,
// The certs.ar file is a UNIX ar format file, concatenating all the
// individual certificates into a single blob in a space-efficient way.
int CertStore::initCertStore(FS &fs, const char *indexFileName, const char *dataFileName) {
int CertStore::initCertStore(fs::FS &fs, const char *indexFileName, const char *dataFileName) {
int count = 0;
uint32_t offset = 0;
@ -101,12 +101,12 @@ int CertStore::initCertStore(FS &fs, const char *indexFileName, const char *data
memcpy_P(_indexName, indexFileName, strlen_P(indexFileName) + 1);
memcpy_P(_dataName, dataFileName, strlen_P(dataFileName) + 1);
File index = _fs->open(_indexName, "w");
fs::File index = _fs->open(_indexName, "w");
if (!index) {
return 0;
}
File data = _fs->open(_dataName, "r");
fs::File data = _fs->open(_dataName, "r");
if (!data) {
index.close();
return 0;
@ -179,7 +179,7 @@ const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn,
return nullptr;
}
File index = cs->_fs->open(cs->_indexName, "r");
fs::File index = cs->_fs->open(cs->_indexName, "r");
if (!index) {
return nullptr;
}
@ -191,12 +191,12 @@ const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn,
if (!der) {
return nullptr;
}
File data = cs->_fs->open(cs->_dataName, "r");
fs::File data = cs->_fs->open(cs->_dataName, "r");
if (!data) {
free(der);
return nullptr;
}
if (!data.seek(ci.offset, SeekSet)) {
if (!data.seek(ci.offset, fs::SeekSet)) {
data.close();
free(der);
return nullptr;

View File

@ -31,7 +31,15 @@
namespace BearSSL {
class CertStore {
class CertStoreBase {
public:
virtual ~CertStoreBase() {}
// Installs the cert store into the X509 decoder (normally via static function callbacks)
virtual void installCertStore(br_x509_minimal_context *ctx) = 0;
};
class CertStore: public CertStoreBase {
public:
CertStore() { };
~CertStore();

View File

@ -25,7 +25,7 @@
#include <stdint.h>
extern "C" {
#include "include/wl_definitions.h"
#include <wl_definitions.h>
}
#include "IPAddress.h"

View File

@ -33,11 +33,11 @@ extern "C" {
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
#include <lwip/init.h> // LWIP_VERSION_*
}
#include "debug.h"
#include "LwipDhcpServer.h"
// -----------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------- Private functions ------------------------------------------------
@ -156,13 +156,7 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch
DEBUG_WIFI("[AP] softap config unchanged\n");
}
if(wifi_softap_dhcps_status() != DHCP_STARTED) {
DEBUG_WIFI("[AP] DHCP not started, starting...\n");
if(!wifi_softap_dhcps_start()) {
DEBUG_WIFI("[AP] wifi_softap_dhcps_start failed!\n");
ret = false;
}
}
dhcpSoftAP.end();
// check IP config
struct ip_info ip;
@ -182,6 +176,8 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch
ret = false;
}
dhcpSoftAP.begin(&ip);
return ret;
}
@ -237,19 +233,22 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA
dhcp_lease.end_ip.addr = ip.v4();
DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str());
if(!wifi_softap_set_dhcps_lease(&dhcp_lease)) {
if(!dhcpSoftAP.set_dhcps_lease(&dhcp_lease))
{
DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n");
ret = false;
}
// set lease time to 720min --> 12h
if(!wifi_softap_set_dhcps_lease_time(720)) {
if(!dhcpSoftAP.set_dhcps_lease_time(720))
{
DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n");
ret = false;
}
uint8 mode = info.gw.addr ? 1 : 0;
if(!wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode)) {
if(!dhcpSoftAP.set_dhcps_offer_option(OFFER_ROUTER, &mode))
{
DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n");
ret = false;
}

View File

@ -1,6 +1,6 @@
/*
ESP8266WiFiGeneric.h - esp8266 Wifi support.
Based on WiFi.h from Ardiono WiFi shield library.
Based on WiFi.h from Arduino WiFi shield library.
Copyright (c) 2011-2014 Arduino. All right reserved.
Modified by Ivan Grokhotkov, December 2014
Reworked by Markus Sattler, December 2015

View File

@ -26,6 +26,7 @@
#include "ESP8266WiFiGeneric.h"
#include "ESP8266WiFiSTA.h"
#include "PolledTimeout.h"
#include "LwipIntf.h"
#include "c_types.h"
#include "ets_sys.h"
@ -281,28 +282,9 @@ bool ESP8266WiFiSTAClass::config(IPAddress local_ip, IPAddress arg1, IPAddress a
return true;
}
//To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, otherwise Arduino order.
IPAddress gateway = arg1;
IPAddress subnet = arg2;
IPAddress dns1 = arg3;
if(subnet[0] != 255)
{
//octet is not 255 => interpret as Arduino order
gateway = arg2;
subnet = arg3[0] == 0 ? IPAddress(255,255,255,0) : arg3; //arg order is arduino and 4th arg not given => assign it arduino default
dns1 = arg1;
}
// check whether all is IPv4 (or gateway not set)
if (!(local_ip.isV4() && subnet.isV4() && (!gateway.isSet() || gateway.isV4()))) {
IPAddress gateway, subnet, dns1;
if (!ipAddressReorder(local_ip, arg1, arg2, arg3, gateway, subnet, dns1))
return false;
}
//ip and gateway must be in the same subnet
if((local_ip.v4() & subnet.v4()) != (gateway.v4() & subnet.v4())) {
return false;
}
#if !CORE_MOCK
// get current->previous IP address
@ -522,94 +504,6 @@ IPAddress ESP8266WiFiSTAClass::dnsIP(uint8_t dns_no) {
return IPAddress(dns_getserver(dns_no));
}
/**
* Get ESP8266 station DHCP hostname
* @return hostname
*/
String ESP8266WiFiSTAClass::hostname(void) {
return wifi_station_get_hostname();
}
/**
* Set ESP8266 station DHCP hostname
* @param aHostname max length:24
* @return ok
*/
bool ESP8266WiFiSTAClass::hostname(const char* aHostname) {
/*
vvvv RFC952 vvvv
ASSUMPTIONS
1. A "name" (Net, Host, Gateway, or Domain name) is a text string up
to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus
sign (-), and period (.). Note that periods are only allowed when
they serve to delimit components of "domain style names". (See
RFC-921, "Domain Name System Implementation Schedule", for
background). No blank or space characters are permitted as part of a
name. No distinction is made between upper and lower case. The first
character must be an alpha character. The last character must not be
a minus sign or period. A host which serves as a GATEWAY should have
"-GATEWAY" or "-GW" as part of its name. Hosts which do not serve as
Internet gateways should not use "-GATEWAY" and "-GW" as part of
their names. A host which is a TAC should have "-TAC" as the last
part of its host name, if it is a DoD host. Single character names
or nicknames are not allowed.
^^^^ RFC952 ^^^^
- 24 chars max
- only a..z A..Z 0..9 '-'
- no '-' as last char
*/
size_t len = strlen(aHostname);
if (len == 0 || len > 32) {
// nonos-sdk limit is 32
// (dhcp hostname option minimum size is ~60)
DEBUG_WIFI_GENERIC("WiFi.(set)hostname(): empty or large(>32) name\n");
return false;
}
// check RFC compliance
bool compliant = (len <= 24);
for (size_t i = 0; compliant && i < len; i++)
if (!isalnum(aHostname[i]) && aHostname[i] != '-')
compliant = false;
if (aHostname[len - 1] == '-')
compliant = false;
if (!compliant) {
DEBUG_WIFI_GENERIC("hostname '%s' is not compliant with RFC952\n", aHostname);
}
bool ret = wifi_station_set_hostname(aHostname);
if (!ret) {
DEBUG_WIFI_GENERIC("WiFi.hostname(%s): wifi_station_set_hostname() failed\n", aHostname);
return false;
}
// now we should inform dhcp server for this change, using lwip_renew()
// looping through all existing interface
// harmless for AP, also compatible with ethernet adapters (to come)
for (netif* intf = netif_list; intf; intf = intf->next) {
// unconditionally update all known interfaces
intf->hostname = wifi_station_get_hostname();
if (netif_dhcp_data(intf) != nullptr) {
// renew already started DHCP leases
err_t lwipret = dhcp_renew(intf);
if (lwipret != ERR_OK) {
DEBUG_WIFI_GENERIC("WiFi.hostname(%s): lwIP error %d on interface %c%c (index %d)\n",
intf->hostname, (int)lwipret, intf->name[0], intf->name[1], intf->num);
ret = false;
}
}
}
return ret && compliant;
}
/**
* Return Connection status.
* @return one of the value defined in wl_status_t

View File

@ -27,9 +27,10 @@
#include "ESP8266WiFiType.h"
#include "ESP8266WiFiGeneric.h"
#include "user_interface.h"
#include "LwipIntf.h"
class ESP8266WiFiSTAClass {
class ESP8266WiFiSTAClass: public LwipIntf {
// ----------------------------------------------------------------------------------------------
// ---------------------------------------- STA function ----------------------------------------
// ----------------------------------------------------------------------------------------------
@ -69,10 +70,6 @@ class ESP8266WiFiSTAClass {
IPAddress gatewayIP();
IPAddress dnsIP(uint8_t dns_no = 0);
String hostname();
bool hostname(const String& aHostname) { return hostname(aHostname.c_str()); }
bool hostname(const char* aHostname);
// STA WiFi info
wl_status_t status();
String SSID() const;

View File

@ -24,7 +24,7 @@
extern "C"
{
#include "include/wl_definitions.h"
#include "wl_definitions.h"
#include "osapi.h"
#include "ets_sys.h"
}
@ -195,7 +195,7 @@ bool WiFiClient::getSync() const
return _client->getSync();
}
size_t WiFiClient::availableForWrite ()
int WiFiClient::availableForWrite ()
{
return _client? _client->availableForWrite(): 0;
}

View File

@ -52,7 +52,7 @@ public:
WiFiClient(const WiFiClient&);
WiFiClient& operator=(const WiFiClient&);
uint8_t status();
virtual uint8_t status();
virtual int connect(IPAddress ip, uint16_t port) override;
virtual int connect(const char *host, uint16_t port) override;
virtual int connect(const String& host, uint16_t port);
@ -86,7 +86,7 @@ public:
static void setLocalPortStart(uint16_t port) { _localPort = port; }
size_t availableForWrite();
int availableForWrite() override;
friend class WiFiServer;

View File

@ -25,6 +25,7 @@
#include <list>
#include <errno.h>
#include <algorithm>
#include <Esp.h>
extern "C" {
#include "osapi.h"
@ -44,6 +45,9 @@ extern "C" {
#include <include/ClientContext.h>
#include "c_types.h"
#include "coredecls.h"
#include <mmu_iram.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
#if !CORE_MOCK
@ -67,7 +71,7 @@ extern "C" {
namespace BearSSL {
void WiFiClientSecure::_clear() {
void WiFiClientSecureCtx::_clear() {
// TLS handshake may take more than the 5 second default timeout
_timeout = 15000;
@ -91,7 +95,7 @@ void WiFiClientSecure::_clear() {
_cipher_cnt = 0;
}
void WiFiClientSecure::_clearAuthenticationSettings() {
void WiFiClientSecureCtx::_clearAuthenticationSettings() {
_use_insecure = false;
_use_fingerprint = false;
_use_self_signed = false;
@ -100,7 +104,7 @@ void WiFiClientSecure::_clearAuthenticationSettings() {
}
WiFiClientSecure::WiFiClientSecure() : WiFiClient() {
WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient() {
_clear();
_clearAuthenticationSettings();
_certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived
@ -108,12 +112,7 @@ WiFiClientSecure::WiFiClientSecure() : WiFiClient() {
stack_thunk_add_ref();
}
WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure &rhs) : WiFiClient(rhs) {
*this = rhs;
stack_thunk_add_ref();
}
WiFiClientSecure::~WiFiClientSecure() {
WiFiClientSecureCtx::~WiFiClientSecureCtx() {
if (_client) {
_client->unref();
_client = nullptr;
@ -123,9 +122,10 @@ WiFiClientSecure::~WiFiClientSecure() {
stack_thunk_del_ref();
}
WiFiClientSecure::WiFiClientSecure(ClientContext* client,
WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client,
const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) {
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta) {
_clear();
_clearAuthenticationSettings();
stack_thunk_add_ref();
@ -133,17 +133,18 @@ WiFiClientSecure::WiFiClientSecure(ClientContext* client,
_iobuf_out_size = iobuf_out_size;
_client = client;
_client->ref();
if (!_connectSSLServerRSA(chain, sk, client_CA_ta)) {
if (!_connectSSLServerRSA(chain, sk, cache, client_CA_ta)) {
_client->unref();
_client = nullptr;
_clear();
}
}
WiFiClientSecure::WiFiClientSecure(ClientContext *client,
WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client,
const X509List *chain,
unsigned cert_issuer_key_type, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) {
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta) {
_clear();
_clearAuthenticationSettings();
stack_thunk_add_ref();
@ -151,19 +152,19 @@ WiFiClientSecure::WiFiClientSecure(ClientContext *client,
_iobuf_out_size = iobuf_out_size;
_client = client;
_client->ref();
if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, client_CA_ta)) {
if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, cache, client_CA_ta)) {
_client->unref();
_client = nullptr;
_clear();
}
}
void WiFiClientSecure::setClientRSACert(const X509List *chain, const PrivateKey *sk) {
void WiFiClientSecureCtx::setClientRSACert(const X509List *chain, const PrivateKey *sk) {
_chain = chain;
_sk = sk;
}
void WiFiClientSecure::setClientECCert(const X509List *chain,
void WiFiClientSecureCtx::setClientECCert(const X509List *chain,
const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
_chain = chain;
_sk = sk;
@ -171,7 +172,7 @@ void WiFiClientSecure::setClientECCert(const X509List *chain,
_cert_issuer_key_type = cert_issuer_key_type;
}
void WiFiClientSecure::setBufferSizes(int recv, int xmit) {
void WiFiClientSecureCtx::setBufferSizes(int recv, int xmit) {
// Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately)
const int MAX_OUT_OVERHEAD = 85;
const int MAX_IN_OVERHEAD = 325;
@ -187,7 +188,7 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) {
_iobuf_out_size = xmit;
}
bool WiFiClientSecure::stop(unsigned int maxWaitMs) {
bool WiFiClientSecureCtx::stop(unsigned int maxWaitMs) {
bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
// Only if we've already connected, store session params and clear the connection options
if (_handshake_done) {
@ -199,19 +200,19 @@ bool WiFiClientSecure::stop(unsigned int maxWaitMs) {
return ret;
}
bool WiFiClientSecure::flush(unsigned int maxWaitMs) {
bool WiFiClientSecureCtx::flush(unsigned int maxWaitMs) {
(void) _run_until(BR_SSL_SENDAPP);
return WiFiClient::flush(maxWaitMs);
}
int WiFiClientSecure::connect(IPAddress ip, uint16_t port) {
int WiFiClientSecureCtx::connect(IPAddress ip, uint16_t port) {
if (!WiFiClient::connect(ip, port)) {
return 0;
}
return _connectSSL(nullptr);
}
int WiFiClientSecure::connect(const char* name, uint16_t port) {
int WiFiClientSecureCtx::connect(const char* name, uint16_t port) {
IPAddress remote_addr;
if (!WiFi.hostByName(name, remote_addr)) {
DEBUG_BSSL("connect: Name lookup failure\n");
@ -224,11 +225,11 @@ int WiFiClientSecure::connect(const char* name, uint16_t port) {
return _connectSSL(name);
}
int WiFiClientSecure::connect(const String& host, uint16_t port) {
int WiFiClientSecureCtx::connect(const String& host, uint16_t port) {
return connect(host.c_str(), port);
}
void WiFiClientSecure::_freeSSL() {
void WiFiClientSecureCtx::_freeSSL() {
// These are smart pointers and will free if refcnt==0
_sc = nullptr;
_sc_svr = nullptr;
@ -245,18 +246,18 @@ void WiFiClientSecure::_freeSSL() {
_timeout = 15000;
}
bool WiFiClientSecure::_clientConnected() {
bool WiFiClientSecureCtx::_clientConnected() {
return (_client && _client->state() == ESTABLISHED);
}
uint8_t WiFiClientSecure::connected() {
uint8_t WiFiClientSecureCtx::connected() {
if (available() || (_clientConnected() && _handshake_done && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED))) {
return true;
}
return false;
}
size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) {
size_t WiFiClientSecureCtx::_write(const uint8_t *buf, size_t size, bool pmem) {
size_t sent_bytes = 0;
if (!connected() || !size || !_handshake_done) {
@ -297,16 +298,16 @@ size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) {
return sent_bytes;
}
size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) {
size_t WiFiClientSecureCtx::write(const uint8_t *buf, size_t size) {
return _write(buf, size, false);
}
size_t WiFiClientSecure::write_P(PGM_P buf, size_t size) {
size_t WiFiClientSecureCtx::write_P(PGM_P buf, size_t size) {
return _write((const uint8_t *)buf, size, true);
}
// We have to manually read and send individual chunks.
size_t WiFiClientSecure::write(Stream& stream) {
size_t WiFiClientSecureCtx::write(Stream& stream) {
size_t totalSent = 0;
size_t countRead;
size_t countSent;
@ -329,7 +330,7 @@ size_t WiFiClientSecure::write(Stream& stream) {
return totalSent;
}
int WiFiClientSecure::read(uint8_t *buf, size_t size) {
int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) {
if (!ctx_present() || !_handshake_done) {
return -1;
}
@ -361,7 +362,7 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size) {
return 0; // If we're connected, no error but no read.
}
int WiFiClientSecure::read() {
int WiFiClientSecureCtx::read() {
uint8_t c;
if (1 == read(&c, 1)) {
return c;
@ -370,7 +371,7 @@ int WiFiClientSecure::read() {
return -1;
}
int WiFiClientSecure::available() {
int WiFiClientSecureCtx::available() {
if (_recvapp_buf) {
return _recvapp_len; // Anything from last call?
}
@ -391,7 +392,7 @@ int WiFiClientSecure::available() {
return 0;
}
int WiFiClientSecure::peek() {
int WiFiClientSecureCtx::peek() {
if (!ctx_present() || !available()) {
DEBUG_BSSL("peek: Not connected, none left available\n");
return -1;
@ -403,7 +404,7 @@ int WiFiClientSecure::peek() {
return -1;
}
size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) {
size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) {
size_t to_copy = 0;
if (!ctx_present()) {
DEBUG_BSSL("peekBytes: Not connected\n");
@ -426,22 +427,22 @@ size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) {
combination of both (the combination matches either). When a match is
achieved, this function returns 0. On error, it returns -1.
*/
int WiFiClientSecure::_run_until(unsigned target, bool blocking) {
int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) {
if (!ctx_present()) {
DEBUG_BSSL("_run_until: Not connected\n");
return -1;
}
esp8266::polledTimeout::oneShotMs loopTimeout(_timeout);
for (int no_work = 0; blocking || no_work < 2;) {
for (int no_work = 0; blocking || no_work < 2;) {
optimistic_yield(100);
if (loopTimeout) {
DEBUG_BSSL("_run_until: Timeout\n");
return -1;
}
int state;
state = br_ssl_engine_current_state(_eng);
if (state & BR_SSL_CLOSED) {
@ -464,15 +465,15 @@ int WiFiClientSecure::_run_until(unsigned target, bool blocking) {
buf = br_ssl_engine_sendrec_buf(_eng, &len);
availForWrite = WiFiClient::availableForWrite();
if (!blocking && len > availForWrite) {
/*
/*
writes on WiFiClient will block if len > availableForWrite()
this is needed to prevent available() calls from blocking
on dropped connections
on dropped connections
*/
len = availForWrite;
}
}
wlen = WiFiClient::write(buf, len);
if (wlen <= 0) {
/*
@ -550,7 +551,7 @@ int WiFiClientSecure::_run_until(unsigned target, bool blocking) {
return -1;
}
bool WiFiClientSecure::_wait_for_handshake() {
bool WiFiClientSecureCtx::_wait_for_handshake() {
_handshake_done = false;
while (!_handshake_done && _clientConnected()) {
int ret = _run_until(BR_SSL_SENDAPP);
@ -575,7 +576,7 @@ static uint8_t htoi (unsigned char c)
}
// Set a fingerprint by parsing an ASCII string
bool WiFiClientSecure::setFingerprint(const char *fpStr) {
bool WiFiClientSecureCtx::setFingerprint(const char *fpStr) {
int idx = 0;
uint8_t c, d;
uint8_t fp[20];
@ -968,7 +969,7 @@ extern "C" {
}
// Set custom list of ciphers
bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) {
bool WiFiClientSecureCtx::setCiphers(const uint16_t *cipherAry, int cipherCount) {
_cipher_list = nullptr;
_cipher_list = std::shared_ptr<uint16_t>(new (std::nothrow) uint16_t[cipherCount], std::default_delete<uint16_t[]>());
if (!_cipher_list.get()) {
@ -980,16 +981,16 @@ bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) {
return true;
}
bool WiFiClientSecure::setCiphersLessSecure() {
bool WiFiClientSecureCtx::setCiphersLessSecure() {
return setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0]));
}
bool WiFiClientSecure::setCiphers(std::vector<uint16_t> list) {
bool WiFiClientSecureCtx::setCiphers(const std::vector<uint16_t>& list) {
return setCiphers(&list[0], list.size());
}
// Installs the appropriate X509 cert validation method for a client connection
bool WiFiClientSecure::_installClientX509Validator() {
bool WiFiClientSecureCtx::_installClientX509Validator() {
if (_use_insecure || _use_fingerprint || _use_self_signed) {
// Use common insecure x509 authenticator
_x509_insecure = std::make_shared<struct br_x509_insecure_context>();
@ -1046,7 +1047,7 @@ bool WiFiClientSecure::_installClientX509Validator() {
// Called by connect() to do the actual SSL setup and handshake.
// Returns if the SSL handshake succeeded.
bool WiFiClientSecure::_connectSSL(const char* hostName) {
bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
DEBUG_BSSL("_connectSSL: start connection\n");
_freeSSL();
_oom_err = false;
@ -1060,8 +1061,17 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) {
_sc = std::make_shared<br_ssl_client_context>();
_eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
//C This was borrowed from @earlephilhower PoC, to exemplify the use of IRAM.
//C Is this something we want to keep in the final release?
{ // ESP.setIramHeap(); would be an alternative to using a class to set a scope for IRAM usage.
HeapSelectIram ephemeral;
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get());
DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get());
DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size);
DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size);
} // ESP.resetHeap();
if (!_sc || !_iobuf_in || !_iobuf_out) {
_freeSSL(); // Frees _sc, _iobuf*
@ -1136,7 +1146,7 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) {
// Slightly different X509 setup for servers who want to validate client
// certificates, so factor it out as it's used in RSA and EC servers.
bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta) {
bool WiFiClientSecureCtx::_installServerX509Validator(const X509List *client_CA_ta) {
if (client_CA_ta) {
_ta = client_CA_ta;
// X509 minimal validator. Checks dates, cert chain for trusted CA, etc.
@ -1169,15 +1179,22 @@ bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta)
// Called by WiFiServerBearSSL when an RSA cert/key is specified.
bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain,
const PrivateKey *sk,
bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain,
const PrivateKey *sk, ServerSessions *cache,
const X509List *client_CA_ta) {
_freeSSL();
_oom_err = false;
_sc_svr = std::make_shared<br_ssl_server_context>();
_eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
{ // ESP.setIramHeap();
HeapSelectIram ephemeral;
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get());
DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get());
DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size);
DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size);
} // ESP.resetHeap();
if (!_sc_svr || !_iobuf_in || !_iobuf_out) {
_freeSSL();
@ -1189,8 +1206,10 @@ bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain,
br_ssl_server_base_init(_sc_svr.get(), suites_server_rsa_P, sizeof(suites_server_rsa_P) / sizeof(suites_server_rsa_P[0]));
br_ssl_server_set_single_rsa(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0,
sk ? sk->getRSA() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default());
br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default());
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
if (cache != nullptr)
br_ssl_server_set_cache(_sc_svr.get(), cache->getCache());
if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) {
DEBUG_BSSL("_connectSSLServerRSA: Can't install serverX509check\n");
return false;
@ -1205,16 +1224,23 @@ bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain,
}
// Called by WiFiServerBearSSL when an elliptic curve cert/key is specified.
bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain,
bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain,
unsigned cert_issuer_key_type, const PrivateKey *sk,
const X509List *client_CA_ta) {
ServerSessions *cache, const X509List *client_CA_ta) {
#ifndef BEARSSL_SSL_BASIC
_freeSSL();
_oom_err = false;
_sc_svr = std::make_shared<br_ssl_server_context>();
_eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
{ // ESP.setIramHeap();
HeapSelectIram ephemeral;
_iobuf_in = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
_iobuf_out = std::shared_ptr<unsigned char>(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get());
DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get());
DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size);
DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size);
} // ESP.resetHeap();
if (!_sc_svr || !_iobuf_in || !_iobuf_out) {
_freeSSL();
@ -1228,6 +1254,8 @@ bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain,
sk ? sk->getEC() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
cert_issuer_key_type, br_ssl_engine_get_ec(_eng), br_ecdsa_i15_sign_asn1);
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
if (cache != nullptr)
br_ssl_server_set_cache(_sc_svr.get(), cache->getCache());
if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) {
DEBUG_BSSL("_connectSSLServerEC: Can't install serverX509check\n");
return false;
@ -1251,14 +1279,25 @@ bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain,
// Returns an error ID and possibly a string (if dest != null) of the last
// BearSSL reported error.
int WiFiClientSecure::getLastSSLError(char *dest, size_t len) {
int WiFiClientSecureCtx::getLastSSLError(char *dest, size_t len) {
int err = 0;
const char *t = PSTR("OK");
const char *recv_fatal = "";
const char *send_fatal = "";
if (_sc || _sc_svr) {
err = br_ssl_engine_last_error(_eng);
}
if (_oom_err) {
err = -1000;
} else {
if (err & BR_ERR_RECV_FATAL_ALERT) {
recv_fatal = PSTR("SSL received fatal alert - ");
err &= ~BR_ERR_RECV_FATAL_ALERT;
}
if (err & BR_ERR_SEND_FATAL_ALERT) {
send_fatal = PSTR("SSL sent fatal alert - ");
err &= ~BR_ERR_SEND_FATAL_ALERT;
}
}
switch (err) {
case -1000: t = PSTR("Unable to allocate memory for SSL structures and buffers."); break;
@ -1323,8 +1362,8 @@ int WiFiClientSecure::getLastSSLError(char *dest, size_t len) {
default: t = PSTR("Unknown error code."); break;
}
if (dest) {
strncpy_P(dest, t, len);
dest[len - 1] = 0;
// snprintf is PSTR safe and guaranteed to 0-terminate
snprintf(dest, len, "%s%s%s", recv_fatal, send_fatal, t);
}
return err;
}
@ -1395,7 +1434,7 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1
0x00, 26 + 14 + 6 + 5, // Extension length
0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03,
0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06,
0x01, 0x02, 0x01, // Supported signature algorithms
0x01, 0x02, 0x01, // Supported signature algorithms
0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19,
0x00, 0x1d, // Supported groups
0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats

View File

@ -31,13 +31,13 @@
namespace BearSSL {
class WiFiClientSecure : public WiFiClient {
class WiFiClientSecureCtx : public WiFiClient {
public:
WiFiClientSecure();
WiFiClientSecure(const WiFiClientSecure &rhs);
~WiFiClientSecure() override;
WiFiClientSecureCtx();
WiFiClientSecureCtx(const WiFiClientSecure &rhs) = delete;
~WiFiClientSecureCtx() override;
WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically
WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete;
int connect(IPAddress ip, uint16_t port) override;
int connect(const String& host, uint16_t port) override;
@ -46,12 +46,6 @@ class WiFiClientSecure : public WiFiClient {
uint8_t connected() override;
size_t write(const uint8_t *buf, size_t size) override;
size_t write_P(PGM_P buf, size_t size) override;
size_t write(const char *buf) {
return write((const uint8_t*)buf, strlen(buf));
}
size_t write_P(const char *buf) {
return write_P((PGM_P)buf, strlen_P(buf));
}
size_t write(Stream& stream); // Note this is not virtual
int read(uint8_t *buf, size_t size) override;
int available() override;
@ -116,21 +110,16 @@ class WiFiClientSecure : public WiFiClient {
int getLastSSLError(char *dest = NULL, size_t len = 0);
// Attach a preconfigured certificate store
void setCertStore(CertStore *certStore) {
void setCertStore(CertStoreBase *certStore) {
_certStore = certStore;
}
// Select specific ciphers (i.e. optimize for speed over security)
// These may be in PROGMEM or RAM, either will run properly
bool setCiphers(const uint16_t *cipherAry, int cipherCount);
bool setCiphers(std::vector<uint16_t> list);
bool setCiphers(const std::vector<uint16_t>& list);
bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC
// Check for Maximum Fragment Length support for given len before connection (possibly insecure)
static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len);
protected:
bool _connectSSL(const char *hostName); // Do initial SSL handshake
@ -151,7 +140,7 @@ class WiFiClientSecure : public WiFiClient {
std::shared_ptr<unsigned char> _iobuf_out;
time_t _now;
const X509List *_ta;
CertStore *_certStore;
CertStoreBase *_certStore;
int _iobuf_in_size;
int _iobuf_out_size;
bool _handshake_done;
@ -188,25 +177,135 @@ class WiFiClientSecure : public WiFiClient {
unsigned _cert_issuer_key_type;
// Methods for handling server.available() call which returns a client connection.
friend class WiFiServerSecure; // Server needs to access these constructors
WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type,
const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta);
WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta);
friend class WiFiClientSecure; // access to private context constructors
WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type,
const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta);
WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta);
// RSA keyed server
bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk, const X509List *client_CA_ta);
bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk,
ServerSessions *cache, const X509List *client_CA_ta);
// EC keyed server
bool _connectSSLServerEC(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk,
const X509List *client_CA_ta);
ServerSessions *cache, const X509List *client_CA_ta);
// X.509 validators differ from server to client
bool _installClientX509Validator(); // Set up X509 validator for a client conn.
bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied
uint8_t *_streamLoad(Stream& stream, size_t size);
};
}; // class WiFiClientSecureCtx
};
class WiFiClientSecure : public WiFiClient {
// WiFiClient's "ClientContext* _client" is always nullptr in this class.
// Instead, all virtual functions call their counterpart in "WiFiClientecureCtx* _ctx"
// which also derives from WiFiClient (this parent is the one which is eventually used)
public:
WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { }
WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { }
~WiFiClientSecure() override { _ctx = nullptr; }
WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically
uint8_t status() override { return _ctx->status(); }
int connect(IPAddress ip, uint16_t port) override { return _ctx->connect(ip, port); }
int connect(const String& host, uint16_t port) override { return _ctx->connect(host, port); }
int connect(const char* name, uint16_t port) override { return _ctx->connect(name, port); }
uint8_t connected() override { return _ctx->connected(); }
size_t write(const uint8_t *buf, size_t size) override { return _ctx->write(buf, size); }
size_t write_P(PGM_P buf, size_t size) override { return _ctx->write_P(buf, size); }
size_t write(const char *buf) { return write((const uint8_t*)buf, strlen(buf)); }
size_t write_P(const char *buf) { return write_P((PGM_P)buf, strlen_P(buf)); }
size_t write(Stream& stream) /* Note this is not virtual */ { return _ctx->write(stream); }
int read(uint8_t *buf, size_t size) override { return _ctx->read(buf, size); }
int available() override { return _ctx->available(); }
int read() override { return _ctx->read(); }
int peek() override { return _ctx->peek(); }
size_t peekBytes(uint8_t *buffer, size_t length) override { return _ctx->peekBytes(buffer, length); }
bool flush(unsigned int maxWaitMs) { return _ctx->flush(maxWaitMs); }
bool stop(unsigned int maxWaitMs) { return _ctx->stop(maxWaitMs); }
void flush() override { (void)flush(0); }
void stop() override { (void)stop(0); }
// Allow sessions to be saved/restored automatically to a memory area
void setSession(Session *session) { _ctx->setSession(session); }
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setInsecure() { _ctx->setInsecure(); }
// Assume a given public key, don't validate or use cert info at all
void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) {
_ctx->setKnownKey(pk, usages);
}
// Only check SHA1 fingerprint of certificate
bool setFingerprint(const uint8_t fingerprint[20]) {
return _ctx->setFingerprint(fingerprint);
}
bool setFingerprint(const char *fpStr) { return _ctx->setFingerprint(fpStr); }
// Accept any certificate that's self-signed
void allowSelfSignedCerts() { _ctx->allowSelfSignedCerts(); }
// Install certificates of trusted CAs or specific site
void setTrustAnchors(const X509List *ta) { _ctx->setTrustAnchors(ta); }
// In cases when NTP is not used, app must set a time manually to check cert validity
void setX509Time(time_t now) { _ctx->setX509Time(now); }
// Install a client certificate for this connection, in case the server requires it (i.e. MQTT)
void setClientRSACert(const X509List *cert, const PrivateKey *sk) { _ctx->setClientRSACert(cert, sk); }
void setClientECCert(const X509List *cert, const PrivateKey *sk,
unsigned allowed_usages, unsigned cert_issuer_key_type) {
_ctx->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
}
// Sets the requested buffer size for transmit and receive
void setBufferSizes(int recv, int xmit) { _ctx->setBufferSizes(recv, xmit); }
// Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
int getMFLNStatus() { return _ctx->getMFLNStatus(); }
// Return an error code and possibly a text string in a passed-in buffer with last SSL failure
int getLastSSLError(char *dest = NULL, size_t len = 0) { return _ctx->getLastSSLError(dest, len); }
// Attach a preconfigured certificate store
void setCertStore(CertStoreBase *certStore) { _ctx->setCertStore(certStore); }
// Select specific ciphers (i.e. optimize for speed over security)
// These may be in PROGMEM or RAM, either will run properly
bool setCiphers(const uint16_t *cipherAry, int cipherCount) { return _ctx->setCiphers(cipherAry, cipherCount); }
bool setCiphers(const std::vector<uint16_t> list) { return _ctx->setCiphers(list); }
bool setCiphersLessSecure() { return _ctx->setCiphersLessSecure(); } // Only use the limited set of RSA ciphers without EC
// Check for Maximum Fragment Length support for given len before connection (possibly insecure)
static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len);
private:
std::shared_ptr<WiFiClientSecureCtx> _ctx;
// Methods for handling server.available() call which returns a client connection.
friend class WiFiServerSecure; // Server needs to access these constructors
WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type,
const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta):
_ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) {
}
WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta):
_ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) {
}
}; // class WiFiClientSecure
}; // namespace BearSSL
#endif

View File

@ -115,9 +115,10 @@ WiFiClient WiFiServer::available(byte* status) {
WiFiClient result(_unclaimed);
// pcb can be null when peer has already closed the connection
if (_unclaimed->getPCB())
if (_unclaimed->getPCB()) {
// give permission to lwIP to accept one more peer
tcp_backlog_accepted(_unclaimed->getPCB());
}
_unclaimed = _unclaimed->next();
result.setNoDelay(getNoDelay());

View File

@ -23,7 +23,7 @@
#define wifiserver_h
extern "C" {
#include "include/wl_definitions.h"
#include "wl_definitions.h"
struct tcp_pcb;
}

View File

@ -79,13 +79,13 @@ WiFiClientSecure WiFiServerSecure::available(uint8_t* status) {
(void) status; // Unused
if (_unclaimed) {
if (_sk && _sk->isRSA()) {
WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta);
WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta);
_unclaimed = _unclaimed->next();
result.setNoDelay(_noDelay);
DEBUGV("WS:av\r\n");
return result;
} else if (_sk && _sk->isEC()) {
WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta);
WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta);
_unclaimed = _unclaimed->next();
result.setNoDelay(_noDelay);
DEBUGV("WS:av\r\n");

View File

@ -42,6 +42,11 @@ class WiFiServerSecure : public WiFiServer {
_iobuf_out_size = xmit;
}
// Sets the server's cache to the given one.
void setCache(ServerSessions *cache) {
_cache = cache;
}
// Set the server's RSA key and x509 certificate (required, pick one).
// Caller needs to preserve the chain and key throughout the life of the server.
void setRSACert(const X509List *chain, const PrivateKey *sk);
@ -69,6 +74,7 @@ class WiFiServerSecure : public WiFiServer {
int _iobuf_in_size = BR_SSL_BUFSIZE_INPUT;
int _iobuf_out_size = 837;
const X509List *_client_CA_ta = nullptr;
ServerSessions *_cache = nullptr;
};

View File

@ -25,7 +25,7 @@
extern "C"
{
#include "include/wl_definitions.h"
#include "wl_definitions.h"
#include "osapi.h"
#include "ets_sys.h"
}

View File

@ -1,88 +0,0 @@
/*
wl_definitions.h - Library for Arduino Wifi shield.
Copyright (c) 2011-2014 Arduino. All right 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.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* wl_definitions.h
*
* Created on: Mar 6, 2011
* Author: dlafauci
*/
#ifndef WL_DEFINITIONS_H_
#define WL_DEFINITIONS_H_
// Maximum size of a SSID
#define WL_SSID_MAX_LENGTH 32
// Length of passphrase. Valid lengths are 8-63.
#define WL_WPA_KEY_MAX_LENGTH 63
// Length of key in bytes. Valid values are 5 and 13.
#define WL_WEP_KEY_MAX_LENGTH 13
// Size of a MAC-address or BSSID
#define WL_MAC_ADDR_LENGTH 6
// Size of a MAC-address or BSSID
#define WL_IPV4_LENGTH 4
// Maximum size of a SSID list
#define WL_NETWORKS_LIST_MAXNUM 10
// Maxmium number of socket
#define MAX_SOCK_NUM 4
// Socket not available constant
#define SOCK_NOT_AVAIL 255
// Default state value for Wifi state field
#define NA_STATE -1
//Maximum number of attempts to establish wifi connection
#define WL_MAX_ATTEMPT_CONNECTION 10
typedef enum {
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL = 1,
WL_SCAN_COMPLETED = 2,
WL_CONNECTED = 3,
WL_CONNECT_FAILED = 4,
WL_CONNECTION_LOST = 5,
WL_WRONG_PASSWORD = 6,
WL_DISCONNECTED = 7
} wl_status_t;
/* Encryption modes */
enum wl_enc_type { /* Values map to 802.11 encryption suites... */
ENC_TYPE_WEP = 5,
ENC_TYPE_TKIP = 2,
ENC_TYPE_CCMP = 4,
/* ... except these two, 7 and 8 are reserved in 802.11-2007 */
ENC_TYPE_NONE = 7,
ENC_TYPE_AUTO = 8
};
#if !defined(LWIP_INTERNAL) && !defined(__LWIP_TCP_H__)
enum wl_tcp_state {
CLOSED = 0,
LISTEN = 1,
SYN_SENT = 2,
SYN_RCVD = 3,
ESTABLISHED = 4,
FIN_WAIT_1 = 5,
FIN_WAIT_2 = 6,
CLOSE_WAIT = 7,
CLOSING = 8,
LAST_ACK = 9,
TIME_WAIT = 10
};
#endif
#endif /* WL_DEFINITIONS_H_ */

View File

@ -1,269 +0,0 @@
/*
ESP8266 mDNS responder clock
This example demonstrates two features of the LEA clsLEAMDNSHost:
1. The host and service domain negotiation process that ensures
the uniqueness of the finally chosen host and service domain name.
2. The dynamic MDNS service TXT feature
A 'clock' service in announced via the MDNS responder and the current
time is set as a TXT item (eg. 'curtime=Mon Oct 15 19:54:35 2018').
The time value is updated every second!
The ESP is initially announced to clients as 'esp8266.local', if this host domain
is already used in the local network, another host domain is negotiated. Keep an
eye on the serial output to learn the final host domain for the clock service.
The service itself is is announced as 'host domain'._espclk._tcp.local.
As the service uses port 80, a very simple HTTP server is also installed to deliver
a small web page containing a greeting and the current time (not updated).
The web server code is taken nearly 1:1 from the 'mDNS_Web_Server.ino' example.
Point your browser to 'host domain'.local to see this web page.
Instructions:
- Update WiFi SSID and password as necessary.
- Flash the sketch to the ESP8266 board
- Install host software:
- For Linux, install Avahi (http://avahi.org/).
- For Windows, install Bonjour (http://www.apple.com/support/bonjour/).
- For Mac OSX and iOS support is built in through Bonjour already.
- Use a MDNS/Bonjour browser like 'Discovery' to find the clock service in your local
network and see the current time updates.
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <LwipIntf.h>
#include <time.h>
#include <PolledTimeout.h>
// uses API MDNSApiVersion::LEAv2
#define NO_GLOBAL_MDNS // our MDNS is defined below
#include <ESP8266mDNS.h>
/*
Global defines and vars
*/
#define TIMEZONE_OFFSET 1 // CET
#define DST_OFFSET 1 // CEST
#define UPDATE_CYCLE (1 * 1000) // every second
#define START_AP_AFTER_MS 10000 // start AP after delay
#define SERVICE_PORT 80 // HTTP port
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
#ifndef APSSID
#define APSSID "ap4mdnsClock"
#define APPSK "mdnsClock"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
clsLEAMDNSHost MDNSRESP; // MDNS responder
bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain
clsLEAMDNSHost::clsService* hMDNSService = 0; // The handle of the clock service in the MDNS responder
// HTTP server at port 'SERVICE_PORT' will respond to HTTP requests
ESP8266WebServer server(SERVICE_PORT);
/*
getTimeString
*/
const char* getTimeString(void) {
static char acTimeString[32];
time_t now = time(nullptr);
ctime_r(&now, acTimeString);
size_t stLength;
while (((stLength = strlen(acTimeString))) &&
('\n' == acTimeString[stLength - 1])) {
acTimeString[stLength - 1] = 0; // Remove trailing line break...
}
return acTimeString;
}
/*
setClock
Set time via NTP
*/
void setClock(void) {
configTime((TIMEZONE_OFFSET * 3600), (DST_OFFSET * 3600), "pool.ntp.org", "time.nist.gov", "time.windows.com");
Serial.print("Waiting for NTP time sync: ");
time_t now = time(nullptr); // Secs since 01.01.1970 (when uninitalized starts with (8 * 3600 = 28800)
while (now < 8 * 3600 * 2) { // Wait for realistic value
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
Serial.printf("Current time: %s\n", getTimeString());
}
/*
setStationHostname
*/
bool setStationHostname(const char* p_pcHostname) {
if (p_pcHostname) {
WiFi.hostname(p_pcHostname);
Serial.printf("setDeviceHostname: Station hostname is set to '%s'\n", p_pcHostname);
}
return true;
}
/*
MDNSDynamicServiceTxtCallback
Add a dynamic MDNS TXT item 'ct' to the clock service.
The callback function is called every time, the TXT items for the clock service
are needed.
This can be triggered by calling MDNSRESP.announce().
*/
void MDNSDynamicServiceTxtCallback(const clsLEAMDNSHost::hMDNSService& p_hService) {
Serial.println("MDNSDynamicServiceTxtCallback");
if (hMDNSService == &p_hService) {
Serial.printf("Updating curtime TXT item to: %s\n", getTimeString());
hMDNSService->addDynamicServiceTxt("curtime", getTimeString());
}
}
/*
handleHTTPClient
*/
void handleHTTPRequest() {
Serial.println("");
Serial.println("HTTP Request");
// Get current time
time_t now = time(nullptr);;
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
String s;
s.reserve(300);
s = "<!DOCTYPE HTML>\r\n<html>Hello from ";
s += WiFi.hostname() + " at " + WiFi.localIP().toString();
// Simple addition of the current time
s += "\r\nCurrent time is: ";
s += getTimeString();
// done :-)
s += "</html>\r\n\r\n";
Serial.println("Sending 200");
server.send(200, "text/html", s);
}
/*
setup
*/
void setup(void) {
Serial.begin(115200);
// Connect to WiFi network
WiFi.persistent(false);
// useless informative callback
if (!LwipIntf::stateUpCB([](netif * nif) {
Serial.printf("New interface %c%c/%d is up\n",
nif->name[0],
nif->name[1],
netif_get_index(nif));
})) {
Serial.println("Error: could not add informative callback\n");
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Sync clock
setClock();
// Setup MDNS responder
// Init the (currently empty) host domain string with 'leamdnsv2'
if (MDNSRESP.begin("leamdnsv2",
[](clsLEAMDNSHost & p_rMDNSHost, const char* p_pcDomainName, bool p_bProbeResult)->void {
if (p_bProbeResult) {
Serial.printf("mDNSHost_AP::ProbeResultCallback: '%s' is %s\n", p_pcDomainName, (p_bProbeResult ? "FREE" : "USED!"));
// Unattended added service
hMDNSService = p_rMDNSHost.addService(0, "espclk", "tcp", 80);
hMDNSService->addDynamicServiceTxt("curtime", getTimeString());
hMDNSService->setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallback);
} else {
// Change hostname, use '-' as divider between base name and index
MDNSRESP.setHostName(clsLEAMDNSHost::indexDomainName(p_pcDomainName, "-", 0));
}
})) {
Serial.println("mDNS-AP started");
} else {
Serial.println("FAILED to start mDNS-AP");
}
// Setup HTTP server
server.on("/", handleHTTPRequest);
server.begin();
Serial.println("HTTP server started");
}
/*
loop
*/
void loop(void) {
// Check if a request has come in
server.handleClient();
// Allow MDNS processing
MDNSRESP.update();
static esp8266::polledTimeout::periodicMs timeout(UPDATE_CYCLE);
if (timeout.expired()) {
if (hMDNSService) {
// Just trigger a new MDNS announcement, this will lead to a call to
// 'MDNSDynamicServiceTxtCallback', which will update the time TXT item
Serial.printf("Announce trigger from user\n");
MDNSRESP.announce();
}
}
static bool AP_started = false;
if (!AP_started && millis() > START_AP_AFTER_MS) {
AP_started = true;
Serial.printf("Starting AP...\n");
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(APSSID, APPSK);
Serial.printf("AP started...(%s:%s, %s)\n",
WiFi.softAPSSID().c_str(),
WiFi.softAPPSK().c_str(),
WiFi.softAPIP().toString().c_str());
}
}

View File

@ -1,259 +0,0 @@
/*
ESP8266 mDNS Responder Service Monitor
This example demonstrates two features of the LEA clsLEAMDNSHost:
1. The host and service domain negotiation process that ensures
the uniqueness of the finally choosen host and service domain name.
2. The dynamic MDNS service lookup/query feature.
A list of 'HTTP' services in the local network is created and kept up to date.
In addition to this, a (very simple) HTTP server is set up on port 80
and announced as a service.
The ESP itself is initially announced to clients as 'esp8266.local', if this host domain
is already used in the local network, another host domain is negociated. Keep an
eye to the serial output to learn the final host domain for the HTTP service.
The service itself is is announced as 'host domain'._http._tcp.local.
The HTTP server delivers a short greeting and the current list of other 'HTTP' services (not updated).
The web server code is taken nearly 1:1 from the 'mDNS_Web_Server.ino' example.
Point your browser to 'host domain'.local to see this web page.
Instructions:
- Update WiFi SSID and password as necessary.
- Flash the sketch to the ESP8266 board
- Install host software:
- For Linux, install Avahi (http://avahi.org/).
- For Windows, install Bonjour (http://www.apple.com/support/bonjour/).
- For Mac OSX and iOS support is built in through Bonjour already.
- Use a browser like 'Safari' to see the page at http://'host domain'.local.
*/
// THIS IS A WORK IN PROGRESS: some TODOs need completion
#ifndef STASSID
#define STASSID "ssid"
#define STAPSK "psk"
#endif
#ifndef APSSID
#define APSSID "esp8266"
//#define APPSK "psk"
#endif
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#define NO_GLOBAL_MDNS // our MDNS is defined below
#include <ESP8266mDNS.h>
/*
Global defines and vars
*/
#define SERVICE_PORT 80 // HTTP port
clsLEAMDNSHost MDNS; // MDNS responder
char* pcHostDomain = 0; // Negociated host domain
bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain
clsLEAMDNSHost::clsService* hMDNSService = 0; // The handle of the http service in the MDNS responder
clsLEAMDNSHost::clsQuery* hMDNSServiceQuery = 0; // The handle of the 'http.tcp' service query in the MDNS responder
const String cstrNoHTTPServices = "Currently no 'http.tcp' services in the local network!<br/>";
String strHTTPServices = cstrNoHTTPServices;
// HTTP server at port 'SERVICE_PORT' will respond to HTTP requests
ESP8266WebServer server(SERVICE_PORT);
/*
setStationHostname
*/
bool setStationHostname(const char* p_pcHostname) {
if (p_pcHostname) {
WiFi.hostname(p_pcHostname);
Serial.printf("setStationHostname: Station hostname is set to '%s'\n", p_pcHostname);
return true;
}
return false;
}
void MDNSServiceQueryCallback(const clsLEAMDNSHost::clsQuery& p_Query,
const clsLEAMDNSHost::clsQuery::clsAnswer& p_Answer,
clsLEAMDNSHost::clsQuery::clsAnswer::typeQueryAnswerType p_QueryAnswerTypeFlags,
bool p_bSetContent) {
(void)p_Query;
String answerInfo;
switch (p_QueryAnswerTypeFlags) {
case static_cast<clsLEAMDNSHost::clsQuery::clsAnswer::typeQueryAnswerType>(clsLEAMDNSHost::clsQuery::clsAnswer::enuQueryAnswerType::ServiceDomain):
answerInfo = "ServiceDomain " + String(p_Answer.m_ServiceDomain.c_str());
break;
case static_cast<clsLEAMDNSHost::clsQuery::clsAnswer::typeQueryAnswerType>(clsLEAMDNSHost::clsQuery::clsAnswer::enuQueryAnswerType::HostDomainPort):
answerInfo = "HostDomainAndPort " + String(p_Answer.m_HostDomain.c_str()) + ":" + String(p_Answer.m_u16Port);
break;
case static_cast<clsLEAMDNSHost::clsQuery::clsAnswer::typeQueryAnswerType>(clsLEAMDNSHost::clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address):
answerInfo = "IP4Address ";
for (auto ip : p_Answer.m_IPv4Addresses) {
answerInfo += "- " + ip->m_IPAddress.toString();
};
break;
case static_cast<clsLEAMDNSHost::clsQuery::clsAnswer::typeQueryAnswerType>(clsLEAMDNSHost::clsQuery::clsAnswer::enuQueryAnswerType::Txts):
answerInfo = "TXT ";
for (auto kv : p_Answer.m_Txts.m_Txts) {
answerInfo += "\nkv : " + String(kv->m_pcKey) + " : " + String(kv->m_pcValue);
}
break;
default :
answerInfo = "Unknown Answertype " + String(p_QueryAnswerTypeFlags);
}
Serial.printf("Answer %s %s\n", answerInfo.c_str(), p_bSetContent ? "Modified" : "Deleted");
}
/*
MDNSServiceProbeResultCallback
Probe result callback for Services
*/
void serviceProbeResult(clsLEAMDNSHost::clsService& p_rMDNSService,
const char* p_pcInstanceName,
bool p_bProbeResult) {
(void)p_rMDNSService;
Serial.printf("MDNSServiceProbeResultCallback: Service %s probe %s\n", p_pcInstanceName, (p_bProbeResult ? "succeeded." : "failed!"));
}
/*
MDNSHostProbeResultCallback
Probe result callback for the host domain.
If the domain is free, the host domain is set and the http service is
added.
If the domain is already used, a new name is created and the probing is
restarted via p_pclsLEAMDNSHost->setHostname().
*/
void hostProbeResult(clsLEAMDNSHost & p_rMDNSHost, String p_pcDomainName, bool p_bProbeResult) {
(void)p_rMDNSHost;
Serial.printf("MDNSHostProbeResultCallback: Host domain '%s.local' is %s\n", p_pcDomainName.c_str(), (p_bProbeResult ? "free" : "already USED!"));
if (true == p_bProbeResult) {
// Set station hostname
setStationHostname(pcHostDomain);
if (!bHostDomainConfirmed) {
// Hostname free -> setup clock service
bHostDomainConfirmed = true;
if (!hMDNSService) {
// Add a 'http.tcp' service to port 'SERVICE_PORT', using the host domain as instance domain
hMDNSService = MDNS.addService(0, "http", "tcp", SERVICE_PORT, serviceProbeResult);
if (hMDNSService) {
hMDNSService->setProbeResultCallback(serviceProbeResult);
// MDNS.setServiceProbeResultCallback(hMDNSService, serviceProbeResult);
// Add some '_http._tcp' protocol specific MDNS service TXT items
// See: http://www.dns-sd.org/txtrecords.html#http
hMDNSService->addServiceTxt("user", "");
hMDNSService->addServiceTxt("password", "");
hMDNSService->addServiceTxt("path", "/");
}
// Install dynamic 'http.tcp' service query
if (!hMDNSServiceQuery) {
hMDNSServiceQuery = MDNS.installServiceQuery("http", "tcp", MDNSServiceQueryCallback);
if (hMDNSServiceQuery) {
Serial.printf("MDNSProbeResultCallback: Service query for 'http.tcp' services installed.\n");
} else {
Serial.printf("MDNSProbeResultCallback: FAILED to install service query for 'http.tcp' services!\n");
}
}
}
}
} else {
// Change hostname, use '-' as divider between base name and index
MDNS.setHostName(clsLEAMDNSHost::indexDomainName(p_pcDomainName.c_str(), "-", 0));
}
}
/*
HTTP request function (not found is handled by server)
*/
void handleHTTPRequest() {
Serial.println("");
Serial.println("HTTP Request");
IPAddress ip = server.client().localIP();
String ipStr = ip.toString();
String s;
s.reserve(200 /* + service listed */);
s = "<!DOCTYPE HTML>\r\n<html><h3><head>Hello from ";
s += WiFi.hostname() + ".local at " + server.client().localIP().toString() + "</h3></head>";
s += "<br/><h4>Local HTTP services are :</h4>";
s += "<ol>";
// TODO: list services
s += "</ol><br/>";
Serial.println("Sending 200");
server.send(200, "text/html", s);
Serial.println("Done with request");
}
/*
setup
*/
void setup(void) {
Serial.begin(115200);
Serial.setDebugOutput(false);
Serial.println("");
Serial.println("THIS IS A WORK IN PROGRESS: some TODOs need completion");
Serial.println("");
// Connect to WiFi network
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(APSSID);
WiFi.begin(STASSID, STAPSK);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(STASSID);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Setup HTTP server
server.on("/", handleHTTPRequest);
// Setup MDNS responders
MDNS.setProbeResultCallback(hostProbeResult);
// Init the (currently empty) host domain string with 'leamdnsv2'
MDNS.begin("leamdnsv2");
Serial.println("MDNS responder started");
// Start HTTP server
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
// Check if a request has come in
server.handleClient();
// Allow MDNS processing
MDNS.update();
}

View File

@ -45,18 +45,11 @@
#ifndef __ESP8266MDNS_H
#define __ESP8266MDNS_H
enum class MDNSApiVersion { LEA, LEAv2 };
#include "LEAmDNS.h" // LEA
#include "LEAmDNS2Host.h" // LEAv2 - API updated
// clsLEAMDNSHost replaces MDNSResponder in LEAv2
using clsLEAMDNSHost = esp8266::experimental::clsLEAMDNSHost;
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type
using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; // LEA
//using MDNSResponder = clsLEAMDNSHost; // LEAv2
extern MDNSResponder MDNS;
#endif

View File

@ -28,7 +28,8 @@
#include "ESP8266mDNS.h"
#include "LEAmDNS_Priv.h"
#include <LwipIntf.h> // LwipIntf::stateUpCB()
#include "lwip/igmp.h"
#include <lwip/igmp.h>
#include <lwip/prot/dns.h>
namespace esp8266
{
@ -1304,7 +1305,7 @@ bool MDNSResponder::_joinMulticastGroups(void)
{
if (netif_is_up(pNetIf))
{
#ifdef MDNS_IPV4_SUPPORT
#ifdef MDNS_IP4_SUPPORT
ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT;
if (!(pNetIf->flags & NETIF_FLAG_IGMP))
{
@ -1354,7 +1355,7 @@ bool MDNSResponder::_leaveMulticastGroups()
bResult = true;
// Leave multicast group(s)
#ifdef MDNS_IPV4_SUPPORT
#ifdef MDNS_IP4_SUPPORT
ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT;
if (ERR_OK != igmp_leavegroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4)))
{

View File

@ -130,8 +130,9 @@ namespace MDNSImplementation
#endif
#define MDNS_IP4_SUPPORT
#if LWIP_IPV6
//#define MDNS_IP6_SUPPORT
#endif
#ifdef MDNS_IP4_SUPPORT
#define MDNS_IP4_SIZE 4

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,328 +0,0 @@
/*
LEAmDNS2Host_Debug.h
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "ESP8266mDNS.h"
#include "LEAmDNS2Host.h"
#include "LEAmDNS2_Priv.h"
namespace esp8266
{
namespace experimental
{
#ifdef DEBUG_ESP_PORT
/*
clsLEAmDNS2_Host::_DH
*/
const char* clsLEAMDNSHost::_DH(const clsLEAMDNSHost::clsService* p_pService /*= 0*/) const
{
static char acBuffer[16 + 64];
*acBuffer = 0;
sprintf_P(acBuffer, PSTR("[mDNS]"));
if (p_pService)
{
strcat_P(acBuffer, PSTR(">"));
strcat(acBuffer, _service2String(p_pService));
}
return acBuffer;
}
/*
clsLEAmDNS2_Host::_service2String
*/
const char* clsLEAMDNSHost::_service2String(const clsLEAMDNSHost::clsService* p_pService) const
{
static char acBuffer[64];
*acBuffer = 0;
if (p_pService)
{
sprintf_P(acBuffer, PSTR("%s.%s%s.%s%s.local"),
(p_pService->m_pcInstanceName ? : "-"),
(p_pService->m_pcType ? ('_' == *(p_pService->m_pcType) ? "" : "_") : "-"),
(p_pService->m_pcType ? : "-"),
(p_pService->m_pcProtocol ? ('_' == *(p_pService->m_pcProtocol) ? "" : "_") : "-"),
(p_pService->m_pcProtocol ? : "-"));
}
return acBuffer;
}
/*
clsLEAmDNS2_Host::_printRRDomain
*/
bool clsLEAMDNSHost::_printRRDomain(const clsLEAMDNSHost::clsRRDomain& p_RRDomain) const
{
//DEBUG_OUTPUT.printf_P(PSTR("Domain: "));
const char* pCursor = p_RRDomain.m_acName;
uint8_t u8Length = *pCursor++;
if (u8Length)
{
while (u8Length)
{
for (uint8_t u = 0; u < u8Length; ++u)
{
DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++));
}
u8Length = *pCursor++;
if (u8Length)
{
DEBUG_OUTPUT.printf_P(PSTR("."));
}
}
}
else // empty domain
{
DEBUG_OUTPUT.printf_P(PSTR("-empty-"));
}
//DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true;
}
/*
clsLEAmDNS2_Host::_printRRAnswer
*/
bool clsLEAMDNSHost::_printRRAnswer(const clsLEAMDNSHost::clsRRAnswer& p_RRAnswer) const
{
DEBUG_OUTPUT.printf_P(PSTR("%s RRAnswer: "), _DH());
_printRRDomain(p_RRAnswer.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL);
switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IPV4_SUPPORT
case DNS_RRTYPE_A:
DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const clsRRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_PTR:
DEBUG_OUTPUT.printf_P(PSTR("PTR "));
_printRRDomain(((const clsRRAnswerPTR*)&p_RRAnswer)->m_PTRDomain);
break;
case DNS_RRTYPE_TXT:
{
size_t stTxtLength = ((const clsRRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength();
char* pTxts = new char[stTxtLength];
if (pTxts)
{
((/*const c_str()!!*/clsRRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts);
DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts);
delete[] pTxts;
}
break;
}
#ifdef MDNS2_IPV6_SUPPORT
case DNS_RRTYPE_AAAA:
DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((clsRRAnswerAAAA*&)p_RRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_SRV:
DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((const clsRRAnswerSRV*)&p_RRAnswer)->m_u16Port);
_printRRDomain(((const clsRRAnswerSRV*)&p_RRAnswer)->m_SRVDomain);
break;
default:
DEBUG_OUTPUT.printf_P(PSTR("generic "));
break;
}
DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true;
}
/*
clsLEAmDNS2_Host::_RRType2Name
*/
const char* clsLEAMDNSHost::_RRType2Name(uint16_t p_u16RRType) const
{
static char acRRName[16];
*acRRName = 0;
switch (p_u16RRType & (~0x8000)) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IPV4_SUPPORT
case DNS_RRTYPE_A: strcpy_P(acRRName, PSTR("A")); break;
#endif
case DNS_RRTYPE_PTR: strcpy_P(acRRName, PSTR("PTR")); break;
case DNS_RRTYPE_TXT: strcpy_P(acRRName, PSTR("TXT")); break;
#ifdef MDNS2_IPV6_SUPPORT
case DNS_RRTYPE_AAAA: strcpy_P(acRRName, PSTR("AAAA")); break;
#endif
case DNS_RRTYPE_SRV: strcpy_P(acRRName, PSTR("SRV")); break;
case clsConsts::u8DNS_RRTYPE_NSEC: strcpy_P(acRRName, PSTR("NSEC")); break;
case DNS_RRTYPE_ANY: strcpy_P(acRRName, PSTR("ANY")); break;
default: sprintf_P(acRRName, PSTR("Unknown(0x%04X"), p_u16RRType); // MAX 15!
}
return acRRName;
}
/*
clsLEAmDNS2_Host::_RRClass2String
*/
const char* clsLEAMDNSHost::_RRClass2String(uint16_t p_u16RRClass,
bool p_bIsQuery) const
{
static char acClassString[16];
*acClassString = 0;
if (p_u16RRClass & 0x0001)
{
strcat_P(acClassString, PSTR("IN ")); // 3
}
if (p_u16RRClass & 0x8000)
{
strcat_P(acClassString, (p_bIsQuery ? PSTR("UNICAST ") : PSTR("FLUSH "))); // 8/6
}
return acClassString; // 11
}
/*
clsLEAmDNS2_Host::_replyFlags2String
*/
const char* clsLEAMDNSHost::_replyFlags2String(uint32_t p_u32ReplyFlags) const
{
static char acFlagsString[64];
*acFlagsString = 0;
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::A))
{
strcat_P(acFlagsString, PSTR("A ")); // 2
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::PTR_IPv4))
{
strcat_P(acFlagsString, PSTR("PTR_IPv4 ")); // 7
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::PTR_IPv6))
{
strcat_P(acFlagsString, PSTR("PTR_IPv6 ")); // 7
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::AAAA))
{
strcat_P(acFlagsString, PSTR("AAAA ")); // 5
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::PTR_TYPE))
{
strcat_P(acFlagsString, PSTR("PTR_TYPE ")); // 9
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::PTR_NAME))
{
strcat_P(acFlagsString, PSTR("PTR_NAME ")); // 9
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::TXT))
{
strcat_P(acFlagsString, PSTR("TXT ")); // 4
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::SRV))
{
strcat_P(acFlagsString, PSTR("SRV ")); // 4
}
if (p_u32ReplyFlags & static_cast<uint32_t>(enuContentFlag::NSEC))
{
strcat_P(acFlagsString, PSTR("NSEC ")); // 5
}
if (0 == p_u32ReplyFlags)
{
strcpy_P(acFlagsString, PSTR("none"));
}
// Remove trailing spaces
while ((*acFlagsString) &&
(' ' == acFlagsString[strlen(acFlagsString) - 1]))
{
acFlagsString[strlen(acFlagsString) - 1] = 0;
}
return acFlagsString; // 63
}
/*
clsLEAmDNS2_Host::_NSECBitmap2String
*/
const char* clsLEAMDNSHost::_NSECBitmap2String(const clsNSECBitmap* p_pNSECBitmap) const
{
static char acFlagsString[32];
*acFlagsString = 0;
#ifdef MDNS_IPV4_SUPPORT
if (p_pNSECBitmap->getBit(DNS_RRTYPE_A))
{
strcat_P(acFlagsString, PSTR("A ")); // 2
}
#endif
if (p_pNSECBitmap->getBit(DNS_RRTYPE_PTR))
{
strcat_P(acFlagsString, PSTR("PTR ")); // 4
}
#ifdef MDNS2_IPV6_SUPPORT
if (p_pNSECBitmap->getBit(DNS_RRTYPE_AAAA))
{
strcat_P(acFlagsString, PSTR("AAAA ")); // 5
}
#endif
if (p_pNSECBitmap->getBit(DNS_RRTYPE_TXT))
{
strcat_P(acFlagsString, PSTR("TXT ")); // 4
}
if (p_pNSECBitmap->getBit(DNS_RRTYPE_SRV))
{
strcat_P(acFlagsString, PSTR("SRV ")); // 4
}
if (p_pNSECBitmap->getBit(clsConsts::u8DNS_RRTYPE_NSEC))
{
strcat_P(acFlagsString, PSTR("NSEC ")); // 5
}
if (!*acFlagsString)
{
strcpy_P(acFlagsString, PSTR("none"));
}
return acFlagsString; // 31
}
#endif // DEBUG_ESP_PORT
} // namespace MDNSImplementation
} // namespace esp8266

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,262 +0,0 @@
/*
LEAmDNS2_Backbone.cpp
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "ESP8266mDNS.h"
#include "LEAmDNS2Host.h"
#include "LEAmDNS2_Priv.h"
namespace esp8266
{
namespace experimental
{
/*
clsLEAmDNS2_Host::clsBackbone::clsBackbone constructor
*/
clsLEAMDNSHost::clsBackbone::clsBackbone(void)
: m_pUDPContext(0),
m_bDelayUDPProcessing(false),
m_u32DelayedDatagrams(0),
m_uniqueHost(0)
{
}
/*
clsLEAmDNS2_Host::clsBackbone::clsBackbone destructor
*/
clsLEAMDNSHost::clsBackbone::~clsBackbone(void)
{
_releaseUDPContext();
}
/*
clsLEAmDNS2_Host::clsBackbone::init
*/
bool clsLEAMDNSHost::clsBackbone::init(void)
{
return _allocUDPContext();
}
/*
clsLEAmDNS2_Host::clsBackbone::addHost
*/
UdpContext* clsLEAMDNSHost::clsBackbone::addHost(clsLEAMDNSHost* p_pHost)
{
UdpContext* pUDPContext = nullptr;
if ((m_pUDPContext) && (p_pHost) && (m_uniqueHost == nullptr))
{
m_uniqueHost = p_pHost;
pUDPContext = m_pUDPContext;
}
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s addHost: %s to add host!\n"), _DH(), (pUDPContext ? "Succeeded" : "FAILED")););
return pUDPContext;
}
/*
clsLEAmDNS2_Host::clsBackbone::removeHost
*/
bool clsLEAMDNSHost::clsBackbone::removeHost(clsLEAMDNSHost* p_pHost)
{
bool bResult = false;
if ((p_pHost) && (m_uniqueHost == p_pHost))
{
m_uniqueHost = nullptr;
bResult = true;
}
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s removeHost: %s to remove host!\n"), _DH(), (bResult ? "Succeeded" : "FAILED")););
return bResult;
}
/*
clsLEAmDNS2_Host::clsBackbone::hostCount
*/
size_t clsLEAMDNSHost::clsBackbone::hostCount(void) const
{
return m_uniqueHost == nullptr ? 0 : 1;
}
/*
clsLEAMDNSHost::clsBackbone::::setDelayUDPProcessing
When executing _sendMessage, with multiple or larger messages, sometimes the ESP IP stack seems
to need a small delay to get the job done. To allow for this delay, a 'delay' was added after one
send operation. However, while 'taking' this delay, sometimes a UDP datagram is received and
processed (which might cause another send operation or change global states).
To avoid 're-entry-like' problems, UDP processing might be blocked for a short period of time.
*/
bool clsLEAMDNSHost::clsBackbone::setDelayUDPProcessing(bool p_bDelayUDPProcessing)
{
if (m_bDelayUDPProcessing != p_bDelayUDPProcessing)
{
m_bDelayUDPProcessing = p_bDelayUDPProcessing;
if ((!m_bDelayUDPProcessing) &&
(m_u32DelayedDatagrams))
{
DEBUG_EX_INFO2(if (6 <= m_u32DelayedDatagrams) DEBUG_OUTPUT.printf_P(PSTR("%s setDelayUDPProcessing: Processing %u delayed datagram(s)\n"), _DH(), m_u32DelayedDatagrams););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s setDelayUDPProcessing: Processing %u delayed datagram(s)\n"), _DH(), m_u32DelayedDatagrams););
_processUDPInput();
}
m_u32DelayedDatagrams = 0;
}
return true;
}
/*
clsLEAmDNS2_Host::clsBackbone::_allocUDPContext
*/
bool clsLEAMDNSHost::clsBackbone::_allocUDPContext(void)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext\n"), _DH()););
if (_releaseUDPContext())
{
m_pUDPContext = new UdpContext;
if (m_pUDPContext)
{
m_pUDPContext->ref();
//ip_set_option(m_pUDPContext->pcb(), SOF_REUSEADDR);
//udp_bind_netif(m_pUDPContext->pcb(), m_pNetIf);
if (m_pUDPContext->listen(IP_ANY_TYPE, DNS_MQUERY_PORT))
{
// This is NOT the TTL (Time-To-Live) for MDNS records, but the subnet level distance MDNS records should travel.
// 1 sets the subnet distance to 'local', which is default for MDNS.
// (Btw.: 255 would set it to 'as far as possible' -> internet), however, RFC 3171 seems to force 255 instead
const uint8_t c_u8MulticastTTL = 255;//1;//255;
m_pUDPContext->setMulticastTTL(c_u8MulticastTTL);
m_pUDPContext->onRx(std::bind(&clsLEAMDNSHost::clsBackbone::_processUDPInput, this));
/* m_pUDPContext->onRx([&](void)->void
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext::onRx Received data!\n"), _DH()););
});*/
m_pUDPContext->connect(IP_ANY_TYPE, DNS_MQUERY_PORT);
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext: Succeeded to alloc UDPContext!\n"), _DH()););
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext: FAILED to make UDPContext listening!\n"), _DH()););
_releaseUDPContext();
}
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext: FAILED to alloc UDPContext!\n"), _DH()););
}
}
DEBUG_EX_ERR(if (!m_pUDPContext) DEBUG_OUTPUT.printf_P(PSTR("%s _allocUDPContext: FAILED!\n"), _DH()););
return (0 != m_pUDPContext);
}
/*
clsLEAmDNS2_Host::clsBackbone::_releaseUDPContext
*/
bool clsLEAMDNSHost::clsBackbone::_releaseUDPContext(void)
{
if (m_pUDPContext)
{
m_pUDPContext->unref();
m_pUDPContext = nullptr;
}
return true;
}
/*
clsLEAmDNS2_Host::clsBackbone::_processUDPInput
Called in SYS context!
*/
bool clsLEAMDNSHost::clsBackbone::_processUDPInput(void)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processUDPInput\n"), _DH()););
bool bResult = true;
if (!m_bDelayUDPProcessing)
{
while ((m_pUDPContext) &&
(m_pUDPContext->next()))
{
clsLEAMDNSHost* pHost = _findHost();
bResult = pHost->_processUDPInput();
DEBUG_EX_INFO2_IF(!bResult,
DEBUG_OUTPUT.printf_P(PSTR("%s _processUDPInput: FAILED to process UDP input!\n"), _DH()));
DEBUG_EX_ERR_IF((-1) != m_pUDPContext->peek(),
DEBUG_OUTPUT.printf_P(PSTR("%s _processUDPInput: !!!! CONTENT LEFT IN UDP BUFFER !!!!\n"),
_DH()));
m_pUDPContext->flush();
}
}
else
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processUDPInput: Delaying datagram!\n"), _DH()););
++m_u32DelayedDatagrams;
}
return bResult;
}
/*
MISC
*/
#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_PORT
/*
clsLEAmDNS2_Host::clsBackbone::_DH
*/
const char* clsLEAMDNSHost::clsBackbone::_DH(void) const
{
static char acBuffer[20] = { 0, };
if (!acBuffer[0])
{
strcpy_P(acBuffer, PSTR("[mDNS::backbone]"));
}
return acBuffer;
}
#endif
} // namespace MDNSImplementation
} // namespace esp8266

View File

@ -1,115 +0,0 @@
/*
LEAmDNS_Priv.h
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef MDNS2_PRIV_H
#define MDNS2_PRIV_H
/*
LWIP_OPEN_SRC
*/
#ifndef LWIP_OPEN_SRC
#define LWIP_OPEN_SRC
#endif
/*
Enable class debug functions
*/
#define ESP_8266_MDNS_INCLUDE
//#define DEBUG_ESP_MDNS_RESPONDER // force debug, arduino IDE uses DEBUG_ESP_MDNS
/*
Enable/disable debug trace macros
*/
#if defined(DEBUG_ESP_PORT)
#define DEBUG_ESP_MDNS_ERR
#endif
#if defined(DEBUG_ESP_PORT) && (defined(DEBUG_ESP_MDNS) || defined(DEBUG_ESP_MDNS_RESPONDER))
#define DEBUG_ESP_MDNS_INFO
#define DEBUG_ESP_MDNS_INFO2
//#define DEBUG_ESP_MDNS_TX
//#define DEBUG_ESP_MDNS_RX
#endif
#ifdef DEBUG_ESP_PORT
#define DEBUG_OUTPUT DEBUG_ESP_PORT
#else
#define DEBUG_OUTPUT Serialx
#endif
#ifdef DEBUG_ESP_MDNS_INFO
#define DEBUG_EX_INFO(A) A
#define DEBUG_EX_INFO_IF(C,A...) do if (C) { A; } while (0)
#else
#define DEBUG_EX_INFO(A)
#define DEBUG_EX_INFO_IF(C,A...)
#endif
#ifdef DEBUG_ESP_MDNS_INFO2
#define DEBUG_EX_INFO2(A) A
#define DEBUG_EX_INFO2_IF(C,A...) do if (C) { A; } while (0)
#else
#define DEBUG_EX_INFO2(A)
#define DEBUG_EX_INFO2_IF(C,A...)
#endif
#ifdef DEBUG_ESP_MDNS_ERR
#define DEBUG_EX_ERR(A) A
#define DEBUG_EX_ERR_IF(C,A...) do if (C) { A; } while (0)
#else
#define DEBUG_EX_ERR(A)
#define DEBUG_EX_ERR_IF(C,A...)
#endif
#ifdef DEBUG_ESP_MDNS_TX
#define DEBUG_EX_TX(A) do { A; } while (0)
#else
#define DEBUG_EX_TX(A)
#endif
#ifdef DEBUG_ESP_MDNS_RX
#define DEBUG_EX_RX(A) do { A; } while (0)
#else
#define DEBUG_EX_RX(A)
#endif
/*
Enable/disable the usage of the F() macro in debug trace printf calls.
There needs to be an PGM comptible printf function to use this.
USE_PGM_PRINTF and F
*/
#define USE_PGM_PRINTF
#ifdef USE_PGM_PRINTF
#else
#ifdef F
#undef F
#endif
#define F(A) A
#endif
#endif // MDNS2_PRIV_H

View File

@ -1,31 +0,0 @@
/*
LEAmDNS2_lwIPdefs.h
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef LEAMDNS2_LWIPDEFS_H
#define LEAMDNS2_LWIPDEFS_H
#include <lwip/init.h>
#include <lwip/prot/dns.h> // DNS_RRTYPE_xxx, DNS_MQUERY_PORT
#endif // LEAMDNS2_LWIPDEFS_H

View File

@ -97,8 +97,8 @@ bool MDNSResponder::_process(bool p_bUserContext)
bool MDNSResponder::_restart(void)
{
return ((_resetProbeStatus(true)) && // Stop and restart probing
(_allocUDPContext())); // Restart UDP
return ((_resetProbeStatus(true/*restart*/)) && // Stop and restart probing
(_allocUDPContext())); // Restart UDP
}
/**

View File

@ -94,13 +94,12 @@ namespace MDNSImplementation
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
#endif
/* Replaced by 'lwip/prot/dns.h' definitions
/* already defined in lwIP ('lwip/prot/dns.h')
#ifdef MDNS_IP4_SUPPORT
#define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT
#define DNS_MQUERY_IPV4_GROUP_INIT (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT
#endif
#ifdef MDNS_IP6_SUPPORT
#define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT
#define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT
#endif*/
//#define MDNS_MULTICAST_PORT 5353

View File

@ -70,14 +70,14 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
}
time_t creation = 0;
if (timeCallback && (openMode & OM_CREATE)) {
if (_timeCallback && (openMode & OM_CREATE)) {
// O_CREATE means we *may* make the file, but not if it already exists.
// See if it exists, and only if not update the creation time
int rc = lfs_file_open(&_lfs, fd.get(), path, LFS_O_RDONLY);
if (rc == 0) {
lfs_file_close(&_lfs, fd.get()); // It exists, don't update create time
} else {
creation = timeCallback(); // File didn't exist or otherwise, so we're going to create this time
creation = _timeCallback(); // File didn't exist or otherwise, so we're going to create this time
}
}

View File

@ -424,7 +424,7 @@ public:
lfs_file_close(_fs->getFS(), _getFD());
_opened = false;
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
if (timeCallback && (_flags & LFS_O_WRONLY)) {
if (_timeCallback && (_flags & LFS_O_WRONLY)) {
// If the file opened with O_CREAT, write the creation time attribute
if (_creation) {
int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation));
@ -433,7 +433,7 @@ public:
}
}
// Add metadata with last write time
time_t now = timeCallback();
time_t now = _timeCallback();
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
if (rc < 0) {
DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now);
@ -556,11 +556,35 @@ public:
}
time_t fileTime() override {
return (time_t)_getAttr4('t');
time_t t;
int32_t t32b;
// If the attribute is 8-bytes, we're all set
if (_getAttr('t', 8, &t)) {
return t;
} else if (_getAttr('t', 4, &t32b)) {
// If it's 4 bytes silently promote to 64b
return (time_t)t32b;
} else {
// OTW, none present
return 0;
}
}
time_t fileCreationTime() override {
return (time_t)_getAttr4('c');
time_t t;
int32_t t32b;
// If the attribute is 8-bytes, we're all set
if (_getAttr('c', 8, &t)) {
return t;
} else if (_getAttr('c', 4, &t32b)) {
// If it's 4 bytes silently promote to 64b
return (time_t)t32b;
} else {
// OTW, none present
return 0;
}
}
@ -599,20 +623,17 @@ protected:
return _dir.get();
}
uint32_t _getAttr4(char attr) {
if (!_valid) {
return 0;
bool _getAttr(char attr, int len, void *dest) {
if (!_valid || !len || !dest) {
return false;
}
int nameLen = 3; // Slashes, terminator
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
nameLen += strlen(_dirent.name);
char tmpName[nameLen];
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
time_t ftime = 0;
int rc = lfs_getattr(_fs->getFS(), tmpName, attr, (void *)&ftime, sizeof(ftime));
if (rc != sizeof(ftime))
ftime = 0; // Error, so clear read value
return ftime;
int rc = lfs_getattr(_fs->getFS(), tmpName, attr, dest, len);
return (rc == len);
}
String _pattern;

View File

@ -84,12 +84,17 @@ void Netdump::fileDump(File& outfile, const Filter nf)
fileDumpProcess(outfile, ndp);
}, nf);
}
void Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf)
bool Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf)
{
if (!packetBuffer)
{
packetBuffer = new (std::nothrow) char[tcpBufferSize];
if (!packetBuffer)
{
return false;
}
}
bufferIndex = 0;
@ -97,6 +102,7 @@ void Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf)
{
tcpDumpLoop(tcpDumpServer, nf);
});
return true;
}
void Netdump::capture(int netif_idx, const char* data, size_t len, int out, int success)
@ -176,7 +182,7 @@ void Netdump::tcpDumpProcess(const Packet& np)
bufferIndex += incl_len;
}
if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex)
if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= (int)bufferIndex)
{
tcpDumpClient.write(packetBuffer, bufferIndex);
bufferIndex = 0;
@ -202,7 +208,7 @@ void Netdump::tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf)
{
setCallback(nullptr);
}
if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex)
if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= (int)bufferIndex)
{
tcpDumpClient.write(packetBuffer, bufferIndex);
bufferIndex = 0;

View File

@ -53,7 +53,7 @@ public:
void printDump(Print& out, Packet::PacketDetail ndd, const Filter nf = nullptr);
void fileDump(File& outfile, const Filter nf = nullptr);
void tcpDump(WiFiServer &tcpDumpServer, const Filter nf = nullptr);
bool tcpDump(WiFiServer &tcpDumpServer, const Filter nf = nullptr);
private:

View File

@ -32,7 +32,7 @@
class SDClass {
public:
boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) {
boolean begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED) {
SDFS.setConfig(SDFSConfig(csPin, cfg));
return (boolean)SDFS.begin();
}
@ -68,6 +68,14 @@ public:
return (boolean)SDFS.exists(filepath.c_str());
}
boolean rename(const char* filepathfrom, const char* filepathto) {
return (boolean)SDFS.rename(filepathfrom, filepathto);
}
boolean rename(const String &filepathfrom, const String &filepathto) {
return (boolean)rename(filepathfrom.c_str(), filepathto.c_str());
}
boolean mkdir(const char *filepath) {
return (boolean)SDFS.mkdir(filepath);
}

View File

@ -25,7 +25,6 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SDFS.h"
#include "SDFSFormatter.h"
#include <FS.h>
using namespace fs;
@ -37,6 +36,8 @@ FS SDFS = FS(FSImplPtr(new sdfs::SDFSImpl()));
namespace sdfs {
// Required to be global because SDFAT doesn't allow a this pointer in it's own time call
time_t (*__sdfs_timeCallback)(void) = nullptr;
FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode)
{
@ -63,13 +64,13 @@ FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode acces
}
free(pathStr);
}
sdfat::File fd = _fs.open(path, flags);
sdfat::File32 fd = _fs.open(path, flags);
if (!fd) {
DEBUGV("SDFSImpl::openFile: fd=%p path=`%s` openMode=%d accessMode=%d",
&fd, path, openMode, accessMode);
return FileImplPtr();
}
auto sharedFd = std::make_shared<sdfat::File>(fd);
auto sharedFd = std::make_shared<sdfat::File32>(fd);
return std::make_shared<SDFSFileImpl>(this, sharedFd, path);
}
@ -89,7 +90,7 @@ DirImplPtr SDFSImpl::openDir(const char* path)
}
// At this point we have a name of "/blah/blah/blah" or "blah" or ""
// If that references a directory, just open it and we're done.
sdfat::File dirFile;
sdfat::File32 dirFile;
const char *filter = "";
if (!pathStr[0]) {
// openDir("") === openDir("/")
@ -134,7 +135,7 @@ DirImplPtr SDFSImpl::openDir(const char* path)
DEBUGV("SDFSImpl::openDir: path=`%s`\n", path);
return DirImplPtr();
}
auto sharedDir = std::make_shared<sdfat::File>(dirFile);
auto sharedDir = std::make_shared<sdfat::File32>(dirFile);
auto ret = std::make_shared<SDFSDirImpl>(filter, this, sharedDir, pathStr);
free(pathStr);
return ret;
@ -144,8 +145,15 @@ bool SDFSImpl::format() {
if (_mounted) {
return false;
}
SDFSFormatter formatter;
bool ret = formatter.format(&_fs, _cfg._csPin, _cfg._spiSettings);
sdfat::SdCardFactory cardFactory;
sdfat::SdCard* card = cardFactory.newCard(sdfat::SdSpiConfig(_cfg._csPin, DEDICATED_SPI, _cfg._spiSettings));
if (!card || card->errorCode()) {
return false;
}
sdfat::FatFormatter fatFormatter;
uint8_t *sectorBuffer = new uint8_t[512];
bool ret = fatFormatter.format(card, sectorBuffer, nullptr);
delete[] sectorBuffer;
return ret;
}

View File

@ -47,7 +47,7 @@ class SDFSConfig : public FSConfig
public:
static constexpr uint32_t FSId = 0x53444653;
SDFSConfig(uint8_t csPin = 4, SPISettings spi = SD_SCK_MHZ(10)) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi) { }
SDFSConfig(uint8_t csPin = 4, uint32_t spi = SD_SCK_MHZ(10)) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi) { }
SDFSConfig setAutoFormat(bool val = true) {
_autoFormat = val;
@ -57,7 +57,7 @@ public:
_csPin = pin;
return *this;
}
SDFSConfig setSPI(SPISettings spi) {
SDFSConfig setSPI(uint32_t spi) {
_spiSettings = spi;
return *this;
}
@ -67,9 +67,9 @@ public:
}
// Inherit _type and _autoFormat
uint8_t _csPin;
uint8_t _part;
SPISettings _spiSettings;
uint8_t _csPin;
uint8_t _part;
uint32_t _spiSettings;
};
class SDFSImpl : public FSImpl
@ -97,11 +97,11 @@ public:
return false;
}
info.maxOpenFiles = 999; // TODO - not valid
info.blockSize = _fs.vol()->blocksPerCluster() * 512;
info.blockSize = _fs.vol()->sectorsPerCluster() * _fs.vol()->bytesPerSector();
info.pageSize = 0; // TODO ?
info.maxPathLength = 255; // TODO ?
info.totalBytes =_fs.vol()->volumeBlockCount() * 512LL;
info.usedBytes = info.totalBytes - (_fs.vol()->freeClusterCount() * _fs.vol()->blocksPerCluster() * 512LL);
info.totalBytes =_fs.vol()->clusterCount() * info.blockSize;
info.usedBytes = info.totalBytes - (_fs.vol()->freeClusterCount() * _fs.vol()->sectorsPerCluster() * _fs.vol()->bytesPerSector());
return true;
}
@ -156,7 +156,7 @@ public:
format();
_mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings);
}
sdfat::SdFile::dateTimeCallback(dateTimeCB);
sdfat::FsDateTime::setCallback(dateTimeCB);
return _mounted;
}
@ -176,7 +176,7 @@ public:
return _fs.vol()->fatType();
}
size_t blocksPerCluster() {
return _fs.vol()->blocksPerCluster();
return _fs.vol()->sectorsPerCluster();
}
size_t totalClusters() {
return _fs.vol()->clusterCount();
@ -185,7 +185,7 @@ public:
return (totalClusters() / blocksPerCluster());
}
size_t clusterSize() {
return blocksPerCluster() * 512; // 512b block size
return blocksPerCluster() * _fs.vol()->bytesPerSector();
}
size_t size() {
return (clusterSize() * totalClusters());
@ -205,12 +205,21 @@ public:
return mktime(&tiempo);
}
virtual void setTimeCallback(time_t (*cb)(void)) override {
extern time_t (*__sdfs_timeCallback)(void);
__sdfs_timeCallback = cb;
}
// Because SdFat has a single, global setting for this we can only use a
// static member of our class to return the time/date. However, since
// this is static, we can't see the time callback variable. Punt for now,
// using time(NULL) as the best we can do.
// static member of our class to return the time/date.
static void dateTimeCB(uint16_t *dosYear, uint16_t *dosTime) {
time_t now = time(nullptr);
time_t now;
extern time_t (*__sdfs_timeCallback)(void);
if (__sdfs_timeCallback) {
now = __sdfs_timeCallback();
} else {
now = time(nullptr);
}
struct tm *tiempo = localtime(&now);
*dosYear = ((tiempo->tm_year - 80) << 9) | ((tiempo->tm_mon + 1) << 5) | tiempo->tm_mday;
*dosTime = (tiempo->tm_hour << 11) | (tiempo->tm_min << 5) | tiempo->tm_sec;
@ -255,7 +264,7 @@ protected:
class SDFSFileImpl : public FileImpl
{
public:
SDFSFileImpl(SDFSImpl *fs, std::shared_ptr<sdfat::File> fd, const char *name)
SDFSFileImpl(SDFSImpl *fs, std::shared_ptr<sdfat::File32> fd, const char *name)
: _fs(fs), _fd(fd), _opened(true)
{
_name = std::shared_ptr<char>(new char[strlen(name) + 1], std::default_delete<char[]>());
@ -268,6 +277,11 @@ public:
close();
}
int availableForWrite() override
{
return _opened ? _fd->availableSpaceForWrite() : 0;
}
size_t write(const uint8_t *buf, size_t size) override
{
return _opened ? _fd->write(buf, size) : -1;
@ -281,7 +295,6 @@ public:
void flush() override
{
if (_opened) {
_fd->flush();
_fd->sync();
}
}
@ -361,15 +374,15 @@ public:
bool isDirectory() const override
{
return _opened ? _fd->isDirectory() : false;
return _opened ? _fd->isDir() : false;
}
time_t getLastWrite() override {
time_t ftime = 0;
if (_opened && _fd) {
sdfat::dir_t tmp;
sdfat::DirFat_t tmp;
if (_fd.get()->dirEntry(&tmp)) {
ftime = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
ftime = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.modifyDate, *(uint16_t*)tmp.modifyTime);
}
}
return ftime;
@ -378,19 +391,17 @@ public:
time_t getCreationTime() override {
time_t ftime = 0;
if (_opened && _fd) {
sdfat::dir_t tmp;
sdfat::DirFat_t tmp;
if (_fd.get()->dirEntry(&tmp)) {
ftime = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
ftime = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.createDate, *(uint16_t*)tmp.createTime);
}
}
return ftime;
}
protected:
SDFSImpl* _fs;
std::shared_ptr<sdfat::File> _fd;
std::shared_ptr<sdfat::File32> _fd;
std::shared_ptr<char> _name;
bool _opened;
};
@ -398,7 +409,7 @@ protected:
class SDFSDirImpl : public DirImpl
{
public:
SDFSDirImpl(const String& pattern, SDFSImpl* fs, std::shared_ptr<sdfat::File> dir, const char *dirPath = nullptr)
SDFSDirImpl(const String& pattern, SDFSImpl* fs, std::shared_ptr<sdfat::File32> dir, const char *dirPath = nullptr)
: _pattern(pattern), _fs(fs), _dir(dir), _valid(false), _dirPath(nullptr)
{
if (dirPath) {
@ -473,17 +484,17 @@ public:
{
const int n = _pattern.length();
do {
sdfat::File file;
sdfat::File32 file;
file.openNext(_dir.get(), sdfat::O_READ);
if (file) {
_valid = 1;
_size = file.fileSize();
_isFile = file.isFile();
_isDirectory = file.isDirectory();
sdfat::dir_t tmp;
_isDirectory = file.isDir();
sdfat::DirFat_t tmp;
if (file.dirEntry(&tmp)) {
_time = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
_creation = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
_time = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.modifyDate, *(uint16_t*)tmp.modifyTime);
_creation = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.createDate, *(uint16_t*)tmp.createTime);
} else {
_time = 0;
_creation = 0;
@ -507,7 +518,7 @@ public:
protected:
String _pattern;
SDFSImpl* _fs;
std::shared_ptr<sdfat::File> _dir;
std::shared_ptr<sdfat::File32> _dir;
bool _valid;
char _lfn[64];
time_t _time;

View File

@ -1,405 +0,0 @@
/*
SDFSFormatter.cpp - Formatter for SdFat SD cards
Copyright (c) 2019 Earle F. Philhower, III. All rights reserved.
A C++ implementation of the SdFat/examples/SdFormatter sketch:
| Copyright (c) 2011-2018 Bill Greiman
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.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SDFSFORMATTER_H
#define _SDFSFORMATTER_H
#include "SDFS.h"
#include <FS.h>
#include <PolledTimeout.h>
namespace sdfs {
class SDFSFormatter {
private:
// Taken from main FS object
sdfat::Sd2Card *card;
sdfat::cache_t *cache;
uint32_t cardSizeBlocks;
uint32_t cardCapacityMB;
// MBR information
uint8_t partType;
uint32_t relSector;
uint32_t partSize;
// Fake disk geometry
uint8_t numberOfHeads;
uint8_t sectorsPerTrack;
// FAT parameters
uint16_t reservedSectors;
uint8_t sectorsPerCluster;
uint32_t fatStart;
uint32_t fatSize;
uint32_t dataStart;
uint8_t writeCache(uint32_t lbn) {
return card->writeBlock(lbn, cache->data);
}
void clearCache(uint8_t addSig) {
memset(cache, 0, sizeof(*cache));
if (addSig) {
cache->mbr.mbrSig0 = sdfat::BOOTSIG0;
cache->mbr.mbrSig1 = sdfat::BOOTSIG1;
}
}
bool clearFatDir(uint32_t bgn, uint32_t count) {
clearCache(false);
if (!card->writeStart(bgn, count)) {
DEBUGV("SDFS: Clear FAT/DIR writeStart failed");
return false;
}
esp8266::polledTimeout::periodicFastMs timeToYield(5); // Yield every 5ms of runtime
for (uint32_t i = 0; i < count; i++) {
if (timeToYield) {
delay(0); // WDT feed
}
if (!card->writeData(cache->data)) {
DEBUGV("SDFS: Clear FAT/DIR writeData failed");
return false;
}
}
if (!card->writeStop()) {
DEBUGV("SDFS: Clear FAT/DIR writeStop failed");
return false;
}
return true;
}
uint16_t lbnToCylinder(uint32_t lbn) {
return lbn / (numberOfHeads * sectorsPerTrack);
}
uint8_t lbnToHead(uint32_t lbn) {
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
}
uint8_t lbnToSector(uint32_t lbn) {
return (lbn % sectorsPerTrack) + 1;
}
bool writeMbr() {
clearCache(true);
sdfat::part_t* p = cache->mbr.part;
p->boot = 0;
uint16_t c = lbnToCylinder(relSector);
if (c > 1023) {
DEBUGV("SDFS: MBR CHS");
return false;
}
p->beginCylinderHigh = c >> 8;
p->beginCylinderLow = c & 0XFF;
p->beginHead = lbnToHead(relSector);
p->beginSector = lbnToSector(relSector);
p->type = partType;
uint32_t endLbn = relSector + partSize - 1;
c = lbnToCylinder(endLbn);
if (c <= 1023) {
p->endCylinderHigh = c >> 8;
p->endCylinderLow = c & 0XFF;
p->endHead = lbnToHead(endLbn);
p->endSector = lbnToSector(endLbn);
} else {
// Too big flag, c = 1023, h = 254, s = 63
p->endCylinderHigh = 3;
p->endCylinderLow = 255;
p->endHead = 254;
p->endSector = 63;
}
p->firstSector = relSector;
p->totalSectors = partSize;
if (!writeCache(0)) {
DEBUGV("SDFS: write MBR");
return false;
}
return true;
}
uint32_t volSerialNumber() {
return (cardSizeBlocks << 8) + micros();
}
bool makeFat16() {
uint16_t const BU16 = 128;
uint32_t nc;
for (dataStart = 2 * BU16;; dataStart += BU16) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 255)/256;
uint32_t r = BU16 + 1 + 2 * fatSize + 32;
if (dataStart < r) {
continue;
}
relSector = dataStart - r + BU16;
break;
}
// check valid cluster count for FAT16 volume
if (nc < 4085 || nc >= 65525) {
DEBUGV("SDFS: Bad cluster count");
}
reservedSectors = 1;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
if (partSize < 32680) {
partType = 0X01;
} else if (partSize < 65536) {
partType = 0X04;
} else {
partType = 0X06;
}
// write MBR
if (!writeMbr()) {
DEBUGV("SDFS: writembr failed");
return false;
}
clearCache(true);
sdfat::fat_boot_t* pb = &cache->fbs;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->rootDirEntryCount = 512;
pb->mediaType = 0XF8;
pb->sectorsPerFat16 = fatSize;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->driveNumber = 0X80;
pb->bootSignature = sdfat::EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber();
memcpy_P(pb->volumeLabel, PSTR("NO NAME "), sizeof(pb->volumeLabel));
memcpy_P(pb->fileSystemType, PSTR("FAT16 "), sizeof(pb->fileSystemType));
// write partition boot sector
if (!writeCache(relSector)) {
DEBUGV("SDFS: FAT16 write PBS failed");
return false;
}
// clear FAT and root directory
if (!clearFatDir(fatStart, dataStart - fatStart)) {
DEBUGV("SDFS: FAT16 clear root failed\n");
return false;
}
clearCache(false);
cache->fat16[0] = 0XFFF8;
cache->fat16[1] = 0XFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) {
DEBUGV("FAT16 reserve failed");
return false;
}
return true;
}
bool makeFat32() {
uint16_t const BU32 = 8192;
uint32_t nc;
relSector = BU32;
for (dataStart = 2 * BU32;; dataStart += BU32) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 127)/128;
uint32_t r = relSector + 9 + 2 * fatSize;
if (dataStart >= r) {
break;
}
}
// error if too few clusters in FAT32 volume
if (nc < 65525) {
DEBUGV("SDFS: Bad cluster count");
return false;
}
reservedSectors = dataStart - relSector - 2 * fatSize;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + dataStart - relSector;
// type depends on address of end sector
// max CHS has lbn = 16450560 = 1024*255*63
if ((relSector + partSize) <= 16450560) {
// FAT32
partType = 0X0B;
} else {
// FAT32 with INT 13
partType = 0X0C;
}
if (!writeMbr()) {
DEBUGV("SDFS: writembr failed");
return false;
}
clearCache(true);
sdfat::fat32_boot_t* pb = &cache->fbs32;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->mediaType = 0XF8;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->sectorsPerFat32 = fatSize;
pb->fat32RootCluster = 2;
pb->fat32FSInfo = 1;
pb->fat32BackBootBlock = 6;
pb->driveNumber = 0X80;
pb->bootSignature = sdfat::EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber();
memcpy_P(pb->volumeLabel, PSTR("NO NAME "), sizeof(pb->volumeLabel));
memcpy_P(pb->fileSystemType, PSTR("FAT32 "), sizeof(pb->fileSystemType));
// write partition boot sector and backup
if (!writeCache(relSector) || !writeCache(relSector + 6)) {
DEBUGV("SDFS: FAT32 write PBS failed");
return false;
}
clearCache(true);
// write extra boot area and backup
if (!writeCache(relSector + 2) || !writeCache(relSector + 8)) {
DEBUGV("SDFS: FAT32 PBS ext failed");
return false;
}
sdfat::fat32_fsinfo_t* pf = &cache->fsinfo;
pf->leadSignature = sdfat::FSINFO_LEAD_SIG;
pf->structSignature = sdfat::FSINFO_STRUCT_SIG;
pf->freeCount = 0XFFFFFFFF;
pf->nextFree = 0XFFFFFFFF;
// write FSINFO sector and backup
if (!writeCache(relSector + 1) || !writeCache(relSector + 7)) {
DEBUGV("SDFS: FAT32 FSINFO failed");
return false;
}
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
clearCache(false);
cache->fat32[0] = 0x0FFFFFF8;
cache->fat32[1] = 0x0FFFFFFF;
cache->fat32[2] = 0x0FFFFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) {
DEBUGV("SDFS: FAT32 reserve failed");
return false;
}
return true;
}
public:
bool format(sdfat::SdFat *_fs, int8_t _csPin, SPISettings _spiSettings) {
card = static_cast<sdfat::Sd2Card*>(_fs->card());
cache = _fs->cacheClear();
if (!card->begin(_csPin, _spiSettings)) {
return false;
}
cardSizeBlocks = card->cardSize();
if (cardSizeBlocks == 0) {
return false;
}
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
if (cardCapacityMB <= 6) {
return false; // Card is too small
} else if (cardCapacityMB <= 16) {
sectorsPerCluster = 2;
} else if (cardCapacityMB <= 32) {
sectorsPerCluster = 4;
} else if (cardCapacityMB <= 64) {
sectorsPerCluster = 8;
} else if (cardCapacityMB <= 128) {
sectorsPerCluster = 16;
} else if (cardCapacityMB <= 1024) {
sectorsPerCluster = 32;
} else if (cardCapacityMB <= 32768) {
sectorsPerCluster = 64;
} else {
// SDXC cards
sectorsPerCluster = 128;
}
// set fake disk geometry
sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
if (cardCapacityMB <= 16) {
numberOfHeads = 2;
} else if (cardCapacityMB <= 32) {
numberOfHeads = 4;
} else if (cardCapacityMB <= 128) {
numberOfHeads = 8;
} else if (cardCapacityMB <= 504) {
numberOfHeads = 16;
} else if (cardCapacityMB <= 1008) {
numberOfHeads = 32;
} else if (cardCapacityMB <= 2016) {
numberOfHeads = 64;
} else if (cardCapacityMB <= 4032) {
numberOfHeads = 128;
} else {
numberOfHeads = 255;
}
// Erase all data on card (TRIM)
uint32_t const ERASE_SIZE = 262144L;
uint32_t firstBlock = 0;
uint32_t lastBlock;
do {
lastBlock = firstBlock + ERASE_SIZE - 1;
if (lastBlock >= cardSizeBlocks) {
lastBlock = cardSizeBlocks - 1;
}
if (!card->erase(firstBlock, lastBlock)) {
return false; // Erase fail
}
delay(0); // yield to the OS to avoid WDT
firstBlock += ERASE_SIZE;
} while (firstBlock < cardSizeBlocks);
if (!card->readBlock(0, cache->data)) {
return false;
}
if (card->type() != sdfat::SD_CARD_TYPE_SDHC) {
return makeFat16();
} else {
return makeFat32();
}
}
}; // class SDFSFormatter
}; // namespace sdfs
#endif // _SDFSFORMATTER_H

View File

@ -27,15 +27,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
uint32_t Servo::_servoMap = 0;
// similiar to map but will have increased accuracy that provides a more
// symetric api (call it and use result to reverse will provide the original value)
// symmetrical api (call it and use result to reverse will provide the original value)
int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
{
const int rangeIn = maxIn - minIn;
const int rangeOut = maxOut - minOut;
const int deltaIn = value - minIn;
// fixed point math constants to improve accurancy of divide and rounding
const int fixedHalfDecimal = 1;
const int fixedDecimal = fixedHalfDecimal * 2;
constexpr int fixedHalfDecimal = 1;
constexpr int fixedDecimal = fixedHalfDecimal * 2;
return ((deltaIn * rangeOut * fixedDecimal) / (rangeIn) + fixedHalfDecimal) / fixedDecimal + minOut;
}
@ -46,9 +46,9 @@ int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
Servo::Servo()
{
_attached = false;
_valueUs = DEFAULT_PULSE_WIDTH;
_minUs = MIN_PULSE_WIDTH;
_maxUs = MAX_PULSE_WIDTH;
_valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH;
_minUs = DEFAULT_MIN_PULSE_WIDTH;
_maxUs = DEFAULT_MAX_PULSE_WIDTH;
}
Servo::~Servo() {
@ -58,14 +58,24 @@ Servo::~Servo() {
uint8_t Servo::attach(int pin)
{
return attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
return attach(pin, DEFAULT_MIN_PULSE_WIDTH, DEFAULT_MAX_PULSE_WIDTH);
}
uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs)
{
return attach(pin, minUs, maxUs, _valueUs);
}
uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs, int value)
{
if (!_attached) {
#ifdef WAVEFORM_LOCKED_PHASE
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
#else
digitalWrite(pin, LOW);
pinMode(pin, OUTPUT);
#endif
_pin = pin;
_attached = true;
}
@ -76,7 +86,7 @@ uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs)
_maxUs = max((uint16_t)250, min((uint16_t)3000, maxUs));
_minUs = max((uint16_t)200, min(_maxUs, minUs));
write(_valueUs);
write(value);
return pin;
}
@ -85,20 +95,24 @@ void Servo::detach()
{
if (_attached) {
_servoMap &= ~(1 << _pin);
#ifdef WAVEFORM_LOCKED_PHASE
startWaveform(_pin, 0, REFRESH_INTERVAL, 1);
#else
// TODO - timeHigh == 0 is illegal in _PWM code branch. Do nothing for now.
#endif
delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances.
stopWaveform(_pin);
_attached = false;
digitalWrite(_pin, LOW);
_valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH;
}
}
void Servo::write(int value)
{
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < _minUs) {
// treat any value less than 200 as angle in degrees (values equal or larger are handled as microseconds)
if (value < 200) {
// assumed to be 0-180 degrees servo
value = constrain(value, 0, 180);
// writeMicroseconds will contrain the calculated value for us
// for any user defined min and max, but we must use default min max
value = improved_map(value, 0, 180, _minUs, _maxUs);
}
writeMicroseconds(value);
@ -106,10 +120,18 @@ void Servo::write(int value)
void Servo::writeMicroseconds(int value)
{
value = constrain(value, _minUs, _maxUs);
_valueUs = value;
if (_attached) {
_servoMap &= ~(1 << _pin);
if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0)) {
#ifdef WAVEFORM_LOCKED_PHASE
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
int phaseReference = __builtin_ffs(_servoMap) - 1;
if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0, phaseReference))
#else
if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0))
#endif
{
_servoMap |= (1 << _pin);
}
}
@ -117,8 +139,7 @@ void Servo::writeMicroseconds(int value)
int Servo::read() // return the value as degrees
{
// read returns the angle for an assumed 0-180, so we calculate using
// the normal min/max constants and not user defined ones
// read returns the angle for an assumed 0-180
return improved_map(readMicroseconds(), _minUs, _maxUs, 0, 180);
}

View File

@ -27,9 +27,9 @@
//
// Servo - Class for manipulating servo motors connected to Arduino pins.
//
// attach(pin ) - Attaches a servo motor to an i/o pin.
// attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
// default min is 544, max is 2400
// attach(pin) - Attaches a servo motor to an i/o pin.
// attach(pin, min, max) - Attaches to a pin setting min and max values in microseconds
// default min is 1000, max is 2000
//
// write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
// writeMicroseconds() - Sets the servo pulse width in microseconds
@ -44,13 +44,17 @@
#include <Arduino.h>
// the following are in us (microseconds)
//
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
#define MAX_SERVOS 12
// The following values are in us (microseconds).
// Since the defaults can be overwritten in the new attach() member function,
// they were modified from the Arduino AVR defaults to be in the safe range
// of publically available specifications. While this implies that many 180°
// servos do not operate the full 0° to 180° sweep using these, it also prevents
// unsuspecting damage. For Arduino AVR, the same change is being discussed.
#define DEFAULT_MIN_PULSE_WIDTH 1000 // uncalibrated default, the shortest duty cycle sent to a servo
#define DEFAULT_MAX_PULSE_WIDTH 2000 // uncalibrated default, the longest duty cycle sent to a servo
#define DEFAULT_NEUTRAL_PULSE_WIDTH 1500 // default duty cycle when servo is attached
#define REFRESH_INTERVAL 20000 // classic default period to refresh servos in microseconds
#define MAX_SERVOS 9 // D0-D8
#if !defined(ESP8266)
@ -63,8 +67,16 @@ class Servo
public:
Servo();
~Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
uint8_t attach(int pin, uint16_t min, uint16_t max); // as above but also sets min and max values for writes.
// attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure.
// returns channel number or 0 if failure.
uint8_t attach(int pin);
// attach the given pin to the next free channel, sets pinMode, min, and max values for write().
// returns channel number or 0 if failure.
uint8_t attach(int pin, uint16_t min, uint16_t max);
// attach the given pin to the next free channel, sets pinMode, min, and max values for write(),
// and sets the initial value, the same as write().
// returns channel number or 0 if failure.
uint8_t attach(int pin, uint16_t min, uint16_t max, int value);
void detach();
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds

View File

@ -0,0 +1,73 @@
/*
ESP8266 LED fade with polledTimeout and locked phase PWM
Modified from an BlinkPolledTimeout.ino,
Copyright (c) 2018 Daniel Salazar. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Note that this sketch uses LED_BUILTIN to find the pin with the internal LED
*/
#include <core_esp8266_waveform.h>
#include <PolledTimeout.h>
esp8266::polledTimeout::periodicFastUs stepPeriod(50000);
// the setup function runs only once at start
void setup() {
Serial.begin(115200);
Serial.println();
// This next line will call will cause the code to use the Phase-Locked waveform generator
// instead of the default one. Comment it out to try the default version.
// For more information on choosing between the two options, see the following pull requests:
// Phase-Locked generator: https://github.com/esp8266/Arduino/pull/7022
// PWM-Locked generator: https://github.com/esp8266/Arduino/pull/7231
enablePhaseLockedWaveform();
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
analogWriteRange(1000);
using esp8266::polledTimeout::oneShotMs; //import the type to the local namespace
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level
oneShotMs timeoutOn(2000);
while (!timeoutOn) {
yield();
}
stepPeriod.reset();
}
void loop() {
static int val = 0;
static int delta = 100;
if (stepPeriod) {
val += delta;
if (val < 0) {
val = 100;
delta = 100;
} else if (val > 1000) {
val = 900;
delta = -100;
}
analogWrite(LED_BUILTIN, val);
}
}

View File

@ -4,6 +4,7 @@
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
void stats(const char* what) {
// we could use getFreeHeap() getMaxFreeBlockSize() and getHeapFragmentation()
@ -109,6 +110,7 @@ void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_OFF);
Serial.printf("\r\nDemo Heap Metrics for DRAM\r\n");
tryit(8000);
tryit(4000);
tryit(2000);
@ -118,6 +120,21 @@ void setup() {
tryit(100);
tryit(50);
tryit(15);
#ifdef UMM_HEAP_IRAM
{
HeapSelectIram ephemeral;
Serial.printf("\r\nDemo Heap Metrics for IRAM\r\n");
tryit(8000);
tryit(4000);
tryit(2000);
tryit(1000);
tryit(500);
tryit(200);
tryit(100);
tryit(50);
tryit(15);
}
#endif
}
void loop() {

View File

@ -0,0 +1,124 @@
/*
Overview: Of the 48KB of IRAM, the remaining IRAM after your code is untouched
during the reboot process. For a sketch that does not use deep-sleep, it is
possible to pass/hold information across boot cycles in this area of IRAM.
With the selection of Arduino IDE Tools Option: 'MMU: 16KB cache + 48KB IRAM
and 2nd Heap (shared)' all of this space goes into a managed 2nd Heap.
Managed, in this case, refers to using malloc, free, realloc, etc. API.
The objective of this example is to show how to modify the 2nd Heap creation
to omit a block of IRAM at the end of the 2nd Heap. In this example, we use
this block to store a boot count.
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#if defined(UMM_HEAP_IRAM)
// durable - as in long life, persisting across reboots.
struct durable {
uint32_t bootCounter;
uint32_t chksum;
};
// Leave a durable block of IRAM after the 2nd heap.
// The block should be in 8-byte increments and fall on an 8-byte alignment.
#define IRAM_RESERVE_SZ ((sizeof(struct durable) + 7UL) & ~7UL)
// Position its address just above the reduced 2nd Heap.
#define IRAM_RESERVE (0x40100000UL + 0xC000UL - IRAM_RESERVE_SZ)
// Define a reference with the right properties to make access easier.
#define DURABLE ((struct durable *)IRAM_RESERVE)
#define INCREMENT_BOOTCOUNT() (DURABLE->bootCounter)++
extern struct rst_info resetInfo;
/*
Define a function to determine if IRAM stored data is valid. The criteria used
here can vary with how exhaustively you want the process to be.
In this example, we are just going to look at the reset cause and assume all
is well in certain situations. For this example, we include
REASON_EXT_SYS_RST as a possible case for IRAM not being valid. The problem
here is some devices will indicate REASON_EXT_SYS_RST for the Power-on case.
If you wanted to be able to isolate the power-on case from a
REASON_EXT_SYS_RST, you could add additional logic to set and verify a CRC or
XOR sum on the IRAM data (or just a section of the IRAM data).
*/
inline bool is_iram_valid(void) {
return (REASON_WDT_RST <= resetInfo.reason &&
REASON_SOFT_RESTART >= resetInfo.reason);
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
delay(10);
Serial.printf_P(PSTR("\r\nSetup ...\r\n"));
if (!is_iram_valid()) {
DURABLE->bootCounter = 0;
}
DURABLE->bootCounter++;
Serial.printf("Number of reboots at %u\r\n", DURABLE->bootCounter);
Serial.printf("\r\nSome less than direct, ways to restart:\r\n");
processKey(Serial, '?');
}
void loop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}
//////////////////////////////////////////////////////////////////////////////
/*
Create a block of unmanaged IRAM for special uses.
This is done by reducing the size of the managed 2nd Heap (Shared) at
initialization time.
*/
extern "C" void _text_end(void);
extern "C" void umm_init_iram(void) {
/*
Calculate the start of 2nd heap, staying clear of possible segment alignment
adjustments and checksums. These can affect the persistence of data across
reboots.
*/
uint32_t sec_heap = (uint32_t)_text_end + 32;
sec_heap &= ~7;
size_t sec_heap_sz = 0xC000UL - (sec_heap - 0x40100000UL);
sec_heap_sz -= IRAM_RESERVE_SZ; // Shrink IRAM heap
if (0xC000UL > sec_heap_sz) {
umm_init_iram_ex((void *)sec_heap, sec_heap_sz, true);
}
}
#else
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
delay(10);
Serial.println("\r\n\r\nThis sketch requires Tools Option: 'MMU: 16KB cache + 48KB IRAM and 2nd Heap (shared)'");
}
void loop(void) {
}
#endif

View File

@ -0,0 +1,116 @@
#include <esp8266_undocumented.h>
void crashMeIfYouCan(void)__attribute__((weak));
int divideA_B(int a, int b);
int* nullPointer = NULL;
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 'r':
out.printf_P(PSTR("Reset, ESP.reset(); ...\r\n"));
ESP.reset();
break;
case 't':
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
ESP.restart();
break;
case 's': {
uint32_t startTime = millis();
out.printf_P(PSTR("Now crashing with Software WDT. This will take about 3 seconds.\r\n"));
ets_install_putc1(ets_putc);
while (true) {
ets_printf("%9lu\r", (millis() - startTime));
ets_delay_us(250000);
// stay in an loop blocking other system activity.
}
}
break;
case 'h':
out.printf_P(PSTR("Now crashing with Hardware WDT. This will take about 6 seconds.\r\n"));
asm volatile("mov.n a2, %0\n\t"
"mov.n a3, %1\n\t"
"mov.n a4, %2\n\t"
"mov.n a5, %3\n\t"
"mov.n a6, %4\n\t"
: : "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa) : "memory");
// Could not find these in the stack dump, unless interrupts were enabled.
{
uint32_t startTime = millis();
// Avoid all the Core functions that play nice, so we can hog
// the system and crash.
ets_install_putc1(ets_putc);
xt_rsil(15);
while (true) {
ets_printf("%9lu\r", (millis() - startTime));
ets_delay_us(250000);
// stay in an loop blocking other system activity.
//
// Note:
// Hardware WDT kicks in if Software WDT is unable to perform.
// With the Hardware WDT, nothing is saved on the stack, that I have seen.
}
}
break;
case 'p':
out.println(F("Time to panic()!"));
panic();
break;
case 'z':
out.println(F("Crashing by dividing by zero. This should generate an exception(0)."));
out.printf_P(PSTR("This should not print %d\n"), divideA_B(1, 0));
break;
case 'w':
out.println(F("Now calling: void crashMeIfYouCan(void)__attribute__((weak));"));
out.println(F("This function has a prototype but was missing when the sketch was linked. ..."));
crashMeIfYouCan();
break;
case 'b':
out.println(F("Executing a break instruction w/o GDB will cause a HWDT reset."));
asm volatile("break 1, 15;");
out.println(F("This line will not be printable w/o running GDB"));
break;
case '0':
out.println(F("Crashing at an embeded 'break 1, 15' instruction that was generated"));
out.println(F("by the compiler after detecting a divide by zero."));
out.printf_P(PSTR("This should not print %d\n"), divideA_B_bp(1, 0));
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" r - Reset, ESP.reset();"));
out.println(F(" t - Restart, ESP.restart();"));
out.println(F(" ? - Print Help"));
out.println();
out.println(F("Crash with:"));
out.println(F(" s - Software WDT"));
out.println(F(" h - Hardware WDT - looping with interrupts disabled"));
out.println(F(" w - Hardware WDT - calling a missing (weak) function."));
out.println(F(" 0 - Hardware WDT - a hard coded compiler breakpoint from a compile time detected divide by zero"));
out.println(F(" b - Hardware WDT - a forgotten hard coded 'break 1, 15;' and no GDB running."));
out.println(F(" z - Divide by zero, exception(0);"));
out.println(F(" p - panic();"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
processKey(out, '?');
break;
}
}
// With the current toolchain 10.1, using this to divide by zero will *not* be
// caught at compile time.
int __attribute__((noinline)) divideA_B(int a, int b) {
return (a / b);
}
// With the current toolchain 10.1, using this to divide by zero *will* be
// caught at compile time. And a hard coded breakpoint will be inserted.
int divideA_B_bp(int a, int b) {
return (a / b);
}

View File

@ -0,0 +1,322 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
uint32_t timed_byte_read(char *pc, uint32_t * o);
uint32_t timed_byte_read2(char *pc, uint32_t * o);
int divideA_B(int a, int b);
int* nullPointer = NULL;
char *probe_b = NULL;
short *probe_s = NULL;
char *probe_c = (char *)0x40110000;
short *unaligned_probe_s = NULL;
uint32_t read_var = 0x11223344;
/*
Notes,
When accessing IRAM as data storage all access must be word aligned and
full word length.
*/
#if defined(MMU_IRAM_HEAP) || defined(MMU_SEC_HEAP)
uint32_t *gobble;
size_t gobble_sz;
#elif (MMU_IRAM_SIZE > 32*1024)
uint32_t gobble[4 * 1024] IRAM_ATTR;
constexpr size_t gobble_sz = sizeof(gobble);
#else
uint32_t gobble[256] IRAM_ATTR;
constexpr size_t gobble_sz = sizeof(gobble);
#endif
bool isValid(uint32_t *probe) {
bool rc = true;
if (NULL == probe) {
ets_uart_printf("\nNULL memory pointer %p ...\n", probe);
return false;
}
ets_uart_printf("\nTesting for valid memory at %p ...\n", probe);
uint32_t savePS = xt_rsil(15);
uint32_t saveData = *probe;
for (size_t i = 0; i < 32; i++) {
*probe = BIT(i);
asm volatile("" ::: "memory");
uint32_t val = *probe;
if (val != BIT(i)) {
ets_uart_printf(" Read 0x%08X != Wrote 0x%08X\n", val, (uint32_t)BIT(i));
rc = false;
}
}
*probe = saveData;
xt_wsr_ps(savePS);
ets_uart_printf(" %s\n", (rc) ? "Pass" : "Fail!");
return rc;
}
void dump_mem32(const void * addr, const size_t len) {
uint32_t *addr32 = (uint32_t *)addr;
ets_uart_printf("\n");
if ((uintptr_t)addr32 & 3) {
ets_uart_printf("non-32-bit access\n");
ets_delay_us(12000);
}
for (size_t i = 0; i < len;) {
ets_uart_printf("%p: ", &addr32[i]);
do {
ets_uart_printf(" 0x%08x", addr32[i]);
} while (i++, (i & 3) && (i < len));
ets_uart_printf("\n");
}
ets_uart_printf("\n");
}
extern "C" void _text_end(void);
// extern void *_text_end;
void print_mmu_status(Print& oStream) {
oStream.println();
oStream.printf_P(PSTR("MMU Configuration"));
oStream.println();
oStream.println();
uint32_t iram_bank_reg = ESP8266_DREG(0x24);
if (0 == (iram_bank_reg & 0x10)) { // if bit clear, is enabled
oStream.printf_P(PSTR(" IRAM block mapped to: 0x40108000"));
oStream.println();
}
if (0 == (iram_bank_reg & 0x08)) {
oStream.printf_P(PSTR(" IRAM block mapped to: 0x4010C000"));
oStream.println();
}
#ifdef MMU_ICACHE_SIZE
oStream.printf_P(PSTR(" ICACHE Size: %u"), MMU_ICACHE_SIZE);
oStream.println();
#endif
#ifdef MMU_IRAM_SIZE
oStream.printf_P(PSTR(" IRAM Size: %u"), MMU_IRAM_SIZE);
oStream.println();
const uint32_t iram_free = MMU_IRAM_SIZE - (uint32_t)((uintptr_t)_text_end - 0x40100000UL);
oStream.printf_P(PSTR(" IRAM free: %u"), iram_free);
oStream.println();
#endif
oStream.printf_P(PSTR(" IRAM _text_end: %p"), _text_end);
oStream.println();
#ifdef MMU_SEC_HEAP
oStream.printf_P(PSTR(" Secondary Heap at: %p"), MMU_SEC_HEAP);
oStream.println();
oStream.printf_P(PSTR(" Secondary Heap Size: %u"), MMU_SEC_HEAP_SIZE);
oStream.println();
#endif
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
// Serial.begin(74880);
Serial.begin(115200);
delay(10);
Serial.printf_P(PSTR("\r\n\r\nSetup ...\r\n"));
print_mmu_status(Serial);
#if defined(MMU_IRAM_HEAP)
{
HeapSelectIram ephemeral;
// Serial.printf_P(PSTR("ESP.getFreeHeap(): %u\n"), ESP.getFreeHeap());
gobble_sz = ESP.getFreeHeap() - UMM_OVERHEAD_ADJUST; // - 4096;
gobble = (uint32_t *)malloc(gobble_sz);
}
Serial.printf_P(PSTR("\r\nmalloc() from IRAM Heap:\r\n"));
Serial.printf_P(PSTR(" gobble_sz: %u\r\n"), gobble_sz);
Serial.printf_P(PSTR(" gobble: %p\r\n"), gobble);
#elif defined(MMU_SEC_HEAP)
gobble = (uint32_t *)MMU_SEC_HEAP;
gobble_sz = MMU_SEC_HEAP_SIZE;
#endif
#if (MMU_IRAM_SIZE > 0x8000) || defined(MMU_IRAM_HEAP) || defined(MMU_SEC_HEAP)
if (isValid(gobble)) {
// Put something in our new memory
for (size_t i = 0; i < (gobble_sz / 4); i++) {
gobble[i] = (uint32_t)&gobble[i];
}
// Now is it there?
dump_mem32(gobble, 32);
// dump_mem32(&gobble[gobble_sz / 4 / 2], 32);
dump_mem32(&gobble[gobble_sz / 4 - 32], 32);
}
#endif
// Lets peak over the edge
Serial.printf_P(PSTR("\r\nPeek over the edge of memory at 0x4010C000\r\n"));
dump_mem32((void *)(0x4010C000 - 16 * 4), 32);
probe_b = (char *)gobble;
probe_s = (short *)((uintptr_t)gobble);
unaligned_probe_s = (short *)((uintptr_t)gobble + 1);
}
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 't': {
uint32_t tmp;
out.printf_P(PSTR("Test how much time is added by exception handling"));
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iRAM %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40108000, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from dRAM %u cpu cycle count, 0x%02X."), timed_byte_read((char *)((uintptr_t)&read_var + 1), &tmp), tmp);
out.println();
out.printf_P(PSTR("Test how much time is used by the inline function method"));
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iRAM %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40108000, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from dRAM %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)((uintptr_t)&read_var + 1), &tmp), tmp);
out.println();
out.println();
break;
}
case '9':
out.printf_P(PSTR("Unaligned exception by reading short"));
out.println();
out.flush();
xt_rsil(3);
out.printf_P(PSTR("Read short, 0x%02X at %p"), unaligned_probe_s[0], unaligned_probe_s);
xt_rsil(0);
out.println();
break;
case 'c':
out.printf_P(PSTR("Load/Store exception by reading byte outside of handler range"));
out.println();
out.flush();
xt_rsil(3);
out.printf_P(PSTR("Read Byte, 0x%02X at %p"), probe_c[0], probe_c);
xt_rsil(0);
out.println();
out.printf_P(PSTR("With Non32-bit access enabled, access range check is only done when 'Tools->Debug Level: CORE ...' is set."));
out.println();
break;
case 'b':
out.printf_P(PSTR("Load/Store exception by reading byte from iRAM"));
out.println();
out.flush();
out.printf_P(PSTR("Read Byte from iRAM, 0x%02X at %p"), probe_b[0], probe_b);
out.println();
break;
case 'B': {
out.printf_P(PSTR("Load/Store exception by writing byte to iRAM"));
out.println();
char val = 0x55;
out.printf_P(PSTR("Write byte, 0x%02X, to iRAM at %p"), val, probe_b);
out.println();
out.flush();
probe_b[0] = val;
out.printf_P(PSTR("Read Byte back from iRAM, 0x%02X at %p"), probe_b[0], probe_b);
out.println();
break;
}
case 's':
out.printf_P(PSTR("Load/Store exception by reading short from iRAM"));
out.println();
out.flush();
out.printf_P(PSTR("Read short from iRAM, 0x%04X at %p"), probe_s[0], probe_s);
out.println();
break;
case 'S': {
out.printf_P(PSTR("Load/Store exception by writing short to iRAM"));
out.println();
short int val = 0x0AA0;
out.printf_P(PSTR("Write short, 0x%04X, to iRAM at %p"), val, probe_s);
out.println();
out.flush();
probe_s[0] = val;
out.printf_P(PSTR("Read short back from iRAM, 0x%04X at %p"), probe_s[0], probe_s);
out.println();
break;
}
case 'R':
out.printf_P(PSTR("Restart, ESP.restart(); ..."));
out.println();
ESP.restart();
break;
case 'p':
out.println(F("Time to panic()!"));
panic();
break;
case '0':
out.println(F("Crashing by dividing by zero."));
out.printf_P(PSTR("This should not print %d"), divideA_B(1, 0));
out.println();
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" R - Restart, ESP.restart();"));
out.println(F(" t - exception vs inline method timing info."));
out.println(F(" ? - Print Help"));
out.println();
#if defined(NON32XFER_HANDLER)
out.println(F("Test exception handling with non-32 bit transfer handler:"));
#else
out.println(F("Crash with:"));
#endif
out.println(F(" b - read byte, Load/Store exception"));
out.println(F(" B - write byte, Load/Store exception"));
out.println(F(" s - read short, Load/Store exception"));
out.println(F(" S - write short, Load/Store exception"));
#if defined(NON32XFER_HANDLER)
out.println();
out.println(F("Crash with:"));
#endif
out.println(F(" c - read byte, Load/Store exception outside of handler range"));
out.println(F(" 9 - read short, Unaligned exception"));
out.println(F(" 0 - Divide by zero, exception(0);"));
out.println(F(" p - panic();"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
break;
}
}
void serialClientLoop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}
void loop() {
serialClientLoop();
}
int __attribute__((noinline)) divideA_B(int a, int b) {
return (a / b);
}

View File

@ -0,0 +1,16 @@
#include <Arduino.h>
#include <mmu_iram.h>
uint32_t IRAM_ATTR timed_byte_read(char *pc, uint32_t * o) {
uint32_t start = esp_get_cycle_count();
*o = *pc;
// return clockCyclesToMicroseconds(esp_get_cycle_count() - start);
return (esp_get_cycle_count() - start);
}
uint32_t IRAM_ATTR timed_byte_read2(char *pc, uint32_t * o) {
uint32_t start = esp_get_cycle_count();
*o = mmu_get_uint8(pc);
// return clockCyclesToMicroseconds(esp_get_cycle_count() - start);
return (esp_get_cycle_count() - start);
}

View File

@ -64,8 +64,9 @@ static time_t now;
static uint32_t now_ms, now_us;
static esp8266::polledTimeout::periodicMs showTimeNow(60000);
static int time_machine_days = 0; // 0 = now
static int time_machine_days = 0; // 0 = present
static bool time_machine_running = false;
static bool time_machine_run_once = false;
// OPTIONAL: change SNTP startup delay
// a weak function is already defined and returns 0 (RFC violation)
@ -112,7 +113,7 @@ void showTime() {
// time from boot
Serial.print("clock: ");
Serial.print((uint32_t)tp.tv_sec);
Serial.print("s / ");
Serial.print("s + ");
Serial.print((uint32_t)tp.tv_nsec);
Serial.println("ns");
@ -125,7 +126,7 @@ void showTime() {
// EPOCH+tz+dst
Serial.print("gtod: ");
Serial.print((uint32_t)tv.tv_sec);
Serial.print("s / ");
Serial.print("s + ");
Serial.print((uint32_t)tv.tv_usec);
Serial.println("us");
@ -140,7 +141,7 @@ void showTime() {
Serial.print("ctime: ");
Serial.print(ctime(&now));
// LwIP v2 is able to list more details about the currently configured SNTP servers
// lwIP v2 is able to list more details about the currently configured SNTP servers
for (int i = 0; i < SNTP_MAX_SERVERS; i++) {
IPAddress sntp = *sntp_getserver(i);
const char* name = sntp_getservername(i);
@ -151,7 +152,7 @@ void showTime() {
} else {
Serial.printf("%s ", sntp.toString().c_str());
}
Serial.printf("IPv6: %s Reachability: %o\n",
Serial.printf("- IPv6: %s - Reachability: %o\n",
sntp.isV6() ? "Yes" : "No",
sntp_getreachability(i));
}
@ -159,40 +160,59 @@ void showTime() {
Serial.println();
// subsecond synchronisation
gettimeofday(&tv, nullptr);
time_t sec = tv.tv_sec;
do {
// show subsecond synchronisation
timeval prevtv;
time_t prevtime = time(nullptr);
gettimeofday(&prevtv, nullptr);
while (true) {
gettimeofday(&tv, nullptr);
Serial.printf("time(): %u gettimeofday(): %u.%06u",
(uint32_t)time(nullptr),
(uint32_t)tv.tv_sec, (uint32_t)tv.tv_usec);
if (tv.tv_sec == sec) {
Serial.println(" second unchanged");
} else {
Serial.println(" <-- second changed");
if (tv.tv_sec != prevtv.tv_sec) {
Serial.printf("time(): %u gettimeofday(): %u.%06u seconds are unchanged\n",
(uint32_t)prevtime,
(uint32_t)prevtv.tv_sec, (uint32_t)prevtv.tv_usec);
Serial.printf("time(): %u gettimeofday(): %u.%06u <-- seconds have changed\n",
(uint32_t)(prevtime = time(nullptr)),
(uint32_t)tv.tv_sec, (uint32_t)tv.tv_usec);
break;
}
prevtv = tv;
delay(50);
} while (tv.tv_sec == sec);
}
Serial.println();
}
void time_is_set_scheduled() {
// everything is allowed in this function
void time_is_set(bool from_sntp /* <= this parameter is optional */) {
// in CONT stack, unlike ISRs,
// any function is allowed in this callback
if (time_machine_days == 0) {
time_machine_running = !time_machine_running;
if (time_machine_running) {
time_machine_run_once = true;
time_machine_running = false;
} else {
time_machine_running = from_sntp && !time_machine_run_once;
}
if (time_machine_running) {
Serial.printf("\n-- \n-- Starting time machine demo to show libc's "
"automatic DST handling\n-- \n");
}
}
Serial.print("settimeofday(");
if (from_sntp) {
Serial.print("SNTP");
} else {
Serial.print("USER");
}
Serial.print(")");
// time machine demo
if (time_machine_running) {
if (time_machine_days == 0)
Serial.printf("---- settimeofday() has been called - possibly from SNTP\n"
" (starting time machine demo to show libc's automatic DST handling)\n\n");
now = time(nullptr);
const tm* tm = localtime(&now);
Serial.printf("future=%3ddays: DST=%s - ",
Serial.printf(": future=%3ddays: DST=%s - ",
time_machine_days,
tm->tm_isdst ? "true " : "false");
Serial.print(ctime(&now));
@ -207,49 +227,55 @@ void time_is_set_scheduled() {
}
settimeofday(&tv, nullptr);
} else {
showTime();
Serial.println();
}
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
Serial.println("\nStarting...\n");
Serial.println("\nStarting in 2secs...\n");
delay(2000);
// install callback - called when settimeofday is called (by SNTP or user)
// once enabled (by DHCP), SNTP is updated every hour by default
// ** optional boolean in callback function is true when triggerred by SNTP **
settimeofday_cb(time_is_set);
// setup RTC time
// it will be used until NTP server will send us real current time
Serial.println("Manually setting some time from some RTC:");
time_t rtc = RTC_UTC_TEST;
timeval tv = { rtc, 0 };
settimeofday(&tv, nullptr);
// install callback - called when settimeofday is called (by SNTP or us)
// once enabled (by DHCP), SNTP is updated every hour
settimeofday_cb(time_is_set_scheduled);
// NTP servers may be overriden by your DHCP server for a more local one
// (see below)
// ----> Here is the ONLY ONE LINE needed in your sketch
configTime(MYTZ, "pool.ntp.org");
// <----
// Replace MYTZ by a value from TZ.h (search for this file in your filesystem).
// Here is the ONLY ONE LINE needed in your sketch <----
// pick a value from TZ.h (search for this file in your filesystem) for MYTZ
// former configTime is still valid, here is the call for 7 hours to the west
// Former configTime is still valid, here is the call for 7 hours to the west
// with an enabled 30mn DST
//configTime(7 * 3600, 3600 / 2, "pool.ntp.org");
// OPTIONAL: disable obtaining SNTP servers from DHCP
//sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default)
// Give now a chance to the settimeofday callback,
// because it is *always* deferred to the next yield()/loop()-call.
yield();
// start network
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
// don't wait for network, observe time changing
// when NTP timestamp is received
Serial.printf("Time is currently set by a constant:\n");
showTime();
}

View File

@ -0,0 +1,471 @@
/*
This sketch assumes you have selected IRAM as a Second Heap from
the Arduino IDE tools menu.
*/
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
// #define USE_SET_IRAM_HEAP
#ifndef ETS_PRINTF
#define ETS_PRINTF ets_uart_printf
#endif
uint32_t cyclesToRead_nKx32(int n, unsigned int *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx32(int n, unsigned int *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx16(int n, unsigned short *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx16(int n, unsigned short *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKxs16(int n, short *x, int32_t *res) {
uint32_t b = ESP.getCycleCount();
int32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKxs16(int n, short *x) {
uint32_t b = ESP.getCycleCount();
int32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx8(int n, unsigned char*x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx8(int n, unsigned char*x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
// Compare with Inline
uint32_t cyclesToRead_nKx16_viaInline(int n, unsigned short *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += mmu_get_uint16(x++); //*(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx16_viaInline(int n, unsigned short *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
// *(x++) = sum;
mmu_set_uint16(x++, sum);
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKxs16_viaInline(int n, short *x, int32_t *res) {
uint32_t b = ESP.getCycleCount();
int32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += mmu_get_int16(x++); //*(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKxs16_viaInline(int n, short *x) {
uint32_t b = ESP.getCycleCount();
int32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
// *(x++) = sum;
mmu_set_int16(x++, sum);
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx8_viaInline(int n, unsigned char*x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += mmu_get_uint8(x++); //*(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char*x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
// *(x++) = sum;
mmu_set_uint8(x++, sum);
}
return ESP.getCycleCount() - b;
}
bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) {
uint32_t res, verify_res;
uint32_t t;
bool success = true;
int sres, verify_sres;
Serial.printf("\r\nPerformance numbers for 16 bit access - using inline macros or exception handling for IRAM.\r\n");;
t = cyclesToWrite_nKx16(nK, (uint16_t*)mem);
Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16(nK, (uint16_t*)mem, &verify_res);
Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_res);
t = cyclesToWrite_nKxs16(nK, (int16_t*)mem);
Serial.printf("DRAM Memory Write: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKxs16(nK, (int16_t*)mem, &verify_sres);
Serial.printf("DRAM Memory Read: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_sres);
t = cyclesToWrite_nKx16_viaInline(nK, (uint16_t*)imem);
Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16_viaInline(nK, (uint16_t*)imem, &res);
Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res);
if (res == verify_res) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_res);
success = false;
}
t = cyclesToWrite_nKxs16_viaInline(nK, (int16_t*)imem);
Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKxs16_viaInline(nK, (int16_t*)imem, &sres);
Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), sres);
if (sres == verify_sres) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_sres);
success = false;
}
t = cyclesToWrite_nKx16(nK, (uint16_t*)imem);
Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16(nK, (uint16_t*)imem, &res);
Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res);
if (res == verify_res) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_res);
success = false;
}
t = cyclesToWrite_nKxs16(nK, (int16_t*)imem);
Serial.printf("IRAM Memory Write: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKxs16(nK, (int16_t*)imem, &sres);
Serial.printf("IRAM Memory Read: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), sres);
if (sres == verify_sres) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_sres);
success = false;
}
Serial.printf("\r\nPerformance numbers for 8 bit access - using inline macros or exception handling for IRAM access.\r\n");;
t = cyclesToWrite_nKx8(nK, (uint8_t*)mem);
Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8(nK, (uint8_t*)mem, &verify_res);
Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_res);
t = cyclesToWrite_nKx8_viaInline(nK, (uint8_t*)imem);
Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8_viaInline(nK, (uint8_t*)imem, &res);
Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res);
if (res == verify_res) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_res);
success = false;
}
t = cyclesToWrite_nKx8(nK, (uint8_t*)imem);
Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8(nK, (uint8_t*)imem, &res);
Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res);
if (res == verify_res) {
Serial.printf("- passed\r\n");
} else {
Serial.printf("!= (sum %08x ) - failed\r\n", verify_res);
success = false;
}
Serial.println();
return success;
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
// Serial.begin(74880);
Serial.begin(115200);
delay(20);
Serial.printf_P(PSTR("\n\nSetup ...\r\n"));
#ifndef UMM_HEAP_IRAM
Serial.printf("\r\n"
"This example needs IRAM Heap support enabled.\r\n"
" eg. Arduino IDE 'Tools->MMU:\"16KB cache + 48KB IRAM and 2nd Heap (shared)\"'\r\n"
"This build has IRAM Heap support disabled.\r\n"
"In this situation, all IRAM requests are satisfied with DRAM.\r\n\r\n");
#endif
// Compiling with Secondary Heap option does not change malloc to use the
// IRAM region. It will continue to use the builtin DRAM until we request
// otherwise.
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
uint32_t *mem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("DRAM buffer: Address %p, free %d\r\n", mem, ESP.getFreeHeap());
if (!mem) {
return;
}
// Now request from the IRAM heap
#ifdef USE_SET_IRAM_HEAP
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
uint32_t *imem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("IRAM buffer: Address %p, free %d\r\n", imem, ESP.getFreeHeap());
// Make sure we go back to the DRAM heap for other allocations. Don't forget to ESP.resetHeap()!
ESP.resetHeap();
#else
uint32_t *imem;
{
HeapSelectIram ephemeral;
// This class effectively does this
// size_t _heap_id = umm_get_current_heap_id();
// umm_set_heap_by_id(UMM_HEAP_IRAM);
// ...
// umm_set_heap_by_id(_heap_id);
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
imem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("IRAM buffer: Address %p, free %d\r\n", imem, ESP.getFreeHeap());
}
#endif
if (!imem) {
return;
}
uint32_t res;
uint32_t t;
int nK = 1;
Serial.printf("\r\nPerformance numbers for 32 bit access - no exception handler or inline macros needed.\r\n");;
t = cyclesToWrite_nKx32(nK, mem);
Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx32(nK, mem, &res);
Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx32(nK, imem);
Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx32(nK, imem, &res);
Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
Serial.println();
if (perfTest_nK(1, mem, imem)) {
Serial.println();
} else {
Serial.println("\r\n*******************************");
Serial.println("*******************************");
Serial.println("** **");
Serial.println("** One or more test failed **");
Serial.println("** **");
Serial.println("*******************************");
Serial.println("*******************************\r\n");
return;
}
#ifdef USE_SET_IRAM_HEAP
// Let's use IRAM heap to make a big ole' String
ESP.setIramHeap();
String s = "";
for (int i = 0; i < 100; i++) {
s += i;
s += ' ';
}
ESP.resetHeap();
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
Serial.printf("String: %s\r\n", s.c_str());
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
#else
{
// Let's use IRAM heap to make a big ole' String
HeapSelectIram ephemeral;
String s = "";
for (int i = 0; i < 100; i++) {
s += i;
s += ' ';
}
{
HeapSelectDram ephemeral;
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
}
// Back to IRAM
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
Serial.printf("String: %s\r\n", s.c_str());
}
{
HeapSelectIram ephemeral;
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
}
#endif
// Note that free/realloc will use the heap specified when the pointer was created.
// No need to change heaps to delete an object, only to create it.
free(imem);
free(mem);
imem = NULL;
mem = NULL;
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
#ifdef USE_SET_IRAM_HEAP
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
#else
{
HeapSelectIram ephemeral;
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
}
#endif
{
ETS_PRINTF("Try and allocate all of the heap in one chunk\n");
HeapSelectIram ephemeral;
size_t free_iram = ESP.getFreeHeap();
ETS_PRINTF("IRAM free: %6d\n", free_iram);
uint32_t hfree;
uint16_t hmax;
uint8_t hfrag;
ESP.getHeapStats(&hfree, &hmax, &hfrag);
ETS_PRINTF("ESP.getHeapStats(free: %u, max: %u, frag: %u)\n",
hfree, hmax, hfrag);
if (free_iram > UMM_OVERHEAD_ADJUST) {
void *all = malloc(free_iram - UMM_OVERHEAD_ADJUST);
ETS_PRINTF("%p = malloc(%u)\n", all, free_iram);
umm_info(NULL, true);
free_iram = ESP.getFreeHeap();
ETS_PRINTF("IRAM free: %6d\n", free_iram);
free(all);
ETS_PRINTF("IRAM free: %6d\n", ESP.getFreeHeap());
}
}
}
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 'd': {
HeapSelectDram ephemeral;
umm_info(NULL, true);
break;
}
case 'i': {
HeapSelectIram ephemeral;
umm_info(NULL, true);
break;
}
case 'h': {
{
HeapSelectIram ephemeral;
Serial.printf(PSTR("IRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap());
}
{
HeapSelectDram ephemeral;
Serial.printf(PSTR("DRAM ESP.getFreeHeap: %u\r\n"), ESP.getFreeHeap());
}
break;
}
case 'R':
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
ESP.restart();
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" h - Free Heap Report;"));
out.println(F(" i - iRAM umm_info(null, true);"));
out.println(F(" d - dRAM umm_info(null, true);"));
out.println(F(" R - Restart, ESP.restart();"));
out.println(F(" ? - Print Help"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
processKey(out, '?');
break;
}
}
void loop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}

View File

@ -71,8 +71,12 @@ getResetInfo KEYWORD2
getResetInfoPtr KEYWORD2
eraseConfig KEYWORD2
getCycleCount KEYWORD2
enableVM KEYWORD2
setExternalHeap KEYWORD2
setDramHeap KEYWORD2
setIramHeap KEYWORD2
resetHeap KEYWORD2
random->KEYWORD2
setCtMinDataLength KEYWORD2
getCtMinDataLength KEYWORD2
setCtMaxDataLength KEYWORD2
@ -87,6 +91,7 @@ produce KEYWORD2
encrypt KEYWORD2
decrypt KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,111 @@
// This is still beta / a work in progress
// To run this sketch an (other) USB-serial converter is needed connected to RX-TX ports (below)
// hardware serial is used for logging
// software serial is used for the PPP link
// this example is subject for changes once everything is stabilized
// testing on linux:
// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth nodetach debug dump
// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth
// proxy arp is needed but we don't have it
// http://lwip.100.n7.nabble.com/PPP-proxy-arp-support-tp33286p33345.html
// using NAT instead
#if LWIP_FEATURES && !LWIP_IPV6
#include <lwip/napt.h>
#include <lwip/dns.h>
#include <PPPServer.h>
#include <dhcpserver.h>
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
#define LOGGERBAUD 115200
#define PPPLINKBAUD 38400
#define NAPT 200
#define NAPT_PORT 3
#define RX 13 // d1mini D7
#define TX 15 // d1mini D8
SoftwareSerial ppplink(RX, TX);
HardwareSerial& logger = Serial;
PPPServer ppp(&ppplink);
void PPPConnectedCallback(netif* nif) {
logger.printf("ppp: ip=%s/mask=%s/gw=%s\n",
IPAddress(&nif->ip_addr).toString().c_str(),
IPAddress(&nif->netmask).toString().c_str(),
IPAddress(&nif->gw).toString().c_str());
logger.printf("Heap before: %d\n", ESP.getFreeHeap());
err_t ret = ip_napt_init(NAPT, NAPT_PORT);
logger.printf("ip_napt_init(%d,%d): ret=%d (OK=%d)\n", NAPT, NAPT_PORT, (int)ret, (int)ERR_OK);
if (ret == ERR_OK) {
ret = ip_napt_enable_no(nif->num, 1);
logger.printf("ip_napt_enable(nif): ret=%d (OK=%d)\n", (int)ret, (int)ERR_OK);
if (ret == ERR_OK) {
logger.printf("PPP client is NATed\n");
}
// could not make this work yet,
// but packets are arriving on ppp client (= linux host)
logger.printf("redirect22=%d\n", ip_portmap_add(IP_PROTO_TCP, ip_2_ip4(&nif->ip_addr)->addr, 22, ip_2_ip4(&nif->gw)->addr, 22));
logger.printf("redirect80=%d\n", ip_portmap_add(IP_PROTO_TCP, ip_2_ip4(&nif->ip_addr)->addr, 80, ip_2_ip4(&nif->gw)->addr, 80));
logger.printf("redirect443=%d\n", ip_portmap_add(IP_PROTO_TCP, ip_2_ip4(&nif->ip_addr)->addr, 443, ip_2_ip4(&nif->gw)->addr, 443));
}
logger.printf("Heap after napt init: %d\n", ESP.getFreeHeap());
if (ret != ERR_OK) {
logger.printf("NAPT initialization failed\n");
}
}
void setup() {
logger.begin(LOGGERBAUD);
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED) {
logger.print('.');
delay(500);
}
logger.printf("\nSTA: %s (dns: %s / %s)\n",
WiFi.localIP().toString().c_str(),
WiFi.dnsIP(0).toString().c_str(),
WiFi.dnsIP(1).toString().c_str());
ppplink.begin(PPPLINKBAUD);
ppplink.enableIntTx(true);
logger.println();
logger.printf("\n\nhey, trying to be a PPP server here\n\n");
logger.printf("Now try this on your linux host:\n\n");
logger.printf("connect a serial<->usb module (e.g. to /dev/ttyUSB1) and link it to the ESP (esprx=%d esptx=%d), then run:\n\n", RX, TX);
logger.printf("sudo /usr/sbin/pppd /dev/ttyUSB1 %d noipdefault nocrtscts local defaultroute noauth nodetach debug dump\n\n", PPPLINKBAUD);
ppp.ifUpCb(PPPConnectedCallback);
bool ret = ppp.begin(WiFi.localIP());
logger.printf("ppp: %d\n", ret);
}
#else
void setup() {
Serial.begin(115200);
Serial.printf("\n\nPPP/NAPT not supported in this configuration\n");
}
#endif
void loop() {
}

View File

@ -0,0 +1,10 @@
name=lwIP_PPP
version=1
author=lwIP
maintainer=esp8266/Arduino
sentence=PPP interface
paragraph=PPP interface for esp8266 arduino
category=Network
url=https://github.com/esp8266/Arduino
architectures=esp8266
dot_a_linkage=true

View File

@ -0,0 +1,196 @@
// This is still beta / a work in progress
// testing on linux:
// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth nodetach debug dump
// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth
// proxy arp is needed but we don't have it
// http://lwip.100.n7.nabble.com/PPP-proxy-arp-support-tp33286p33345.html
// using NAT instead (see in example)
#include <Arduino.h>
#include <Schedule.h>
#include <IPAddress.h>
#include <lwip/dns.h>
#include "PPPServer.h"
PPPServer::PPPServer(Stream* sio): _sio(sio), _cb(netif_status_cb_s), _enabled(false)
{
}
bool PPPServer::handlePackets()
{
size_t avail;
if ((avail = _sio->available()) > 0)
{
// XXX block peeking would be useful here
if (avail > _bufsize)
{
avail = _bufsize;
}
avail = _sio->readBytes(_buf, avail);
pppos_input(_ppp, _buf, avail);
}
return _enabled;
}
void PPPServer::link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx)
{
bool stop = true;
netif* nif = ppp_netif(pcb);
switch (err_code)
{
case PPPERR_NONE: /* No error. */
{
#if LWIP_DNS
const ip_addr_t *ns;
#endif /* LWIP_DNS */
ets_printf("ppp_link_status_cb: PPPERR_NONE\n\r");
#if LWIP_IPV4
ets_printf(" our_ip4addr = %s\n\r", ip4addr_ntoa(netif_ip4_addr(nif)));
ets_printf(" his_ipaddr = %s\n\r", ip4addr_ntoa(netif_ip4_gw(nif)));
ets_printf(" netmask = %s\n\r", ip4addr_ntoa(netif_ip4_netmask(nif)));
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
ets_printf(" our_ip6addr = %s\n\r", ip6addr_ntoa(netif_ip6_addr(nif, 0)));
#endif /* LWIP_IPV6 */
#if LWIP_DNS
ns = dns_getserver(0);
ets_printf(" dns1 = %s\n\r", ipaddr_ntoa(ns));
ns = dns_getserver(1);
ets_printf(" dns2 = %s\n\r", ipaddr_ntoa(ns));
#endif /* LWIP_DNS */
#if PPP_IPV6_SUPPORT
ets_printf(" our6_ipaddr = %s\n\r", ip6addr_ntoa(netif_ip6_addr(nif, 0)));
#endif /* PPP_IPV6_SUPPORT */
}
stop = false;
break;
case PPPERR_PARAM: /* Invalid parameter. */
ets_printf("ppp_link_status_cb: PPPERR_PARAM\n");
break;
case PPPERR_OPEN: /* Unable to open PPP session. */
ets_printf("ppp_link_status_cb: PPPERR_OPEN\n");
break;
case PPPERR_DEVICE: /* Invalid I/O device for PPP. */
ets_printf("ppp_link_status_cb: PPPERR_DEVICE\n");
break;
case PPPERR_ALLOC: /* Unable to allocate resources. */
ets_printf("ppp_link_status_cb: PPPERR_ALLOC\n");
break;
case PPPERR_USER: /* User interrupt. */
ets_printf("ppp_link_status_cb: PPPERR_USER\n");
break;
case PPPERR_CONNECT: /* Connection lost. */
ets_printf("ppp_link_status_cb: PPPERR_CONNECT\n");
break;
case PPPERR_AUTHFAIL: /* Failed authentication challenge. */
ets_printf("ppp_link_status_cb: PPPERR_AUTHFAIL\n");
break;
case PPPERR_PROTOCOL: /* Failed to meet protocol. */
ets_printf("ppp_link_status_cb: PPPERR_PROTOCOL\n");
break;
case PPPERR_PEERDEAD: /* Connection timeout. */
ets_printf("ppp_link_status_cb: PPPERR_PEERDEAD\n");
break;
case PPPERR_IDLETIMEOUT: /* Idle Timeout. */
ets_printf("ppp_link_status_cb: PPPERR_IDLETIMEOUT\n");
break;
case PPPERR_CONNECTTIME: /* PPPERR_CONNECTTIME. */
ets_printf("ppp_link_status_cb: PPPERR_CONNECTTIME\n");
break;
case PPPERR_LOOPBACK: /* Connection timeout. */
ets_printf("ppp_link_status_cb: PPPERR_LOOPBACK\n");
break;
default:
ets_printf("ppp_link_status_cb: unknown errCode %d\n", err_code);
break;
}
if (stop)
{
netif_remove(&static_cast<PPPServer*>(ctx)->_netif);
}
}
u32_t PPPServer::output_cb_s(ppp_pcb* pcb, u8_t* data, u32_t len, void* ctx)
{
(void)pcb;
(void)ctx;
return static_cast<PPPServer*>(ctx)->_sio->write(data, len);
}
void PPPServer::netif_status_cb_s(netif* nif)
{
ets_printf("PPPNETIF: %c%c%d is %s\n", nif->name[0], nif->name[1], nif->num,
netif_is_up(nif) ? "UP" : "DOWN");
#if LWIP_IPV4
ets_printf("IPV4: Host at %s ", ip4addr_ntoa(netif_ip4_addr(nif)));
ets_printf("mask %s ", ip4addr_ntoa(netif_ip4_netmask(nif)));
ets_printf("gateway %s\n", ip4addr_ntoa(netif_ip4_gw(nif)));
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
ets_printf("IPV6: Host at %s\n", ip6addr_ntoa(netif_ip6_addr(nif, 0)));
#endif /* LWIP_IPV6 */
ets_printf("FQDN: %s\n", netif_get_hostname(nif));
}
bool PPPServer::begin(const IPAddress& ourAddress, const IPAddress& peer)
{
// lwip2-src/doc/ppp.txt
_ppp = pppos_create(&_netif, PPPServer::output_cb_s, PPPServer::link_status_cb_s, this);
if (!_ppp)
{
return false;
}
ppp_set_ipcp_ouraddr(_ppp, ip_2_ip4((const ip_addr_t*)ourAddress));
ppp_set_ipcp_hisaddr(_ppp, ip_2_ip4((const ip_addr_t*)peer));
//ip4_addr_t addr;
//IP4_ADDR(&addr, 10,0,1,254);
//ppp_set_ipcp_dnsaddr(_ppp, 0, &addr);
//ppp_set_auth(_ppp, PPPAUTHTYPE_ANY, "login", "password");
//ppp_set_auth_required(_ppp, 1);
ppp_set_silent(_ppp, 1);
ppp_listen(_ppp);
netif_set_status_callback(&_netif, _cb);
_enabled = true;
if (!schedule_recurrent_function_us([&]()
{
return this->handlePackets();
}, 1000))
{
netif_remove(&_netif);
return false;
}
return true;
}
void PPPServer::stop()
{
_enabled = false;
ppp_close(_ppp, 0);
}

View File

@ -0,0 +1,77 @@
/*
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
This file is part of the lwIP TCP/IP stack.
Author: Dirk Ziegelmeier <dziegel@gmx.de>
*/
#ifndef __PPPSERVER_H
#define __PPPSERVER_H
#include <Arduino.h>
#include <IPAddress.h>
#include <lwip/netif.h>
#include <netif/ppp/ppp.h>
#include <netif/ppp/pppos.h>
class PPPServer
{
public:
PPPServer(Stream* sio);
bool begin(const IPAddress& ourAddress, const IPAddress& peer = IPAddress(172, 31, 255, 254));
void stop();
void ifUpCb(void (*cb)(netif*))
{
_cb = cb;
}
const ip_addr_t* getPeerAddress() const
{
return &_netif.gw;
}
protected:
static constexpr size_t _bufsize = 128;
Stream* _sio;
ppp_pcb* _ppp;
netif _netif;
void (*_cb)(netif*);
uint8_t _buf[_bufsize];
bool _enabled;
// feed ppp from stream - to call on a regular basis or on interrupt
bool handlePackets();
static u32_t output_cb_s(ppp_pcb* pcb, u8_t* data, u32_t len, void* ctx);
static void link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx);
static void netif_status_cb_s(netif* nif);
};
#endif // __PPPSERVER_H

View File

@ -0,0 +1,10 @@
name=lwIP_enc28j60
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=ENC28J60 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/EtherSia/tree/master/src/enc28j60.cpp
category=Network
url=https://github.com/esp8266/Arduino
architectures=esp8266
dot_a_linkage=true

View File

@ -0,0 +1,10 @@
#ifndef _ENC28J60LWIP_H
#define _ENC28J60LWIP_H
#include <LwipIntfDev.h>
#include <utility/enc28j60.h>
using ENC28J60lwIP = LwipIntfDev<ENC28J60>;
#endif // _ENC28J60LWIP_H

View File

@ -0,0 +1,744 @@
/*
Copyright (c) 2012-2013, Thingsquare, http://www.thingsquare.com/.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/EtherSia/tree/master/src/enc28j60.cpp
#include <Arduino.h>
#include <SPI.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "enc28j60.h"
void serial_printf(const char *fmt, ...)
{
char buf[128];
va_list args;
va_start(args, fmt);
vsnprintf(buf, 128, fmt, args);
va_end(args);
Serial.print(buf);
}
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...) do { (void)0; } while (0)
#endif
#define EIE 0x1b
#define EIR 0x1c
#define ESTAT 0x1d
#define ECON2 0x1e
#define ECON1 0x1f
#define ESTAT_CLKRDY 0x01
#define ESTAT_TXABRT 0x02
#define ECON1_RXEN 0x04
#define ECON1_TXRTS 0x08
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define EIR_TXIF 0x08
#define ERXTX_BANK 0x00
#define ERDPTL 0x00
#define ERDPTH 0x01
#define EWRPTL 0x02
#define EWRPTH 0x03
#define ETXSTL 0x04
#define ETXSTH 0x05
#define ETXNDL 0x06
#define ETXNDH 0x07
#define ERXSTL 0x08
#define ERXSTH 0x09
#define ERXNDL 0x0a
#define ERXNDH 0x0b
#define ERXRDPTL 0x0c
#define ERXRDPTH 0x0d
#define RX_BUF_START 0x0000
#define RX_BUF_END 0x0fff
#define TX_BUF_START 0x1200
/* MACONx registers are in bank 2 */
#define MACONX_BANK 0x02
#define MACON1 0x00
#define MACON3 0x02
#define MACON4 0x03
#define MABBIPG 0x04
#define MAIPGL 0x06
#define MAIPGH 0x07
#define MAMXFLL 0x0a
#define MAMXFLH 0x0b
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_MARXEN 0x01
#define MACON3_PADCFG_FULL 0xe0
#define MACON3_TXCRCEN 0x10
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
#define MAX_MAC_LENGTH 1518
#define MAADRX_BANK 0x03
#define MAADR1 0x04 /* MAADR<47:40> */
#define MAADR2 0x05 /* MAADR<39:32> */
#define MAADR3 0x02 /* MAADR<31:24> */
#define MAADR4 0x03 /* MAADR<23:16> */
#define MAADR5 0x00 /* MAADR<15:8> */
#define MAADR6 0x01 /* MAADR<7:0> */
#define MISTAT 0x0a
#define EREVID 0x12
#define EPKTCNT_BANK 0x01
#define ERXFCON 0x18
#define EPKTCNT 0x19
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
// The ENC28J60 SPI Interface supports clock speeds up to 20 MHz
static const SPISettings spiSettings(20000000, MSBFIRST, SPI_MODE0);
ENC28J60::ENC28J60(int8_t cs, SPIClass& spi, int8_t intr):
_bank(ERXTX_BANK), _cs(cs), _spi(spi)
{
(void)intr;
}
void
ENC28J60::enc28j60_arch_spi_select(void)
{
SPI.beginTransaction(spiSettings);
digitalWrite(_cs, LOW);
}
void
ENC28J60::enc28j60_arch_spi_deselect(void)
{
digitalWrite(_cs, HIGH);
SPI.endTransaction();
}
/*---------------------------------------------------------------------------*/
uint8_t
ENC28J60::is_mac_mii_reg(uint8_t reg)
{
/* MAC or MII register (otherwise, ETH register)? */
switch (_bank)
{
case MACONX_BANK:
return reg < EIE;
case MAADRX_BANK:
return reg <= MAADR2 || reg == MISTAT;
case ERXTX_BANK:
case EPKTCNT_BANK:
default:
return 0;
}
}
/*---------------------------------------------------------------------------*/
uint8_t
ENC28J60::readreg(uint8_t reg)
{
uint8_t r;
enc28j60_arch_spi_select();
SPI.transfer(0x00 | (reg & 0x1f));
if (is_mac_mii_reg(reg))
{
/* MAC and MII registers require that a dummy byte be read first. */
SPI.transfer(0);
}
r = SPI.transfer(0);
enc28j60_arch_spi_deselect();
return r;
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::writereg(uint8_t reg, uint8_t data)
{
enc28j60_arch_spi_select();
SPI.transfer(0x40 | (reg & 0x1f));
SPI.transfer(data);
enc28j60_arch_spi_deselect();
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::setregbitfield(uint8_t reg, uint8_t mask)
{
if (is_mac_mii_reg(reg))
{
writereg(reg, readreg(reg) | mask);
}
else
{
enc28j60_arch_spi_select();
SPI.transfer(0x80 | (reg & 0x1f));
SPI.transfer(mask);
enc28j60_arch_spi_deselect();
}
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::clearregbitfield(uint8_t reg, uint8_t mask)
{
if (is_mac_mii_reg(reg))
{
writereg(reg, readreg(reg) & ~mask);
}
else
{
enc28j60_arch_spi_select();
SPI.transfer(0xa0 | (reg & 0x1f));
SPI.transfer(mask);
enc28j60_arch_spi_deselect();
}
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::setregbank(uint8_t new_bank)
{
writereg(ECON1, (readreg(ECON1) & 0xfc) | (new_bank & 0x03));
_bank = new_bank;
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::writedata(const uint8_t *data, int datalen)
{
int i;
enc28j60_arch_spi_select();
/* The Write Buffer Memory (WBM) command is 0 1 1 1 1 0 1 0 */
SPI.transfer(0x7a);
for (i = 0; i < datalen; i++)
{
SPI.transfer(data[i]);
}
enc28j60_arch_spi_deselect();
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::writedatabyte(uint8_t byte)
{
writedata(&byte, 1);
}
/*---------------------------------------------------------------------------*/
int
ENC28J60::readdata(uint8_t *buf, int len)
{
int i;
enc28j60_arch_spi_select();
/* THe Read Buffer Memory (RBM) command is 0 0 1 1 1 0 1 0 */
SPI.transfer(0x3a);
for (i = 0; i < len; i++)
{
buf[i] = SPI.transfer(0);
}
enc28j60_arch_spi_deselect();
return i;
}
/*---------------------------------------------------------------------------*/
uint8_t
ENC28J60::readdatabyte(void)
{
uint8_t r;
readdata(&r, 1);
return r;
}
/*---------------------------------------------------------------------------*/
void
ENC28J60::softreset(void)
{
enc28j60_arch_spi_select();
/* The System Command (soft reset) is 1 1 1 1 1 1 1 1 */
SPI.transfer(0xff);
enc28j60_arch_spi_deselect();
_bank = ERXTX_BANK;
}
/*---------------------------------------------------------------------------*/
//#if DEBUG
uint8_t
ENC28J60::readrev(void)
{
uint8_t rev;
setregbank(MAADRX_BANK);
rev = readreg(EREVID);
switch (rev)
{
case 2:
return 1;
case 6:
return 7;
default:
return rev;
}
}
//#endif
/*---------------------------------------------------------------------------*/
bool
ENC28J60::reset(void)
{
PRINTF("enc28j60: resetting chip\n");
pinMode(_cs, OUTPUT);
digitalWrite(_cs, HIGH);
SPI.begin();
/*
6.0 INITIALIZATION
Before the ENC28J60 can be used to transmit and receive packets,
certain device settings must be initialized. Depending on the
application, some configuration options may need to be
changed. Normally, these tasks may be accomplished once after
Reset and do not need to be changed thereafter.
6.1 Receive Buffer
Before receiving any packets, the receive buffer must be
initialized by programming the ERXST and ERXND pointers. All
memory between and including the ERXST and ERXND addresses will be
dedicated to the receive hardware. It is recommended that the
ERXST pointer be programmed with an even address.
Applications expecting large amounts of data and frequent packet
delivery may wish to allocate most of the memory as the receive
buffer. Applications that may need to save older packets or have
several packets ready for transmission should allocate less
memory.
When programming the ERXST pointer, the ERXWRPT registers will
automatically be updated with the same values. The address in
ERXWRPT will be used as the starting location when the receive
hardware begins writing received data. For tracking purposes, the
ERXRDPT registers should additionally be programmed with the same
value. To program ERXRDPT, the host controller must write to
ERXRDPTL first, followed by ERXRDPTH. See Section 7.2.4 “Freeing
Receive Buffer Space for more information
6.2 Transmission Buffer
All memory which is not used by the receive buffer is considered
the transmission buffer. Data which is to be transmitted should be
written into any unused space. After a packet is transmitted,
however, the hardware will write a seven-byte status vector into
memory after the last byte in the packet. Therefore, the host
controller should leave at least seven bytes between each packet
and the beginning of the receive buffer. No explicit action is
required to initialize the transmission buffer.
6.3 Receive Filters
The appropriate receive filters should be enabled or disabled by
writing to the ERXFCON register. See Section 8.0 “Receive Filters
for information on how to configure it.
6.4 Waiting For OST
If the initialization procedure is being executed immediately
following a Power-on Reset, the ESTAT.CLKRDY bit should be polled
to make certain that enough time has elapsed before proceeding to
modify the MAC and PHY registers. For more information on the OST,
see Section 2.2 “Oscillator Start-up Timer.
*/
softreset();
/* Workaround for erratum #2. */
delayMicroseconds(1000);
/* Wait for OST */
PRINTF("waiting for ESTAT_CLKRDY\n");
while ((readreg(ESTAT) & ESTAT_CLKRDY) == 0) {};
PRINTF("ESTAT_CLKRDY\n");
setregbank(ERXTX_BANK);
/* Set up receive buffer */
writereg(ERXSTL, RX_BUF_START & 0xff);
writereg(ERXSTH, RX_BUF_START >> 8);
writereg(ERXNDL, RX_BUF_END & 0xff);
writereg(ERXNDH, RX_BUF_END >> 8);
writereg(ERDPTL, RX_BUF_START & 0xff);
writereg(ERDPTH, RX_BUF_START >> 8);
writereg(ERXRDPTL, RX_BUF_END & 0xff);
writereg(ERXRDPTH, RX_BUF_END >> 8);
/* Receive filters */
setregbank(EPKTCNT_BANK);
writereg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_MCEN);
/*
6.5 MAC Initialization Settings
Several of the MAC registers require configuration during
initialization. This only needs to be done once; the order of
programming is unimportant.
1. Set the MARXEN bit in MACON1 to enable the MAC to receive
frames. If using full duplex, most applications should also set
TXPAUS and RXPAUS to allow IEEE defined flow control to function.
2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3. Most
applications should enable automatic padding to at least 60 bytes
and always append a valid CRC. For convenience, many applications
may wish to set the FRMLNEN bit as well to enable frame length
status reporting. The FULDPX bit should be set if the application
will be connected to a full-duplex configured remote node;
otherwise, it should be left clear.
3. Configure the bits in MACON4. For conformance to the IEEE 802.3
standard, set the DEFER bit.
4. Program the MAMXFL registers with the maximum frame length to
be permitted to be received or transmitted. Normal network nodes
are designed to handle packets that are 1518 bytes or less.
5. Configure the Back-to-Back Inter-Packet Gap register,
MABBIPG. Most applications will program this register with 15h
when Full-Duplex mode is used and 12h when Half-Duplex mode is
used.
6. Configure the Non-Back-to-Back Inter-Packet Gap register low
byte, MAIPGL. Most applications will program this register with
12h.
7. If half duplex is used, the Non-Back-to-Back Inter-Packet Gap
register high byte, MAIPGH, should be programmed. Most
applications will program this register to 0Ch.
8. If Half-Duplex mode is used, program the Retransmission and
Collision Window registers, MACLCON1 and MACLCON2. Most
applications will not need to change the default Reset values. If
the network is spread over exceptionally long cables, the default
value of MACLCON2 may need to be increased.
9. Program the local MAC address into the MAADR1:MAADR6 registers.
*/
setregbank(MACONX_BANK);
/* Turn on reception and IEEE-defined flow control */
setregbitfield(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
/* Set padding, crc, full duplex */
setregbitfield(MACON3, MACON3_PADCFG_FULL | MACON3_TXCRCEN | MACON3_FULDPX |
MACON3_FRMLNEN);
/* Don't modify MACON4 */
/* Set maximum frame length in MAMXFL */
writereg(MAMXFLL, MAX_MAC_LENGTH & 0xff);
writereg(MAMXFLH, MAX_MAC_LENGTH >> 8);
/* Set back-to-back inter packet gap */
writereg(MABBIPG, 0x15);
/* Set non-back-to-back packet gap */
writereg(MAIPGL, 0x12);
/* Set MAC address */
setregbank(MAADRX_BANK);
writereg(MAADR6, _localMac[5]);
writereg(MAADR5, _localMac[4]);
writereg(MAADR4, _localMac[3]);
writereg(MAADR3, _localMac[2]);
writereg(MAADR2, _localMac[1]);
writereg(MAADR1, _localMac[0]);
/*
6.6 PHY Initialization Settings
Depending on the application, bits in three of the PHY modules
registers may also require configuration. The PHCON1.PDPXMD bit
partially controls the devices half/full-duplex
configuration. Normally, this bit is initialized correctly by the
external circuitry (see Section 2.6 “LED Configuration). If the
external circuitry is not present or incorrect, however, the host
controller must program the bit properly. Alternatively, for an
externally configurable system, the PDPXMD bit may be read and the
FULDPX bit be programmed to match.
For proper duplex operation, the PHCON1.PDPXMD bit must also match
the value of the MACON3.FULDPX bit.
If using half duplex, the host controller may wish to set the
PHCON2.HDLDIS bit to prevent automatic loopback of the data which
is transmitted. The PHY register, PHLCON, controls the outputs of
LEDA and LEDB. If an application requires a LED configuration
other than the default, PHLCON must be altered to match the new
requirements. The settings for LED operation are discussed in
Section 2.6 “LED Configuration. The PHLCON register is shown in
Register 2-2 (page 9).
*/
/* Don't worry about PHY configuration for now */
/* Turn on autoincrement for buffer access */
setregbitfield(ECON2, ECON2_AUTOINC);
/* Turn on reception */
writereg(ECON1, ECON1_RXEN);
return true;
}
/*---------------------------------------------------------------------------*/
boolean
ENC28J60::begin(const uint8_t *address)
{
_localMac = address;
bool ret = reset();
uint8_t rev = readrev();
PRINTF("ENC28J60 rev. B%d\n", rev);
return ret && rev != 255;
}
/*---------------------------------------------------------------------------*/
uint16_t
ENC28J60::sendFrame(const uint8_t *data, uint16_t datalen)
{
uint16_t dataend;
/*
1. Appropriately program the ETXST pointer to point to an unused
location in memory. It will point to the per packet control
byte. In the example, it would be programmed to 0120h. It is
recommended that an even address be used for ETXST.
2. Use the WBM SPI command to write the per packet control byte,
the destination address, the source MAC address, the
type/length and the data payload.
3. Appropriately program the ETXND pointer. It should point to the
last byte in the data payload. In the example, it would be
programmed to 0156h.
4. Clear EIR.TXIF, set EIE.TXIE and set EIE.INTIE to enable an
interrupt when done (if desired).
5. Start the transmission process by setting
ECON1.TXRTS.
*/
setregbank(ERXTX_BANK);
/* Set up the transmit buffer pointer */
writereg(ETXSTL, TX_BUF_START & 0xff);
writereg(ETXSTH, TX_BUF_START >> 8);
writereg(EWRPTL, TX_BUF_START & 0xff);
writereg(EWRPTH, TX_BUF_START >> 8);
/* Write the transmission control register as the first byte of the
output packet. We write 0x00 to indicate that the default
configuration (the values in MACON3) will be used. */
writedatabyte(0x00); /* MACON3 */
writedata(data, datalen);
/* Write a pointer to the last data byte. */
dataend = TX_BUF_START + datalen;
writereg(ETXNDL, dataend & 0xff);
writereg(ETXNDH, dataend >> 8);
/* Clear EIR.TXIF */
clearregbitfield(EIR, EIR_TXIF);
/* Don't care about interrupts for now */
/* Send the packet */
setregbitfield(ECON1, ECON1_TXRTS);
while ((readreg(ECON1) & ECON1_TXRTS) > 0);
#if DEBUG
if ((readreg(ESTAT) & ESTAT_TXABRT) != 0)
{
uint16_t erdpt;
uint8_t tsv[7];
erdpt = (readreg(ERDPTH) << 8) | readreg(ERDPTL);
writereg(ERDPTL, (dataend + 1) & 0xff);
writereg(ERDPTH, (dataend + 1) >> 8);
readdata(tsv, sizeof(tsv));
writereg(ERDPTL, erdpt & 0xff);
writereg(ERDPTH, erdpt >> 8);
PRINTF("enc28j60: tx err: %d: %02x:%02x:%02x:%02x:%02x:%02x\n"
" tsv: %02x%02x%02x%02x%02x%02x%02x\n", datalen,
0xff & data[0], 0xff & data[1], 0xff & data[2],
0xff & data[3], 0xff & data[4], 0xff & data[5],
tsv[6], tsv[5], tsv[4], tsv[3], tsv[2], tsv[1], tsv[0]);
}
else
{
PRINTF("enc28j60: tx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", datalen,
0xff & data[0], 0xff & data[1], 0xff & data[2],
0xff & data[3], 0xff & data[4], 0xff & data[5]);
}
#endif
//sent_packets++;
//PRINTF("enc28j60: sent_packets %d\n", sent_packets);
return datalen;
}
/*---------------------------------------------------------------------------*/
uint16_t
ENC28J60::readFrame(uint8_t *buffer, uint16_t bufsize)
{
readFrameSize();
return readFrameData(buffer, bufsize);
}
uint16_t
ENC28J60::readFrameSize()
{
uint8_t n;
uint8_t nxtpkt[2];
uint8_t status[2];
uint8_t length[2];
setregbank(EPKTCNT_BANK);
n = readreg(EPKTCNT);
if (n == 0)
{
return 0;
}
PRINTF("enc28j60: EPKTCNT 0x%02x\n", n);
setregbank(ERXTX_BANK);
/* Read the next packet pointer */
nxtpkt[0] = readdatabyte();
nxtpkt[1] = readdatabyte();
_next = (nxtpkt[1] << 8) + nxtpkt[0];
PRINTF("enc28j60: nxtpkt 0x%02x%02x\n", _nxtpkt[1], _nxtpkt[0]);
length[0] = readdatabyte();
length[1] = readdatabyte();
_len = (length[1] << 8) + length[0];
PRINTF("enc28j60: length 0x%02x%02x\n", length[1], length[0]);
status[0] = readdatabyte();
status[1] = readdatabyte();
/* This statement is just to avoid a compiler warning: */
(void)status[0];
PRINTF("enc28j60: status 0x%02x%02x\n", status[1], status[0]);
return _len;
}
void
ENC28J60::discardFrame(uint16_t framesize)
{
(void)framesize;
(void)readFrameData(nullptr, 0);
}
uint16_t
ENC28J60::readFrameData(uint8_t *buffer, uint16_t framesize)
{
if (framesize < _len)
{
buffer = nullptr;
/* flush rx fifo */
for (uint16_t i = 0; i < _len; i++)
{
readdatabyte();
}
}
else
{
readdata(buffer, _len);
}
/* Read an additional byte at odd lengths, to avoid FIFO corruption */
if ((_len % 2) != 0)
{
readdatabyte();
}
/* Errata #14 */
if (_next == RX_BUF_START)
{
_next = RX_BUF_END;
}
else
{
_next = _next - 1;
}
writereg(ERXRDPTL, _next & 0xff);
writereg(ERXRDPTH, _next >> 8);
setregbitfield(ECON2, ECON2_PKTDEC);
if (!buffer)
{
PRINTF("enc28j60: rx err: flushed %d\n", _len);
return 0;
}
PRINTF("enc28j60: rx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", _len,
0xff & buffer[0], 0xff & buffer[1], 0xff & buffer[2],
0xff & buffer[3], 0xff & buffer[4], 0xff & buffer[5]);
//received_packets++;
//PRINTF("enc28j60: received_packets %d\n", received_packets);
return _len;
}

View File

@ -0,0 +1,149 @@
/**
Header file for direct Ethernet frame access to the ENC28J60 controller
@file enc28j60.h
*/
/*
Copyright (c) 2012-2013, Thingsquare, http://www.thingsquare.com/.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/EtherSia/tree/master/src/enc28j60.h
#ifndef ENC28J60_H
#define ENC28J60_H
#include <SPI.h>
/**
Send and receive Ethernet frames directly using a ENC28J60 controller.
*/
class ENC28J60
{
public:
/**
Constructor that uses the default hardware SPI pins
@param cs the Arduino Chip Select / Slave Select pin (default 10 on Uno)
*/
ENC28J60(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1);
/**
Initialise the Ethernet controller
Must be called before sending or receiving Ethernet frames
@param address the local MAC address for the Ethernet interface
@return Returns true if setting up the Ethernet interface was successful
*/
boolean begin(const uint8_t *address);
/**
Send an Ethernet frame
@param data a pointer to the data to send
@param datalen the length of the data in the packet
@return the number of bytes transmitted
*/
virtual uint16_t sendFrame(const uint8_t *data, uint16_t datalen);
/**
Read an Ethernet frame
@param buffer a pointer to a buffer to write the packet to
@param bufsize the available space in the buffer
@return the length of the received packet
or 0 if no packet was received
*/
virtual uint16_t readFrame(uint8_t *buffer, uint16_t bufsize);
protected:
static constexpr bool interruptIsPossible()
{
return false;
}
/**
Read an Ethernet frame size
@return the length of data do receive
or 0 if no frame was received
*/
uint16_t readFrameSize();
/**
discard an Ethernet frame
@param framesize readFrameSize()'s result
*/
void discardFrame(uint16_t framesize);
/**
Read an Ethernet frame data
readFrameSize() must be called first,
its result must be passed into framesize parameter
@param buffer a pointer to a buffer to write the frame to
@param framesize readFrameSize()'s result
@return the length of the received frame
or 0 if a problem occured
*/
uint16_t readFrameData(uint8_t *frame, uint16_t framesize);
private:
uint8_t is_mac_mii_reg(uint8_t reg);
uint8_t readreg(uint8_t reg);
void writereg(uint8_t reg, uint8_t data);
void setregbitfield(uint8_t reg, uint8_t mask);
void clearregbitfield(uint8_t reg, uint8_t mask);
void setregbank(uint8_t new_bank);
void writedata(const uint8_t *data, int datalen);
void writedatabyte(uint8_t byte);
int readdata(uint8_t *buf, int len);
uint8_t readdatabyte(void);
void softreset(void);
uint8_t readrev(void);
bool reset(void);
void enc28j60_arch_spi_init(void);
uint8_t enc28j60_arch_spi_write(uint8_t data);
uint8_t enc28j60_arch_spi_read(void);
void enc28j60_arch_spi_select(void);
void enc28j60_arch_spi_deselect(void);
// Previously defined in contiki/core/sys/clock.h
void clock_delay_usec(uint16_t dt);
uint8_t _bank;
int8_t _cs;
SPIClass& _spi;
const uint8_t *_localMac;
/* readFrame*() state */
uint16_t _next, _len;
};
#endif /* ENC28J60_H */

View File

@ -0,0 +1,10 @@
name=lwIP_w5500
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=Wiznet5100 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5100MacRaw
category=Network
url=https://github.com/esp8266/Arduino
architectures=esp8266
dot_a_linkage=true

View File

@ -0,0 +1,10 @@
#ifndef _W5100LWIP_H
#define _W5100LWIP_H
#include <LwipIntfDev.h>
#include <utility/w5100.h>
using Wiznet5100lwIP = LwipIntfDev<Wiznet5100>;
#endif // _W5500LWIP_H

View File

@ -0,0 +1,369 @@
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5100MacRaw
#include <SPI.h>
#include "w5100.h"
uint8_t Wiznet5100::wizchip_read(uint16_t address)
{
uint8_t ret;
wizchip_cs_select();
_spi.transfer(0x0F);
_spi.transfer((address & 0xFF00) >> 8);
_spi.transfer((address & 0x00FF) >> 0);
ret = _spi.transfer(0);
wizchip_cs_deselect();
return ret;
}
uint16_t Wiznet5100::wizchip_read_word(uint16_t address)
{
return ((uint16_t)wizchip_read(address) << 8) + wizchip_read(address + 1);
}
void Wiznet5100::wizchip_read_buf(uint16_t address, uint8_t* pBuf, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
pBuf[i] = wizchip_read(address + i);
}
}
void Wiznet5100::wizchip_write(uint16_t address, uint8_t wb)
{
wizchip_cs_select();
_spi.transfer(0xF0);
_spi.transfer((address & 0xFF00) >> 8);
_spi.transfer((address & 0x00FF) >> 0);
_spi.transfer(wb); // Data write (write 1byte data)
wizchip_cs_deselect();
}
void Wiznet5100::wizchip_write_word(uint16_t address, uint16_t word)
{
wizchip_write(address, (uint8_t)(word >> 8));
wizchip_write(address + 1, (uint8_t) word);
}
void Wiznet5100::wizchip_write_buf(uint16_t address, const uint8_t* pBuf, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
wizchip_write(address + i, pBuf[i]);
}
}
void Wiznet5100::setSn_CR(uint8_t cr)
{
// Write the command to the Command Register
wizchip_write(Sn_CR, cr);
// Now wait for the command to complete
while (wizchip_read(Sn_CR));
}
uint16_t Wiznet5100::getSn_TX_FSR()
{
uint16_t val = 0, val1 = 0;
do
{
val1 = wizchip_read_word(Sn_TX_FSR);
if (val1 != 0)
{
val = wizchip_read_word(Sn_TX_FSR);
}
} while (val != val1);
return val;
}
uint16_t Wiznet5100::getSn_RX_RSR()
{
uint16_t val = 0, val1 = 0;
do
{
val1 = wizchip_read_word(Sn_RX_RSR);
if (val1 != 0)
{
val = wizchip_read_word(Sn_RX_RSR);
}
} while (val != val1);
return val;
}
void Wiznet5100::wizchip_send_data(const uint8_t *wizdata, uint16_t len)
{
uint16_t ptr;
uint16_t size;
uint16_t dst_mask;
uint16_t dst_ptr;
ptr = getSn_TX_WR();
dst_mask = ptr & TxBufferMask;
dst_ptr = TxBufferAddress + dst_mask;
if (dst_mask + len > TxBufferLength)
{
size = TxBufferLength - dst_mask;
wizchip_write_buf(dst_ptr, wizdata, size);
wizdata += size;
size = len - size;
dst_ptr = TxBufferAddress;
wizchip_write_buf(dst_ptr, wizdata, size);
}
else
{
wizchip_write_buf(dst_ptr, wizdata, len);
}
ptr += len;
setSn_TX_WR(ptr);
}
void Wiznet5100::wizchip_recv_data(uint8_t *wizdata, uint16_t len)
{
uint16_t ptr;
uint16_t size;
uint16_t src_mask;
uint16_t src_ptr;
ptr = getSn_RX_RD();
src_mask = ptr & RxBufferMask;
src_ptr = RxBufferAddress + src_mask;
if ((src_mask + len) > RxBufferLength)
{
size = RxBufferLength - src_mask;
wizchip_read_buf(src_ptr, wizdata, size);
wizdata += size;
size = len - size;
src_ptr = RxBufferAddress;
wizchip_read_buf(src_ptr, wizdata, size);
}
else
{
wizchip_read_buf(src_ptr, wizdata, len);
}
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5100::wizchip_recv_ignore(uint16_t len)
{
uint16_t ptr;
ptr = getSn_RX_RD();
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5100::wizchip_sw_reset()
{
setMR(MR_RST);
getMR(); // for delay
setSHAR(_mac_address);
}
Wiznet5100::Wiznet5100(int8_t cs, SPIClass& spi, int8_t intr):
_spi(spi), _cs(cs)
{
(void)intr;
}
boolean Wiznet5100::begin(const uint8_t *mac_address)
{
memcpy(_mac_address, mac_address, 6);
pinMode(_cs, OUTPUT);
wizchip_cs_deselect();
#if 0
_spi.begin();
_spi.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz?
_spi.setBitOrder(MSBFIRST);
_spi.setDataMode(SPI_MODE0);
#endif
wizchip_sw_reset();
// Set the size of the Rx and Tx buffers
wizchip_write(RMSR, RxBufferSize);
wizchip_write(TMSR, TxBufferSize);
// Set our local MAC address
setSHAR(_mac_address);
// Open Socket 0 in MACRaw mode
setSn_MR(Sn_MR_MACRAW);
setSn_CR(Sn_CR_OPEN);
if (getSn_SR() != SOCK_MACRAW)
{
// Failed to put socket 0 into MACRaw mode
return false;
}
// Success
return true;
}
void Wiznet5100::end()
{
setSn_CR(Sn_CR_CLOSE);
// clear all interrupt of the socket
setSn_IR(0xFF);
// Wait for socket to change to closed
while (getSn_SR() != SOCK_CLOSED);
}
uint16_t Wiznet5100::readFrame(uint8_t *buffer, uint16_t bufsize)
{
uint16_t data_len = readFrameSize();
if (data_len == 0)
{
return 0;
}
if (data_len > bufsize)
{
// Packet is bigger than buffer - drop the packet
discardFrame(data_len);
return 0;
}
return readFrameData(buffer, data_len);
}
uint16_t Wiznet5100::readFrameSize()
{
uint16_t len = getSn_RX_RSR();
if (len == 0)
{
return 0;
}
uint8_t head[2];
uint16_t data_len = 0;
wizchip_recv_data(head, 2);
setSn_CR(Sn_CR_RECV);
data_len = head[0];
data_len = (data_len << 8) + head[1];
data_len -= 2;
return data_len;
}
void Wiznet5100::discardFrame(uint16_t framesize)
{
wizchip_recv_ignore(framesize);
setSn_CR(Sn_CR_RECV);
}
uint16_t Wiznet5100::readFrameData(uint8_t *buffer, uint16_t framesize)
{
wizchip_recv_data(buffer, framesize);
setSn_CR(Sn_CR_RECV);
#if 1
// let lwIP deal with mac address filtering
return framesize;
#else
// W5100 doesn't have any built-in MAC address filtering
if ((buffer[0] & 0x01) || memcmp(&buffer[0], _mac_address, 6) == 0)
{
// Addressed to an Ethernet multicast address or our unicast address
return framesize;
}
else
{
return 0;
}
#endif
}
uint16_t Wiznet5100::sendFrame(const uint8_t *buf, uint16_t len)
{
// Wait for space in the transmit buffer
while (1)
{
uint16_t freesize = getSn_TX_FSR();
if (getSn_SR() == SOCK_CLOSED)
{
return -1;
}
if (len <= freesize)
{
break;
}
};
wizchip_send_data(buf, len);
setSn_CR(Sn_CR_SEND);
while (1)
{
uint8_t tmp = getSn_IR();
if (tmp & Sn_IR_SENDOK)
{
setSn_IR(Sn_IR_SENDOK);
// Packet sent ok
break;
}
else if (tmp & Sn_IR_TIMEOUT)
{
setSn_IR(Sn_IR_TIMEOUT);
// There was a timeout
return -1;
}
}
return len;
}

View File

@ -0,0 +1,499 @@
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5100MacRaw
#ifndef W5100_H
#define W5100_H
#include <stdint.h>
#include <Arduino.h>
#include <SPI.h>
class Wiznet5100
{
public:
/**
Constructor that uses the default hardware SPI pins
@param cs the Arduino Chip Select / Slave Select pin (default 10)
*/
Wiznet5100(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1);
/**
Initialise the Ethernet controller
Must be called before sending or receiving Ethernet frames
@param address the local MAC address for the Ethernet interface
@return Returns true if setting up the Ethernet interface was successful
*/
boolean begin(const uint8_t *address);
/**
Shut down the Ethernet controlled
*/
void end();
/**
Send an Ethernet frame
@param data a pointer to the data to send
@param datalen the length of the data in the packet
@return the number of bytes transmitted
*/
uint16_t sendFrame(const uint8_t *data, uint16_t datalen);
/**
Read an Ethernet frame
@param buffer a pointer to a buffer to write the packet to
@param bufsize the available space in the buffer
@return the length of the received packet
or 0 if no packet was received
*/
uint16_t readFrame(uint8_t *buffer, uint16_t bufsize);
protected:
static constexpr bool interruptIsPossible()
{
return false;
}
/**
Read an Ethernet frame size
@return the length of data do receive
or 0 if no frame was received
*/
uint16_t readFrameSize();
/**
discard an Ethernet frame
@param framesize readFrameSize()'s result
*/
void discardFrame(uint16_t framesize);
/**
Read an Ethernet frame data
readFrameSize() must be called first,
its result must be passed into framesize parameter
@param buffer a pointer to a buffer to write the frame to
@param framesize readFrameSize()'s result
@return the length of the received frame
or 0 if a problem occured
*/
uint16_t readFrameData(uint8_t *frame, uint16_t framesize);
private:
static const uint16_t TxBufferAddress = 0x4000; /* Internal Tx buffer address of the iinchip */
static const uint16_t RxBufferAddress = 0x6000; /* Internal Rx buffer address of the iinchip */
static const uint8_t TxBufferSize = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */
static const uint8_t RxBufferSize = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */
static const uint16_t TxBufferLength = (1 << TxBufferSize) << 10; /* Length of Tx buffer in bytes */
static const uint16_t RxBufferLength = (1 << RxBufferSize) << 10; /* Length of Rx buffer in bytes */
static const uint16_t TxBufferMask = TxBufferLength - 1;
static const uint16_t RxBufferMask = RxBufferLength - 1;
SPIClass& _spi;
int8_t _cs;
uint8_t _mac_address[6];
/**
Default function to select chip.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline void wizchip_cs_select()
{
digitalWrite(_cs, LOW);
}
/**
Default function to deselect chip.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline void wizchip_cs_deselect()
{
digitalWrite(_cs, HIGH);
}
/**
Read a 1 byte value from a register.
@param address Register address
@return The value of register
*/
uint8_t wizchip_read(uint16_t address);
/**
Reads a 2 byte value from a register.
@param address Register address
@return The value of register
*/
uint16_t wizchip_read_word(uint16_t address);
/**
It reads sequence data from registers.
@param address Register address
@param pBuf Pointer buffer to read data
@param len Data length
*/
void wizchip_read_buf(uint16_t address, uint8_t* pBuf, uint16_t len);
/**
Write a 1 byte value to a register.
@param address Register address
@param wb Write data
@return void
*/
void wizchip_write(uint16_t address, uint8_t wb);
/**
Write a 2 byte value to a register.
@param address Register address
@param wb Write data
@return void
*/
void wizchip_write_word(uint16_t address, uint16_t word);
/**
It writes sequence data to registers.
@param address Register address
@param pBuf Pointer buffer to write data
@param len Data length
*/
void wizchip_write_buf(uint16_t address, const uint8_t* pBuf, uint16_t len);
/**
Reset WIZCHIP by softly.
*/
void wizchip_sw_reset(void);
/**
It copies data to internal TX memory
@details This function reads the Tx write pointer register and after that,
it copies the <i>wizdata(pointer buffer)</i> of the length of <i>len(variable)</i> bytes to internal TX memory
and updates the Tx write pointer register.
This function is being called by send() and sendto() function also.
@param wizdata Pointer buffer to write data
@param len Data length
@sa wizchip_recv_data()
*/
void wizchip_send_data(const uint8_t *wizdata, uint16_t len);
/**
It copies data to your buffer from internal RX memory
@details This function read the Rx read pointer register and after that,
it copies the received data from internal RX memory
to <i>wizdata(pointer variable)</i> of the length of <i>len(variable)</i> bytes.
This function is being called by recv() also.
@param wizdata Pointer buffer to read data
@param len Data length
@sa wizchip_send_data()
*/
void wizchip_recv_data(uint8_t *wizdata, uint16_t len);
/**
It discard the received data in RX memory.
@details It discards the data of the length of <i>len(variable)</i> bytes in internal RX memory.
@param len Data length
*/
void wizchip_recv_ignore(uint16_t len);
/**
Get @ref Sn_TX_FSR register
@return uint16_t. Value of @ref Sn_TX_FSR.
*/
uint16_t getSn_TX_FSR();
/**
Get @ref Sn_RX_RSR register
@return uint16_t. Value of @ref Sn_RX_RSR.
*/
uint16_t getSn_RX_RSR();
/** Common registers */
enum
{
MR = 0x0000, ///< Mode Register address (R/W)
GAR = 0x0001, ///< Gateway IP Register address (R/W)
SUBR = 0x0005, ///< Subnet mask Register address (R/W)
SHAR = 0x0009, ///< Source MAC Register address (R/W)
SIPR = 0x000F, ///< Source IP Register address (R/W)
IR = 0x0015, ///< Interrupt Register (R/W)
IMR = 0x0016, ///< Socket Interrupt Mask Register (R/W)
RTR = 0x0017, ///< Timeout register address (1 is 100us) (R/W)
RCR = 0x0019, ///< Retry count register (R/W)
RMSR = 0x001A, ///< Receive Memory Size
TMSR = 0x001B, ///< Transmit Memory Size
};
/** Socket registers */
enum
{
Sn_MR = 0x0400, ///< Socket Mode register(R/W)
Sn_CR = 0x0401, ///< Socket command register (R/W)
Sn_IR = 0x0402, ///< Socket interrupt register (R)
Sn_SR = 0x0403, ///< Socket status register (R)
Sn_PORT = 0x0404, ///< Source port register (R/W)
Sn_DHAR = 0x0406, ///< Peer MAC register address (R/W)
Sn_DIPR = 0x040C, ///< Peer IP register address (R/W)
Sn_DPORT = 0x0410, ///< Peer port register address (R/W)
Sn_MSSR = 0x0412, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W)
Sn_PROTO = 0x0414, ///< IP Protocol(PROTO) Register (R/W)
Sn_TOS = 0x0415, ///< IP Type of Service(TOS) Register (R/W)
Sn_TTL = 0x0416, ///< IP Time to live(TTL) Register (R/W)
Sn_TX_FSR = 0x0420, ///< Transmit free memory size register (R)
Sn_TX_RD = 0x0422, ///< Transmit memory read pointer register address (R)
Sn_TX_WR = 0x0424, ///< Transmit memory write pointer register address (R/W)
Sn_RX_RSR = 0x0426, ///< Received data size register (R)
Sn_RX_RD = 0x0428, ///< Read point of Receive memory (R/W)
Sn_RX_WR = 0x042A, ///< Write point of Receive memory (R)
};
/** Mode register values */
enum
{
MR_RST = 0x80, ///< Reset
MR_PB = 0x10, ///< Ping block
MR_AI = 0x02, ///< Address Auto-Increment in Indirect Bus Interface
MR_IND = 0x01, ///< Indirect Bus Interface mode
};
/** Socket Mode Register values @ref Sn_MR */
enum
{
Sn_MR_CLOSE = 0x00, ///< Unused socket
Sn_MR_TCP = 0x01, ///< TCP
Sn_MR_UDP = 0x02, ///< UDP
Sn_MR_IPRAW = 0x03, ///< IP LAYER RAW SOCK
Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK
Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP) flag
Sn_MR_MF = 0x40, ///< Use MAC filter
Sn_MR_MULTI = 0x80, ///< support multicating
};
/** Socket Command Register values */
enum
{
Sn_CR_OPEN = 0x01, ///< Initialise or open socket
Sn_CR_CLOSE = 0x10, ///< Close socket
Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data
Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process
Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message
Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data
};
/** Socket Interrupt register values */
enum
{
Sn_IR_CON = 0x01, ///< CON Interrupt
Sn_IR_DISCON = 0x02, ///< DISCON Interrupt
Sn_IR_RECV = 0x04, ///< RECV Interrupt
Sn_IR_TIMEOUT = 0x08, ///< TIMEOUT Interrupt
Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt
};
/** Socket Status Register values */
enum
{
SOCK_CLOSED = 0x00, ///< Closed
SOCK_INIT = 0x13, ///< Initiate state
SOCK_LISTEN = 0x14, ///< Listen state
SOCK_SYNSENT = 0x15, ///< Connection state
SOCK_SYNRECV = 0x16, ///< Connection state
SOCK_ESTABLISHED = 0x17, ///< Success to connect
SOCK_FIN_WAIT = 0x18, ///< Closing state
SOCK_CLOSING = 0x1A, ///< Closing state
SOCK_TIME_WAIT = 0x1B, ///< Closing state
SOCK_CLOSE_WAIT = 0x1C, ///< Closing state
SOCK_LAST_ACK = 0x1D, ///< Closing state
SOCK_UDP = 0x22, ///< UDP socket
SOCK_IPRAW = 0x32, ///< IP raw mode socket
SOCK_MACRAW = 0x42, ///< MAC raw mode socket
};
/**
Set Mode Register
@param (uint8_t)mr The value to be set.
@sa getMR()
*/
inline void setMR(uint8_t mode)
{
wizchip_write(MR, mode);
}
/**
Get Mode Register
@return uint8_t. The value of Mode register.
@sa setMR()
*/
inline uint8_t getMR()
{
return wizchip_read(MR);
}
/**
Set local MAC address
@param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 bytes.
@sa getSHAR()
*/
inline void setSHAR(const uint8_t* macaddr)
{
wizchip_write_buf(SHAR, macaddr, 6);
}
/**
Get local MAC address
@param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 bytes.
@sa setSHAR()
*/
inline void getSHAR(uint8_t* macaddr)
{
wizchip_read_buf(SHAR, macaddr, 6);
}
/**
Get @ref Sn_TX_WR register
@param (uint16_t)txwr Value to set @ref Sn_TX_WR
@sa GetSn_TX_WR()
*/
inline uint16_t getSn_TX_WR()
{
return wizchip_read_word(Sn_TX_WR);
}
/**
Set @ref Sn_TX_WR register
@param (uint16_t)txwr Value to set @ref Sn_TX_WR
@sa GetSn_TX_WR()
*/
inline void setSn_TX_WR(uint16_t txwr)
{
wizchip_write_word(Sn_TX_WR, txwr);
}
/**
Get @ref Sn_RX_RD register
@regurn uint16_t. Value of @ref Sn_RX_RD.
@sa setSn_RX_RD()
*/
inline uint16_t getSn_RX_RD()
{
return wizchip_read_word(Sn_RX_RD);
}
/**
Set @ref Sn_RX_RD register
@param (uint16_t)rxrd Value to set @ref Sn_RX_RD
@sa getSn_RX_RD()
*/
inline void setSn_RX_RD(uint16_t rxrd)
{
wizchip_write_word(Sn_RX_RD, rxrd);
}
/**
Set @ref Sn_MR register
@param (uint8_t)mr Value to set @ref Sn_MR
@sa getSn_MR()
*/
inline void setSn_MR(uint8_t mr)
{
wizchip_write(Sn_MR, mr);
}
/**
Get @ref Sn_MR register
@return uint8_t. Value of @ref Sn_MR.
@sa setSn_MR()
*/
inline uint8_t getSn_MR()
{
return wizchip_read(Sn_MR);
}
/**
Set @ref Sn_CR register, then wait for the command to execute
@param (uint8_t)cr Value to set @ref Sn_CR
@sa getSn_CR()
*/
void setSn_CR(uint8_t cr);
/**
Get @ref Sn_CR register
@return uint8_t. Value of @ref Sn_CR.
@sa setSn_CR()
*/
inline uint8_t getSn_CR()
{
return wizchip_read(Sn_CR);
}
/**
Get @ref Sn_SR register
@return uint8_t. Value of @ref Sn_SR.
*/
inline uint8_t getSn_SR()
{
return wizchip_read(Sn_SR);
}
/**
Get @ref Sn_IR register
@return uint8_t. Value of @ref Sn_IR.
@sa setSn_IR()
*/
inline uint8_t getSn_IR()
{
return wizchip_read(Sn_IR);
}
/**
Set @ref Sn_IR register
@param (uint8_t)ir Value to set @ref Sn_IR
@sa getSn_IR()
*/
inline void setSn_IR(uint8_t ir)
{
wizchip_write(Sn_IR, ir);
}
};
#endif // W5100_H

View File

@ -0,0 +1,102 @@
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.
*/
#include <SPI.h>
#include <W5500lwIP.h>
//or #include <W5100lwIP.h>
//or #include <ENC28J60lwIP.h>
#include <WiFiClient.h> // WiFiClient (-> TCPClient)
#include <ESP8266WiFi.h> // ESP8266WiFiClass::preinitWiFiOff()
const char* host = "djxmmx.net";
const uint16_t port = 17;
using TCPClient = WiFiClient;
#define CSPIN 16 // wemos/lolin/nodemcu D0
Wiznet5500lwIP eth(CSPIN);
void preinit() {
// (no C++ in function)
// disable wifi
ESP8266WiFiClass::preinitWiFiOff();
}
void setup() {
Serial.begin(115200);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz?
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
eth.setDefault(); // use ethernet for default route
if (!eth.begin()) {
Serial.println("ethernet hardware not found ... sleeping");
while (1) {
delay(1000);
}
} else {
Serial.print("connecting ethernet");
while (!eth.connected()) {
Serial.print(".");
delay(1000);
}
}
Serial.println();
Serial.print("ethernet IP address: ");
Serial.println(eth.localIP());
}
void loop() {
static bool wait = false;
Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);
TCPClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}
// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from ESP8266");
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}
// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
// not testing 'client.connected()' since we do not need to send data here
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();
if (wait) {
delay(300000); // execute once every 5 minutes, don't flood remote service
}
wait = true;
}

View File

@ -0,0 +1,10 @@
name=lwIP_w5500
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=Wiznet5500 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5500MacRaw
category=Network
url=https://github.com/esp8266/Arduino
architectures=esp8266
dot_a_linkage=true

View File

@ -0,0 +1,10 @@
#ifndef _W5500LWIP_H
#define _W5500LWIP_H
#include <LwipIntfDev.h>
#include <utility/w5500.h>
using Wiznet5500lwIP = LwipIntfDev<Wiznet5500>;
#endif // _W5500LWIP_H

View File

@ -0,0 +1,442 @@
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5500MacRaw
#include <SPI.h>
#include "w5500.h"
uint8_t Wiznet5500::wizchip_read(uint8_t block, uint16_t address)
{
uint8_t ret;
wizchip_cs_select();
block |= AccessModeRead;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
ret = wizchip_spi_read_byte();
wizchip_cs_deselect();
return ret;
}
uint16_t Wiznet5500::wizchip_read_word(uint8_t block, uint16_t address)
{
return ((uint16_t)wizchip_read(block, address) << 8) + wizchip_read(block, address + 1);
}
void Wiznet5500::wizchip_read_buf(uint8_t block, uint16_t address, uint8_t* pBuf, uint16_t len)
{
uint16_t i;
wizchip_cs_select();
block |= AccessModeRead;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
for (i = 0; i < len; i++)
{
pBuf[i] = wizchip_spi_read_byte();
}
wizchip_cs_deselect();
}
void Wiznet5500::wizchip_write(uint8_t block, uint16_t address, uint8_t wb)
{
wizchip_cs_select();
block |= AccessModeWrite;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
wizchip_spi_write_byte(wb);
wizchip_cs_deselect();
}
void Wiznet5500::wizchip_write_word(uint8_t block, uint16_t address, uint16_t word)
{
wizchip_write(block, address, (uint8_t)(word >> 8));
wizchip_write(block, address + 1, (uint8_t) word);
}
void Wiznet5500::wizchip_write_buf(uint8_t block, uint16_t address, const uint8_t* pBuf, uint16_t len)
{
uint16_t i;
wizchip_cs_select();
block |= AccessModeWrite;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
for (i = 0; i < len; i++)
{
wizchip_spi_write_byte(pBuf[i]);
}
wizchip_cs_deselect();
}
void Wiznet5500::setSn_CR(uint8_t cr)
{
// Write the command to the Command Register
wizchip_write(BlockSelectSReg, Sn_CR, cr);
// Now wait for the command to complete
while (wizchip_read(BlockSelectSReg, Sn_CR));
}
uint16_t Wiznet5500::getSn_TX_FSR()
{
uint16_t val = 0, val1 = 0;
do
{
val1 = wizchip_read_word(BlockSelectSReg, Sn_TX_FSR);
if (val1 != 0)
{
val = wizchip_read_word(BlockSelectSReg, Sn_TX_FSR);
}
} while (val != val1);
return val;
}
uint16_t Wiznet5500::getSn_RX_RSR()
{
uint16_t val = 0, val1 = 0;
do
{
val1 = wizchip_read_word(BlockSelectSReg, Sn_RX_RSR);
if (val1 != 0)
{
val = wizchip_read_word(BlockSelectSReg, Sn_RX_RSR);
}
} while (val != val1);
return val;
}
void Wiznet5500::wizchip_send_data(const uint8_t *wizdata, uint16_t len)
{
uint16_t ptr = 0;
if (len == 0)
{
return;
}
ptr = getSn_TX_WR();
wizchip_write_buf(BlockSelectTxBuf, ptr, wizdata, len);
ptr += len;
setSn_TX_WR(ptr);
}
void Wiznet5500::wizchip_recv_data(uint8_t *wizdata, uint16_t len)
{
uint16_t ptr;
if (len == 0)
{
return;
}
ptr = getSn_RX_RD();
wizchip_read_buf(BlockSelectRxBuf, ptr, wizdata, len);
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5500::wizchip_recv_ignore(uint16_t len)
{
uint16_t ptr;
ptr = getSn_RX_RD();
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5500::wizchip_sw_reset()
{
setMR(MR_RST);
getMR(); // for delay
setSHAR(_mac_address);
}
int8_t Wiznet5500::wizphy_getphylink()
{
int8_t tmp;
if (getPHYCFGR() & PHYCFGR_LNK_ON)
{
tmp = PHY_LINK_ON;
}
else
{
tmp = PHY_LINK_OFF;
}
return tmp;
}
int8_t Wiznet5500::wizphy_getphypmode()
{
int8_t tmp = 0;
if (getPHYCFGR() & PHYCFGR_OPMDC_PDOWN)
{
tmp = PHY_POWER_DOWN;
}
else
{
tmp = PHY_POWER_NORM;
}
return tmp;
}
void Wiznet5500::wizphy_reset()
{
uint8_t tmp = getPHYCFGR();
tmp &= PHYCFGR_RST;
setPHYCFGR(tmp);
tmp = getPHYCFGR();
tmp |= ~PHYCFGR_RST;
setPHYCFGR(tmp);
}
int8_t Wiznet5500::wizphy_setphypmode(uint8_t pmode)
{
uint8_t tmp = 0;
tmp = getPHYCFGR();
if ((tmp & PHYCFGR_OPMD) == 0)
{
return -1;
}
tmp &= ~PHYCFGR_OPMDC_ALLA;
if (pmode == PHY_POWER_DOWN)
{
tmp |= PHYCFGR_OPMDC_PDOWN;
}
else
{
tmp |= PHYCFGR_OPMDC_ALLA;
}
setPHYCFGR(tmp);
wizphy_reset();
tmp = getPHYCFGR();
if (pmode == PHY_POWER_DOWN)
{
if (tmp & PHYCFGR_OPMDC_PDOWN)
{
return 0;
}
}
else
{
if (tmp & PHYCFGR_OPMDC_ALLA)
{
return 0;
}
}
return -1;
}
Wiznet5500::Wiznet5500(int8_t cs, SPIClass& spi, int8_t intr):
_spi(spi), _cs(cs)
{
(void)intr;
}
boolean Wiznet5500::begin(const uint8_t *mac_address)
{
memcpy(_mac_address, mac_address, 6);
pinMode(_cs, OUTPUT);
wizchip_cs_deselect();
#if 0
_spi.begin();
_spi.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz?
_spi.setBitOrder(MSBFIRST);
_spi.setDataMode(SPI_MODE0);
#endif
wizchip_sw_reset();
// Use the full 16Kb of RAM for Socket 0
setSn_RXBUF_SIZE(16);
setSn_TXBUF_SIZE(16);
// Set our local MAC address
setSHAR(_mac_address);
// Open Socket 0 in MACRaw mode
setSn_MR(Sn_MR_MACRAW);
setSn_CR(Sn_CR_OPEN);
if (getSn_SR() != SOCK_MACRAW)
{
// Failed to put socket 0 into MACRaw mode
return false;
}
// Success
return true;
}
void Wiznet5500::end()
{
setSn_CR(Sn_CR_CLOSE);
// clear all interrupt of the socket
setSn_IR(0xFF);
// Wait for socket to change to closed
while (getSn_SR() != SOCK_CLOSED);
}
uint16_t Wiznet5500::readFrame(uint8_t *buffer, uint16_t bufsize)
{
uint16_t data_len = readFrameSize();
if (data_len == 0)
{
return 0;
}
if (data_len > bufsize)
{
// Packet is bigger than buffer - drop the packet
discardFrame(data_len);
return 0;
}
return readFrameData(buffer, data_len);
}
uint16_t Wiznet5500::readFrameSize()
{
uint16_t len = getSn_RX_RSR();
if (len == 0)
{
return 0;
}
uint8_t head[2];
uint16_t data_len = 0;
wizchip_recv_data(head, 2);
setSn_CR(Sn_CR_RECV);
data_len = head[0];
data_len = (data_len << 8) + head[1];
data_len -= 2;
return data_len;
}
void Wiznet5500::discardFrame(uint16_t framesize)
{
wizchip_recv_ignore(framesize);
setSn_CR(Sn_CR_RECV);
}
uint16_t Wiznet5500::readFrameData(uint8_t *buffer, uint16_t framesize)
{
wizchip_recv_data(buffer, framesize);
setSn_CR(Sn_CR_RECV);
#if 1
// let lwIP deal with mac address filtering
return framesize;
#else
// Had problems with W5500 MAC address filtering (the Sn_MR_MFEN option)
// Do it in software instead:
if ((buffer[0] & 0x01) || memcmp(&buffer[0], _mac_address, 6) == 0)
{
// Addressed to an Ethernet multicast address or our unicast address
return framesize;
}
else
{
return 0;
}
#endif
}
uint16_t Wiznet5500::sendFrame(const uint8_t *buf, uint16_t len)
{
// Wait for space in the transmit buffer
while (1)
{
uint16_t freesize = getSn_TX_FSR();
if (getSn_SR() == SOCK_CLOSED)
{
return -1;
}
if (len <= freesize)
{
break;
}
};
wizchip_send_data(buf, len);
setSn_CR(Sn_CR_SEND);
while (1)
{
uint8_t tmp = getSn_IR();
if (tmp & Sn_IR_SENDOK)
{
setSn_IR(Sn_IR_SENDOK);
// Packet sent ok
break;
}
else if (tmp & Sn_IR_TIMEOUT)
{
setSn_IR(Sn_IR_TIMEOUT);
// There was a timeout
return -1;
}
}
return len;
}

View File

@ -0,0 +1,767 @@
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5500MacRaw
#ifndef W5500_H
#define W5500_H
#include <stdint.h>
#include <Arduino.h>
#include <SPI.h>
class Wiznet5500
{
public:
/**
Constructor that uses the default hardware SPI pins
@param cs the Arduino Chip Select / Slave Select pin (default 10)
*/
Wiznet5500(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1);
/**
Initialise the Ethernet controller
Must be called before sending or receiving Ethernet frames
@param address the local MAC address for the Ethernet interface
@return Returns true if setting up the Ethernet interface was successful
*/
boolean begin(const uint8_t *address);
/**
Shut down the Ethernet controlled
*/
void end();
/**
Send an Ethernet frame
@param data a pointer to the data to send
@param datalen the length of the data in the packet
@return the number of bytes transmitted
*/
uint16_t sendFrame(const uint8_t *data, uint16_t datalen);
/**
Read an Ethernet frame
@param buffer a pointer to a buffer to write the packet to
@param bufsize the available space in the buffer
@return the length of the received packet
or 0 if no packet was received
*/
uint16_t readFrame(uint8_t *buffer, uint16_t bufsize);
protected:
static constexpr bool interruptIsPossible()
{
return false;
}
/**
Read an Ethernet frame size
@return the length of data do receive
or 0 if no frame was received
*/
uint16_t readFrameSize();
/**
discard an Ethernet frame
@param framesize readFrameSize()'s result
*/
void discardFrame(uint16_t framesize);
/**
Read an Ethernet frame data
readFrameSize() must be called first,
its result must be passed into framesize parameter
@param buffer a pointer to a buffer to write the frame to
@param framesize readFrameSize()'s result
@return the length of the received frame
or 0 if a problem occured
*/
uint16_t readFrameData(uint8_t *frame, uint16_t framesize);
private:
//< SPI interface Read operation in Control Phase
static const uint8_t AccessModeRead = (0x00 << 2);
//< SPI interface Read operation in Control Phase
static const uint8_t AccessModeWrite = (0x01 << 2);
//< Common register block in Control Phase
static const uint8_t BlockSelectCReg = (0x00 << 3);
//< Socket 0 register block in Control Phase
static const uint8_t BlockSelectSReg = (0x01 << 3);
//< Socket 0 Tx buffer address block
static const uint8_t BlockSelectTxBuf = (0x02 << 3);
//< Socket 0 Rx buffer address block
static const uint8_t BlockSelectRxBuf = (0x03 << 3);
SPIClass& _spi;
int8_t _cs;
uint8_t _mac_address[6];
/**
Default function to select chip.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline void wizchip_cs_select()
{
digitalWrite(_cs, LOW);
}
/**
Default function to deselect chip.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline void wizchip_cs_deselect()
{
digitalWrite(_cs, HIGH);
}
/**
Default function to read in SPI interface.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline uint8_t wizchip_spi_read_byte()
{
return _spi.transfer(0);
}
/**
Default function to write in SPI interface.
@note This function help not to access wrong address. If you do not describe this function or register any functions,
null function is called.
*/
inline void wizchip_spi_write_byte(uint8_t wb)
{
_spi.transfer(wb);
}
/**
Read a 1 byte value from a register.
@param address Register address
@return The value of register
*/
uint8_t wizchip_read(uint8_t block, uint16_t address);
/**
Reads a 2 byte value from a register.
@param address Register address
@return The value of register
*/
uint16_t wizchip_read_word(uint8_t block, uint16_t address);
/**
It reads sequence data from registers.
@param address Register address
@param pBuf Pointer buffer to read data
@param len Data length
*/
void wizchip_read_buf(uint8_t block, uint16_t address, uint8_t* pBuf, uint16_t len);
/**
Write a 1 byte value to a register.
@param address Register address
@param wb Write data
@return void
*/
void wizchip_write(uint8_t block, uint16_t address, uint8_t wb);
/**
Write a 2 byte value to a register.
@param address Register address
@param wb Write data
@return void
*/
void wizchip_write_word(uint8_t block, uint16_t address, uint16_t word);
/**
It writes sequence data to registers.
@param address Register address
@param pBuf Pointer buffer to write data
@param len Data length
*/
void wizchip_write_buf(uint8_t block, uint16_t address, const uint8_t* pBuf, uint16_t len);
/**
Get @ref Sn_TX_FSR register
@return uint16_t. Value of @ref Sn_TX_FSR.
*/
uint16_t getSn_TX_FSR();
/**
Get @ref Sn_RX_RSR register
@return uint16_t. Value of @ref Sn_RX_RSR.
*/
uint16_t getSn_RX_RSR();
/**
Reset WIZCHIP by softly.
*/
void wizchip_sw_reset();
/**
Get the link status of phy in WIZCHIP
*/
int8_t wizphy_getphylink();
/**
Get the power mode of PHY in WIZCHIP
*/
int8_t wizphy_getphypmode();
/**
Reset Phy
*/
void wizphy_reset();
/**
set the power mode of phy inside WIZCHIP. Refer to @ref PHYCFGR in W5500, @ref PHYSTATUS in W5200
@param pmode Settig value of power down mode.
*/
int8_t wizphy_setphypmode(uint8_t pmode);
/**
It copies data to internal TX memory
@details This function reads the Tx write pointer register and after that,
it copies the <i>wizdata(pointer buffer)</i> of the length of <i>len(variable)</i> bytes to internal TX memory
and updates the Tx write pointer register.
This function is being called by send() and sendto() function also.
@param wizdata Pointer buffer to write data
@param len Data length
@sa wizchip_recv_data()
*/
void wizchip_send_data(const uint8_t *wizdata, uint16_t len);
/**
It copies data to your buffer from internal RX memory
@details This function read the Rx read pointer register and after that,
it copies the received data from internal RX memory
to <i>wizdata(pointer variable)</i> of the length of <i>len(variable)</i> bytes.
This function is being called by recv() also.
@param wizdata Pointer buffer to read data
@param len Data length
@sa wizchip_send_data()
*/
void wizchip_recv_data(uint8_t *wizdata, uint16_t len);
/**
It discard the received data in RX memory.
@details It discards the data of the length of <i>len(variable)</i> bytes in internal RX memory.
@param len Data length
*/
void wizchip_recv_ignore(uint16_t len);
/** Common registers */
enum
{
MR = 0x0000, ///< Mode Register address (R/W)
SHAR = 0x0009, ///< Source MAC Register address (R/W)
INTLEVEL = 0x0013, ///< Set Interrupt low level timer register address (R/W)
IR = 0x0015, ///< Interrupt Register (R/W)
_IMR_ = 0x0016, ///< Interrupt mask register (R/W)
SIR = 0x0017, ///< Socket Interrupt Register (R/W)
SIMR = 0x0018, ///< Socket Interrupt Mask Register (R/W)
_RTR_ = 0x0019, ///< Timeout register address (1 is 100us) (R/W)
_RCR_ = 0x001B, ///< Retry count register (R/W)
UIPR = 0x0028, ///< Unreachable IP register address in UDP mode (R)
UPORTR = 0x002C, ///< Unreachable Port register address in UDP mode (R)
PHYCFGR = 0x002E, ///< PHY Status Register (R/W)
VERSIONR = 0x0039, ///< Chip version register address (R)
};
/** Socket registers */
enum
{
Sn_MR = 0x0000, ///< Socket Mode register (R/W)
Sn_CR = 0x0001, ///< Socket command register (R/W)
Sn_IR = 0x0002, ///< Socket interrupt register (R)
Sn_SR = 0x0003, ///< Socket status register (R)
Sn_PORT = 0x0004, ///< Source port register (R/W)
Sn_DHAR = 0x0006, ///< Peer MAC register address (R/W)
Sn_DIPR = 0x000C, ///< Peer IP register address (R/W)
Sn_DPORT = 0x0010, ///< Peer port register address (R/W)
Sn_MSSR = 0x0012, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W)
Sn_TOS = 0x0015, ///< IP Type of Service(TOS) Register (R/W)
Sn_TTL = 0x0016, ///< IP Time to live(TTL) Register (R/W)
Sn_RXBUF_SIZE = 0x001E, ///< Receive memory size register (R/W)
Sn_TXBUF_SIZE = 0x001F, ///< Transmit memory size register (R/W)
Sn_TX_FSR = 0x0020, ///< Transmit free memory size register (R)
Sn_TX_RD = 0x0022, ///< Transmit memory read pointer register address (R)
Sn_TX_WR = 0x0024, ///< Transmit memory write pointer register address (R/W)
Sn_RX_RSR = 0x0026, ///< Received data size register (R)
Sn_RX_RD = 0x0028, ///< Read point of Receive memory (R/W)
Sn_RX_WR = 0x002A, ///< Write point of Receive memory (R)
Sn_IMR = 0x002C, ///< Socket interrupt mask register (R)
Sn_FRAG = 0x002D, ///< Fragment field value in IP header register (R/W)
Sn_KPALVTR = 0x002F, ///< Keep Alive Timer register (R/W)
};
/** Mode register values */
enum
{
MR_RST = 0x80, ///< Reset
MR_WOL = 0x20, ///< Wake on LAN
MR_PB = 0x10, ///< Ping block
MR_PPPOE = 0x08, ///< Enable PPPoE
MR_FARP = 0x02, ///< Enable UDP_FORCE_ARP CHECHK
};
/* Interrupt Register values */
enum
{
IR_CONFLICT = 0x80, ///< Check IP conflict
IR_UNREACH = 0x40, ///< Get the destination unreachable message in UDP sending
IR_PPPoE = 0x20, ///< Get the PPPoE close message
IR_MP = 0x10, ///< Get the magic packet interrupt
};
/* Interrupt Mask Register values */
enum
{
IM_IR7 = 0x80, ///< IP Conflict Interrupt Mask
IM_IR6 = 0x40, ///< Destination unreachable Interrupt Mask
IM_IR5 = 0x20, ///< PPPoE Close Interrupt Mask
IM_IR4 = 0x10, ///< Magic Packet Interrupt Mask
};
/** Socket Mode Register values @ref Sn_MR */
enum
{
Sn_MR_CLOSE = 0x00, ///< Unused socket
Sn_MR_TCP = 0x01, ///< TCP
Sn_MR_UDP = 0x02, ///< UDP
Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK
Sn_MR_UCASTB = 0x10, ///< Unicast Block in UDP Multicasting
Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP), Multicast flag
Sn_MR_BCASTB = 0x40, ///< Broadcast block in UDP Multicasting
Sn_MR_MULTI = 0x80, ///< Support UDP Multicasting
Sn_MR_MIP6B = 0x10, ///< IPv6 packet Blocking in @ref Sn_MR_MACRAW mode
Sn_MR_MMB = 0x20, ///< Multicast Blocking in @ref Sn_MR_MACRAW mode
Sn_MR_MFEN = 0x80, ///< MAC filter enable in @ref Sn_MR_MACRAW mode
};
/** Socket Command Register values */
enum
{
Sn_CR_OPEN = 0x01, ///< Initialise or open socket
Sn_CR_LISTEN = 0x02, ///< Wait connection request in TCP mode (Server mode)
Sn_CR_CONNECT = 0x04, ///< Send connection request in TCP mode (Client mode)
Sn_CR_DISCON = 0x08, ///< Send closing request in TCP mode
Sn_CR_CLOSE = 0x10, ///< Close socket
Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data
Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process
Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message
Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data
};
/** Socket Interrupt register values */
enum
{
Sn_IR_CON = 0x01, ///< CON Interrupt
Sn_IR_DISCON = 0x02, ///< DISCON Interrupt
Sn_IR_RECV = 0x04, ///< RECV Interrupt
Sn_IR_TIMEOUT = 0x08, ///< TIMEOUT Interrupt
Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt
};
/** Socket Status Register values */
enum
{
SOCK_CLOSED = 0x00, ///< Closed
SOCK_INIT = 0x13, ///< Initiate state
SOCK_LISTEN = 0x14, ///< Listen state
SOCK_SYNSENT = 0x15, ///< Connection state
SOCK_SYNRECV = 0x16, ///< Connection state
SOCK_ESTABLISHED = 0x17, ///< Success to connect
SOCK_FIN_WAIT = 0x18, ///< Closing state
SOCK_CLOSING = 0x1A, ///< Closing state
SOCK_TIME_WAIT = 0x1B, ///< Closing state
SOCK_CLOSE_WAIT = 0x1C, ///< Closing state
SOCK_LAST_ACK = 0x1D, ///< Closing state
SOCK_UDP = 0x22, ///< UDP socket
SOCK_MACRAW = 0x42, ///< MAC raw mode socket
};
/* PHYCFGR register value */
enum
{
PHYCFGR_RST = ~(1 << 7), //< For PHY reset, must operate AND mask.
PHYCFGR_OPMD = (1 << 6), // Configre PHY with OPMDC value
PHYCFGR_OPMDC_ALLA = (7 << 3),
PHYCFGR_OPMDC_PDOWN = (6 << 3),
PHYCFGR_OPMDC_NA = (5 << 3),
PHYCFGR_OPMDC_100FA = (4 << 3),
PHYCFGR_OPMDC_100F = (3 << 3),
PHYCFGR_OPMDC_100H = (2 << 3),
PHYCFGR_OPMDC_10F = (1 << 3),
PHYCFGR_OPMDC_10H = (0 << 3),
PHYCFGR_DPX_FULL = (1 << 2),
PHYCFGR_DPX_HALF = (0 << 2),
PHYCFGR_SPD_100 = (1 << 1),
PHYCFGR_SPD_10 = (0 << 1),
PHYCFGR_LNK_ON = (1 << 0),
PHYCFGR_LNK_OFF = (0 << 0),
};
enum
{
PHY_SPEED_10 = 0, ///< Link Speed 10
PHY_SPEED_100 = 1, ///< Link Speed 100
PHY_DUPLEX_HALF = 0, ///< Link Half-Duplex
PHY_DUPLEX_FULL = 1, ///< Link Full-Duplex
PHY_LINK_OFF = 0, ///< Link Off
PHY_LINK_ON = 1, ///< Link On
PHY_POWER_NORM = 0, ///< PHY power normal mode
PHY_POWER_DOWN = 1, ///< PHY power down mode
};
/**
Set Mode Register
@param (uint8_t)mr The value to be set.
@sa getMR()
*/
inline void setMR(uint8_t mode)
{
wizchip_write(BlockSelectCReg, MR, mode);
}
/**
Get Mode Register
@return uint8_t. The value of Mode register.
@sa setMR()
*/
inline uint8_t getMR()
{
return wizchip_read(BlockSelectCReg, MR);
}
/**
Set local MAC address
@param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 bytes.
@sa getSHAR()
*/
inline void setSHAR(const uint8_t* macaddr)
{
wizchip_write_buf(BlockSelectCReg, SHAR, macaddr, 6);
}
/**
Get local MAC address
@param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 bytes.
@sa setSHAR()
*/
inline void getSHAR(uint8_t* macaddr)
{
wizchip_read_buf(BlockSelectCReg, SHAR, macaddr, 6);
}
/**
Set @ref IR register
@param (uint8_t)ir Value to set @ref IR register.
@sa getIR()
*/
inline void setIR(uint8_t ir)
{
wizchip_write(BlockSelectCReg, IR, (ir & 0xF0));
}
/**
Get @ref IR register
@return uint8_t. Value of @ref IR register.
@sa setIR()
*/
inline uint8_t getIR()
{
return wizchip_read(BlockSelectCReg, IR) & 0xF0;
}
/**
Set @ref _IMR_ register
@param (uint8_t)imr Value to set @ref _IMR_ register.
@sa getIMR()
*/
inline void setIMR(uint8_t imr)
{
wizchip_write(BlockSelectCReg, _IMR_, imr);
}
/**
Get @ref _IMR_ register
@return uint8_t. Value of @ref _IMR_ register.
@sa setIMR()
*/
inline uint8_t getIMR()
{
return wizchip_read(BlockSelectCReg, _IMR_);
}
/**
Set @ref PHYCFGR register
@param (uint8_t)phycfgr Value to set @ref PHYCFGR register.
@sa getPHYCFGR()
*/
inline void setPHYCFGR(uint8_t phycfgr)
{
wizchip_write(BlockSelectCReg, PHYCFGR, phycfgr);
}
/**
Get @ref PHYCFGR register
@return uint8_t. Value of @ref PHYCFGR register.
@sa setPHYCFGR()
*/
inline uint8_t getPHYCFGR()
{
return wizchip_read(BlockSelectCReg, PHYCFGR);
}
/**
Get @ref VERSIONR register
@return uint8_t. Value of @ref VERSIONR register.
*/
inline uint8_t getVERSIONR()
{
return wizchip_read(BlockSelectCReg, VERSIONR);
}
/**
Set @ref Sn_MR register
@param (uint8_t)mr Value to set @ref Sn_MR
@sa getSn_MR()
*/
inline void setSn_MR(uint8_t mr)
{
wizchip_write(BlockSelectSReg, Sn_MR, mr);
}
/**
Get @ref Sn_MR register
@return uint8_t. Value of @ref Sn_MR.
@sa setSn_MR()
*/
inline uint8_t getSn_MR()
{
return wizchip_read(BlockSelectSReg, Sn_MR);
}
/**
Set @ref Sn_CR register, then wait for the command to execute
@param (uint8_t)cr Value to set @ref Sn_CR
@sa getSn_CR()
*/
void setSn_CR(uint8_t cr);
/**
Get @ref Sn_CR register
@return uint8_t. Value of @ref Sn_CR.
@sa setSn_CR()
*/
inline uint8_t getSn_CR()
{
return wizchip_read(BlockSelectSReg, Sn_CR);
}
/**
Set @ref Sn_IR register
@param (uint8_t)ir Value to set @ref Sn_IR
@sa getSn_IR()
*/
inline void setSn_IR(uint8_t ir)
{
wizchip_write(BlockSelectSReg, Sn_IR, (ir & 0x1F));
}
/**
Get @ref Sn_IR register
@return uint8_t. Value of @ref Sn_IR.
@sa setSn_IR()
*/
inline uint8_t getSn_IR()
{
return (wizchip_read(BlockSelectSReg, Sn_IR) & 0x1F);
}
/**
Set @ref Sn_IMR register
@param (uint8_t)imr Value to set @ref Sn_IMR
@sa getSn_IMR()
*/
inline void setSn_IMR(uint8_t imr)
{
wizchip_write(BlockSelectSReg, Sn_IMR, (imr & 0x1F));
}
/**
Get @ref Sn_IMR register
@return uint8_t. Value of @ref Sn_IMR.
@sa setSn_IMR()
*/
inline uint8_t getSn_IMR()
{
return (wizchip_read(BlockSelectSReg, Sn_IMR) & 0x1F);
}
/**
Get @ref Sn_SR register
@return uint8_t. Value of @ref Sn_SR.
*/
inline uint8_t getSn_SR()
{
return wizchip_read(BlockSelectSReg, Sn_SR);
}
/**
Set @ref Sn_RXBUF_SIZE register
@param (uint8_t)rxbufsize Value to set @ref Sn_RXBUF_SIZE
@sa getSn_RXBUF_SIZE()
*/
inline void setSn_RXBUF_SIZE(uint8_t rxbufsize)
{
wizchip_write(BlockSelectSReg, Sn_RXBUF_SIZE, rxbufsize);
}
/**
Get @ref Sn_RXBUF_SIZE register
@return uint8_t. Value of @ref Sn_RXBUF_SIZE.
@sa setSn_RXBUF_SIZE()
*/
inline uint8_t getSn_RXBUF_SIZE()
{
return wizchip_read(BlockSelectSReg, Sn_RXBUF_SIZE);
}
/**
Set @ref Sn_TXBUF_SIZE register
@param (uint8_t)txbufsize Value to set @ref Sn_TXBUF_SIZE
@sa getSn_TXBUF_SIZE()
*/
inline void setSn_TXBUF_SIZE(uint8_t txbufsize)
{
wizchip_write(BlockSelectSReg, Sn_TXBUF_SIZE, txbufsize);
}
/**
Get @ref Sn_TXBUF_SIZE register
@return uint8_t. Value of @ref Sn_TXBUF_SIZE.
@sa setSn_TXBUF_SIZE()
*/
inline uint8_t getSn_TXBUF_SIZE()
{
return wizchip_read(BlockSelectSReg, Sn_TXBUF_SIZE);
}
/**
Get @ref Sn_TX_RD register
@return uint16_t. Value of @ref Sn_TX_RD.
*/
inline uint16_t getSn_TX_RD()
{
return wizchip_read_word(BlockSelectSReg, Sn_TX_RD);
}
/**
Set @ref Sn_TX_WR register
@param (uint16_t)txwr Value to set @ref Sn_TX_WR
@sa GetSn_TX_WR()
*/
inline void setSn_TX_WR(uint16_t txwr)
{
wizchip_write_word(BlockSelectSReg, Sn_TX_WR, txwr);
}
/**
Get @ref Sn_TX_WR register
@return uint16_t. Value of @ref Sn_TX_WR.
@sa setSn_TX_WR()
*/
inline uint16_t getSn_TX_WR()
{
return wizchip_read_word(BlockSelectSReg, Sn_TX_WR);
}
/**
Set @ref Sn_RX_RD register
@param (uint16_t)rxrd Value to set @ref Sn_RX_RD
@sa getSn_RX_RD()
*/
inline void setSn_RX_RD(uint16_t rxrd)
{
wizchip_write_word(BlockSelectSReg, Sn_RX_RD, rxrd);
}
/**
Get @ref Sn_RX_RD register
@return uint16_t. Value of @ref Sn_RX_RD.
@sa setSn_RX_RD()
*/
inline uint16_t getSn_RX_RD()
{
return wizchip_read_word(BlockSelectSReg, Sn_RX_RD);
}
/**
Get @ref Sn_RX_WR register
@return uint16_t. Value of @ref Sn_RX_WR.
*/
inline uint16_t getSn_RX_WR()
{
return wizchip_read_word(BlockSelectSReg, Sn_RX_WR);
}
};
#endif // W5500_H