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:
@ -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() {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
Submodule libraries/ESP8266SdFat updated: b240d2231a...0a46e4ebb2
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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: ");
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
#######################################
|
||||
|
@ -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 );
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
#include "include/wl_definitions.h"
|
||||
#include <wl_definitions.h>
|
||||
}
|
||||
|
||||
#include "IPAddress.h"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -23,7 +23,7 @@
|
||||
#define wifiserver_h
|
||||
|
||||
extern "C" {
|
||||
#include "include/wl_definitions.h"
|
||||
#include "wl_definitions.h"
|
||||
|
||||
struct tcp_pcb;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "include/wl_definitions.h"
|
||||
#include "wl_definitions.h"
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
}
|
||||
|
@ -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_ */
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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)))
|
||||
{
|
||||
|
@ -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
@ -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
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
|
Submodule libraries/LittleFS/lib/littlefs updated: a049f1318e...1a59954ec6
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Submodule libraries/SoftwareSerial updated: 4c08ee8d2c...adec3fe4f1
@ -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);
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
124
libraries/esp8266/examples/IramReserve/IramReserve.ino
Normal file
124
libraries/esp8266/examples/IramReserve/IramReserve.ino
Normal 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
|
116
libraries/esp8266/examples/IramReserve/ProcessKey.ino
Normal file
116
libraries/esp8266/examples/IramReserve/ProcessKey.ino
Normal 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);
|
||||
}
|
322
libraries/esp8266/examples/MMU48K/MMU48K.ino
Normal file
322
libraries/esp8266/examples/MMU48K/MMU48K.ino
Normal 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);
|
||||
}
|
16
libraries/esp8266/examples/MMU48K/timed.cpp
Normal file
16
libraries/esp8266/examples/MMU48K/timed.cpp
Normal 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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
471
libraries/esp8266/examples/irammem/irammem.ino
Normal file
471
libraries/esp8266/examples/irammem/irammem.ino
Normal 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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
#######################################
|
||||
|
111
libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino
Normal file
111
libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino
Normal 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() {
|
||||
}
|
10
libraries/lwIP_PPP/library.properties
Normal file
10
libraries/lwIP_PPP/library.properties
Normal 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
|
196
libraries/lwIP_PPP/src/PPPServer.cpp
Normal file
196
libraries/lwIP_PPP/src/PPPServer.cpp
Normal 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);
|
||||
}
|
77
libraries/lwIP_PPP/src/PPPServer.h
Normal file
77
libraries/lwIP_PPP/src/PPPServer.h
Normal 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
|
10
libraries/lwIP_enc28j60/library.properties
Normal file
10
libraries/lwIP_enc28j60/library.properties
Normal 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
|
10
libraries/lwIP_enc28j60/src/ENC28J60lwIP.h
Normal file
10
libraries/lwIP_enc28j60/src/ENC28J60lwIP.h
Normal 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
|
744
libraries/lwIP_enc28j60/src/utility/enc28j60.cpp
Normal file
744
libraries/lwIP_enc28j60/src/utility/enc28j60.cpp
Normal 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 module’s
|
||||
registers may also require configuration. The PHCON1.PDPXMD bit
|
||||
partially controls the device’s 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;
|
||||
}
|
149
libraries/lwIP_enc28j60/src/utility/enc28j60.h
Normal file
149
libraries/lwIP_enc28j60/src/utility/enc28j60.h
Normal 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 */
|
10
libraries/lwIP_w5100/library.properties
Normal file
10
libraries/lwIP_w5100/library.properties
Normal 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
|
10
libraries/lwIP_w5100/src/W5100lwIP.h
Normal file
10
libraries/lwIP_w5100/src/W5100lwIP.h
Normal 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
|
369
libraries/lwIP_w5100/src/utility/w5100.cpp
Normal file
369
libraries/lwIP_w5100/src/utility/w5100.cpp
Normal 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;
|
||||
}
|
499
libraries/lwIP_w5100/src/utility/w5100.h
Normal file
499
libraries/lwIP_w5100/src/utility/w5100.h
Normal 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
|
102
libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino
Normal file
102
libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino
Normal 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;
|
||||
}
|
10
libraries/lwIP_w5500/library.properties
Normal file
10
libraries/lwIP_w5500/library.properties
Normal 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
|
10
libraries/lwIP_w5500/src/W5500lwIP.h
Normal file
10
libraries/lwIP_w5500/src/W5500lwIP.h
Normal 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
|
442
libraries/lwIP_w5500/src/utility/w5500.cpp
Normal file
442
libraries/lwIP_w5500/src/utility/w5500.cpp
Normal 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;
|
||||
}
|
767
libraries/lwIP_w5500/src/utility/w5500.h
Normal file
767
libraries/lwIP_w5500/src/utility/w5500.h
Normal 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
|
Reference in New Issue
Block a user