Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed76b23c69 | ||
|
|
a469a466ff | ||
|
|
589d3ef4d8 | ||
|
|
5aba2eb4a1 | ||
|
|
b7df8d7b37 | ||
|
|
61554e6c7e | ||
|
|
d8cc61bc0e | ||
|
|
4b6e3956d6 | ||
|
|
9659ff8afa | ||
|
|
783955cc5d | ||
|
|
048556803b | ||
|
|
5c46939556 | ||
|
|
222d58e973 | ||
|
|
38343112a5 | ||
|
|
770a7bc4fa | ||
|
|
d6695f127d | ||
|
|
35e5fc5173 | ||
|
|
b58ea46c22 | ||
|
|
470eb64051 | ||
|
|
6192e9be72 | ||
|
|
b518451888 | ||
|
|
7b5dcdf07e | ||
|
|
515d47f055 |
29
.clang-format
Normal file
29
.clang-format
Normal file
@@ -0,0 +1,29 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignArrayOfStructures: Left
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveMacros: AcrossComments
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
ColumnLimit: 120
|
||||
ContinuationIndentWidth: 4
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
FixNamespaceComments: true
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
LambdaBodyIndentation: Signature
|
||||
MaxEmptyLinesToKeep: 1
|
||||
# PointerAlignment: Left # TODO enable this and reformat project
|
||||
QualifierAlignment: Left
|
||||
ReflowComments: true
|
||||
SeparateDefinitionBlocks: Always
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
21
.github/workflows/docs.yml
vendored
Normal file
21
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Deploy docs on GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CONFIG_FILE: mkdocs.yml
|
||||
EXTRA_PACKAGES: build-base
|
||||
REQUIREMENTS: docs/requirements.txt
|
||||
35
.github/workflows/lint.yml
vendored
Normal file
35
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Lint check
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint-clang-format:
|
||||
name: Lint with clang-format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Check code with clang-format
|
||||
uses: jidicula/clang-format-action@v4.5.0
|
||||
with:
|
||||
clang-format-version: "14"
|
||||
lint-black:
|
||||
name: Lint with black
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- name: Install test dependencies
|
||||
uses: BSFishy/pip-action@v1
|
||||
with:
|
||||
packages: |
|
||||
black
|
||||
isort
|
||||
- name: Check code with black
|
||||
run: black --check .
|
||||
- name: Check code with isort
|
||||
run: isort --profile black . --check-only
|
||||
52
README.md
52
README.md
@@ -1,13 +1,18 @@
|
||||
# LibreTuya
|
||||
|
||||
<div align="center">
|
||||
<div align="center" markdown>
|
||||
|
||||
[](https://kuba2k2.github.io/libretuya/)
|
||||

|
||||
|
||||
[](https://github.com/psf/black)
|
||||
[](https://github.com/psf/black)
|
||||
|
||||
[](https://discord.gg/SyGCB9Xwtf)
|
||||
[](https://registry.platformio.org/platforms/kuba2k2/libretuya)
|
||||

|
||||
|
||||

|
||||
|
||||
<!-- [](https://registry.platformio.org/platforms/kuba2k2/libretuya) -->
|
||||
</div>
|
||||
|
||||
PlatformIO development platform for IoT modules manufactured by Tuya Inc.
|
||||
@@ -23,9 +28,9 @@ LibreTuya also provides a common interface for all platform implementations. The
|
||||
## Usage
|
||||
|
||||
1. [Install PlatformIO](https://platformio.org/platformio-ide)
|
||||
2. `platformio platform install libretuya`
|
||||
2. `platformio platform install https://github.com/kuba2k2/libretuya`
|
||||
3. Create a project, build it and upload!
|
||||
4. See the [docs](docs/README.md) for any questions/problems.
|
||||
4. See the [docs](https://kuba2k2.github.io/libretuya/) for any questions/problems.
|
||||
|
||||
## Board List
|
||||
|
||||
@@ -91,11 +96,12 @@ A (mostly) complete* list of Tuya wireless module boards.
|
||||
|
||||
```
|
||||
arduino/
|
||||
├─ <platform name>/ Arduino Core for specific platform
|
||||
│ ├─ cores/ Core files
|
||||
│ ├─ libraries/ Supported built-in libraries
|
||||
├─ <platform name>/ Arduino Core for specific SoC
|
||||
│ ├─ cores/ Wiring core files
|
||||
│ ├─ libraries/ Supported built-in platform libraries
|
||||
├─ libretuya/
|
||||
│ ├─ api/ LibreTuya API for Arduino frameworks
|
||||
│ ├─ api/ LibreTuya API (interfaces + LT class) for Arduino frameworks
|
||||
│ ├─ common/ Units common to all platforms
|
||||
│ ├─ compat/ Fixes for compatibility with ESP32 framework
|
||||
│ ├─ libraries/ Built-in platform-independent libraries
|
||||
boards/
|
||||
@@ -109,6 +115,7 @@ builder/
|
||||
│ ├─ <platform name>-arduino.py Arduino Core build system
|
||||
├─ arduino-common.py Builder to provide ArduinoCore-API and LibreTuya APIs
|
||||
├─ main.py Main PlatformIO builder
|
||||
├─ utils.py SCons utils used during the build
|
||||
docs/ Project documentation, guides, tips, etc.
|
||||
platform/
|
||||
├─ <platform name>/ Platform-specific configurations
|
||||
@@ -133,12 +140,14 @@ Platform name | Supported MCU(s)
|
||||
### Realtek Ameba
|
||||
|
||||
The logic behind naming of Realtek chips and their series took me some time to figure out:
|
||||
|
||||
- RTL8xxxA - Ameba1/Ameba Series
|
||||
- RTL8xxxB - AmebaZ Series
|
||||
- RTL8xxxC - AmebaZ2/ZII Series
|
||||
- RTL8xxxD - AmebaD Series
|
||||
|
||||
As such, there are numerous CPUs with the same numbers but different series, which makes them require different code and SDKs.
|
||||
|
||||
- [RTL8195AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8195am)
|
||||
- RTL8710AF (found in amb1_arduino)
|
||||
- [RTL8711AM](https://www.realtek.com/en/products/communications-network-ics/item/rtl8711am)
|
||||
@@ -171,27 +180,38 @@ SoftwareSerial | ❌
|
||||
SPI | ❌
|
||||
Wire | ❌
|
||||
**OTHER LIBRARIES** |
|
||||
Wi-Fi Station | ✔️
|
||||
Wi-Fi Access Point | ✔️
|
||||
Wi-Fi Events | ❌
|
||||
Wi-Fi Client (SSL) | ✔️ (❌)
|
||||
Wi-Fi STA/AP/Mixed | ✔️
|
||||
Wi-Fi Client (SSL) | ✔️ (✔️)
|
||||
Wi-Fi Server | ✔️
|
||||
Wi-Fi Events | ❌
|
||||
IPv6 | ❌
|
||||
HTTP Client (SSL) | ✔️ (❌)
|
||||
HTTP Server | ✔️
|
||||
NVS / Preferences | ❌
|
||||
SPIFFS | ❌
|
||||
BLE | -
|
||||
HTTP | ❌
|
||||
NTP | ❌
|
||||
OTA | ❌
|
||||
MDNS | ❌
|
||||
MQTT | ❌
|
||||
MQTT | ✅
|
||||
SD | ❌
|
||||
|
||||
Legend:
|
||||
Symbols:
|
||||
|
||||
- ✔️ working
|
||||
- ❗ broken
|
||||
- ✅ tested, external library
|
||||
- ❓ untested
|
||||
- ❗ broken
|
||||
- ❌ not implemented (yet?)
|
||||
- \- not applicable
|
||||
|
||||
Names:
|
||||
|
||||
- Core functions - stuff like delay(), millis(), yield(), etc.
|
||||
- **CORE LIBRARIES** - included normally in all Arduino cores
|
||||
- **OTHER LIBRARIES** - included in ESP32 core or downloadable
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE](LICENSE). Project is licensed under MIT License.
|
||||
|
||||
228
arduino/libretuya/api/FS.cpp
Normal file
228
arduino/libretuya/api/FS.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
FS.cpp - file system wrapper
|
||||
Copyright (c) 2015 Ivan Grokhotkov. 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
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
size_t File::write(uint8_t c) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->write(&c, 1);
|
||||
}
|
||||
|
||||
time_t File::getLastWrite() {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->getLastWrite();
|
||||
}
|
||||
|
||||
size_t File::write(const uint8_t *buf, size_t size) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->write(buf, size);
|
||||
}
|
||||
|
||||
int File::available() {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->size() - _p->position();
|
||||
}
|
||||
|
||||
int File::read() {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t result;
|
||||
if (_p->read(&result, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t File::read(uint8_t *buf, size_t size) {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
return _p->read(buf, size);
|
||||
}
|
||||
|
||||
int File::peek() {
|
||||
if (!*this) {
|
||||
return -1;
|
||||
}
|
||||
size_t curPos = _p->position();
|
||||
int result = read();
|
||||
seek(curPos, SeekSet);
|
||||
return result;
|
||||
}
|
||||
|
||||
void File::flush() {
|
||||
if (!*this) {
|
||||
return;
|
||||
}
|
||||
_p->flush();
|
||||
}
|
||||
|
||||
bool File::seek(uint32_t pos, SeekMode mode) {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->seek(pos, mode);
|
||||
}
|
||||
|
||||
size_t File::position() const {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->position();
|
||||
}
|
||||
|
||||
size_t File::size() const {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->size();
|
||||
}
|
||||
|
||||
bool File::setBufferSize(size_t size) {
|
||||
if (!*this) {
|
||||
return 0;
|
||||
}
|
||||
return _p->setBufferSize(size);
|
||||
}
|
||||
|
||||
void File::close() {
|
||||
if (_p) {
|
||||
_p->close();
|
||||
_p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
File::operator bool() const {
|
||||
return _p != nullptr && *_p != false;
|
||||
}
|
||||
|
||||
const char *File::path() const {
|
||||
if (!*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return _p->path();
|
||||
}
|
||||
|
||||
const char *File::name() const {
|
||||
if (!*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return _p->name();
|
||||
}
|
||||
|
||||
// to implement
|
||||
boolean File::isDirectory(void) {
|
||||
if (!*this) {
|
||||
return false;
|
||||
}
|
||||
return _p->isDirectory();
|
||||
}
|
||||
|
||||
File File::openNextFile(const char *mode) {
|
||||
if (!*this) {
|
||||
return File();
|
||||
}
|
||||
return _p->openNextFile(mode);
|
||||
}
|
||||
|
||||
void File::rewindDirectory(void) {
|
||||
if (!*this) {
|
||||
return;
|
||||
}
|
||||
_p->rewindDirectory();
|
||||
}
|
||||
|
||||
File FS::open(const String &path, const char *mode, const bool create) {
|
||||
return open(path.c_str(), mode, create);
|
||||
}
|
||||
|
||||
File FS::open(const char *path, const char *mode, const bool create) {
|
||||
if (!_impl) {
|
||||
return File();
|
||||
}
|
||||
|
||||
return File(_impl->open(path, mode, create));
|
||||
}
|
||||
|
||||
bool FS::exists(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->exists(path);
|
||||
}
|
||||
|
||||
bool FS::exists(const String &path) {
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::remove(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->remove(path);
|
||||
}
|
||||
|
||||
bool FS::remove(const String &path) {
|
||||
return remove(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rename(const char *pathFrom, const char *pathTo) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rename(pathFrom, pathTo);
|
||||
}
|
||||
|
||||
bool FS::rename(const String &pathFrom, const String &pathTo) {
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
bool FS::mkdir(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->mkdir(path);
|
||||
}
|
||||
|
||||
bool FS::mkdir(const String &path) {
|
||||
return mkdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rmdir(const char *path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rmdir(path);
|
||||
}
|
||||
|
||||
bool FS::rmdir(const String &path) {
|
||||
return rmdir(path.c_str());
|
||||
}
|
||||
152
arduino/libretuya/api/FS.h
Normal file
152
arduino/libretuya/api/FS.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
FS.h - file system wrapper
|
||||
Copyright (c) 2015 Ivan Grokhotkov. 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <memory>
|
||||
|
||||
namespace fs {
|
||||
|
||||
#define FILE_READ "r"
|
||||
#define FILE_WRITE "w"
|
||||
#define FILE_APPEND "a"
|
||||
|
||||
class File;
|
||||
|
||||
class FileImpl;
|
||||
typedef std::shared_ptr<FileImpl> FileImplPtr;
|
||||
class FSImpl;
|
||||
typedef std::shared_ptr<FSImpl> FSImplPtr;
|
||||
|
||||
enum SeekMode { SeekSet = 0, SeekCur = 1, SeekEnd = 2 };
|
||||
|
||||
class File : public Stream {
|
||||
public:
|
||||
File(FileImplPtr p = FileImplPtr()) : _p(p) {
|
||||
_timeout = 0;
|
||||
}
|
||||
|
||||
size_t write(uint8_t) override;
|
||||
size_t write(const uint8_t *buf, size_t size) override;
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
size_t read(uint8_t *buf, size_t size);
|
||||
|
||||
size_t readBytes(char *buffer, size_t length) {
|
||||
return read((uint8_t *)buffer, length);
|
||||
}
|
||||
|
||||
bool seek(uint32_t pos, SeekMode mode);
|
||||
|
||||
bool seek(uint32_t pos) {
|
||||
return seek(pos, SeekSet);
|
||||
}
|
||||
|
||||
size_t position() const;
|
||||
size_t size() const;
|
||||
bool setBufferSize(size_t size);
|
||||
void close();
|
||||
operator bool() const;
|
||||
time_t getLastWrite();
|
||||
const char *path() const;
|
||||
const char *name() const;
|
||||
|
||||
boolean isDirectory(void);
|
||||
File openNextFile(const char *mode = FILE_READ);
|
||||
void rewindDirectory(void);
|
||||
|
||||
protected:
|
||||
FileImplPtr _p;
|
||||
};
|
||||
|
||||
class FileImpl {
|
||||
public:
|
||||
virtual ~FileImpl() {}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual size_t read(uint8_t *buf, size_t size) = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual bool seek(uint32_t pos, SeekMode mode) = 0;
|
||||
virtual size_t position() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
virtual bool setBufferSize(size_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual time_t getLastWrite() = 0;
|
||||
virtual const char *path() const = 0;
|
||||
virtual const char *name() const = 0;
|
||||
virtual boolean isDirectory(void) = 0;
|
||||
virtual FileImplPtr openNextFile(const char *mode) = 0;
|
||||
virtual void rewindDirectory(void) = 0;
|
||||
virtual operator bool() = 0;
|
||||
};
|
||||
|
||||
class FS {
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) {}
|
||||
|
||||
File open(const char *path, const char *mode = FILE_READ, const bool create = false);
|
||||
File open(const String &path, const char *mode = FILE_READ, const bool create = false);
|
||||
|
||||
bool exists(const char *path);
|
||||
bool exists(const String &path);
|
||||
|
||||
bool remove(const char *path);
|
||||
bool remove(const String &path);
|
||||
|
||||
bool rename(const char *pathFrom, const char *pathTo);
|
||||
bool rename(const String &pathFrom, const String &pathTo);
|
||||
|
||||
bool mkdir(const char *path);
|
||||
bool mkdir(const String &path);
|
||||
|
||||
bool rmdir(const char *path);
|
||||
bool rmdir(const String &path);
|
||||
|
||||
protected:
|
||||
FSImplPtr _impl;
|
||||
};
|
||||
|
||||
class FSImpl {
|
||||
public:
|
||||
FSImpl() {}
|
||||
|
||||
virtual ~FSImpl() {}
|
||||
|
||||
virtual FileImplPtr open(const char *path, const char *mode, const bool create) = 0;
|
||||
virtual bool exists(const char *path) = 0;
|
||||
virtual bool rename(const char *pathFrom, const char *pathTo) = 0;
|
||||
virtual bool remove(const char *path) = 0;
|
||||
virtual bool mkdir(const char *path) = 0;
|
||||
virtual bool rmdir(const char *path) = 0;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#ifndef FS_NO_GLOBALS
|
||||
using fs::File;
|
||||
using fs::FS;
|
||||
using fs::SeekCur;
|
||||
using fs::SeekEnd;
|
||||
using fs::SeekMode;
|
||||
using fs::SeekSet;
|
||||
#endif // FS_NO_GLOBALS
|
||||
@@ -1,3 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
IPv6Address.cpp - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. 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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Print.h>
|
||||
|
||||
#include "IPv6Address.h"
|
||||
|
||||
IPv6Address::IPv6Address()
|
||||
{
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint32_t *address)
|
||||
{
|
||||
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address& IPv6Address::operator=(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPv6Address::operator==(const uint8_t* addr) const
|
||||
{
|
||||
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||
}
|
||||
|
||||
size_t IPv6Address::printTo(Print& p) const
|
||||
{
|
||||
/* size_t n = 0;
|
||||
for(int i = 0; i < 16; i+=2) {
|
||||
if(i){
|
||||
n += p.print(':');
|
||||
}
|
||||
n += p.printf("%02x", _address.bytes[i]);
|
||||
n += p.printf("%02x", _address.bytes[i+1]);
|
||||
|
||||
}
|
||||
return n; */
|
||||
}
|
||||
|
||||
String IPv6Address::toString() const
|
||||
{
|
||||
char szRet[40];
|
||||
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
|
||||
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
|
||||
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
|
||||
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
|
||||
return String(szRet);
|
||||
}
|
||||
|
||||
bool IPv6Address::fromString(const char *address)
|
||||
{
|
||||
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
|
||||
if(strlen(address) != 39){
|
||||
return false;
|
||||
}
|
||||
char * pos = (char *)address;
|
||||
size_t i = 0;
|
||||
for(i = 0; i < 16; i+=2) {
|
||||
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){
|
||||
return false;
|
||||
}
|
||||
pos += 5;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
IPv6Address.h - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <api/String.h>
|
||||
#include <api/Print.h>
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
|
||||
namespace arduino {
|
||||
|
||||
class IPv6Address: public Printable
|
||||
{
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[16]; // IPv4 address
|
||||
uint32_t dword[4];
|
||||
} _address;
|
||||
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t* raw_address()
|
||||
{
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
IPv6Address();
|
||||
IPv6Address(const uint8_t *address);
|
||||
IPv6Address(const uint32_t *address);
|
||||
virtual ~IPv6Address() {}
|
||||
|
||||
bool fromString(const char *address);
|
||||
bool fromString(const String &address) { return fromString(address.c_str()); }
|
||||
|
||||
operator const uint8_t*() const
|
||||
{
|
||||
return _address.bytes;
|
||||
}
|
||||
operator const uint32_t*() const
|
||||
{
|
||||
return _address.dword;
|
||||
}
|
||||
bool operator==(const IPv6Address& addr) const
|
||||
{
|
||||
return (_address.dword[0] == addr._address.dword[0])
|
||||
&& (_address.dword[1] == addr._address.dword[1])
|
||||
&& (_address.dword[2] == addr._address.dword[2])
|
||||
&& (_address.dword[3] == addr._address.dword[3]);
|
||||
}
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const
|
||||
{
|
||||
return _address.bytes[index];
|
||||
}
|
||||
uint8_t& operator[](int index)
|
||||
{
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
|
||||
IPv6Address& operator=(const uint8_t *address);
|
||||
|
||||
// TODO implement printTo()
|
||||
virtual size_t printTo(Print& p) const;
|
||||
String toString() const;
|
||||
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using arduino::IPv6Address;
|
||||
17
arduino/libretuya/api/LibreTuyaAPI.cpp
Normal file
17
arduino/libretuya/api/LibreTuyaAPI.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
|
||||
|
||||
#include "LibreTuyaAPI.h"
|
||||
|
||||
__weak char *strdup(const char *s) {
|
||||
size_t len = strlen(s) + 1;
|
||||
void *newp = malloc(len);
|
||||
if (newp == NULL)
|
||||
return NULL;
|
||||
return (char *)memcpy(newp, s, len);
|
||||
}
|
||||
|
||||
String ipToString(const IPAddress &ip) {
|
||||
char szRet[16];
|
||||
sprintf(szRet, "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]);
|
||||
return String(szRet);
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
|
||||
|
||||
#pragma once
|
||||
|
||||
// LibreTuya version macros
|
||||
#ifndef LT_VERSION
|
||||
#define LT_VERSION 1.0.0
|
||||
#endif
|
||||
|
||||
#ifndef LT_BOARD
|
||||
#define LT_BOARD unknown
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define STRINGIFY_MACRO(x) STRINGIFY(x)
|
||||
#define LT_VERSION_STR STRINGIFY_MACRO(LT_VERSION)
|
||||
#define LT_BOARD_STR STRINGIFY_MACRO(LT_BOARD)
|
||||
|
||||
// Includes
|
||||
#include "LibreTuyaConfig.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "LibreTuyaConfig.h"
|
||||
|
||||
// C includes
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
@@ -27,10 +29,23 @@ extern "C" {
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
// Functional macros
|
||||
#define LT_BANNER() \
|
||||
LT_LOG( \
|
||||
LT_LEVEL_INFO, \
|
||||
"main.cpp", \
|
||||
__FUNCTION__, \
|
||||
__LINE__, \
|
||||
"LibreTuya v" LT_VERSION_STR " on " LT_BOARD_STR ", compiled at " __DATE__ " " __TIME__ \
|
||||
)
|
||||
|
||||
// ArduinoCore-API doesn't define these anymore
|
||||
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
|
||||
#define PGM_VOID_P const void *
|
||||
|
||||
// C functions
|
||||
extern char *strdup(const char *);
|
||||
|
||||
// C++ only functions
|
||||
#ifdef __cplusplus
|
||||
String ipToString(const IPAddress &ip);
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
|
||||
|
||||
#pragma once
|
||||
|
||||
// see docs/API Configuration
|
||||
@@ -21,8 +23,8 @@
|
||||
#define LT_LOGGER_TIMESTAMP 1
|
||||
#endif
|
||||
|
||||
#ifndef LT_LOGGER_FILE
|
||||
#define LT_LOGGER_FILE 0
|
||||
#ifndef LT_LOGGER_CALLER
|
||||
#define LT_LOGGER_CALLER 1
|
||||
#endif
|
||||
|
||||
#ifndef LT_LOGGER_TASK
|
||||
@@ -62,3 +64,7 @@
|
||||
#ifndef LT_DEBUG_WIFI_AP
|
||||
#define LT_DEBUG_WIFI_AP 0
|
||||
#endif
|
||||
|
||||
#ifndef LT_DEBUG_SSL
|
||||
#define LT_DEBUG_SSL 0
|
||||
#endif
|
||||
|
||||
@@ -35,12 +35,15 @@ class IWiFiClient : public Client {
|
||||
|
||||
virtual size_t write(Stream &stream) = 0;
|
||||
|
||||
size_t write_P(PGM_P buffer, size_t size) {
|
||||
return write((const uint8_t *)buffer, size);
|
||||
}
|
||||
|
||||
virtual int fd() const = 0;
|
||||
virtual int socket() = 0;
|
||||
virtual int setTimeout(uint32_t seconds) = 0;
|
||||
|
||||
virtual IWiFiClient &operator=(const IWiFiClient &other) = 0;
|
||||
virtual bool operator==(const IWiFiClient &other) const = 0;
|
||||
bool operator==(const IWiFiClient &other) const;
|
||||
|
||||
operator bool() {
|
||||
return connected();
|
||||
|
||||
@@ -22,42 +22,28 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "WiFi.h"
|
||||
#include "WiFiClient.h"
|
||||
|
||||
class IWiFiClientSecure : public IWiFiClient {
|
||||
class IWiFiClientSecure {
|
||||
public:
|
||||
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
|
||||
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
|
||||
virtual int
|
||||
connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
|
||||
virtual int
|
||||
connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey) = 0;
|
||||
virtual int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) = 0;
|
||||
virtual int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) = 0;
|
||||
|
||||
int lastError(char *buf, const size_t size);
|
||||
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||
void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex
|
||||
void setCACert(const char *rootCA);
|
||||
void setCertificate(const char *client_ca);
|
||||
void setPrivateKey(const char *private_key);
|
||||
bool loadCACert(Stream &stream, size_t size);
|
||||
bool loadCertificate(Stream &stream, size_t size);
|
||||
bool loadPrivateKey(Stream &stream, size_t size);
|
||||
bool verify(const char *fingerprint, const char *domain_name);
|
||||
void setHandshakeTimeout(unsigned long handshake_timeout);
|
||||
|
||||
WiFiClientSecure &operator=(const WiFiClientSecure &other);
|
||||
|
||||
bool operator==(const bool value) {
|
||||
return bool() == value;
|
||||
}
|
||||
|
||||
bool operator!=(const bool value) {
|
||||
return bool() != value;
|
||||
}
|
||||
|
||||
bool operator==(const WiFiClientSecure &);
|
||||
|
||||
bool operator!=(const WiFiClientSecure &rhs) {
|
||||
return !this->operator==(rhs);
|
||||
};
|
||||
|
||||
using Print::write;
|
||||
virtual int lastError(char *buf, const size_t size) = 0;
|
||||
virtual void setInsecure() = 0; // Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||
virtual void setPreSharedKey(const char *pskIdent, const char *psk) = 0; // psk in hex
|
||||
virtual void setCACert(const char *rootCA) = 0;
|
||||
virtual void setCertificate(const char *clientCA) = 0;
|
||||
virtual void setPrivateKey(const char *privateKey) = 0;
|
||||
virtual bool loadCACert(Stream &stream, size_t size) = 0;
|
||||
virtual bool loadCertificate(Stream &stream, size_t size) = 0;
|
||||
virtual bool loadPrivateKey(Stream &stream, size_t size) = 0;
|
||||
virtual bool verify(const char *fingerprint, const char *domainName) = 0;
|
||||
virtual void setHandshakeTimeout(unsigned long handshakeTimeout) = 0;
|
||||
virtual void setAlpnProtocols(const char **alpnProtocols) = 0;
|
||||
virtual bool getFingerprintSHA256(uint8_t result[32]) = 0;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,8 @@ class IWiFiServer : public Print { // arduino::Server is useless anyway
|
||||
|
||||
IWiFiServer(uint16_t port = 80, uint8_t maxClients = 4) {}
|
||||
|
||||
IWiFiServer(const IPAddress &addr, uint16_t port = 80, uint8_t maxClients = 4) {}
|
||||
|
||||
~IWiFiServer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
|
||||
|
||||
#include "lt_logger.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
@@ -40,8 +42,8 @@ const uint8_t colors[] = {
|
||||
|
||||
unsigned long millis(void);
|
||||
|
||||
#if LT_LOGGER_FILE
|
||||
void lt_log(const uint8_t level, const char *filename, const unsigned short line, const char *format, ...) {
|
||||
#if LT_LOGGER_CALLER
|
||||
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...) {
|
||||
#else
|
||||
void lt_log(const uint8_t level, const char *format, ...) {
|
||||
#endif
|
||||
@@ -85,8 +87,8 @@ void lt_log(const uint8_t level, const char *format, ...) {
|
||||
#if LT_LOGGER_COLOR
|
||||
"\e[0m"
|
||||
#endif
|
||||
#if LT_LOGGER_FILE
|
||||
"%s:%hu: "
|
||||
#if LT_LOGGER_CALLER
|
||||
"%s():%hu: "
|
||||
#endif
|
||||
#if LT_LOGGER_TASK
|
||||
"%s%c "
|
||||
@@ -106,9 +108,9 @@ void lt_log(const uint8_t level, const char *format, ...) {
|
||||
zero // append missing zeroes if printf "%11.3f" prints "0."
|
||||
#endif
|
||||
#endif
|
||||
#if LT_LOGGER_FILE
|
||||
#if LT_LOGGER_CALLER
|
||||
,
|
||||
filename,
|
||||
caller,
|
||||
line
|
||||
#endif
|
||||
#if LT_LOGGER_TASK
|
||||
|
||||
@@ -1,68 +1,131 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LibreTuyaConfig.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#if LT_LOGGER_FILE
|
||||
#define LT_LOG(level, file, line, ...) lt_log(level, file, line, __VA_ARGS__)
|
||||
void lt_log(const uint8_t level, const char *filename, const unsigned short line, const char *format, ...);
|
||||
#if LT_LOGGER_CALLER
|
||||
#define LT_LOG(level, caller, line, ...) lt_log(level, caller, line, __VA_ARGS__)
|
||||
void lt_log(const uint8_t level, const char *caller, const unsigned short line, const char *format, ...);
|
||||
#else
|
||||
#define LT_LOG(level, file, line, ...) lt_log(level, __VA_ARGS__)
|
||||
#define LT_LOG(level, caller, line, ...) lt_log(level, __VA_ARGS__)
|
||||
void lt_log(const uint8_t level, const char *format, ...);
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_TRACE >= LT_LOGLEVEL
|
||||
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_T(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#define LT_V(...) LT_LOG(LT_LEVEL_TRACE, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_T(...)
|
||||
#define LT_V(...)
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_DEBUG >= LT_LOGLEVEL
|
||||
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_D(...) LT_LOG(LT_LEVEL_DEBUG, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_D(...)
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_INFO >= LT_LOGLEVEL
|
||||
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_I(...) LT_LOG(LT_LEVEL_INFO, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_I(...)
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_WARN >= LT_LOGLEVEL
|
||||
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_W(...) LT_LOG(LT_LEVEL_WARN, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_W(...)
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_ERROR >= LT_LOGLEVEL
|
||||
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_E(...) LT_LOG(LT_LEVEL_ERROR, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_E(...)
|
||||
#endif
|
||||
|
||||
#if LT_LEVEL_FATAL >= LT_LOGLEVEL
|
||||
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LT_F(...) LT_LOG(LT_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LT_F(...)
|
||||
#endif
|
||||
|
||||
// ESP32 compat
|
||||
#define log_printf(...) LT_I(__VA_ARGS__)
|
||||
#define log_v(...) LT_V(__VA_ARGS__)
|
||||
#define log_d(...) LT_D(__VA_ARGS__)
|
||||
#define log_i(...) LT_I(__VA_ARGS__)
|
||||
#define log_w(...) LT_W(__VA_ARGS__)
|
||||
#define log_e(...) LT_E(__VA_ARGS__)
|
||||
#define log_n(...) LT_E(__VA_ARGS__)
|
||||
#define isr_log_v(...) LT_V(__VA_ARGS__)
|
||||
#define isr_log_d(...) LT_D(__VA_ARGS__)
|
||||
#define isr_log_i(...) LT_I(__VA_ARGS__)
|
||||
#define isr_log_w(...) LT_W(__VA_ARGS__)
|
||||
#define isr_log_e(...) LT_E(__VA_ARGS__)
|
||||
#define isr_log_n(...) LT_E(__VA_ARGS__)
|
||||
#define ESP_LOGV(...) LT_V(__VA_ARGS__)
|
||||
#define ESP_LOGD(...) LT_D(__VA_ARGS__)
|
||||
#define ESP_LOGI(...) LT_I(__VA_ARGS__)
|
||||
#define ESP_LOGW(...) LT_W(__VA_ARGS__)
|
||||
#define ESP_LOGE(...) LT_E(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGV(...) LT_V(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGD(...) LT_D(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGI(...) LT_I(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGW(...) LT_W(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGE(...) LT_E(__VA_ARGS__)
|
||||
|
||||
#define LT_T_MOD(module, ...) \
|
||||
do { \
|
||||
if (module) { \
|
||||
LT_T(__VA_ARGS__) \
|
||||
LT_T(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LT_D_MOD(module, ...) \
|
||||
do { \
|
||||
if (module) { \
|
||||
LT_D(__VA_ARGS__) \
|
||||
LT_D(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LT_RET(ret) \
|
||||
LT_E("ret=%d", ret); \
|
||||
return ret;
|
||||
|
||||
#define LT_RET_NZ(ret) \
|
||||
if (ret) { \
|
||||
LT_E("ret=%d", ret); \
|
||||
return ret; \
|
||||
}
|
||||
#define LT_RET_LZ(ret) \
|
||||
if (ret < 0) { \
|
||||
LT_E("ret=%d", ret); \
|
||||
return ret; \
|
||||
}
|
||||
#define LT_RET_LEZ(ret) \
|
||||
if (ret <= 0) { \
|
||||
LT_E("ret=%d", ret); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define LT_ERRNO_NZ(ret) \
|
||||
if (ret) { \
|
||||
LT_E("errno=%d, ret=%d", errno, ret); \
|
||||
return ret; \
|
||||
}
|
||||
#define LT_ERRNO_LZ(ret) \
|
||||
if (ret < 0) { \
|
||||
LT_E("errno=%d, ret=%d", errno, ret); \
|
||||
return ret; \
|
||||
}
|
||||
#define LT_ERRNO_LEZ(ret) \
|
||||
if (ret <= 0) { \
|
||||
LT_E("errno=%d, ret=%d", errno, ret); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
// WiFi.cpp
|
||||
#define LT_T_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
|
||||
#define LT_V_WG(...) LT_T_MOD(LT_DEBUG_WIFI, __VA_ARGS__)
|
||||
@@ -87,3 +150,8 @@ void lt_log(const uint8_t level, const char *format, ...);
|
||||
#define LT_T_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
|
||||
#define LT_V_WAP(...) LT_T_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
|
||||
#define LT_D_WAP(...) LT_D_MOD(LT_DEBUG_WIFI_AP, __VA_ARGS__)
|
||||
|
||||
// WiFiClientSecure.cpp & implementations
|
||||
#define LT_T_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
|
||||
#define LT_V_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__)
|
||||
#define LT_D_SSL(...) LT_D_MOD(LT_DEBUG_SSL, __VA_ARGS__)
|
||||
|
||||
55
arduino/libretuya/common/WMath.cpp
Normal file
55
arduino/libretuya/common/WMath.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void randomSeed(uint32_t dwSeed) {
|
||||
if (dwSeed != 0) {
|
||||
srand(dwSeed);
|
||||
}
|
||||
}
|
||||
|
||||
long random(long howbig) {
|
||||
if (howbig == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rand() % howbig;
|
||||
}
|
||||
|
||||
long random(long howsmall, long howbig) {
|
||||
if (howsmall >= howbig) {
|
||||
return howsmall;
|
||||
}
|
||||
|
||||
long diff = howbig - howsmall;
|
||||
|
||||
return random(diff) + howsmall;
|
||||
}
|
||||
|
||||
extern long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
extern uint16_t makeWord(uint16_t w) {
|
||||
return w;
|
||||
}
|
||||
|
||||
extern uint16_t makeWord(uint8_t h, uint8_t l) {
|
||||
return (h << 8) | l;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012 Arduino. All right reserved.
|
||||
Copyright (c) 2014 Arduino LLC. 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
|
||||
@@ -16,19 +16,21 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef RESET_H
|
||||
#define RESET_H
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
|
||||
extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__));
|
||||
|
||||
void initiateReset(int ms);
|
||||
void tickReset();
|
||||
void cancelReset();
|
||||
|
||||
#ifdef __cplusplus
|
||||
void __cxa_pure_virtual(void) {
|
||||
// We might want to write some diagnostics to uart in this case
|
||||
// std::terminate();
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
void __cxa_deleted_virtual(void) {
|
||||
// We might want to write some diagnostics to uart in this case
|
||||
// std::terminate();
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
@@ -18,16 +18,11 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef ARDUINO_AMEBA
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
|
||||
char fmt[20];
|
||||
sprintf(fmt, "%%%d.%df", width, prec);
|
||||
sprintf(sout, fmt, val);
|
||||
return sout;
|
||||
char *dtostrf(double val, signed char width, unsigned char prec, char *sout) {
|
||||
char fmt[20];
|
||||
sprintf(fmt, "%%%d.%df", width, prec);
|
||||
sprintf(sout, fmt, val);
|
||||
return sout;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012 Arduino. All right reserved.
|
||||
Copyright (c) 2015 Arduino LLC. 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
|
||||
@@ -29,12 +29,7 @@ static void __empty() {
|
||||
// Empty
|
||||
}
|
||||
|
||||
#include "cmsis_os.h"
|
||||
|
||||
void yield(void) {
|
||||
vTaskDelay(1);
|
||||
taskYIELD();
|
||||
}
|
||||
void yield(void) __attribute__((weak, alias("__empty")));
|
||||
|
||||
/**
|
||||
* SysTick hook
|
||||
@@ -46,7 +41,8 @@ static int __false() {
|
||||
// Return false
|
||||
return 0;
|
||||
}
|
||||
int sysTickHook(void) __attribute__ ((weak, alias("__false")));
|
||||
|
||||
int sysTickHook(void) __attribute__((weak, alias("__false")));
|
||||
|
||||
/**
|
||||
* SVC hook
|
||||
@@ -60,5 +56,6 @@ static void __halt() {
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
void svcHook(void) __attribute__ ((weak, alias("__halt")));
|
||||
void pendSVHook(void) __attribute__ ((weak, alias("__halt")));
|
||||
|
||||
void svcHook(void) __attribute__((weak, alias("__halt")));
|
||||
void pendSVHook(void) __attribute__((weak, alias("__halt")));
|
||||
111
arduino/libretuya/common/itoa.c
Normal file
111
arduino/libretuya/common/itoa.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright (c) 2014 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *ltoa(long value, char *string, int radix) {
|
||||
char tmp[33];
|
||||
char *tp = tmp;
|
||||
long i;
|
||||
unsigned long v;
|
||||
int sign;
|
||||
char *sp;
|
||||
|
||||
if (string == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (radix > 36 || radix <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sign = (radix == 10 && value < 0);
|
||||
if (sign) {
|
||||
v = -value;
|
||||
} else {
|
||||
v = (unsigned long)value;
|
||||
}
|
||||
|
||||
while (v || tp == tmp) {
|
||||
i = v % radix;
|
||||
v = v / radix;
|
||||
if (i < 10)
|
||||
*tp++ = i + '0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
sp = string;
|
||||
|
||||
if (sign)
|
||||
*sp++ = '-';
|
||||
while (tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
*sp = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
char *ultoa(unsigned long value, char *string, int radix) {
|
||||
char tmp[33];
|
||||
char *tp = tmp;
|
||||
long i;
|
||||
unsigned long v = value;
|
||||
char *sp;
|
||||
|
||||
if (string == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (radix > 36 || radix <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (v || tp == tmp) {
|
||||
i = v % radix;
|
||||
v = v / radix;
|
||||
if (i < 10)
|
||||
*tp++ = i + '0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
sp = string;
|
||||
|
||||
while (tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
*sp = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
char *itoa(int value, char *string, int radix) {
|
||||
return ltoa(value, string, radix);
|
||||
}
|
||||
|
||||
char *utoa(unsigned int value, char *string, int radix) {
|
||||
return ultoa(value, string, radix);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
54
arduino/libretuya/common/wiring_shift.c
Normal file
54
arduino/libretuya/common/wiring_shift.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (c) 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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint8_t shiftIn(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder) {
|
||||
uint8_t value = 0;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
digitalWrite(ulClockPin, HIGH);
|
||||
|
||||
if (ulBitOrder == LSBFIRST) {
|
||||
value |= digitalRead(ulDataPin) << i;
|
||||
} else {
|
||||
value |= digitalRead(ulDataPin) << (7 - i);
|
||||
}
|
||||
|
||||
digitalWrite(ulClockPin, LOW);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void shiftOut(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder, uint8_t ulVal) {
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (ulBitOrder == LSBFIRST) {
|
||||
digitalWrite(ulDataPin, !!(ulVal & (1 << i)));
|
||||
} else {
|
||||
digitalWrite(ulDataPin, !!(ulVal & (1 << (7 - i))));
|
||||
}
|
||||
|
||||
digitalWrite(ulClockPin, HIGH);
|
||||
digitalWrite(ulClockPin, LOW);
|
||||
}
|
||||
}
|
||||
5
arduino/libretuya/compat/FS.h
Normal file
5
arduino/libretuya/compat/FS.h
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/FS.h>
|
||||
3
arduino/libretuya/compat/FSImpl.h
Normal file
3
arduino/libretuya/compat/FSImpl.h
Normal file
@@ -0,0 +1,3 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
// nop
|
||||
@@ -1,3 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
|
||||
|
||||
#pragma once
|
||||
|
||||
// ESP32 WiFi examples use WiFiAP.h include
|
||||
|
||||
5
arduino/libretuya/compat/pgmspace.h
Normal file
5
arduino/libretuya/compat/pgmspace.h
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/deprecated-avr-comp/avr/pgmspace.h>
|
||||
3
arduino/libretuya/compat/vfs_api.h
Normal file
3
arduino/libretuya/compat/vfs_api.h
Normal file
@@ -0,0 +1,3 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
// nop
|
||||
1625
arduino/libretuya/libraries/HTTPClient/HTTPClient.cpp
Normal file
1625
arduino/libretuya/libraries/HTTPClient/HTTPClient.cpp
Normal file
File diff suppressed because it is too large
Load Diff
305
arduino/libretuya/libraries/HTTPClient/HTTPClient.h
Normal file
305
arduino/libretuya/libraries/HTTPClient/HTTPClient.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* HTTPClient.h
|
||||
*
|
||||
* Created on: 02.11.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the HTTPClient for Arduino.
|
||||
* Port to ESP32 by Evandro Luis Copercini (2017),
|
||||
* changed fingerprints to CA verification.
|
||||
*
|
||||
* 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 HTTPClient_H_
|
||||
#define HTTPClient_H_
|
||||
|
||||
#ifndef HTTPCLIENT_1_1_COMPATIBLE
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
// #include <WiFiClientSecure.h>
|
||||
#include <memory>
|
||||
|
||||
/// Cookie jar support
|
||||
#include <vector>
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
typedef enum {
|
||||
HTTP_CODE_CONTINUE = 100,
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
HTTP_CODE_PROCESSING = 102,
|
||||
HTTP_CODE_OK = 200,
|
||||
HTTP_CODE_CREATED = 201,
|
||||
HTTP_CODE_ACCEPTED = 202,
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
HTTP_CODE_NO_CONTENT = 204,
|
||||
HTTP_CODE_RESET_CONTENT = 205,
|
||||
HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
HTTP_CODE_MULTI_STATUS = 207,
|
||||
HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
HTTP_CODE_IM_USED = 226,
|
||||
HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
HTTP_CODE_FOUND = 302,
|
||||
HTTP_CODE_SEE_OTHER = 303,
|
||||
HTTP_CODE_NOT_MODIFIED = 304,
|
||||
HTTP_CODE_USE_PROXY = 305,
|
||||
HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
HTTP_CODE_BAD_REQUEST = 400,
|
||||
HTTP_CODE_UNAUTHORIZED = 401,
|
||||
HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
HTTP_CODE_FORBIDDEN = 403,
|
||||
HTTP_CODE_NOT_FOUND = 404,
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
HTTP_CODE_CONFLICT = 409,
|
||||
HTTP_CODE_GONE = 410,
|
||||
HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
HTTP_CODE_URI_TOO_LONG = 414,
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
HTTP_CODE_LOCKED = 423,
|
||||
HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
HTTP_CODE_BAD_GATEWAY = 502,
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
HTTP_CODE_LOOP_DETECTED = 508,
|
||||
HTTP_CODE_NOT_EXTENDED = 510,
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
} t_http_codes;
|
||||
|
||||
typedef enum { HTTPC_TE_IDENTITY, HTTPC_TE_CHUNKED } transferEncoding_t;
|
||||
|
||||
/**
|
||||
* redirection follow mode.
|
||||
* + `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
* + `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
* GET or HEAD methods will be redirected (using the same method),
|
||||
* since the RFC requires end-user confirmation in other cases.
|
||||
* + `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
* regardless of a used method. New request will use the same method,
|
||||
* and they will include the same body data and the same headers.
|
||||
* In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
typedef enum {
|
||||
HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
} followRedirects_t;
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
#endif
|
||||
|
||||
// cookie jar support
|
||||
typedef struct {
|
||||
String host; // host which tries to set the cookie
|
||||
time_t date; // timestamp of the response that set the cookie
|
||||
String name;
|
||||
String value;
|
||||
String domain;
|
||||
String path = "";
|
||||
|
||||
struct {
|
||||
time_t date = 0;
|
||||
bool valid = false;
|
||||
} expires;
|
||||
|
||||
struct {
|
||||
time_t duration = 0;
|
||||
bool valid = false;
|
||||
} max_age;
|
||||
|
||||
bool http_only = false;
|
||||
bool secure = false;
|
||||
} Cookie;
|
||||
|
||||
typedef std::vector<Cookie> CookieJar;
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClient
|
||||
*/
|
||||
bool begin(WiFiClient &client, String url);
|
||||
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool begin(String url);
|
||||
bool begin(String url, const char *CAcert);
|
||||
bool begin(String host, uint16_t port, String uri = "/");
|
||||
bool begin(String host, uint16_t port, String uri, const char *CAcert);
|
||||
bool begin(String host, uint16_t port, String uri, const char *CAcert, const char *cli_cert, const char *cli_key);
|
||||
#endif
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String &userAgent);
|
||||
void setAuthorization(const char *user, const char *password);
|
||||
void setAuthorization(const char *auth);
|
||||
void setAuthorizationType(const char *authType);
|
||||
void setConnectTimeout(int32_t connectTimeout);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String &url);
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int PATCH(uint8_t *payload, size_t size);
|
||||
int PATCH(String payload);
|
||||
int POST(uint8_t *payload, size_t size);
|
||||
int POST(String payload);
|
||||
int PUT(uint8_t *payload, size_t size);
|
||||
int PUT(String payload);
|
||||
int sendRequest(const char *type, String payload);
|
||||
int sendRequest(const char *type, uint8_t *payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char *type, Stream *stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String &name, const String &value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char *name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char *name); // check if header exists
|
||||
|
||||
int getSize(void);
|
||||
const String &getLocation(void);
|
||||
|
||||
WiFiClient &getStream(void);
|
||||
WiFiClient *getStreamPtr(void);
|
||||
int writeToStream(Stream *stream);
|
||||
// String getString(void);
|
||||
|
||||
static String errorToString(int error);
|
||||
|
||||
/// Cookie jar support
|
||||
void setCookieJar(CookieJar *cookieJar);
|
||||
void resetCookieJar();
|
||||
void clearAllCookies();
|
||||
|
||||
protected:
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char *expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char *type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream *stream, int len);
|
||||
|
||||
/// Cookie jar support
|
||||
void setCookie(String date, String headerValue);
|
||||
bool generateCookieString(String *cookieString);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient *_client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
int32_t _connectTimeout = -1;
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
bool _secure = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _userAgent = "ESP32HTTPClient";
|
||||
String _base64Authorization;
|
||||
String _authorizationType = "Basic";
|
||||
|
||||
/// Response handling
|
||||
RequestArgument *_currentHeaders = nullptr;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
|
||||
/// Cookie jar support
|
||||
CookieJar *_cookieJar = nullptr;
|
||||
};
|
||||
|
||||
#endif /* HTTPClient_H_ */
|
||||
7
arduino/libretuya/libraries/HTTPClient/strptime.h
Normal file
7
arduino/libretuya/libraries/HTTPClient/strptime.h
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
|
||||
extern char *strptime(const char *buf, const char *fmt, struct tm *tm);
|
||||
@@ -0,0 +1,2 @@
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
197
arduino/libretuya/libraries/HTTPClient/strptime/strptime.c
Normal file
197
arduino/libretuya/libraries/HTTPClient/strptime/strptime.c
Normal file
@@ -0,0 +1,197 @@
|
||||
#include <stdlib.h>
|
||||
#include <langinfo.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
|
||||
{
|
||||
int i, w, neg, adj, min, range, *dest, dummy;
|
||||
const char *ex;
|
||||
size_t len;
|
||||
int want_century = 0, century = 0;
|
||||
while (*f) {
|
||||
if (*f != '%') {
|
||||
if (isspace(*f)) for (; *s && isspace(*s); s++);
|
||||
else if (*s != *f) return 0;
|
||||
else s++;
|
||||
f++;
|
||||
continue;
|
||||
}
|
||||
f++;
|
||||
if (*f == '+') f++;
|
||||
if (isdigit(*f)) w=strtoul(f, (void *)&f, 10);
|
||||
else w=-1;
|
||||
adj=0;
|
||||
switch (*f++) {
|
||||
case 'a': case 'A':
|
||||
dest = &tm->tm_wday;
|
||||
min = ABDAY_1;
|
||||
range = 7;
|
||||
goto symbolic_range;
|
||||
case 'b': case 'B': case 'h':
|
||||
dest = &tm->tm_mon;
|
||||
min = ABMON_1;
|
||||
range = 12;
|
||||
goto symbolic_range;
|
||||
case 'c':
|
||||
s = strptime(s, nl_langinfo(D_T_FMT), tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'C':
|
||||
dest = ¢ury;
|
||||
if (w<0) w=2;
|
||||
want_century |= 2;
|
||||
goto numeric_digits;
|
||||
case 'd': case 'e':
|
||||
dest = &tm->tm_mday;
|
||||
min = 1;
|
||||
range = 31;
|
||||
goto numeric_range;
|
||||
case 'D':
|
||||
s = strptime(s, "%m/%d/%y", tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'H':
|
||||
dest = &tm->tm_hour;
|
||||
min = 0;
|
||||
range = 24;
|
||||
goto numeric_range;
|
||||
case 'I':
|
||||
dest = &tm->tm_hour;
|
||||
min = 1;
|
||||
range = 12;
|
||||
goto numeric_range;
|
||||
case 'j':
|
||||
dest = &tm->tm_yday;
|
||||
min = 1;
|
||||
range = 366;
|
||||
goto numeric_range;
|
||||
case 'm':
|
||||
dest = &tm->tm_mon;
|
||||
min = 1;
|
||||
range = 12;
|
||||
adj = 1;
|
||||
goto numeric_range;
|
||||
case 'M':
|
||||
dest = &tm->tm_min;
|
||||
min = 0;
|
||||
range = 60;
|
||||
goto numeric_range;
|
||||
case 'n': case 't':
|
||||
for (; *s && isspace(*s); s++);
|
||||
break;
|
||||
case 'p':
|
||||
ex = nl_langinfo(AM_STR);
|
||||
len = strlen(ex);
|
||||
if (!strncasecmp(s, ex, len)) {
|
||||
tm->tm_hour %= 12;
|
||||
break;
|
||||
}
|
||||
ex = nl_langinfo(PM_STR);
|
||||
len = strlen(ex);
|
||||
if (!strncasecmp(s, ex, len)) {
|
||||
tm->tm_hour %= 12;
|
||||
tm->tm_hour += 12;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case 'r':
|
||||
s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'R':
|
||||
s = strptime(s, "%H:%M", tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'S':
|
||||
dest = &tm->tm_sec;
|
||||
min = 0;
|
||||
range = 61;
|
||||
goto numeric_range;
|
||||
case 'T':
|
||||
s = strptime(s, "%H:%M:%S", tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'U':
|
||||
case 'W':
|
||||
/* Throw away result, for now. (FIXME?) */
|
||||
dest = &dummy;
|
||||
min = 0;
|
||||
range = 54;
|
||||
goto numeric_range;
|
||||
case 'w':
|
||||
dest = &tm->tm_wday;
|
||||
min = 0;
|
||||
range = 7;
|
||||
goto numeric_range;
|
||||
case 'x':
|
||||
s = strptime(s, nl_langinfo(D_FMT), tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'X':
|
||||
s = strptime(s, nl_langinfo(T_FMT), tm);
|
||||
if (!s) return 0;
|
||||
break;
|
||||
case 'y':
|
||||
dest = &tm->tm_year;
|
||||
w = 2;
|
||||
want_century |= 1;
|
||||
goto numeric_digits;
|
||||
case 'Y':
|
||||
dest = &tm->tm_year;
|
||||
if (w<0) w=4;
|
||||
adj = 1900;
|
||||
want_century = 0;
|
||||
goto numeric_digits;
|
||||
case '%':
|
||||
if (*s++ != '%') return 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
numeric_range:
|
||||
if (!isdigit(*s)) return 0;
|
||||
*dest = 0;
|
||||
for (i=1; i<=min+range && isdigit(*s); i*=10)
|
||||
*dest = *dest * 10 + *s++ - '0';
|
||||
if (*dest - min >= (unsigned)range) return 0;
|
||||
*dest -= adj;
|
||||
switch((char *)dest - (char *)tm) {
|
||||
case offsetof(struct tm, tm_yday):
|
||||
;
|
||||
}
|
||||
goto update;
|
||||
numeric_digits:
|
||||
neg = 0;
|
||||
if (*s == '+') s++;
|
||||
else if (*s == '-') neg=1, s++;
|
||||
if (!isdigit(*s)) return 0;
|
||||
for (*dest=i=0; i<w && isdigit(*s); i++)
|
||||
*dest = *dest * 10 + *s++ - '0';
|
||||
if (neg) *dest = -*dest;
|
||||
*dest -= adj;
|
||||
goto update;
|
||||
symbolic_range:
|
||||
for (i=2*range-1; i>=0; i--) {
|
||||
ex = nl_langinfo(min+i);
|
||||
len = strlen(ex);
|
||||
if (strncasecmp(s, ex, len)) continue;
|
||||
s += len;
|
||||
*dest = i % range;
|
||||
break;
|
||||
}
|
||||
if (i<0) return 0;
|
||||
goto update;
|
||||
update:
|
||||
//FIXME
|
||||
;
|
||||
}
|
||||
}
|
||||
if (want_century) {
|
||||
if (want_century & 2) tm->tm_year += century * 100 - 1900;
|
||||
else if (tm->tm_year <= 68) tm->tm_year += 100;
|
||||
}
|
||||
return (char *)s;
|
||||
}
|
||||
98
arduino/libretuya/libraries/NetUtils/IPv6Address.cpp
Normal file
98
arduino/libretuya/libraries/NetUtils/IPv6Address.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
IPv6Address.cpp - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. 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
|
||||
*/
|
||||
|
||||
#include "IPv6Address.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/Print.h>
|
||||
|
||||
IPv6Address::IPv6Address() {
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint8_t *address) {
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address::IPv6Address(const uint32_t *address) {
|
||||
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPv6Address &IPv6Address::operator=(const uint8_t *address) {
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPv6Address::operator==(const uint8_t *addr) const {
|
||||
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||
}
|
||||
|
||||
size_t IPv6Address::printTo(Print &p) const {
|
||||
/* size_t n = 0;
|
||||
for(int i = 0; i < 16; i+=2) {
|
||||
if(i){
|
||||
n += p.print(':');
|
||||
}
|
||||
n += p.printf("%02x", _address.bytes[i]);
|
||||
n += p.printf("%02x", _address.bytes[i+1]);
|
||||
|
||||
}
|
||||
return n; */
|
||||
}
|
||||
|
||||
String IPv6Address::toString() const {
|
||||
char szRet[40];
|
||||
sprintf(
|
||||
szRet,
|
||||
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||
_address.bytes[0],
|
||||
_address.bytes[1],
|
||||
_address.bytes[2],
|
||||
_address.bytes[3],
|
||||
_address.bytes[4],
|
||||
_address.bytes[5],
|
||||
_address.bytes[6],
|
||||
_address.bytes[7],
|
||||
_address.bytes[8],
|
||||
_address.bytes[9],
|
||||
_address.bytes[10],
|
||||
_address.bytes[11],
|
||||
_address.bytes[12],
|
||||
_address.bytes[13],
|
||||
_address.bytes[14],
|
||||
_address.bytes[15]
|
||||
);
|
||||
return String(szRet);
|
||||
}
|
||||
|
||||
bool IPv6Address::fromString(const char *address) {
|
||||
// format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
|
||||
if (strlen(address) != 39) {
|
||||
return false;
|
||||
}
|
||||
char *pos = (char *)address;
|
||||
size_t i = 0;
|
||||
for (i = 0; i < 16; i += 2) {
|
||||
if (!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos + 2, "%2hhx", &_address.bytes[i + 1])) {
|
||||
return false;
|
||||
}
|
||||
pos += 5;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
97
arduino/libretuya/libraries/NetUtils/IPv6Address.h
Normal file
97
arduino/libretuya/libraries/NetUtils/IPv6Address.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
IPv6Address.h - Base class that provides IPv6Address
|
||||
Copyright (c) 2011 Adrian McEwen. 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/Print.h>
|
||||
#include <api/String.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
|
||||
namespace arduino {
|
||||
|
||||
class IPv6Address : public Printable {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[16]; // IPv4 address
|
||||
uint32_t dword[4];
|
||||
} _address;
|
||||
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t *raw_address() {
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
IPv6Address();
|
||||
IPv6Address(const uint8_t *address);
|
||||
IPv6Address(const uint32_t *address);
|
||||
|
||||
virtual ~IPv6Address() {}
|
||||
|
||||
bool fromString(const char *address);
|
||||
|
||||
bool fromString(const String &address) {
|
||||
return fromString(address.c_str());
|
||||
}
|
||||
|
||||
operator const uint8_t *() const {
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
operator const uint32_t *() const {
|
||||
return _address.dword;
|
||||
}
|
||||
|
||||
bool operator==(const IPv6Address &addr) const {
|
||||
return (_address.dword[0] == addr._address.dword[0]) && (_address.dword[1] == addr._address.dword[1]) &&
|
||||
(_address.dword[2] == addr._address.dword[2]) && (_address.dword[3] == addr._address.dword[3]);
|
||||
}
|
||||
|
||||
bool operator==(const uint8_t *addr) const;
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
uint8_t &operator[](int index) {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
|
||||
IPv6Address &operator=(const uint8_t *address);
|
||||
|
||||
// TODO implement printTo()
|
||||
virtual size_t printTo(Print &p) const;
|
||||
String toString() const;
|
||||
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
};
|
||||
|
||||
} // namespace arduino
|
||||
|
||||
using arduino::IPv6Address;
|
||||
@@ -19,7 +19,7 @@ size_t LwIPRxBuffer::r_available() {
|
||||
return 0;
|
||||
}
|
||||
uint16_t count = 0;
|
||||
int res = lwip_ioctl(_sock, FIONREAD, &count);
|
||||
int res = lwip_ioctl(_sock, FIONREAD, &count);
|
||||
if (res < 0) {
|
||||
_failed = true;
|
||||
return 0;
|
||||
5
arduino/libretuya/libraries/NetUtils/api/IPv6Address.h
Normal file
5
arduino/libretuya/libraries/NetUtils/api/IPv6Address.h
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IPv6Address.h"
|
||||
443
arduino/libretuya/libraries/NetUtils/ssl/MbedTLSClient.cpp
Normal file
443
arduino/libretuya/libraries/NetUtils/ssl/MbedTLSClient.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#include "MbedTLSClient.h"
|
||||
|
||||
#include <IPAddress.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <mbedtls/debug.h>
|
||||
#include <mbedtls/platform.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
MbedTLSClient::MbedTLSClient() : WiFiClient() {}
|
||||
|
||||
MbedTLSClient::MbedTLSClient(int sock) : WiFiClient(sock) {}
|
||||
|
||||
void MbedTLSClient::stop() {
|
||||
WiFiClient::stop();
|
||||
LT_V_SSL("Closing SSL connection");
|
||||
|
||||
if (_sslCfg.ca_chain) {
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
}
|
||||
if (_sslCfg.key_cert) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
mbedtls_pk_free(&_clientKey);
|
||||
}
|
||||
mbedtls_ssl_free(&_sslCtx);
|
||||
mbedtls_ssl_config_free(&_sslCfg);
|
||||
}
|
||||
|
||||
void MbedTLSClient::init() {
|
||||
// Realtek AmbZ: init platform here to ensure HW crypto is initialized in ssl_init
|
||||
mbedtls_platform_set_calloc_free(calloc, free);
|
||||
mbedtls_ssl_init(&_sslCtx);
|
||||
mbedtls_ssl_config_init(&_sslCfg);
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
return connect(ipToString(ip).c_str(), port, timeout) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(const char *host, uint16_t port, int32_t timeout) {
|
||||
if (_pskIdentStr && _pskStr)
|
||||
return connect(host, port, NULL, NULL, NULL, _pskIdentStr, _pskStr, _alpnProtocols) == 0;
|
||||
return connect(host, port, _caCertStr, _clientCertStr, _clientKeyStr, NULL, NULL, _alpnProtocols) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
|
||||
) {
|
||||
return connect(ipToString(ip).c_str(), port, rootCABuf, clientCert, clientKey, NULL, NULL, _alpnProtocols) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey
|
||||
) {
|
||||
return connect(host, port, rootCABuf, clientCert, clientKey, NULL, NULL, _alpnProtocols) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk) {
|
||||
return connect(ipToString(ip).c_str(), port, NULL, NULL, NULL, pskIdent, psk, _alpnProtocols) == 0;
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(const char *host, uint16_t port, const char *pskIdent, const char *psk) {
|
||||
return connect(host, port, NULL, NULL, NULL, pskIdent, psk, _alpnProtocols) == 0;
|
||||
}
|
||||
|
||||
static int ssl_random(void *data, unsigned char *output, size_t len) {
|
||||
int *buf = (int *)output;
|
||||
size_t i;
|
||||
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
|
||||
buf[i++] = rand();
|
||||
}
|
||||
if (len) {
|
||||
int rem = rand();
|
||||
unsigned char *pRem = (unsigned char *)&rem;
|
||||
memcpy(output + i * sizeof(int), pRem, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void debug_cb(void *ctx, int level, const char *file, int line, const char *str) {
|
||||
LT_I("%04d: |%d| %s", line, level, str);
|
||||
}
|
||||
|
||||
int MbedTLSClient::connect(
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
const char *rootCABuf,
|
||||
const char *clientCert,
|
||||
const char *clientKey,
|
||||
const char *pskIdent,
|
||||
const char *psk,
|
||||
const char **alpnProtocols
|
||||
) {
|
||||
LT_D_SSL("Free heap before TLS: TODO");
|
||||
|
||||
if (!rootCABuf && !pskIdent && !psk && !_insecure && !_useRootCA)
|
||||
return -1;
|
||||
|
||||
IPAddress addr = WiFi.hostByName(host);
|
||||
if (!(uint32_t)addr)
|
||||
return -1;
|
||||
|
||||
int ret = WiFiClient::connect(addr, port, _timeout);
|
||||
if (ret < 0) {
|
||||
LT_E("SSL socket failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *uid = "lt-ssl"; // TODO
|
||||
|
||||
LT_V_SSL("Init SSL");
|
||||
init();
|
||||
|
||||
// mbedtls_debug_set_threshold(4);
|
||||
// mbedtls_ssl_conf_dbg(&_sslCfg, debug_cb, NULL);
|
||||
|
||||
ret = mbedtls_ssl_config_defaults(
|
||||
&_sslCfg,
|
||||
MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT
|
||||
);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
#ifdef MBEDTLS_SSL_ALPN
|
||||
if (alpnProtocols) {
|
||||
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, alpnProtocols);
|
||||
LT_RET_NZ(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_insecure) {
|
||||
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_NONE);
|
||||
} else if (rootCABuf) {
|
||||
mbedtls_x509_crt_init(&_caCert);
|
||||
mbedtls_ssl_conf_authmode(&_sslCfg, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
ret = mbedtls_x509_crt_parse(&_caCert, (const unsigned char *)rootCABuf, strlen(rootCABuf) + 1);
|
||||
mbedtls_ssl_conf_ca_chain(&_sslCfg, &_caCert, NULL);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
} else if (_useRootCA) {
|
||||
return -1; // not implemented
|
||||
} else if (pskIdent && psk) {
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED
|
||||
uint16_t len = strlen(psk);
|
||||
if ((len & 1) != 0 || len > 2 * MBEDTLS_PSK_MAX_LEN) {
|
||||
LT_E("PSK length invalid");
|
||||
return -1;
|
||||
}
|
||||
unsigned char pskBin[MBEDTLS_PSK_MAX_LEN] = {};
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t c = psk[i];
|
||||
c |= 0b00100000; // make lowercase
|
||||
c -= '0' * (c >= '0' && c <= '9');
|
||||
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
|
||||
if (c > 0xf)
|
||||
return -1;
|
||||
pskBin[i / 2] |= c << (4 * ((i & 1) ^ 1));
|
||||
}
|
||||
ret = mbedtls_ssl_conf_psk(&_sslCfg, pskBin, len / 2, (const unsigned char *)pskIdent, strlen(pskIdent));
|
||||
LT_RET_NZ(ret);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!_insecure && clientCert && clientKey) {
|
||||
mbedtls_x509_crt_init(&_clientCert);
|
||||
mbedtls_pk_init(&_clientKey);
|
||||
LT_V_SSL("Loading client cert");
|
||||
ret = mbedtls_x509_crt_parse(&_clientCert, (const unsigned char *)clientCert, strlen(clientCert) + 1);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
LT_V_SSL("Loading private key");
|
||||
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)clientKey, strlen(clientKey) + 1, NULL, 0);
|
||||
if (ret < 0) {
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
LT_RET(ret);
|
||||
}
|
||||
mbedtls_ssl_conf_own_cert(&_sslCfg, &_clientCert, &_clientKey);
|
||||
}
|
||||
|
||||
LT_V_SSL("Setting TLS hostname");
|
||||
ret = mbedtls_ssl_set_hostname(&_sslCtx, host);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
mbedtls_ssl_conf_rng(&_sslCfg, ssl_random, NULL);
|
||||
ret = mbedtls_ssl_setup(&_sslCtx, &_sslCfg);
|
||||
LT_RET_NZ(ret);
|
||||
|
||||
_sockTls = fd();
|
||||
mbedtls_ssl_set_bio(&_sslCtx, &_sockTls, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
|
||||
LT_V_SSL("SSL handshake");
|
||||
if (_handshakeTimeout == 0)
|
||||
_handshakeTimeout = _timeout * 1000;
|
||||
unsigned long start = millis();
|
||||
while (ret = mbedtls_ssl_handshake(&_sslCtx)) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
LT_RET(ret);
|
||||
}
|
||||
if ((millis() - start) > _handshakeTimeout) {
|
||||
LT_E("SSL handshake timeout");
|
||||
return -1;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
|
||||
if (clientCert && clientKey) {
|
||||
LT_D_SSL(
|
||||
"Protocol %s, ciphersuite %s",
|
||||
mbedtls_ssl_get_version(&_sslCtx),
|
||||
mbedtls_ssl_get_ciphersuite(&_sslCtx)
|
||||
);
|
||||
ret = mbedtls_ssl_get_record_expansion(&_sslCtx);
|
||||
if (ret >= 0)
|
||||
LT_D_SSL("Record expansion: %d", ret);
|
||||
else {
|
||||
LT_W("Record expansion unknown");
|
||||
}
|
||||
}
|
||||
|
||||
LT_V_SSL("Verifying certificate");
|
||||
ret = mbedtls_ssl_get_verify_result(&_sslCtx);
|
||||
if (ret) {
|
||||
char buf[512];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", ret);
|
||||
LT_E("Failed to verify peer certificate! Verification info: %s", buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rootCABuf)
|
||||
mbedtls_x509_crt_free(&_caCert);
|
||||
if (clientCert)
|
||||
mbedtls_x509_crt_free(&_clientCert);
|
||||
if (clientKey != NULL)
|
||||
mbedtls_pk_free(&_clientKey);
|
||||
return 0; // OK
|
||||
}
|
||||
|
||||
size_t MbedTLSClient::write(const uint8_t *buf, size_t size) {
|
||||
int ret = -1;
|
||||
while ((ret = mbedtls_ssl_write(&_sslCtx, buf, size)) <= 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
|
||||
LT_RET(ret);
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int MbedTLSClient::available() {
|
||||
bool peeked = _peeked >= 0;
|
||||
if (!connected())
|
||||
return peeked;
|
||||
|
||||
int ret = mbedtls_ssl_read(&_sslCtx, NULL, 0);
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
|
||||
stop();
|
||||
return peeked ? peeked : ret;
|
||||
}
|
||||
return mbedtls_ssl_get_bytes_avail(&_sslCtx) + peeked;
|
||||
}
|
||||
|
||||
int MbedTLSClient::read(uint8_t *buf, size_t size) {
|
||||
bool peeked = false;
|
||||
int toRead = available();
|
||||
if ((!buf && size) || toRead <= 0)
|
||||
return -1;
|
||||
if (!size)
|
||||
return 0;
|
||||
if (_peeked >= 0) {
|
||||
buf[0] = _peeked;
|
||||
_peeked = -1;
|
||||
size--;
|
||||
toRead--;
|
||||
if (!size || !toRead)
|
||||
return 1;
|
||||
buf++;
|
||||
peeked = true;
|
||||
}
|
||||
|
||||
int ret = mbedtls_ssl_read(&_sslCtx, buf, size);
|
||||
if (ret < 0) {
|
||||
stop();
|
||||
return peeked ? peeked : ret;
|
||||
}
|
||||
return ret + peeked;
|
||||
}
|
||||
|
||||
int MbedTLSClient::peek() {
|
||||
if (_peeked >= 0)
|
||||
return _peeked;
|
||||
_peeked = timedRead();
|
||||
return _peeked;
|
||||
}
|
||||
|
||||
void MbedTLSClient::flush() {}
|
||||
|
||||
int MbedTLSClient::lastError(char *buf, const size_t size) {
|
||||
return 0; // TODO (?)
|
||||
}
|
||||
|
||||
void MbedTLSClient::setInsecure() {
|
||||
_caCertStr = NULL;
|
||||
_clientCertStr = NULL;
|
||||
_clientKeyStr = NULL;
|
||||
_pskIdentStr = NULL;
|
||||
_pskStr = NULL;
|
||||
_insecure = true;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setPreSharedKey(const char *pskIdent, const char *psk) {
|
||||
_pskIdentStr = pskIdent;
|
||||
_pskStr = psk;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setCACert(const char *rootCA) {
|
||||
_caCertStr = rootCA;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setCertificate(const char *clientCA) {
|
||||
_clientCertStr = clientCA;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setPrivateKey(const char *privateKey) {
|
||||
_clientKeyStr = privateKey;
|
||||
}
|
||||
|
||||
char *streamToStr(Stream &stream, size_t size) {
|
||||
char *buf = (char *)malloc(size + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
if (size != stream.readBytes(buf, size)) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadCACert(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_caCertStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadCertificate(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_clientCertStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::loadPrivateKey(Stream &stream, size_t size) {
|
||||
char *str = streamToStr(stream, size);
|
||||
if (str) {
|
||||
_clientKeyStr = str;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::verify(const char *fingerprint, const char *domainName) {
|
||||
uint8_t fpLocal[32] = {};
|
||||
uint16_t len = strlen(fingerprint);
|
||||
uint8_t byte = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t c = fingerprint[i];
|
||||
while ((c == ' ' || c == ':') && i < len) {
|
||||
c = fingerprint[++i];
|
||||
}
|
||||
c |= 0b00100000; // make lowercase
|
||||
c -= '0' * (c >= '0' && c <= '9');
|
||||
c -= ('a' - 10) * (c >= 'a' && c <= 'z');
|
||||
if (c > 0xf)
|
||||
return -1;
|
||||
fpLocal[byte / 2] |= c << (4 * ((byte & 1) ^ 1));
|
||||
byte++;
|
||||
if (byte >= 64)
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t fpRemote[32];
|
||||
if (!getFingerprintSHA256(fpRemote))
|
||||
return false;
|
||||
|
||||
if (memcmp(fpLocal, fpRemote, 32)) {
|
||||
LT_D_SSL("Fingerprints don't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!domainName)
|
||||
return true;
|
||||
// TODO domain name verification
|
||||
return true;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setHandshakeTimeout(unsigned long handshakeTimeout) {
|
||||
_handshakeTimeout = handshakeTimeout * 1000;
|
||||
}
|
||||
|
||||
void MbedTLSClient::setAlpnProtocols(const char **alpnProtocols) {
|
||||
_alpnProtocols = alpnProtocols;
|
||||
}
|
||||
|
||||
bool MbedTLSClient::getFingerprintSHA256(uint8_t result[32]) {
|
||||
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&_sslCtx);
|
||||
if (!cert) {
|
||||
LT_E("Failed to get peer certificate");
|
||||
return false;
|
||||
}
|
||||
mbedtls_sha256_context shaCtx;
|
||||
mbedtls_sha256_init(&shaCtx);
|
||||
mbedtls_sha256_starts(&shaCtx, false);
|
||||
mbedtls_sha256_update(&shaCtx, cert->raw.p, cert->raw.len);
|
||||
mbedtls_sha256_finish(&shaCtx, result);
|
||||
return true;
|
||||
}
|
||||
90
arduino/libretuya/libraries/NetUtils/ssl/MbedTLSClient.h
Normal file
90
arduino/libretuya/libraries/NetUtils/ssl/MbedTLSClient.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/WiFiClient.h>
|
||||
#include <api/WiFiClientSecure.h>
|
||||
|
||||
#include <WiFiClient.h> // extend platform's WiFiClient impl
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <mbedtls/net.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
class MbedTLSClient : public WiFiClient, public IWiFiClientSecure {
|
||||
private:
|
||||
mbedtls_ssl_context _sslCtx;
|
||||
mbedtls_ssl_config _sslCfg;
|
||||
mbedtls_x509_crt _caCert;
|
||||
mbedtls_x509_crt _clientCert;
|
||||
mbedtls_pk_context _clientKey;
|
||||
uint32_t _handshakeTimeout = 0;
|
||||
|
||||
void init();
|
||||
int _sockTls = -1;
|
||||
bool _insecure = false;
|
||||
bool _useRootCA = false;
|
||||
int _peeked = -1;
|
||||
|
||||
const char *_caCertStr;
|
||||
const char *_clientCertStr;
|
||||
const char *_clientKeyStr;
|
||||
const char *_pskIdentStr;
|
||||
const char *_pskStr;
|
||||
const char **_alpnProtocols;
|
||||
|
||||
int connect(
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
const char *rootCABuf,
|
||||
const char *clientCert,
|
||||
const char *clientKey,
|
||||
const char *pskIdent,
|
||||
const char *psk,
|
||||
const char **alpnProtocols
|
||||
);
|
||||
|
||||
public:
|
||||
MbedTLSClient();
|
||||
MbedTLSClient(int sock);
|
||||
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout);
|
||||
|
||||
int connect(IPAddress ip, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
|
||||
int connect(const char *host, uint16_t port, const char *rootCABuf, const char *clientCert, const char *clientKey);
|
||||
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psk);
|
||||
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psk);
|
||||
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
|
||||
int available();
|
||||
|
||||
int read(uint8_t *buf, size_t size);
|
||||
int peek();
|
||||
void flush();
|
||||
void stop();
|
||||
|
||||
int lastError(char *buf, const size_t size);
|
||||
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||
void setPreSharedKey(const char *pskIdent, const char *psk); // psk in hex
|
||||
void setCACert(const char *rootCA);
|
||||
void setCertificate(const char *clientCA);
|
||||
void setPrivateKey(const char *privateKey);
|
||||
bool loadCACert(Stream &stream, size_t size);
|
||||
bool loadCertificate(Stream &stream, size_t size);
|
||||
bool loadPrivateKey(Stream &stream, size_t size);
|
||||
bool verify(const char *fingerprint, const char *domainName);
|
||||
void setHandshakeTimeout(unsigned long handshakeTimeout);
|
||||
void setAlpnProtocols(const char **alpnProtocols);
|
||||
bool getFingerprintSHA256(uint8_t result[32]);
|
||||
|
||||
using WiFiClient::connect;
|
||||
using WiFiClient::read;
|
||||
};
|
||||
56
arduino/libretuya/libraries/WebServer/HTTP_Method.h
Normal file
56
arduino/libretuya/libraries/WebServer/HTTP_Method.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M - SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
/* icecast */ \
|
||||
XX(33, SOURCE, SOURCE)
|
||||
|
||||
enum http_method {
|
||||
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
typedef enum http_method HTTPMethod;
|
||||
#define HTTP_ANY (HTTPMethod)(255)
|
||||
605
arduino/libretuya/libraries/WebServer/Parsing.cpp
Normal file
605
arduino/libretuya/libraries/WebServer/Parsing.cpp
Normal file
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
Parsing.cpp - HTTP request parsing.
|
||||
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
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
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "WebServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiServer.h"
|
||||
#include "detail/mimetable.h"
|
||||
|
||||
#ifndef WEBSERVER_MAX_POST_ARGS
|
||||
#define WEBSERVER_MAX_POST_ARGS 32
|
||||
#endif
|
||||
|
||||
#define __STR(a) #a
|
||||
#define _STR(a) __STR(a)
|
||||
const char *_http_method_str[] = {
|
||||
#define XX(num, name, string) _STR(name),
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
static const char Content_Type[] PROGMEM = "Content-Type";
|
||||
static const char filename[] PROGMEM = "filename";
|
||||
|
||||
static char *readBytesWithTimeout(WiFiClient &client, size_t maxLength, size_t &dataLength, int timeout_ms) {
|
||||
char *buf = nullptr;
|
||||
dataLength = 0;
|
||||
while (dataLength < maxLength) {
|
||||
int tries = timeout_ms;
|
||||
size_t newLength;
|
||||
while (!(newLength = client.available()) && tries--)
|
||||
delay(1);
|
||||
if (!newLength) {
|
||||
break;
|
||||
}
|
||||
if (!buf) {
|
||||
buf = (char *)malloc(newLength + 1);
|
||||
if (!buf) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
char *newBuf = (char *)realloc(buf, dataLength + newLength + 1);
|
||||
if (!newBuf) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
buf = newBuf;
|
||||
}
|
||||
client.readBytes(buf + dataLength, newLength);
|
||||
dataLength += newLength;
|
||||
buf[dataLength] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool WebServer::_parseRequest(WiFiClient &client) {
|
||||
// Read the first line of HTTP request
|
||||
String req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
// reset header value
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
_currentHeaders[i].value = String();
|
||||
}
|
||||
|
||||
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||
// Retrieve the "/path" part by finding the spaces
|
||||
int addr_start = req.indexOf(' ');
|
||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||
if (addr_start == -1 || addr_end == -1) {
|
||||
log_e("Invalid request: %s", req.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String methodStr = req.substring(0, addr_start);
|
||||
String url = req.substring(addr_start + 1, addr_end);
|
||||
String versionEnd = req.substring(addr_end + 8);
|
||||
_currentVersion = atoi(versionEnd.c_str());
|
||||
String searchStr = "";
|
||||
int hasSearch = url.indexOf('?');
|
||||
if (hasSearch != -1) {
|
||||
searchStr = url.substring(hasSearch + 1);
|
||||
url = url.substring(0, hasSearch);
|
||||
}
|
||||
_currentUri = url;
|
||||
_chunked = false;
|
||||
|
||||
HTTPMethod method = HTTP_ANY;
|
||||
size_t num_methods = sizeof(_http_method_str) / sizeof(const char *);
|
||||
for (size_t i = 0; i < num_methods; i++) {
|
||||
if (methodStr == _http_method_str[i]) {
|
||||
method = (HTTPMethod)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (method == HTTP_ANY) {
|
||||
log_e("Unknown HTTP Method: %s", methodStr.c_str());
|
||||
return false;
|
||||
}
|
||||
_currentMethod = method;
|
||||
|
||||
log_v("method: %s url: %s search: %s", methodStr.c_str(), url.c_str(), searchStr.c_str());
|
||||
|
||||
// attach handler
|
||||
RequestHandler *handler;
|
||||
for (handler = _firstHandler; handler; handler = handler->next()) {
|
||||
if (handler->canHandle(_currentMethod, _currentUri))
|
||||
break;
|
||||
}
|
||||
_currentHandler = handler;
|
||||
|
||||
String formData;
|
||||
// below is needed only when POST type request
|
||||
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE) {
|
||||
String boundaryStr;
|
||||
String headerName;
|
||||
String headerValue;
|
||||
bool isForm = false;
|
||||
bool isEncoded = false;
|
||||
uint32_t contentLength = 0;
|
||||
// parse headers
|
||||
while (1) {
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "")
|
||||
break; // no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1) {
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 1);
|
||||
headerValue.trim();
|
||||
_collectHeader(headerName.c_str(), headerValue.c_str());
|
||||
|
||||
log_v("headerName: %s", headerName.c_str());
|
||||
log_v("headerValue: %s", headerValue.c_str());
|
||||
|
||||
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))) {
|
||||
using namespace mime;
|
||||
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))) {
|
||||
isForm = false;
|
||||
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))) {
|
||||
isForm = false;
|
||||
isEncoded = true;
|
||||
} else if (headerValue.startsWith(F("multipart/"))) {
|
||||
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
|
||||
boundaryStr.replace("\"", "");
|
||||
isForm = true;
|
||||
}
|
||||
} else if (headerName.equalsIgnoreCase(F("Content-Length"))) {
|
||||
contentLength = headerValue.toInt();
|
||||
} else if (headerName.equalsIgnoreCase(F("Host"))) {
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isForm) {
|
||||
size_t plainLength;
|
||||
char *plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
|
||||
if (plainLength < contentLength) {
|
||||
free(plainBuf);
|
||||
return false;
|
||||
}
|
||||
if (contentLength > 0) {
|
||||
if (isEncoded) {
|
||||
// url encoded form
|
||||
if (searchStr != "")
|
||||
searchStr += '&';
|
||||
searchStr += plainBuf;
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
if (!isEncoded) {
|
||||
// plain post json or other data
|
||||
RequestArgument &arg = _currentArgs[_currentArgCount++];
|
||||
arg.key = F("plain");
|
||||
arg.value = String(plainBuf);
|
||||
}
|
||||
|
||||
log_v("Plain: %s", plainBuf);
|
||||
free(plainBuf);
|
||||
} else {
|
||||
// No content - but we can still have arguments in the URL.
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (isForm) {
|
||||
_parseArguments(searchStr);
|
||||
if (!_parseForm(client, boundaryStr, contentLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String headerName;
|
||||
String headerValue;
|
||||
// parse headers
|
||||
while (1) {
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "")
|
||||
break; // no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1) {
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 2);
|
||||
_collectHeader(headerName.c_str(), headerValue.c_str());
|
||||
|
||||
log_v("headerName: %s", headerName.c_str());
|
||||
log_v("headerValue: %s", headerValue.c_str());
|
||||
|
||||
if (headerName.equalsIgnoreCase("Host")) {
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
client.flush();
|
||||
|
||||
log_v("Request: %s", url.c_str());
|
||||
log_v(" Arguments: %s", searchStr.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebServer::_collectHeader(const char *headerName, const char *headerValue) {
|
||||
for (int i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
|
||||
_currentHeaders[i].value = headerValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebServer::_parseArguments(String data) {
|
||||
log_v("args: %s", data.c_str());
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = 0;
|
||||
if (data.length() == 0) {
|
||||
_currentArgCount = 0;
|
||||
_currentArgs = new RequestArgument[1];
|
||||
return;
|
||||
}
|
||||
_currentArgCount = 1;
|
||||
|
||||
for (int i = 0; i < (int)data.length();) {
|
||||
i = data.indexOf('&', i);
|
||||
if (i == -1)
|
||||
break;
|
||||
++i;
|
||||
++_currentArgCount;
|
||||
}
|
||||
log_v("args count: %d", _currentArgCount);
|
||||
|
||||
_currentArgs = new RequestArgument[_currentArgCount + 1];
|
||||
int pos = 0;
|
||||
int iarg;
|
||||
for (iarg = 0; iarg < _currentArgCount;) {
|
||||
int equal_sign_index = data.indexOf('=', pos);
|
||||
int next_arg_index = data.indexOf('&', pos);
|
||||
log_v("pos %d =@%d &@%d", pos, equal_sign_index, next_arg_index);
|
||||
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
|
||||
log_e("arg missing value: %d", iarg);
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
continue;
|
||||
}
|
||||
RequestArgument &arg = _currentArgs[iarg];
|
||||
arg.key = urlDecode(data.substring(pos, equal_sign_index));
|
||||
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
|
||||
log_v("arg %d key: %s value: %s", iarg, arg.key.c_str(), arg.value.c_str());
|
||||
++iarg;
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
log_v("args count: %d", _currentArgCount);
|
||||
}
|
||||
|
||||
void WebServer::_uploadWriteByte(uint8_t b) {
|
||||
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) {
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->totalSize += _currentUpload->currentSize;
|
||||
_currentUpload->currentSize = 0;
|
||||
}
|
||||
_currentUpload->buf[_currentUpload->currentSize++] = b;
|
||||
}
|
||||
|
||||
int WebServer::_uploadReadByte(WiFiClient &client) {
|
||||
int res = client.read();
|
||||
if (res < 0) {
|
||||
// keep trying until you either read a valid byte or timeout
|
||||
unsigned long startMillis = millis();
|
||||
long timeoutIntervalMillis = client.getTimeout();
|
||||
boolean timedOut = false;
|
||||
for (;;) {
|
||||
if (!client.connected())
|
||||
return -1;
|
||||
// loosely modeled after blinkWithoutDelay pattern
|
||||
while (!timedOut && !client.available() && client.connected()) {
|
||||
delay(2);
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
}
|
||||
|
||||
res = client.read();
|
||||
if (res >= 0) {
|
||||
return res; // exit on a valid read
|
||||
}
|
||||
// NOTE: it is possible to get here and have all of the following
|
||||
// assertions hold true
|
||||
//
|
||||
// -- client.available() > 0
|
||||
// -- client.connected == true
|
||||
// -- res == -1
|
||||
//
|
||||
// a simple retry strategy overcomes this which is to say the
|
||||
// assertion is not permanent, but the reason that this works
|
||||
// is elusive, and possibly indicative of a more subtle underlying
|
||||
// issue
|
||||
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
if (timedOut) {
|
||||
return res; // exit on a timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool WebServer::_parseForm(WiFiClient &client, String boundary, uint32_t len) {
|
||||
(void)len;
|
||||
log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len);
|
||||
String line;
|
||||
int retry = 0;
|
||||
do {
|
||||
line = client.readStringUntil('\r');
|
||||
++retry;
|
||||
} while (line.length() == 0 && retry < 3);
|
||||
|
||||
client.readStringUntil('\n');
|
||||
// start reading the form
|
||||
if (line == ("--" + boundary)) {
|
||||
if (_postArgs)
|
||||
delete[] _postArgs;
|
||||
_postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS];
|
||||
_postArgsLen = 0;
|
||||
while (1) {
|
||||
String argName;
|
||||
String argValue;
|
||||
String argType;
|
||||
String argFilename;
|
||||
bool argIsFile = false;
|
||||
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) {
|
||||
int nameStart = line.indexOf('=');
|
||||
if (nameStart != -1) {
|
||||
argName = line.substring(nameStart + 2);
|
||||
nameStart = argName.indexOf('=');
|
||||
if (nameStart == -1) {
|
||||
argName = argName.substring(0, argName.length() - 1);
|
||||
} else {
|
||||
argFilename = argName.substring(nameStart + 2, argName.length() - 1);
|
||||
argName = argName.substring(0, argName.indexOf('"'));
|
||||
argIsFile = true;
|
||||
log_v("PostArg FileName: %s", argFilename.c_str());
|
||||
// use GET to set the filename if uploading using blob
|
||||
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
|
||||
argFilename = arg(FPSTR(filename));
|
||||
}
|
||||
log_v("PostArg Name: %s", argName.c_str());
|
||||
using namespace mime;
|
||||
argType = FPSTR(mimeTable[txt].mimeType);
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) {
|
||||
argType = line.substring(line.indexOf(':') + 2);
|
||||
// skip next line
|
||||
client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
}
|
||||
log_v("PostArg Type: %s", argType.c_str());
|
||||
if (!argIsFile) {
|
||||
while (1) {
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.startsWith("--" + boundary))
|
||||
break;
|
||||
if (argValue.length() > 0)
|
||||
argValue += "\n";
|
||||
argValue += line;
|
||||
}
|
||||
log_v("PostArg Value: %s", argValue.c_str());
|
||||
|
||||
RequestArgument &arg = _postArgs[_postArgsLen++];
|
||||
arg.key = argName;
|
||||
arg.value = argValue;
|
||||
|
||||
if (line == ("--" + boundary + "--")) {
|
||||
log_v("Done Parsing POST");
|
||||
break;
|
||||
} else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) {
|
||||
log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
_currentUpload.reset(new HTTPUpload());
|
||||
_currentUpload->status = UPLOAD_FILE_START;
|
||||
_currentUpload->name = argName;
|
||||
_currentUpload->filename = argFilename;
|
||||
_currentUpload->type = argType;
|
||||
_currentUpload->totalSize = 0;
|
||||
_currentUpload->currentSize = 0;
|
||||
log_v(
|
||||
"Start File: %s Type: %s",
|
||||
_currentUpload->filename.c_str(),
|
||||
_currentUpload->type.c_str()
|
||||
);
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||
int argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
|
||||
while (argByte != 0x0D) {
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-') {
|
||||
// continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-') {
|
||||
// continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
goto readfile;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t endBuf[boundary.length()];
|
||||
uint32_t i = 0;
|
||||
while (i < boundary.length()) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0)
|
||||
return _parseFormUploadAborted();
|
||||
if ((char)argByte == 0x0D) {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
uint32_t j = 0;
|
||||
while (j < i) {
|
||||
_uploadWriteByte(endBuf[j++]);
|
||||
}
|
||||
goto readfile;
|
||||
}
|
||||
endBuf[i++] = (uint8_t)argByte;
|
||||
}
|
||||
|
||||
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);
|
||||
log_v(
|
||||
"End File: %s Type: %s Size: %d",
|
||||
_currentUpload->filename.c_str(),
|
||||
_currentUpload->type.c_str(),
|
||||
_currentUpload->totalSize
|
||||
);
|
||||
line = client.readStringUntil(0x0D);
|
||||
client.readStringUntil(0x0A);
|
||||
if (line == "--") {
|
||||
log_v("Done Parsing POST");
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iarg;
|
||||
int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)
|
||||
? (WEBSERVER_MAX_POST_ARGS - _postArgsLen)
|
||||
: _currentArgCount;
|
||||
for (iarg = 0; iarg < totalArgs; iarg++) {
|
||||
RequestArgument &arg = _postArgs[_postArgsLen++];
|
||||
arg.key = _currentArgs[iarg].key;
|
||||
arg.value = _currentArgs[iarg].value;
|
||||
}
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = new RequestArgument[_postArgsLen];
|
||||
for (iarg = 0; iarg < _postArgsLen; iarg++) {
|
||||
RequestArgument &arg = _currentArgs[iarg];
|
||||
arg.key = _postArgs[iarg].key;
|
||||
arg.value = _postArgs[iarg].value;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
if (_postArgs) {
|
||||
delete[] _postArgs;
|
||||
_postArgs = nullptr;
|
||||
_postArgsLen = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
log_e("Error: line: %s", line.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::urlDecode(const String &text) {
|
||||
String decoded = "";
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
while (i < len) {
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
if ((encodedChar == '%') && (i + 1 < len)) {
|
||||
temp[2] = text.charAt(i++);
|
||||
temp[3] = text.charAt(i++);
|
||||
|
||||
decodedChar = strtol(temp, NULL, 16);
|
||||
} else {
|
||||
if (encodedChar == '+') {
|
||||
decodedChar = ' ';
|
||||
} else {
|
||||
decodedChar = encodedChar; // normal ascii char
|
||||
}
|
||||
}
|
||||
decoded += decodedChar;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool WebServer::_parseFormUploadAborted() {
|
||||
_currentUpload->status = UPLOAD_FILE_ABORTED;
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
return false;
|
||||
}
|
||||
29
arduino/libretuya/libraries/WebServer/Uri.h
Normal file
29
arduino/libretuya/libraries/WebServer/Uri.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
class Uri {
|
||||
|
||||
protected:
|
||||
const String _uri;
|
||||
|
||||
public:
|
||||
Uri(const char *uri) : _uri(uri) {}
|
||||
|
||||
Uri(const String &uri) : _uri(uri) {}
|
||||
|
||||
Uri(const __FlashStringHelper *uri) : _uri(String(uri)) {}
|
||||
|
||||
virtual ~Uri() {}
|
||||
|
||||
virtual Uri *clone() const {
|
||||
return new Uri(_uri);
|
||||
};
|
||||
|
||||
virtual void initPathArgs(__attribute__((unused)) std::vector<String> &pathArgs) {}
|
||||
|
||||
virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
|
||||
return _uri == requestUri;
|
||||
}
|
||||
};
|
||||
717
arduino/libretuya/libraries/WebServer/WebServer.cpp
Normal file
717
arduino/libretuya/libraries/WebServer/WebServer.cpp
Normal file
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
WebServer.cpp - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
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
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "FS.h"
|
||||
#include "WebServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiServer.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
// #include "mbedtls/md5.h"
|
||||
#include <libb64/cencode.h>
|
||||
|
||||
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
||||
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
|
||||
static const char WWW_Authenticate[] = "WWW-Authenticate";
|
||||
static const char Content_Length[] = "Content-Length";
|
||||
|
||||
WebServer::WebServer(IPAddress addr, int port)
|
||||
: _corsEnabled(false), _server(addr, port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE),
|
||||
_statusChange(0), _nullDelay(true), _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr),
|
||||
_currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), _headerKeysCount(0),
|
||||
_currentHeaders(nullptr), _contentLength(0), _chunked(false) {
|
||||
log_v("WebServer::Webserver(addr=%s, port=%d)", addr.toString().c_str(), port);
|
||||
}
|
||||
|
||||
WebServer::WebServer(int port)
|
||||
: _corsEnabled(false), _server(port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE),
|
||||
_statusChange(0), _nullDelay(true), _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr),
|
||||
_currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), _headerKeysCount(0),
|
||||
_currentHeaders(nullptr), _contentLength(0), _chunked(false) {
|
||||
log_v("WebServer::Webserver(port=%d)", port);
|
||||
}
|
||||
|
||||
WebServer::~WebServer() {
|
||||
_server.close();
|
||||
if (_currentHeaders)
|
||||
delete[] _currentHeaders;
|
||||
RequestHandler *handler = _firstHandler;
|
||||
while (handler) {
|
||||
RequestHandler *next = handler->next();
|
||||
delete handler;
|
||||
handler = next;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::begin() {
|
||||
close();
|
||||
_server.begin();
|
||||
_server.setNoDelay(true);
|
||||
}
|
||||
|
||||
void WebServer::begin(uint16_t port) {
|
||||
close();
|
||||
_server.begin(port);
|
||||
_server.setNoDelay(true);
|
||||
}
|
||||
|
||||
String WebServer::_extractParam(String &authReq, const String ¶m, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1)
|
||||
return "";
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
static String md5str(String &in) {
|
||||
/* char out[33] = {0};
|
||||
mbedtls_md5_context _ctx;
|
||||
uint8_t i;
|
||||
uint8_t *_buf = (uint8_t *)malloc(16);
|
||||
if (_buf == NULL)
|
||||
return String(out);
|
||||
memset(_buf, 0x00, 16);
|
||||
mbedtls_md5_init(&_ctx);
|
||||
mbedtls_md5_starts_ret(&_ctx);
|
||||
mbedtls_md5_update_ret(&_ctx, (const uint8_t *)in.c_str(), in.length());
|
||||
mbedtls_md5_finish_ret(&_ctx, _buf);
|
||||
for (i = 0; i < 16; i++) {
|
||||
sprintf(out + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
out[32] = 0;
|
||||
free(_buf);
|
||||
return String(out); */
|
||||
return "";
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(const char *username, const char *password) {
|
||||
if (hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||
if (authReq.startsWith(F("Basic"))) {
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username) + strlen(password) + 1;
|
||||
char *toencode = new char[toencodeLen + 1];
|
||||
if (toencode == NULL) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1];
|
||||
if (encoded == NULL) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
} else if (authReq.startsWith(F("Digest"))) {
|
||||
authReq = authReq.substring(7);
|
||||
log_v("%s", authReq.c_str());
|
||||
String _username = _extractParam(authReq, F("username=\""), '\"');
|
||||
if (!_username.length() || _username != String(username)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""), '\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""), '\"');
|
||||
String _response = _extractParam(authReq, F("response=\""), '\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
||||
|
||||
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) ||
|
||||
(!_opaque.length())) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc, _cnonce;
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
||||
}
|
||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||
String _H2 = "";
|
||||
if (_currentMethod == HTTP_GET) {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_POST) {
|
||||
_H2 = md5str(String(F("POST:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_PUT) {
|
||||
_H2 = md5str(String(F("PUT:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_DELETE) {
|
||||
_H2 = md5str(String(F("DELETE:")) + _uri);
|
||||
} else {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}
|
||||
log_v("Hash of GET:uri=%s", _H2.c_str());
|
||||
String _responsecheck = "";
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
||||
if (_response == _responsecheck) {
|
||||
authReq = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
authReq = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::_getRandomHexString() {
|
||||
char buffer[33]; // buffer to hold 32 Hex Digit + /0
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf(buffer + (i * 8), "%08x", rand());
|
||||
}
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
void WebServer::requestAuthentication(HTTPAuthMethod mode, const char *realm, const String &authFailMsg) {
|
||||
if (realm == NULL) {
|
||||
_srealm = String(F("Login Required"));
|
||||
} else {
|
||||
_srealm = String(realm);
|
||||
}
|
||||
if (mode == BASIC_AUTH) {
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
|
||||
} else {
|
||||
_snonce = _getRandomHexString();
|
||||
_sopaque = _getRandomHexString();
|
||||
sendHeader(
|
||||
String(FPSTR(WWW_Authenticate)),
|
||||
String(F("Digest realm=\"")) + _srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce +
|
||||
String(F("\", opaque=\"")) + _sopaque + String(F("\""))
|
||||
);
|
||||
}
|
||||
using namespace mime;
|
||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) {
|
||||
on(uri, HTTP_ANY, handler);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
|
||||
on(uri, method, fn, _fileUploadHandler);
|
||||
}
|
||||
|
||||
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
|
||||
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
|
||||
}
|
||||
|
||||
void WebServer::addHandler(RequestHandler *handler) {
|
||||
_addRequestHandler(handler);
|
||||
}
|
||||
|
||||
void WebServer::_addRequestHandler(RequestHandler *handler) {
|
||||
if (!_lastHandler) {
|
||||
_firstHandler = handler;
|
||||
_lastHandler = handler;
|
||||
} else {
|
||||
_lastHandler->next(handler);
|
||||
_lastHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::serveStatic(const char *uri, FS &fs, const char *path, const char *cache_header) {
|
||||
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
|
||||
}
|
||||
|
||||
void WebServer::handleClient() {
|
||||
if (_currentStatus == HC_NONE) {
|
||||
WiFiClient client = _server.available();
|
||||
if (!client) {
|
||||
if (_nullDelay) {
|
||||
delay(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log_v("New client: client.localIP()=%s", client.localIP().toString().c_str());
|
||||
|
||||
_currentClient = client;
|
||||
_currentStatus = HC_WAIT_READ;
|
||||
_statusChange = millis();
|
||||
}
|
||||
|
||||
bool keepCurrentClient = false;
|
||||
bool callYield = false;
|
||||
|
||||
if (_currentClient.connected()) {
|
||||
switch (_currentStatus) {
|
||||
case HC_NONE:
|
||||
// No-op to avoid C++ compiler warning
|
||||
break;
|
||||
case HC_WAIT_READ:
|
||||
// Wait for data from client to become available
|
||||
if (_currentClient.available()) {
|
||||
if (_parseRequest(_currentClient)) {
|
||||
// because HTTP_MAX_SEND_WAIT is expressed in milliseconds,
|
||||
// it must be divided by 1000
|
||||
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000);
|
||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||
_handleRequest();
|
||||
|
||||
// Fix for issue with Chrome based browsers:
|
||||
// https://github.com/espressif/arduino-esp32/issues/3652
|
||||
// if (_currentClient.connected()) {
|
||||
// _currentStatus = HC_WAIT_CLOSE;
|
||||
// _statusChange = millis();
|
||||
// keepCurrentClient = true;
|
||||
// }
|
||||
}
|
||||
} else { // !_currentClient.available()
|
||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
}
|
||||
callYield = true;
|
||||
}
|
||||
break;
|
||||
case HC_WAIT_CLOSE:
|
||||
// Wait for client to close the connection
|
||||
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
callYield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepCurrentClient) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
_currentUpload.reset();
|
||||
}
|
||||
|
||||
if (callYield) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::close() {
|
||||
_server.close();
|
||||
_currentStatus = HC_NONE;
|
||||
if (!_headerKeysCount)
|
||||
collectHeaders(0, 0);
|
||||
}
|
||||
|
||||
void WebServer::stop() {
|
||||
close();
|
||||
}
|
||||
|
||||
void WebServer::sendHeader(const String &name, const String &value, bool first) {
|
||||
String headerLine = name;
|
||||
headerLine += F(": ");
|
||||
headerLine += value;
|
||||
headerLine += "\r\n";
|
||||
|
||||
if (first) {
|
||||
_responseHeaders = headerLine + _responseHeaders;
|
||||
} else {
|
||||
_responseHeaders += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::setContentLength(const size_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
|
||||
void WebServer::enableDelay(boolean value) {
|
||||
_nullDelay = value;
|
||||
}
|
||||
|
||||
void WebServer::enableCORS(boolean value) {
|
||||
_corsEnabled = value;
|
||||
}
|
||||
|
||||
void WebServer::enableCrossOrigin(boolean value) {
|
||||
enableCORS(value);
|
||||
}
|
||||
|
||||
void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) {
|
||||
response = String(F("HTTP/1.")) + String(_currentVersion) + ' ';
|
||||
response += String(code);
|
||||
response += ' ';
|
||||
response += _responseCodeToString(code);
|
||||
response += "\r\n";
|
||||
|
||||
using namespace mime;
|
||||
if (!content_type)
|
||||
content_type = mimeTable[html].mimeType;
|
||||
|
||||
sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true);
|
||||
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(contentLength));
|
||||
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(_contentLength));
|
||||
} else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion) { // HTTP/1.1 or above client
|
||||
// let's do chunked
|
||||
_chunked = true;
|
||||
sendHeader(String(F("Accept-Ranges")), String(F("none")));
|
||||
sendHeader(String(F("Transfer-Encoding")), String(F("chunked")));
|
||||
}
|
||||
if (_corsEnabled) {
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
|
||||
}
|
||||
sendHeader(String(F("Connection")), String(F("close")));
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = "";
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const char *content_type, const String &content) {
|
||||
String header;
|
||||
// Can we asume the following?
|
||||
// if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
|
||||
// _contentLength = CONTENT_LENGTH_UNKNOWN;
|
||||
_prepareHeader(header, code, content_type, content.length());
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
if (content.length())
|
||||
sendContent(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
|
||||
size_t contentLength = 0;
|
||||
|
||||
if (content != NULL) {
|
||||
contentLength = strlen_P(content);
|
||||
}
|
||||
|
||||
String header;
|
||||
char type[64];
|
||||
strncpy_P(type, (PGM_P)content_type, sizeof(type));
|
||||
_prepareHeader(header, code, (const char *)type, contentLength);
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
sendContent_P(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
|
||||
String header;
|
||||
char type[64];
|
||||
strncpy_P(type, (PGM_P)content_type, sizeof(type));
|
||||
_prepareHeader(header, code, (const char *)type, contentLength);
|
||||
sendContent(header);
|
||||
sendContent_P(content, contentLength);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, char *content_type, const String &content) {
|
||||
send(code, (const char *)content_type, content);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const String &content_type, const String &content) {
|
||||
send(code, (const char *)content_type.c_str(), content);
|
||||
}
|
||||
|
||||
void WebServer::sendContent(const String &content) {
|
||||
sendContent(content.c_str(), content.length());
|
||||
}
|
||||
|
||||
void WebServer::sendContent(const char *content, size_t contentLength) {
|
||||
const char *footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char *chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", contentLength, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite(content, contentLength);
|
||||
if (_chunked) {
|
||||
_currentClient.write(footer, 2);
|
||||
if (contentLength == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content) {
|
||||
sendContent_P(content, strlen_P(content));
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content, size_t size) {
|
||||
const char *footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char *chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", size, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite_P(content, size);
|
||||
if (_chunked) {
|
||||
_currentClient.write(footer, 2);
|
||||
if (size == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::_streamFileCore(const size_t fileSize, const String &fileName, const String &contentType) {
|
||||
using namespace mime;
|
||||
setContentLength(fileSize);
|
||||
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) &&
|
||||
contentType != String(FPSTR(mimeTable[gz].mimeType)) &&
|
||||
contentType != String(FPSTR(mimeTable[none].mimeType))) {
|
||||
sendHeader(F("Content-Encoding"), F("gzip"));
|
||||
}
|
||||
send(200, contentType, "");
|
||||
}
|
||||
|
||||
String WebServer::pathArg(unsigned int i) {
|
||||
if (_currentHandler != nullptr)
|
||||
return _currentHandler->pathArg(i);
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::arg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name)
|
||||
return _postArgs[j].value;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return _currentArgs[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::arg(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].value;
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::argName(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].key;
|
||||
return "";
|
||||
}
|
||||
|
||||
int WebServer::args() {
|
||||
return _currentArgCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasArg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name)
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::header(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(name))
|
||||
return _currentHeaders[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void WebServer::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) {
|
||||
_headerKeysCount = headerKeysCount + 1;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
String WebServer::header(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].value;
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::headerName(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].key;
|
||||
return "";
|
||||
}
|
||||
|
||||
int WebServer::headers() {
|
||||
return _headerKeysCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasHeader(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::hostHeader() {
|
||||
return _hostHeader;
|
||||
}
|
||||
|
||||
void WebServer::onFileUpload(THandlerFunction fn) {
|
||||
_fileUploadHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::onNotFound(THandlerFunction fn) {
|
||||
_notFoundHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::_handleRequest() {
|
||||
bool handled = false;
|
||||
if (!_currentHandler) {
|
||||
log_e("request handler not found");
|
||||
} else {
|
||||
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
||||
if (!handled) {
|
||||
log_e("request handler failed to handle request");
|
||||
}
|
||||
}
|
||||
if (!handled && _notFoundHandler) {
|
||||
_notFoundHandler();
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
using namespace mime;
|
||||
send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri);
|
||||
handled = true;
|
||||
}
|
||||
if (handled) {
|
||||
_finalizeResponse();
|
||||
}
|
||||
_currentUri = "";
|
||||
}
|
||||
|
||||
void WebServer::_finalizeResponse() {
|
||||
if (_chunked) {
|
||||
sendContent("");
|
||||
}
|
||||
}
|
||||
|
||||
String WebServer::_responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100:
|
||||
return F("Continue");
|
||||
case 101:
|
||||
return F("Switching Protocols");
|
||||
case 200:
|
||||
return F("OK");
|
||||
case 201:
|
||||
return F("Created");
|
||||
case 202:
|
||||
return F("Accepted");
|
||||
case 203:
|
||||
return F("Non-Authoritative Information");
|
||||
case 204:
|
||||
return F("No Content");
|
||||
case 205:
|
||||
return F("Reset Content");
|
||||
case 206:
|
||||
return F("Partial Content");
|
||||
case 300:
|
||||
return F("Multiple Choices");
|
||||
case 301:
|
||||
return F("Moved Permanently");
|
||||
case 302:
|
||||
return F("Found");
|
||||
case 303:
|
||||
return F("See Other");
|
||||
case 304:
|
||||
return F("Not Modified");
|
||||
case 305:
|
||||
return F("Use Proxy");
|
||||
case 307:
|
||||
return F("Temporary Redirect");
|
||||
case 400:
|
||||
return F("Bad Request");
|
||||
case 401:
|
||||
return F("Unauthorized");
|
||||
case 402:
|
||||
return F("Payment Required");
|
||||
case 403:
|
||||
return F("Forbidden");
|
||||
case 404:
|
||||
return F("Not Found");
|
||||
case 405:
|
||||
return F("Method Not Allowed");
|
||||
case 406:
|
||||
return F("Not Acceptable");
|
||||
case 407:
|
||||
return F("Proxy Authentication Required");
|
||||
case 408:
|
||||
return F("Request Time-out");
|
||||
case 409:
|
||||
return F("Conflict");
|
||||
case 410:
|
||||
return F("Gone");
|
||||
case 411:
|
||||
return F("Length Required");
|
||||
case 412:
|
||||
return F("Precondition Failed");
|
||||
case 413:
|
||||
return F("Request Entity Too Large");
|
||||
case 414:
|
||||
return F("Request-URI Too Large");
|
||||
case 415:
|
||||
return F("Unsupported Media Type");
|
||||
case 416:
|
||||
return F("Requested range not satisfiable");
|
||||
case 417:
|
||||
return F("Expectation Failed");
|
||||
case 500:
|
||||
return F("Internal Server Error");
|
||||
case 501:
|
||||
return F("Not Implemented");
|
||||
case 502:
|
||||
return F("Bad Gateway");
|
||||
case 503:
|
||||
return F("Service Unavailable");
|
||||
case 504:
|
||||
return F("Gateway Time-out");
|
||||
case 505:
|
||||
return F("HTTP Version not supported");
|
||||
default:
|
||||
return F("");
|
||||
}
|
||||
}
|
||||
224
arduino/libretuya/libraries/WebServer/WebServer.h
Normal file
224
arduino/libretuya/libraries/WebServer/WebServer.h
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
WebServer.h - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
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
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "HTTP_Method.h"
|
||||
#include "Uri.h"
|
||||
#include <WiFi.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED };
|
||||
|
||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
||||
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1436
|
||||
|
||||
#ifndef HTTP_UPLOAD_BUFLEN
|
||||
#define HTTP_UPLOAD_BUFLEN 1436
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_DATA_WAIT 5000 // ms to wait for the client to send the request
|
||||
#define HTTP_MAX_POST_WAIT 5000 // ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 // ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 // ms to wait for the client to close the connection
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t)-1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t)-2)
|
||||
|
||||
class WebServer;
|
||||
|
||||
typedef struct {
|
||||
HTTPUploadStatus status;
|
||||
String filename;
|
||||
String name;
|
||||
String type;
|
||||
size_t totalSize; // file size
|
||||
size_t currentSize; // size of data currently in buf
|
||||
uint8_t buf[HTTP_UPLOAD_BUFLEN];
|
||||
} HTTPUpload;
|
||||
|
||||
#include "detail/RequestHandler.h"
|
||||
|
||||
namespace fs {
|
||||
class FS;
|
||||
}
|
||||
|
||||
class WebServer {
|
||||
public:
|
||||
WebServer(IPAddress addr, int port = 80);
|
||||
WebServer(int port = 80);
|
||||
virtual ~WebServer();
|
||||
|
||||
virtual void begin();
|
||||
virtual void begin(uint16_t port);
|
||||
virtual void handleClient();
|
||||
|
||||
virtual void close();
|
||||
void stop();
|
||||
|
||||
bool authenticate(const char *username, const char *password);
|
||||
void requestAuthentication(
|
||||
HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, const String &authFailMsg = String("")
|
||||
);
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const Uri &uri, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); // ufn handles file uploads
|
||||
void addHandler(RequestHandler *handler);
|
||||
void serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_header = NULL);
|
||||
void onNotFound(THandlerFunction fn); // called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction ufn); // handle file uploads
|
||||
|
||||
String uri() {
|
||||
return _currentUri;
|
||||
}
|
||||
|
||||
HTTPMethod method() {
|
||||
return _currentMethod;
|
||||
}
|
||||
|
||||
virtual WiFiClient client() {
|
||||
return _currentClient;
|
||||
}
|
||||
|
||||
HTTPUpload &upload() {
|
||||
return *_currentUpload;
|
||||
}
|
||||
|
||||
String pathArg(unsigned int i); // get request path argument by number
|
||||
String arg(String name); // get request argument value by name
|
||||
String arg(int i); // get request argument value by number
|
||||
String argName(int i); // get request argument name by number
|
||||
int args(); // get arguments count
|
||||
bool hasArg(String name); // check if argument exists
|
||||
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); // set the request headers to collect
|
||||
String header(String name); // get request header value by name
|
||||
String header(int i); // get request header value by number
|
||||
String headerName(int i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(String name); // check if header exists
|
||||
|
||||
String hostHeader(); // get request host header if available or empty String if not
|
||||
|
||||
// send response to the client
|
||||
// code - HTTP response code, can be 200 or 404
|
||||
// content_type - HTTP content type, like "text/plain" or "image/png"
|
||||
// content - actual content body
|
||||
void send(int code, const char *content_type = NULL, const String &content = String(""));
|
||||
void send(int code, char *content_type, const String &content);
|
||||
void send(int code, const String &content_type, const String &content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
|
||||
void enableDelay(boolean value);
|
||||
void enableCORS(boolean value = true);
|
||||
void enableCrossOrigin(boolean value = true);
|
||||
|
||||
void setContentLength(const size_t contentLength);
|
||||
void sendHeader(const String &name, const String &value, bool first = false);
|
||||
void sendContent(const String &content);
|
||||
void sendContent(const char *content, size_t contentLength);
|
||||
void sendContent_P(PGM_P content);
|
||||
void sendContent_P(PGM_P content, size_t size);
|
||||
|
||||
static String urlDecode(const String &text);
|
||||
|
||||
template <typename T>
|
||||
size_t streamFile(T &file, const String &contentType) {
|
||||
_streamFileCore(file.size(), file.name(), contentType);
|
||||
return _currentClient.write(file);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual size_t _currentClientWrite(const char *b, size_t l) {
|
||||
return _currentClient.write(b, l);
|
||||
}
|
||||
|
||||
virtual size_t _currentClientWrite_P(PGM_P b, size_t l) {
|
||||
return _currentClient.write_P(b, l);
|
||||
}
|
||||
|
||||
void _addRequestHandler(RequestHandler *handler);
|
||||
void _handleRequest();
|
||||
void _finalizeResponse();
|
||||
bool _parseRequest(WiFiClient &client);
|
||||
void _parseArguments(String data);
|
||||
static String _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient &client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
int _uploadReadByte(WiFiClient &client);
|
||||
void _prepareHeader(String &response, int code, const char *content_type, size_t contentLength);
|
||||
bool _collectHeader(const char *headerName, const char *headerValue);
|
||||
|
||||
void _streamFileCore(const size_t fileSize, const String &fileName, const String &contentType);
|
||||
|
||||
String _getRandomHexString();
|
||||
// for extracting Auth parameters
|
||||
String _extractParam(String &authReq, const String ¶m, const char delimit = '"');
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
boolean _corsEnabled;
|
||||
WiFiServer _server;
|
||||
|
||||
WiFiClient _currentClient;
|
||||
HTTPMethod _currentMethod;
|
||||
String _currentUri;
|
||||
uint8_t _currentVersion;
|
||||
HTTPClientStatus _currentStatus;
|
||||
unsigned long _statusChange;
|
||||
boolean _nullDelay;
|
||||
|
||||
RequestHandler *_currentHandler;
|
||||
RequestHandler *_firstHandler;
|
||||
RequestHandler *_lastHandler;
|
||||
THandlerFunction _notFoundHandler;
|
||||
THandlerFunction _fileUploadHandler;
|
||||
|
||||
int _currentArgCount;
|
||||
RequestArgument *_currentArgs;
|
||||
int _postArgsLen;
|
||||
RequestArgument *_postArgs;
|
||||
|
||||
std::unique_ptr<HTTPUpload> _currentUpload;
|
||||
|
||||
int _headerKeysCount;
|
||||
RequestArgument *_currentHeaders;
|
||||
size_t _contentLength;
|
||||
String _responseHeaders;
|
||||
|
||||
String _hostHeader;
|
||||
bool _chunked;
|
||||
|
||||
String _snonce; // Store noance and opaque for future comparison
|
||||
String _sopaque;
|
||||
String _srealm; // Store the Auth realm between Calls
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
virtual ~RequestHandler() {}
|
||||
|
||||
virtual bool canHandle(HTTPMethod method, String uri) {
|
||||
(void)method;
|
||||
(void)uri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool canUpload(String uri) {
|
||||
(void)uri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) {
|
||||
(void)server;
|
||||
(void)requestMethod;
|
||||
(void)requestUri;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void upload(WebServer &server, String requestUri, HTTPUpload &upload) {
|
||||
(void)server;
|
||||
(void)requestUri;
|
||||
(void)upload;
|
||||
}
|
||||
|
||||
RequestHandler *next() {
|
||||
return _next;
|
||||
}
|
||||
|
||||
void next(RequestHandler *r) {
|
||||
_next = r;
|
||||
}
|
||||
|
||||
private:
|
||||
RequestHandler *_next = nullptr;
|
||||
|
||||
protected:
|
||||
std::vector<String> pathArgs;
|
||||
|
||||
public:
|
||||
const String &pathArg(unsigned int i) {
|
||||
assert(i < pathArgs.size());
|
||||
return pathArgs[i];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "RequestHandler.h"
|
||||
#include "Uri.h"
|
||||
#include "WString.h"
|
||||
#include "mimetable.h"
|
||||
|
||||
using namespace mime;
|
||||
|
||||
class FunctionRequestHandler : public RequestHandler {
|
||||
public:
|
||||
FunctionRequestHandler(
|
||||
WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const Uri &uri, HTTPMethod method
|
||||
)
|
||||
: _fn(fn), _ufn(ufn), _uri(uri.clone()), _method(method) {
|
||||
_uri->initPathArgs(pathArgs);
|
||||
}
|
||||
|
||||
~FunctionRequestHandler() {
|
||||
delete _uri;
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
return _uri->canHandle(requestUri, pathArgs);
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
if (!_ufn || !canHandle(HTTP_POST, requestUri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
|
||||
(void)server;
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
_fn();
|
||||
return true;
|
||||
}
|
||||
|
||||
void upload(WebServer &server, String requestUri, HTTPUpload &upload) override {
|
||||
(void)server;
|
||||
(void)upload;
|
||||
if (canUpload(requestUri))
|
||||
_ufn();
|
||||
}
|
||||
|
||||
protected:
|
||||
WebServer::THandlerFunction _fn;
|
||||
WebServer::THandlerFunction _ufn;
|
||||
Uri *_uri;
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
public:
|
||||
StaticRequestHandler(FS &fs, const char *path, const char *uri, const char *cache_header)
|
||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header) {
|
||||
File f = fs.open(path);
|
||||
_isFile = (f && (!f.isDirectory()));
|
||||
log_v(
|
||||
"StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n",
|
||||
path,
|
||||
uri,
|
||||
_isFile,
|
||||
cache_header ? cache_header : ""
|
||||
); // issue 5506 - cache_header can be nullptr
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != HTTP_GET)
|
||||
return false;
|
||||
|
||||
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
|
||||
String path(_path);
|
||||
|
||||
if (!_isFile) {
|
||||
// Base URI doesn't point to a file.
|
||||
// If a directory is requested, look for index file.
|
||||
if (requestUri.endsWith("/"))
|
||||
requestUri += "index.htm";
|
||||
|
||||
// Append whatever follows this URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
}
|
||||
log_v("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
|
||||
String contentType = getContentType(path);
|
||||
|
||||
// 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 || !f.available())
|
||||
return false;
|
||||
|
||||
if (_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", _cache_header);
|
||||
|
||||
server.streamFile(f, contentType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static String getContentType(const String &path) {
|
||||
char buff[sizeof(mimeTable[0].mimeType)];
|
||||
// Check all entries but last one for match, return if found
|
||||
for (size_t i = 0; i < sizeof(mimeTable) / sizeof(mimeTable[0]) - 1; i++) {
|
||||
strcpy_P(buff, mimeTable[i].endsWith);
|
||||
if (path.endsWith(buff)) {
|
||||
strcpy_P(buff, mimeTable[i].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
}
|
||||
// Fall-through and just return default type
|
||||
strcpy_P(buff, mimeTable[sizeof(mimeTable) / sizeof(mimeTable[0]) - 1].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _cache_header;
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
33
arduino/libretuya/libraries/WebServer/detail/mimetable.cpp
Normal file
33
arduino/libretuya/libraries/WebServer/detail/mimetable.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "mimetable.h"
|
||||
#include "pgmspace.h"
|
||||
|
||||
namespace mime {
|
||||
|
||||
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
|
||||
const Entry mimeTable[maxType] = {
|
||||
{".html", "text/html" },
|
||||
{".htm", "text/html" },
|
||||
{".css", "text/css" },
|
||||
{".txt", "text/plain" },
|
||||
{".js", "application/javascript" },
|
||||
{".json", "application/json" },
|
||||
{".png", "image/png" },
|
||||
{".gif", "image/gif" },
|
||||
{".jpg", "image/jpeg" },
|
||||
{".ico", "image/x-icon" },
|
||||
{".svg", "image/svg+xml" },
|
||||
{".ttf", "application/x-font-ttf" },
|
||||
{".otf", "application/x-font-opentype" },
|
||||
{".woff", "application/font-woff" },
|
||||
{".woff2", "application/font-woff2" },
|
||||
{".eot", "application/vnd.ms-fontobject"},
|
||||
{".sfnt", "application/font-sfnt" },
|
||||
{".xml", "text/xml" },
|
||||
{".pdf", "application/pdf" },
|
||||
{".zip", "application/zip" },
|
||||
{".gz", "application/x-gzip" },
|
||||
{".appcache", "text/cache-manifest" },
|
||||
{"", "application/octet-stream" }
|
||||
};
|
||||
|
||||
} // namespace mime
|
||||
38
arduino/libretuya/libraries/WebServer/detail/mimetable.h
Normal file
38
arduino/libretuya/libraries/WebServer/detail/mimetable.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
namespace mime {
|
||||
|
||||
enum type {
|
||||
html,
|
||||
htm,
|
||||
css,
|
||||
txt,
|
||||
js,
|
||||
json,
|
||||
png,
|
||||
gif,
|
||||
jpg,
|
||||
ico,
|
||||
svg,
|
||||
ttf,
|
||||
otf,
|
||||
woff,
|
||||
woff2,
|
||||
eot,
|
||||
sfnt,
|
||||
xml,
|
||||
pdf,
|
||||
zip,
|
||||
gz,
|
||||
appcache,
|
||||
none,
|
||||
maxType
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
const char endsWith[16];
|
||||
const char mimeType[32];
|
||||
};
|
||||
|
||||
extern const Entry mimeTable[maxType];
|
||||
} // namespace mime
|
||||
61
arduino/libretuya/libraries/WebServer/uri/UriBraces.h
Normal file
61
arduino/libretuya/libraries/WebServer/uri/UriBraces.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
|
||||
class UriBraces : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriBraces(const char *uri) : Uri(uri){};
|
||||
explicit UriBraces(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriBraces(_uri);
|
||||
};
|
||||
|
||||
void initPathArgs(std::vector<String> &pathArgs) override final {
|
||||
int numParams = 0, start = 0;
|
||||
do {
|
||||
start = _uri.indexOf("{}", start);
|
||||
if (start > 0) {
|
||||
numParams++;
|
||||
start += 2;
|
||||
}
|
||||
} while (start > 0);
|
||||
pathArgs.resize(numParams);
|
||||
}
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
size_t uriLength = _uri.length();
|
||||
unsigned int pathArgIndex = 0;
|
||||
unsigned int requestUriIndex = 0;
|
||||
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
|
||||
char uriChar = _uri[i];
|
||||
char requestUriChar = requestUri[requestUriIndex];
|
||||
|
||||
if (uriChar == requestUriChar)
|
||||
continue;
|
||||
if (uriChar != '{')
|
||||
return false;
|
||||
|
||||
i += 2; // index of char after '}'
|
||||
if (i >= uriLength) {
|
||||
// there is no char after '}'
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex);
|
||||
return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/'
|
||||
} else {
|
||||
char charEnd = _uri[i];
|
||||
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
|
||||
if (uriIndex < 0)
|
||||
return false;
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex);
|
||||
requestUriIndex = (unsigned int)uriIndex;
|
||||
}
|
||||
pathArgIndex++;
|
||||
}
|
||||
|
||||
return requestUriIndex >= requestUri.length();
|
||||
}
|
||||
};
|
||||
19
arduino/libretuya/libraries/WebServer/uri/UriGlob.h
Normal file
19
arduino/libretuya/libraries/WebServer/uri/UriGlob.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
#include <fnmatch.h>
|
||||
|
||||
class UriGlob : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriGlob(const char *uri) : Uri(uri){};
|
||||
explicit UriGlob(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriGlob(_uri);
|
||||
};
|
||||
|
||||
bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) override final {
|
||||
return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0;
|
||||
}
|
||||
};
|
||||
41
arduino/libretuya/libraries/WebServer/uri/UriRegex.h
Normal file
41
arduino/libretuya/libraries/WebServer/uri/UriRegex.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "Uri.h"
|
||||
#include <regex>
|
||||
|
||||
class UriRegex : public Uri {
|
||||
|
||||
public:
|
||||
explicit UriRegex(const char *uri) : Uri(uri){};
|
||||
explicit UriRegex(const String &uri) : Uri(uri){};
|
||||
|
||||
Uri *clone() const override final {
|
||||
return new UriRegex(_uri);
|
||||
};
|
||||
|
||||
void initPathArgs(std::vector<String> &pathArgs) override final {
|
||||
std::regex rgx((_uri + "|").c_str());
|
||||
std::smatch matches;
|
||||
std::string s{""};
|
||||
std::regex_search(s, matches, rgx);
|
||||
pathArgs.resize(matches.size() - 1);
|
||||
}
|
||||
|
||||
bool canHandle(const String &requestUri, std::vector<String> &pathArgs) override final {
|
||||
if (Uri::canHandle(requestUri, pathArgs))
|
||||
return true;
|
||||
|
||||
unsigned int pathArgIndex = 0;
|
||||
std::regex rgx(_uri.c_str());
|
||||
std::smatch matches;
|
||||
std::string s(requestUri.c_str());
|
||||
if (std::regex_search(s, matches, rgx)) {
|
||||
for (size_t i = 1; i < matches.size(); ++i) { // skip first
|
||||
pathArgs[pathArgIndex] = String(matches[i].str().c_str());
|
||||
pathArgIndex++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
238
arduino/libretuya/libraries/WiFiMulti/WiFiMulti.cpp
Normal file
238
arduino/libretuya/libraries/WiFiMulti/WiFiMulti.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
*
|
||||
* @file WiFiMulti.cpp
|
||||
* @date 16.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WiFiMulti.h"
|
||||
#include <Arduino.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
WiFiMulti::WiFiMulti() {}
|
||||
|
||||
WiFiMulti::~WiFiMulti() {
|
||||
for (uint32_t i = 0; i < APlist.size(); i++) {
|
||||
WifiAPlist_t entry = APlist[i];
|
||||
if (entry.ssid) {
|
||||
free(entry.ssid);
|
||||
}
|
||||
if (entry.passphrase) {
|
||||
free(entry.passphrase);
|
||||
}
|
||||
}
|
||||
APlist.clear();
|
||||
}
|
||||
|
||||
bool WiFiMulti::addAP(const char *ssid, const char *passphrase) {
|
||||
WifiAPlist_t newAP;
|
||||
|
||||
if (!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
|
||||
// fail SSID too long or missing!
|
||||
LT_E("SSID missing or too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passphrase && strlen(passphrase) > 64) {
|
||||
// fail passphrase too long!
|
||||
LT_E("Passphrase too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
newAP.ssid = strdup(ssid);
|
||||
|
||||
if (!newAP.ssid) {
|
||||
LT_E("Fail newAP.ssid == 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passphrase && *passphrase != 0x00) {
|
||||
newAP.passphrase = strdup(passphrase);
|
||||
if (!newAP.passphrase) {
|
||||
LT_E("Fail newAP.passphrase == 0");
|
||||
free(newAP.ssid);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
newAP.passphrase = NULL;
|
||||
}
|
||||
|
||||
APlist.push_back(newAP);
|
||||
LT_V("Add SSID: %s", newAP.ssid);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t WiFiMulti::run(uint32_t connectTimeout) {
|
||||
int8_t scanResult;
|
||||
uint8_t status = WiFi.status();
|
||||
if (status == WL_CONNECTED) {
|
||||
for (uint32_t x = 0; x < APlist.size(); x++) {
|
||||
if (WiFi.SSID() == APlist[x].ssid) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
WiFi.disconnect(false);
|
||||
delay(10);
|
||||
status = WiFi.status();
|
||||
}
|
||||
|
||||
scanResult = WiFi.scanNetworks();
|
||||
if (scanResult == WIFI_SCAN_RUNNING) {
|
||||
// scan is running
|
||||
return WL_NO_SSID_AVAIL;
|
||||
} else if (scanResult >= 0) {
|
||||
// scan done analyze
|
||||
WifiAPlist_t bestNetwork{NULL, NULL};
|
||||
int bestNetworkDb = INT_MIN;
|
||||
uint8_t bestBSSID[6];
|
||||
int32_t bestChannel = 0;
|
||||
|
||||
LT_I("Scan finished");
|
||||
|
||||
if (scanResult == 0) {
|
||||
LT_I("No networks found");
|
||||
} else {
|
||||
LT_I("%d networks found", scanResult);
|
||||
for (int8_t i = 0; i < scanResult; ++i) {
|
||||
|
||||
String ssid_scan;
|
||||
int32_t rssi_scan;
|
||||
WiFiAuthMode sec_scan;
|
||||
uint8_t *BSSID_scan;
|
||||
int32_t chan_scan;
|
||||
|
||||
WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
|
||||
|
||||
bool known = false;
|
||||
for (uint32_t x = APlist.size(); x > 0; x--) {
|
||||
WifiAPlist_t entry = APlist[x - 1];
|
||||
|
||||
if (ssid_scan == entry.ssid) { // SSID match
|
||||
known = true;
|
||||
if (rssi_scan > bestNetworkDb) { // best network
|
||||
if (sec_scan == WIFI_AUTH_OPEN ||
|
||||
entry.passphrase) { // check for passphrase if not open wlan
|
||||
bestNetworkDb = rssi_scan;
|
||||
bestChannel = chan_scan;
|
||||
memcpy((void *)&bestNetwork, (void *)&entry, sizeof(bestNetwork));
|
||||
memcpy((void *)&bestBSSID, (void *)BSSID_scan, sizeof(bestBSSID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (known) {
|
||||
LT_D(
|
||||
" ---> %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
|
||||
i,
|
||||
chan_scan,
|
||||
BSSID_scan[0],
|
||||
BSSID_scan[1],
|
||||
BSSID_scan[2],
|
||||
BSSID_scan[3],
|
||||
BSSID_scan[4],
|
||||
BSSID_scan[5],
|
||||
ssid_scan.c_str(),
|
||||
rssi_scan,
|
||||
(sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*'
|
||||
);
|
||||
} else {
|
||||
LT_D(
|
||||
" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c",
|
||||
i,
|
||||
chan_scan,
|
||||
BSSID_scan[0],
|
||||
BSSID_scan[1],
|
||||
BSSID_scan[2],
|
||||
BSSID_scan[3],
|
||||
BSSID_scan[4],
|
||||
BSSID_scan[5],
|
||||
ssid_scan.c_str(),
|
||||
rssi_scan,
|
||||
(sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up ram
|
||||
WiFi.scanDelete();
|
||||
|
||||
if (bestNetwork.ssid) {
|
||||
LT_I(
|
||||
"Connecting to BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)",
|
||||
bestBSSID[0],
|
||||
bestBSSID[1],
|
||||
bestBSSID[2],
|
||||
bestBSSID[3],
|
||||
bestBSSID[4],
|
||||
bestBSSID[5],
|
||||
bestNetwork.ssid,
|
||||
bestChannel,
|
||||
bestNetworkDb
|
||||
);
|
||||
|
||||
WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
|
||||
status = WiFi.status();
|
||||
|
||||
auto startTime = millis();
|
||||
// wait for connection, fail, or timeout
|
||||
while (status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED &&
|
||||
(millis() - startTime) <= connectTimeout) {
|
||||
delay(10);
|
||||
status = WiFi.status();
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case WL_CONNECTED:
|
||||
LT_I("Connecting done");
|
||||
LT_D("SSID: %s", WiFi.SSID().c_str());
|
||||
LT_D("IP: %s", WiFi.localIP().toString().c_str());
|
||||
LT_D("MAC: %s", WiFi.BSSIDstr().c_str());
|
||||
LT_D("Channel: %d", WiFi.channel());
|
||||
break;
|
||||
case WL_NO_SSID_AVAIL:
|
||||
LT_E("Connecting failed; AP not found");
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
LT_E("Connecting failed");
|
||||
break;
|
||||
default:
|
||||
LT_E("Connecting failed (%d)", status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LT_E("No matching network found!");
|
||||
}
|
||||
} else {
|
||||
// start scan
|
||||
LT_V("Delete old wifi config...");
|
||||
WiFi.disconnect();
|
||||
|
||||
LT_D("Start scan");
|
||||
// scan wifi async mode
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
47
arduino/libretuya/libraries/WiFiMulti/WiFiMulti.h
Normal file
47
arduino/libretuya/libraries/WiFiMulti/WiFiMulti.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
*
|
||||
* @file ESP8266WiFiMulti.h
|
||||
* @date 16.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WiFi.h"
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
char *ssid;
|
||||
char *passphrase;
|
||||
} WifiAPlist_t;
|
||||
|
||||
class WiFiMulti {
|
||||
public:
|
||||
WiFiMulti();
|
||||
~WiFiMulti();
|
||||
|
||||
bool addAP(const char *ssid, const char *passphrase = NULL);
|
||||
|
||||
uint8_t run(uint32_t connectTimeout = 10000);
|
||||
|
||||
private:
|
||||
std::vector<WifiAPlist_t> APlist;
|
||||
};
|
||||
2
arduino/libretuya/libraries/base64/.clang-format
Normal file
2
arduino/libretuya/libraries/base64/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
64
arduino/libretuya/libraries/base64/base64.cpp
Normal file
64
arduino/libretuya/libraries/base64/base64.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* base64.cpp
|
||||
*
|
||||
* Created on: 09.12.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP31B core for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "libb64/cdecode.h"
|
||||
#include "libb64/cencode.h"
|
||||
}
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param data const uint8_t *
|
||||
* @param length size_t
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const uint8_t * data, size_t length)
|
||||
{
|
||||
size_t size = base64_encode_expected_len(length) + 1;
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
return base64;
|
||||
}
|
||||
return String("-FAIL-");
|
||||
}
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param text const String&
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const String& text)
|
||||
{
|
||||
return base64::encode((uint8_t *) text.c_str(), text.length());
|
||||
}
|
||||
|
||||
9
arduino/libretuya/libraries/base64/base64.h
Normal file
9
arduino/libretuya/libraries/base64/base64.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class base64 {
|
||||
public:
|
||||
static String encode(const uint8_t *data, size_t length);
|
||||
static String encode(const String &text);
|
||||
};
|
||||
7
arduino/libretuya/libraries/base64/libb64/AUTHORS
Normal file
7
arduino/libretuya/libraries/base64/libb64/AUTHORS
Normal file
@@ -0,0 +1,7 @@
|
||||
libb64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Authors:
|
||||
-------
|
||||
|
||||
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
||||
29
arduino/libretuya/libraries/base64/libb64/LICENSE
Normal file
29
arduino/libretuya/libraries/base64/libb64/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
||||
99
arduino/libretuya/libraries/base64/libb64/cdecode.c
Normal file
99
arduino/libretuya/libraries/base64/libb64/cdecode.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cdecode.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static int base64_decode_value_signed(int8_t value_in){
|
||||
static const int8_t decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static const int8_t decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in >= decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in){
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in){
|
||||
const int8_t* codechar = code_in;
|
||||
int8_t* plainchar = plaintext_out;
|
||||
int8_t fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step){
|
||||
while (1){
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in){
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out){
|
||||
base64_decodestate _state;
|
||||
base64_init_decodestate(&_state);
|
||||
int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state);
|
||||
if(len > 0) plaintext_out[len] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
int base64_decode_value(char value_in){
|
||||
return base64_decode_value_signed(*((int8_t *) &value_in));
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in){
|
||||
return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in);
|
||||
}
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out){
|
||||
return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out);
|
||||
}
|
||||
38
arduino/libretuya/libraries/base64/libb64/cdecode.h
Normal file
38
arduino/libretuya/libraries/base64/libb64/cdecode.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CDECODE_H
|
||||
#define BASE64_CDECODE_H
|
||||
|
||||
#define base64_decode_expected_len(n) ((n * 3) / 4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* BASE64_CDECODE_H */
|
||||
102
arduino/libretuya/libraries/base64/libb64/cencode.c
Normal file
102
arduino/libretuya/libraries/base64/libb64/cencode.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) {
|
||||
return '=';
|
||||
}
|
||||
return encoding[(int)value_in];
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step) {
|
||||
while (1) {
|
||||
case step_A:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
case step_B:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
case step_C:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step) {
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out)
|
||||
{
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(plaintext_in, length_in, code_out, &_state);
|
||||
return len + base64_encode_blockend((code_out + len), &_state);
|
||||
}
|
||||
41
arduino/libretuya/libraries/base64/libb64/cencode.h
Normal file
41
arduino/libretuya/libraries/base64/libb64/cencode.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CENCODE_H
|
||||
#define BASE64_CENCODE_H
|
||||
|
||||
#define base64_encode_expected_len(n) ((((4 * n) / 3) + 3) & ~3)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* BASE64_CENCODE_H */
|
||||
@@ -1,58 +1,29 @@
|
||||
/*
|
||||
Arduino.h - Main include file for the Arduino SDK
|
||||
Copyright (c) 2005-2013 Arduino Team. 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
|
||||
*/
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-23. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "WCharacterFixup.h"
|
||||
#endif
|
||||
|
||||
#define PinMode PinModeArduino // this conflicts with SDK enum
|
||||
#include <api/ArduinoAPI.h>
|
||||
#include <api/LibreTuyaAPI.h>
|
||||
#undef PinMode
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
extern "C" uint32_t SystemCoreClock;
|
||||
#else
|
||||
extern uint32_t SystemCoreClock;
|
||||
|
||||
#define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L )
|
||||
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) )
|
||||
#define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) )
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
#define clockCyclesPerMicrosecond() (SystemCoreClock / 1000000L)
|
||||
#define clockCyclesToMicroseconds(a) (((a)*1000L) / (SystemCoreClock / 1000L))
|
||||
#define microsecondsToClockCycles(a) ((a) * (SystemCoreClock / 1000000L))
|
||||
|
||||
// Include board variant
|
||||
#include "variant.h"
|
||||
|
||||
#define interrupts() vPortClearInterruptMask(0)
|
||||
#define interrupts() vPortClearInterruptMask(0)
|
||||
#define noInterrupts() ulPortSetInterruptMask()
|
||||
|
||||
/*
|
||||
* \brief SAM3 products have only one reference for ADC
|
||||
*/
|
||||
typedef enum _eAnalogReference
|
||||
{
|
||||
AR_DEFAULT,
|
||||
} eAnalogReference ;
|
||||
// Include platform-specific code
|
||||
#include "WVariant.h"
|
||||
// Include board variant
|
||||
#include "variant.h"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include <api/Print.h>
|
||||
|
||||
class CountingStream : public Print
|
||||
{
|
||||
virtual size_t write(uint8_t) { return 1; };
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) { return size; };
|
||||
};
|
||||
@@ -1,198 +0,0 @@
|
||||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2015 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef MBED_FUNCTIONPOINTER_H
|
||||
#define MBED_FUNCTIONPOINTER_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* If we had variaditic templates, this wouldn't be a problem, but until C++11 is enabled, we are stuck with multiple classes... */
|
||||
|
||||
/** A class for storing and calling a pointer to a static or member function
|
||||
*/
|
||||
template <typename R, typename A1>
|
||||
class FunctionPointerArg1{
|
||||
public:
|
||||
/** Create a FunctionPointer, attaching a static function
|
||||
*
|
||||
* @param function The static function to attach (default is none)
|
||||
*/
|
||||
FunctionPointerArg1(R (*function)(A1) = 0) {
|
||||
attach(function);
|
||||
}
|
||||
|
||||
/** Create a FunctionPointer, attaching a member function
|
||||
*
|
||||
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
|
||||
* @param function The address of the member function to attach
|
||||
*/
|
||||
template<typename T>
|
||||
FunctionPointerArg1(T *object, R (T::*member)(A1)) {
|
||||
attach(object, member);
|
||||
}
|
||||
|
||||
/** Attach a static function
|
||||
*
|
||||
* @param function The static function to attach (default is none)
|
||||
*/
|
||||
void attach(R (*function)(A1)) {
|
||||
_p.function = function;
|
||||
_membercaller = 0;
|
||||
}
|
||||
|
||||
/** Attach a member function
|
||||
*
|
||||
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
|
||||
* @param function The address of the member function to attach
|
||||
*/
|
||||
template<typename T>
|
||||
void attach(T *object, R (T::*member)(A1)) {
|
||||
_p.object = static_cast<void*>(object);
|
||||
*reinterpret_cast<R (T::**)(A1)>(_member) = member;
|
||||
_membercaller = &FunctionPointerArg1::membercaller<T>;
|
||||
}
|
||||
|
||||
/** Call the attached static or member function
|
||||
*/
|
||||
R call(A1 a) {
|
||||
if (_membercaller == 0 && _p.function) {
|
||||
return _p.function(a);
|
||||
} else if (_membercaller && _p.object) {
|
||||
return _membercaller(_p.object, _member, a);
|
||||
}
|
||||
return (R)0;
|
||||
}
|
||||
|
||||
/** Get registered static function
|
||||
*/
|
||||
R(*get_function(A1))() {
|
||||
return _membercaller ? (R(*)(A1))0 : (R(*)(A1))_p.function;
|
||||
}
|
||||
|
||||
R operator ()(A1 a) {
|
||||
return call(a);
|
||||
}
|
||||
operator bool(void) const {
|
||||
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static R membercaller(void *object, uintptr_t *member, A1 a) {
|
||||
T* o = static_cast<T*>(object);
|
||||
R (T::**m)(A1) = reinterpret_cast<R (T::**)(A1)>(member);
|
||||
return (o->**m)(a);
|
||||
}
|
||||
|
||||
union {
|
||||
R (*function)(A1); // static function pointer
|
||||
void *object; // object this pointer
|
||||
} _p;
|
||||
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
|
||||
R (*_membercaller)(void*, uintptr_t*, A1); // registered membercaller function to convert back and call _m.member on _object
|
||||
};
|
||||
|
||||
/** A class for storing and calling a pointer to a static or member function (R ()(void))
|
||||
*/
|
||||
template <typename R>
|
||||
class FunctionPointerArg1<R, void>{
|
||||
public:
|
||||
/** Create a FunctionPointer, attaching a static function
|
||||
*
|
||||
* @param function The static function to attach (default is none)
|
||||
*/
|
||||
FunctionPointerArg1(R (*function)(void) = 0) {
|
||||
attach(function);
|
||||
}
|
||||
|
||||
/** Create a FunctionPointer, attaching a member function
|
||||
*
|
||||
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
|
||||
* @param function The address of the void member function to attach
|
||||
*/
|
||||
template<typename T>
|
||||
FunctionPointerArg1(T *object, R (T::*member)(void)) {
|
||||
attach(object, member);
|
||||
}
|
||||
|
||||
/** Attach a static function
|
||||
*
|
||||
* @param function The void static function to attach (default is none)
|
||||
*/
|
||||
void attach(R (*function)(void)) {
|
||||
_p.function = function;
|
||||
_membercaller = 0;
|
||||
}
|
||||
|
||||
/** Attach a member function
|
||||
*
|
||||
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
|
||||
* @param function The address of the void member function to attach
|
||||
*/
|
||||
template<typename T>
|
||||
void attach(T *object, R (T::*member)(void)) {
|
||||
_p.object = static_cast<void*>(object);
|
||||
*reinterpret_cast<R (T::**)(void)>(_member) = member;
|
||||
_membercaller = &FunctionPointerArg1::membercaller<T>;
|
||||
}
|
||||
|
||||
/** Call the attached static or member function
|
||||
*/
|
||||
R call(){
|
||||
if (_membercaller == 0 && _p.function) {
|
||||
return _p.function();
|
||||
} else if (_membercaller && _p.object) {
|
||||
return _membercaller(_p.object, _member);
|
||||
}
|
||||
return (R)0;
|
||||
}
|
||||
|
||||
/** Get registered static function
|
||||
*/
|
||||
R(*get_function())() {
|
||||
return _membercaller ? (R(*)())0 : (R(*)())_p.function;
|
||||
}
|
||||
|
||||
R operator ()(void) {
|
||||
return call();
|
||||
}
|
||||
operator bool(void) const {
|
||||
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static R membercaller(void *object, uintptr_t *member) {
|
||||
T* o = static_cast<T*>(object);
|
||||
R (T::**m)(void) = reinterpret_cast<R (T::**)(void)>(member);
|
||||
return (o->**m)();
|
||||
}
|
||||
|
||||
union {
|
||||
R (*function)(void); // static function pointer
|
||||
void *object; // object this pointer
|
||||
} _p;
|
||||
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
|
||||
R (*_membercaller)(void*, uintptr_t*); // registered membercaller function to convert back and call _m.member on _object
|
||||
};
|
||||
|
||||
typedef FunctionPointerArg1<void, void> FunctionPointer;
|
||||
typedef FunctionPointerArg1<void, int> event_callback_t;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,117 +16,83 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "LOGUARTClass.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define LOG_UART_MODIFIABLE_BAUD_RATE 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "osdep_service.h"
|
||||
#include "rtl8710b.h"
|
||||
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
RingBuffer rx_buffer0;
|
||||
|
||||
LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer )
|
||||
{
|
||||
_rx_buffer = pRx_buffer;
|
||||
_dwIrq = dwIrq;
|
||||
LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer) {
|
||||
_rx_buffer = pRx_buffer;
|
||||
_dwIrq = dwIrq;
|
||||
}
|
||||
|
||||
// Protected Methods //////////////////////////////////////////////////////////////
|
||||
void IrqHandler(void) {
|
||||
uint8_t data = 0;
|
||||
BOOL PullMode = _FALSE;
|
||||
uint32_t IrqEn = DiagGetIsrEnReg();
|
||||
|
||||
DiagSetIsrEnReg(0);
|
||||
|
||||
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void IrqHandler( void )
|
||||
{
|
||||
|
||||
uint8_t data = 0;
|
||||
BOOL PullMode = _FALSE;
|
||||
uint32_t IrqEn = DiagGetIsrEnReg();
|
||||
|
||||
DiagSetIsrEnReg(0);
|
||||
|
||||
data = DiagGetChar(PullMode);
|
||||
if ( data > 0 )
|
||||
data = DiagGetChar(PullMode);
|
||||
if (data > 0)
|
||||
rx_buffer0.store_char(data);
|
||||
|
||||
DiagSetIsrEnReg(IrqEn);
|
||||
|
||||
DiagSetIsrEnReg(IrqEn);
|
||||
}
|
||||
|
||||
|
||||
void LOGUARTClass::begin( const uint32_t dwBaudRate )
|
||||
{
|
||||
DIAG_UartReInit((IRQ_FUN) IrqHandler);
|
||||
NVIC_SetPriority(UART_LOG_IRQ, 10);
|
||||
LOGUART_SetBaud(dwBaudRate);
|
||||
void LOGUARTClass::begin(const uint32_t dwBaudRate) {
|
||||
DIAG_UartReInit((IRQ_FUN)IrqHandler);
|
||||
NVIC_SetPriority(UART_LOG_IRQ, 10);
|
||||
LOGUART_SetBaud(dwBaudRate);
|
||||
}
|
||||
|
||||
void LOGUARTClass::end( void )
|
||||
{
|
||||
// clear any received data
|
||||
_rx_buffer->_iHead = _rx_buffer->_iTail ;
|
||||
void LOGUARTClass::end(void) {
|
||||
// clear any received data
|
||||
_rx_buffer->_iHead = _rx_buffer->_iTail;
|
||||
}
|
||||
|
||||
int LOGUARTClass::available( void )
|
||||
{
|
||||
return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ;
|
||||
int LOGUARTClass::available(void) {
|
||||
return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
int LOGUARTClass::peek( void )
|
||||
{
|
||||
int LOGUARTClass::peek(void) {
|
||||
|
||||
if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
|
||||
return -1 ;
|
||||
|
||||
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
|
||||
if (_rx_buffer->_iHead == _rx_buffer->_iTail)
|
||||
return -1;
|
||||
|
||||
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
|
||||
}
|
||||
|
||||
int LOGUARTClass::read( void )
|
||||
{
|
||||
// if the head isn't ahead of the tail, we don't have any characters
|
||||
if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
|
||||
return -1 ;
|
||||
|
||||
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
|
||||
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ;
|
||||
return uc ;
|
||||
int LOGUARTClass::read(void) {
|
||||
// if the head isn't ahead of the tail, we don't have any characters
|
||||
if (_rx_buffer->_iHead == _rx_buffer->_iTail)
|
||||
return -1;
|
||||
|
||||
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
|
||||
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE;
|
||||
return uc;
|
||||
}
|
||||
|
||||
void LOGUARTClass::flush( void )
|
||||
{
|
||||
// TODO:
|
||||
// while ( serial_writable(&(this->sobj)) != 1 );
|
||||
/*
|
||||
// Wait for transmission to complete
|
||||
while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
|
||||
;
|
||||
*/
|
||||
void LOGUARTClass::flush(void) {
|
||||
// TODO:
|
||||
// while ( serial_writable(&(this->sobj)) != 1 );
|
||||
/*
|
||||
// Wait for transmission to complete
|
||||
while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
|
||||
;
|
||||
*/
|
||||
}
|
||||
|
||||
size_t LOGUARTClass::write( const uint8_t uc_data )
|
||||
{
|
||||
DiagPutChar(uc_data);
|
||||
return 1;
|
||||
size_t LOGUARTClass::write(const uint8_t uc_data) {
|
||||
DiagPutChar(uc_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOGUARTClass Serial(UART_LOG_IRQ, &rx_buffer0);
|
||||
|
||||
bool Serial_available() {
|
||||
return Serial.available() > 0;
|
||||
return Serial.available() > 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
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.
|
||||
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
|
||||
@@ -18,36 +18,41 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/HardwareSerial.h"
|
||||
#include "api/RingBuffer.h"
|
||||
#include <api/HardwareSerial.h>
|
||||
#include <api/RingBuffer.h>
|
||||
|
||||
using namespace arduino;
|
||||
|
||||
class LOGUARTClass : public HardwareSerial
|
||||
{
|
||||
// TODO this class begs to be rewritten :(
|
||||
|
||||
class LOGUARTClass : public HardwareSerial {
|
||||
public:
|
||||
LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer );
|
||||
LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer);
|
||||
|
||||
void begin(const uint32_t dwBaudRate);
|
||||
inline void begin(const uint32_t dwBaudRate, uint16_t config) {
|
||||
begin(dwBaudRate); // TODO implement this properly
|
||||
}
|
||||
void end(void);
|
||||
int available(void);
|
||||
int peek(void);
|
||||
int read(void);
|
||||
void flush(void);
|
||||
size_t write(const uint8_t c);
|
||||
void begin(const uint32_t dwBaudRate);
|
||||
|
||||
using Print::write; // pull in write(str) and write(buf, size) from Print
|
||||
inline void begin(const uint32_t dwBaudRate, uint16_t config) {
|
||||
begin(dwBaudRate); // TODO implement this properly
|
||||
}
|
||||
|
||||
operator bool() { return true; }; // UART always active
|
||||
void end(void);
|
||||
int available(void);
|
||||
int peek(void);
|
||||
int read(void);
|
||||
void flush(void);
|
||||
size_t write(const uint8_t c);
|
||||
|
||||
using Print::write; // pull in write(str) and write(buf, size) from Print
|
||||
|
||||
operator bool() {
|
||||
return true; // UART always active
|
||||
}
|
||||
|
||||
protected:
|
||||
RingBuffer *_rx_buffer;
|
||||
RingBuffer *_rx_buffer;
|
||||
|
||||
int _dwIrq;
|
||||
int _dwIrq;
|
||||
|
||||
private:
|
||||
friend bool Serial_available();
|
||||
friend bool Serial_available();
|
||||
};
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#ifndef _POWER_MANAGEMENT_H_
|
||||
#define _POWER_MANAGEMENT_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/**
|
||||
* @class PowerManagementClass PowerManagement.h
|
||||
* @brief Power management in Ameba
|
||||
*/
|
||||
class PowerManagementClass {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Allow OS automatically save power while idle
|
||||
*
|
||||
* As OS consider it would idle for more than 2s, it will invoke system suspend.
|
||||
* If wlan is associated with AP, than it will under asslociated idle state.
|
||||
*/
|
||||
static void sleep(void);
|
||||
static void sleep(uint32_t bitflg);
|
||||
|
||||
/**
|
||||
* @brief Disallow OS automatically save power while idle
|
||||
*/
|
||||
static void active(void);
|
||||
static void active(uint32_t bitflg);
|
||||
|
||||
/**
|
||||
* @brief Reserved PLL while sleep
|
||||
*
|
||||
* Reserve PLL would keep FIFO of peripherals (Ex. UART) but cost more power (around 5mA).
|
||||
* If we don't reserve PLL, it saves more power but we might missing data because FIFO is turned of this way.
|
||||
*
|
||||
* @param[in] reserve true for reserved, false for non-reserved
|
||||
*/
|
||||
static void setPllReserved(bool reserve);
|
||||
|
||||
/**
|
||||
* @brief Enter deepsleep immediately
|
||||
*
|
||||
* Invoke deepsleep would make system enter deepsleep state immediately.
|
||||
* It's the state that saves most power.
|
||||
* As it wakeup from deepsleep, the system would behave just like reboot.
|
||||
*
|
||||
* @param[in] duration_ms wakeup after specific time in unit of millisecond
|
||||
*/
|
||||
static void deepsleep(uint32_t duration_ms);
|
||||
|
||||
/**
|
||||
* @brief Check if system is allowed enter any power save state
|
||||
*
|
||||
* The pin 18 (GPIOE_5) is designed as safe lock.
|
||||
* If pin 18 is HIGH, then we prevent Ameba enter any power save state.\n\n
|
||||
* Under any power save state, we are not able to flash image to Ameba.
|
||||
* Thus if user misuse deepsleep and make Ameba enter deepsleep immediately after boot up,
|
||||
* then he would find it's hard to flash image.
|
||||
* In this case, he can pull up pin 18.
|
||||
*
|
||||
* @return true if system not allowed enter any power save state, and false vise versa
|
||||
*/
|
||||
static bool safeLock();
|
||||
|
||||
/**
|
||||
* @brief Reboot system
|
||||
*
|
||||
* Reboot system in soft way. Some registers is not powered off in this case, but mostly we could regard this as reboot.
|
||||
*/
|
||||
static void softReset();
|
||||
|
||||
private:
|
||||
static bool reservePLL;
|
||||
};
|
||||
|
||||
extern PowerManagementClass PowerManagement;
|
||||
|
||||
#endif
|
||||
@@ -1,17 +1,12 @@
|
||||
#if 1 // !defined(BOARD_RTL8710)
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern void pinRemoveMode(pin_size_t pinNumber);
|
||||
extern void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration);
|
||||
}
|
||||
#endif
|
||||
|
||||
// a wrapper that support default value of duration
|
||||
void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration)
|
||||
{
|
||||
_tone(ulPin, frequency, duration);
|
||||
void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) {
|
||||
_tone(ulPin, frequency, duration);
|
||||
}
|
||||
|
||||
#endif
|
||||
void noTone(uint32_t ulPin) {
|
||||
pinRemoveMode(ulPin);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "gpio_api.h"
|
||||
#include "gpio_irq_api.h"
|
||||
#include "gpio_irq_ex_api.h"
|
||||
#include <Arduino.h>
|
||||
#include <gpio_api.h>
|
||||
#include <gpio_irq_api.h>
|
||||
#include <gpio_irq_ex_api.h>
|
||||
|
||||
extern void *gpio_pin_struct[PINS_COUNT];
|
||||
extern void *gpio_irq_handler_list[PINS_COUNT];
|
||||
|
||||
extern bool pinInvalid(pin_size_t pinNumber);
|
||||
extern void pinRemoveMode(pin_size_t pinNumber);
|
||||
|
||||
void gpioIrqHandler(uint32_t id, gpio_irq_event event) {
|
||||
if (gpio_irq_handler_list[id] != NULL) {
|
||||
((void (*)(uint32_t, uint32_t))gpio_irq_handler_list[id])(id, (uint32_t)event);
|
||||
@@ -36,7 +34,7 @@ void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus
|
||||
|
||||
if (g_APinDescription[interruptNumber].ulPinType == NOT_INITIAL) {
|
||||
// allocate memory if pin not used before
|
||||
gpio = malloc(sizeof(gpio_irq_t));
|
||||
gpio = malloc(sizeof(gpio_irq_t));
|
||||
gpio_pin_struct[interruptNumber] = gpio;
|
||||
gpio_irq_init(gpio, g_APinDescription[interruptNumber].pinname, gpioIrqHandler, interruptNumber);
|
||||
g_APinDescription[interruptNumber].ulPinType = PIO_GPIO_IRQ;
|
||||
@@ -78,7 +76,3 @@ void detachInterrupt(pin_size_t interruptNumber) {
|
||||
}
|
||||
gpio_irq_handler_list[interruptNumber] = NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2011 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
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "api/Common.h"
|
||||
|
||||
extern "C" {
|
||||
extern void rtl_srandom( uint32_t seed );
|
||||
extern uint32_t rtl_random( void );
|
||||
}
|
||||
|
||||
#ifndef srand
|
||||
#define srand rtl_srandom
|
||||
#endif
|
||||
#ifndef rand
|
||||
#define rand rtl_random
|
||||
#endif
|
||||
|
||||
extern void randomSeed( uint32_t dwSeed )
|
||||
{
|
||||
if ( dwSeed != 0 )
|
||||
{
|
||||
srand( dwSeed ) ;
|
||||
}
|
||||
}
|
||||
|
||||
extern long random( long howbig )
|
||||
{
|
||||
if ( howbig == 0 )
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
return rand() % howbig;
|
||||
}
|
||||
|
||||
extern long random( long howsmall, long howbig )
|
||||
{
|
||||
if (howsmall >= howbig)
|
||||
{
|
||||
return howsmall;
|
||||
}
|
||||
|
||||
long diff = howbig - howsmall;
|
||||
|
||||
return random(diff) + howsmall;
|
||||
}
|
||||
|
||||
extern long map(long x, long in_min, long in_max, long out_min, long out_max)
|
||||
{
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
extern uint16_t makeWord( uint16_t w )
|
||||
{
|
||||
return w ;
|
||||
}
|
||||
|
||||
extern uint16_t makeWord( uint8_t h, uint8_t l )
|
||||
{
|
||||
return (h << 8) | l ;
|
||||
}
|
||||
@@ -1,104 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include "wiring_os.h"
|
||||
#include "wiring_watchdog.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#define boolean boolean_rtl
|
||||
#include "rtl_lib.h"
|
||||
#undef boolean
|
||||
#include "sdk_extern.h"
|
||||
#include "sdk_mem.h"
|
||||
#include "sdk_os.h"
|
||||
|
||||
/* moved from Arduino.h */
|
||||
/*
|
||||
* \brief Set CPU CLK 166MHz
|
||||
* clk : 0 - 166666666 Hz, 1 - 83333333 Hz, 2 - 41666666 Hz, 3 - 20833333 Hz, 4 - 10416666 Hz, 5 - 4000000 Hz
|
||||
* baud: 38400,...
|
||||
*/
|
||||
extern void Init_CPU_CLK_UART(int clkn, int baud);
|
||||
extern void sys_info(void);
|
||||
/* HalGetChipId:
|
||||
* 0xff - RTL8711AM, 0xfe - RTL8195AM, 0xfd - RTL8711AF,
|
||||
* 0xfc - RTL8710AF, 0xfb - RTL8711AN, 0xfa - RTL8710AM */
|
||||
extern unsigned char HalGetChipId(void);
|
||||
extern unsigned int HalGetCpuClk(void);
|
||||
|
||||
extern void wait_us(int us);
|
||||
#define delay_us wait_us
|
||||
|
||||
extern void yield(void);
|
||||
|
||||
#ifndef printf
|
||||
#define printf rtl_printf
|
||||
#endif
|
||||
#ifndef sprintf
|
||||
#define sprintf rtl_sprintf
|
||||
#endif
|
||||
|
||||
#define NOT_INITIAL (1UL<<0)
|
||||
#define PIO_GPIO (1UL<<1)
|
||||
#define PIO_PWM (1UL<<2)
|
||||
#define PIO_I2C (1UL<<3)
|
||||
#define PIO_ADC (1UL<<4)
|
||||
#define PIO_DAC (1UL<<5)
|
||||
#define PIO_GPIO_IRQ (1UL<<6)
|
||||
#define NOT_INITIAL (1UL << 0)
|
||||
#define PIO_GPIO (1UL << 1)
|
||||
#define PIO_PWM (1UL << 2)
|
||||
#define PIO_I2C (1UL << 3)
|
||||
#define PIO_ADC (1UL << 4)
|
||||
#define PIO_DAC (1UL << 5)
|
||||
#define PIO_GPIO_IRQ (1UL << 6)
|
||||
|
||||
#define PWM_MODE_ENABLED 1
|
||||
#define PWM_MODE_DISABLED 0
|
||||
|
||||
/* Types used for the tables below */
|
||||
typedef struct _PinDescription
|
||||
{
|
||||
// HW PinNames
|
||||
uint32_t pinname;
|
||||
// Current Pin Type
|
||||
uint32_t ulPinType;
|
||||
// Supported Pin Function
|
||||
uint32_t ulPinAttribute;
|
||||
// Current Pin Mode
|
||||
uint32_t ulPinMode;
|
||||
} PinDescription ;
|
||||
|
||||
/* Pins table to be instanciated into variant.cpp */
|
||||
extern PinDescription g_APinDescription[];
|
||||
|
||||
extern bool pinInvalid(pin_size_t pinNumber);
|
||||
extern void pinRemoveMode(pin_size_t pinNumber);
|
||||
|
||||
/* moved from wiring_digital.h */
|
||||
/**************************** Extend API by RTK ***********************************/
|
||||
extern uint32_t digitalPinToPort( uint32_t pinNumber );
|
||||
extern uint32_t digitalPinToBitMask( uint32_t pinNumber );
|
||||
|
||||
/* moved from wiring_analog.h */
|
||||
/*
|
||||
* \brief Set the resolution of analogRead return values. Default is 10 bits (range from 0 to 1023).
|
||||
*
|
||||
* \param res
|
||||
*/
|
||||
extern void analogReadResolution(int res);
|
||||
/*
|
||||
* \brief Set the resolution of analogWrite parameters. Default is 8 bits (range from 0 to 255).
|
||||
*
|
||||
* \param res
|
||||
*/
|
||||
extern void analogWriteResolution(int res);
|
||||
extern void analogOutputInit( void ) ;
|
||||
extern void analogWritePeriod(int us);
|
||||
|
||||
/* moved from wiring_constants.h */
|
||||
#define DEFAULT 1
|
||||
#define DEFAULT 1
|
||||
#define EXTERNAL 0
|
||||
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
#define round(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x)-0.5))
|
||||
|
||||
extern uint32_t ulPortSetInterruptMask( void );
|
||||
extern void vPortClearInterruptMask( uint32_t ulNewMask );
|
||||
typedef enum _eAnalogReference {
|
||||
AR_DEFAULT,
|
||||
} eAnalogReference;
|
||||
|
||||
// Types used for the table below
|
||||
typedef struct _PinDescription {
|
||||
// HW PinNames
|
||||
uint32_t pinname;
|
||||
// Current Pin Type
|
||||
uint32_t ulPinType;
|
||||
// Supported Pin Function
|
||||
uint32_t ulPinAttribute;
|
||||
// Current Pin Mode
|
||||
uint32_t ulPinMode;
|
||||
} PinDescription;
|
||||
|
||||
// Pins table to be instantiated into variant.cpp
|
||||
extern PinDescription g_APinDescription[];
|
||||
|
||||
// Additional Wiring functions
|
||||
extern uint32_t digitalPinToPort(uint32_t pinNumber);
|
||||
extern uint32_t digitalPinToBitMask(uint32_t pinNumber);
|
||||
extern void analogReadResolution(int res);
|
||||
extern void analogWriteResolution(int res);
|
||||
extern void analogOutputInit(void);
|
||||
extern void analogWritePeriod(int us);
|
||||
extern void wait_for_debug();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* WebSocketClient + SSL/TSL
|
||||
* RTL8710AF pvvx 12/12/2016
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "WebSocketClient.h"
|
||||
|
||||
WebSocketClient::WebSocketClient() {
|
||||
}
|
||||
|
||||
WebSocketClient::WebSocketClient(char *url, int port, char *path, char* origin) {
|
||||
client = create_wsclient(url, port, path, origin);
|
||||
}
|
||||
|
||||
WebSocketClient::~WebSocketClient() {
|
||||
close();
|
||||
}
|
||||
|
||||
int WebSocketClient::begin(char *url, int port, char *path, char* origin) {
|
||||
client = create_wsclient(url, port, path, origin);
|
||||
if(client != NULL) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int WebSocketClient::connect() {
|
||||
if(client != NULL) return ws_connect_url(client);
|
||||
else return -1;
|
||||
}
|
||||
|
||||
void WebSocketClient::send(char* message, int message_len, int use_mask) {
|
||||
if(client != NULL && client->readyState > CLOSED) {
|
||||
ws_send(message, message_len, use_mask, client);
|
||||
ws_poll(0, client);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::sendBinary(uint8_t* message, int message_len, int use_mask) {
|
||||
if(client != NULL && client->readyState > CLOSED) {
|
||||
ws_sendBinary(message, message_len, use_mask, client);
|
||||
ws_poll(0, client);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::sendPing() {
|
||||
if(client != NULL && client->readyState > CLOSED) {
|
||||
ws_sendPing(client);
|
||||
ws_poll(0, client);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::poll(int timeout) {
|
||||
if(client != NULL) ws_poll(timeout, client);
|
||||
}
|
||||
|
||||
readyStateValues WebSocketClient::getReadyState() {
|
||||
if(client != NULL) return ws_getReadyState(client);
|
||||
else return CLOSED;
|
||||
}
|
||||
|
||||
void WebSocketClient::dispatch(void (*callback)(wsclient_context *, int))
|
||||
{
|
||||
if(client != NULL) ws_dispatch(callback);
|
||||
}
|
||||
|
||||
void WebSocketClient::close() {
|
||||
if(client != NULL) {
|
||||
ws_close(client);
|
||||
if(client->ssl) {
|
||||
free(client->ssl);
|
||||
client->ssl = NULL;
|
||||
}
|
||||
client = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void set_ssl_func(wsclient_context *wsclient); // in example_wsclient.c
|
||||
|
||||
void WebSocketClient::ssl_func_on(void)
|
||||
{
|
||||
set_ssl_func(client);
|
||||
/*
|
||||
client->fun_ops.ssl_fun_ops.memory_set_own = memory_set_own;
|
||||
client->fun_ops.ssl_fun_ops.net_connect = net_connect;
|
||||
client->fun_ops.ssl_fun_ops.ssl_init = ssl_init;
|
||||
client->fun_ops.ssl_fun_ops.ssl_set_endpoint = ssl_set_endpoint;
|
||||
client->fun_ops.ssl_fun_ops.ssl_set_authmode = ssl_set_authmode;
|
||||
client->fun_ops.ssl_fun_ops.ssl_set_rng = ssl_set_rng;
|
||||
client->fun_ops.ssl_fun_ops.ssl_set_bio = ssl_set_bio;
|
||||
client->fun_ops.ssl_fun_ops.ssl_handshake = ssl_handshake;
|
||||
client->fun_ops.ssl_fun_ops.net_close = net_close;
|
||||
client->fun_ops.ssl_fun_ops.ssl_free = ssl_free;
|
||||
client->fun_ops.ssl_fun_ops.ssl_read = ssl_read;
|
||||
client->fun_ops.ssl_fun_ops.ssl_write = ssl_write;
|
||||
client->fun_ops.ssl_fun_ops.net_recv = net_recv;
|
||||
client->fun_ops.ssl_fun_ops.net_send = net_send;
|
||||
client->ssl = (void *)zalloc(sizeof(struct _ssl_context)); // 380 bytes
|
||||
*/
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* WebSocketClient + SSL/TSL
|
||||
* RTL8710AF pvvx 12/12/2016
|
||||
*
|
||||
*/
|
||||
#ifndef WEBSOCKETCLIENT_H_
|
||||
#define WEBSOCKETCLIENT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
extern "C" {
|
||||
#include "libwsclient.h"
|
||||
#include "wsclient_api.h"
|
||||
}
|
||||
|
||||
class WebSocketClient {
|
||||
public:
|
||||
WebSocketClient();
|
||||
~WebSocketClient();
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : begin
|
||||
** Description : Creating the websocket client context structure
|
||||
** Input : url:websocket server's url
|
||||
** port:websocket server's port, if not given, default 80 for "ws", 443 for "wss"
|
||||
** origin: the address or url of your self
|
||||
** Return : Ok: 1
|
||||
** Failed: 0
|
||||
**************************************************************************************************/
|
||||
WebSocketClient(char *url, int port, char *path = NULL, char* origin = NULL);
|
||||
int begin(char *url, int port, char *path = NULL, char* origin = NULL);
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : connect
|
||||
** Description : Connecting to the websocket server
|
||||
** Input : wsclient: the websocket client context created by create_wsclientfunction
|
||||
** Return : Connected: the socket value
|
||||
** Failed: -1
|
||||
**************************************************************************************************/
|
||||
int connect();
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : ws_send
|
||||
** Description : Create the sending string data and copy to tx_buf
|
||||
** Input : message: the string that send to server(cannot exceeding the MAX_DATA_LEN
|
||||
** message_len: the length of the string
|
||||
** use_mask: 0/1; 1 means using mask for bynary
|
||||
** wsclient: the websocket client context
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void send(char* message, int message_len, int use_mask);
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : sendBinary
|
||||
** Description : Create the sending binary data and copy to tx_buf
|
||||
** Input : message: the binary that send to server(cannot exceeding the MAX_DATA_LEN
|
||||
** message_len: the length of the binary
|
||||
** use_mask: 0/1; 1 means using mask for bynary
|
||||
** wsclient: the websocket client context
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void sendBinary(uint8_t* message, int message_len, int use_mask);
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : sendPing
|
||||
** Description : Sending Ping to websocket server
|
||||
** Input : wsclient: the websocket client context
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void sendPing();
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : poll
|
||||
** Description : Receicing data from server and send the data in tx_buf
|
||||
** Input : timeout(in milliseconds)
|
||||
wsclient: the websocket client context
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void poll(int timeout);
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : dispatch
|
||||
** Description : callback function when getting message from server
|
||||
** Input : function that resolve the message received and the message length
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void dispatch(void (*callback)(wsclient_context *, int));
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : getReadyState
|
||||
** Description : Getting the connection status
|
||||
** Input : wsclient: the websocket client context
|
||||
** Return : readyStateValues(4 types: CLOSING, CLOSED, CONNECTING, OPEN )
|
||||
**************************************************************************************************/
|
||||
readyStateValues getReadyState();
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : close
|
||||
** Description : Closing the connection with websocket server
|
||||
** Input : wsclient: the websocket client context
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void close();
|
||||
|
||||
/*************************************************************************************************
|
||||
** Function Name : ssl_func_on
|
||||
** Description : Set SSL/TSL function
|
||||
** Input : None
|
||||
** Return : None
|
||||
**************************************************************************************************/
|
||||
void ssl_func_on(void);
|
||||
|
||||
private:
|
||||
wsclient_context *client;
|
||||
};
|
||||
|
||||
#endif // WEBSOCKETCLIENT_H_
|
||||
@@ -1,214 +0,0 @@
|
||||
#include <platform_opts.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netif.h>
|
||||
#include <platform/platform_stdlib.h>
|
||||
#include "ard_socket.h"
|
||||
|
||||
int start_server(uint16_t port, uint8_t protMode)
|
||||
{
|
||||
int _sock;
|
||||
int timeout;
|
||||
|
||||
if(protMode == 0) {
|
||||
timeout = 3000;
|
||||
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
} else {
|
||||
timeout = 1000;
|
||||
_sock = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
if (_sock < 0) {
|
||||
printf("\r\nERROR opening socket\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in localHost;
|
||||
memset(&localHost, 0, sizeof(localHost));
|
||||
|
||||
localHost.sin_family = AF_INET;
|
||||
localHost.sin_port = htons(port);
|
||||
localHost.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (lwip_bind(_sock, (struct sockaddr *)&localHost, sizeof(localHost)) < 0) {
|
||||
printf("\r\nERROR on binding\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _sock;
|
||||
}
|
||||
|
||||
int sock_listen(int sock, int max)
|
||||
{
|
||||
if(lwip_listen(sock , max) < 0){
|
||||
printf("\r\nERROR on listening\r\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_available(int sock)
|
||||
{
|
||||
int enable = 1;
|
||||
int timeout;
|
||||
int client_fd;
|
||||
int err;
|
||||
struct sockaddr_in cli_addr;
|
||||
|
||||
socklen_t client = sizeof(cli_addr);
|
||||
|
||||
do {
|
||||
client_fd = lwip_accept(sock, (struct sockaddr *) &cli_addr, &client);
|
||||
if (client_fd < 0) {
|
||||
err = get_sock_errno(sock);
|
||||
if (err != EAGAIN) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (client_fd < 0);
|
||||
|
||||
if(client_fd < 0){
|
||||
printf("\r\nERROR on accept\r\n");
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
timeout = 3000;
|
||||
lwip_setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
timeout = 30000;
|
||||
lwip_setsockopt(client_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
lwip_setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||
lwip_setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
|
||||
printf("\r\nA client connected to this server :\r\n[PORT]: %d\r\n[IP]:%s\r\n\r\n", ntohs(cli_addr.sin_port), inet_ntoa(cli_addr.sin_addr.s_addr));
|
||||
return client_fd;
|
||||
}
|
||||
}
|
||||
|
||||
int get_receive(int sock, uint8_t* data, int length, int flag, uint32_t *peer_addr, uint16_t *peer_port)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr from;
|
||||
socklen_t fromlen;
|
||||
|
||||
uint8_t backup_recvtimeout = 0;
|
||||
int backup_recv_timeout, recv_timeout, len;
|
||||
|
||||
if (flag & 0x01) {
|
||||
// for MSG_PEEK, we try to peek packets by changing receiving timeout to 10ms
|
||||
ret = lwip_getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, &len);
|
||||
if (ret >= 0) {
|
||||
recv_timeout = 10;
|
||||
ret = lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout, sizeof(recv_timeout));
|
||||
if (ret >= 0) {
|
||||
backup_recvtimeout = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = lwip_recvfrom(sock, data, length, flag, &from, &fromlen);
|
||||
if ( ret >= 0 ) {
|
||||
if (peer_addr != NULL) {
|
||||
*peer_addr = ((struct sockaddr_in *)&from)->sin_addr.s_addr;
|
||||
}
|
||||
if (peer_port != NULL) {
|
||||
*peer_port = ntohs(((struct sockaddr_in *)&from)->sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flag & 0x01) && (backup_recvtimeout == 1)) {
|
||||
// restore receiving timeout
|
||||
lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, sizeof(recv_timeout));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_sock_errno(int sock)
|
||||
{
|
||||
int so_error;
|
||||
socklen_t len = sizeof(so_error);
|
||||
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||
return so_error;
|
||||
}
|
||||
|
||||
int set_sock_recv_timeout(int sock, int timeout)
|
||||
{
|
||||
return lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
void stop_socket(int sock)
|
||||
{
|
||||
lwip_close(sock);
|
||||
}
|
||||
|
||||
int send_data(int sock, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lwip_write(sock, data, len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sendto_data(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct sockaddr_in peer_addr;
|
||||
memset(&peer_addr, 0, sizeof(peer_addr));
|
||||
peer_addr.sin_family = AF_INET;
|
||||
peer_addr.sin_addr.s_addr = peer_ip;
|
||||
peer_addr.sin_port = htons(peer_port);
|
||||
|
||||
ret = lwip_sendto(sock, data, len, 0, (struct sockaddr*)&peer_addr, sizeof(struct sockaddr_in));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int start_client(uint32_t ipAddress, uint16_t port, uint8_t protMode)
|
||||
{
|
||||
int enable = 1;
|
||||
int timeout;
|
||||
int _sock;
|
||||
|
||||
if(protMode == 0)//tcp
|
||||
_sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
else
|
||||
_sock = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (_sock < 0) {
|
||||
printf("\r\nERROR opening socket\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = ipAddress;
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
if (protMode == 0){//TCP MODE
|
||||
if(connect(_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0){
|
||||
printf("\r\nConnect to Server successful!\r\n");
|
||||
|
||||
timeout = 3000;
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
timeout = 30000;
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
lwip_setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
|
||||
lwip_setsockopt(_sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
|
||||
|
||||
return _sock;
|
||||
}
|
||||
else{
|
||||
printf("\r\nConnect to Server failed!\r\n");
|
||||
stop_socket(_sock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//printf("\r\nUdp client setup Server's information successful!\r\n");
|
||||
}
|
||||
|
||||
return _sock;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef ARD_SOCKET_H
|
||||
#define ARD_SOCKET_H
|
||||
#include "main.h"
|
||||
|
||||
int start_server(uint16_t port, uint8_t protMode);
|
||||
|
||||
int sock_listen(int sock, int max);
|
||||
|
||||
int get_available(int sock);
|
||||
|
||||
int get_receive(int sock, uint8_t* data, int length, int flag, uint32_t *peer_addr, uint16_t *peer_port);
|
||||
|
||||
int get_sock_errno(int sock);
|
||||
|
||||
int set_sock_recv_timeout(int sock, int timeout);
|
||||
|
||||
void stop_socket(int sock);
|
||||
|
||||
int send_data(int sock, const uint8_t *data, uint16_t len);
|
||||
|
||||
int sendto_data(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port);
|
||||
|
||||
int start_client(uint32_t ipAddress, uint16_t port, uint8_t protMode);
|
||||
|
||||
#endif
|
||||
@@ -1,402 +0,0 @@
|
||||
#include "Arduino.h"
|
||||
#include <sockets.h>
|
||||
#include <lwip/netif.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/net_sockets.h>
|
||||
#include <mbedtls/error.h>
|
||||
#include <mbedtls/debug.h>
|
||||
#include "ard_ssl.h"
|
||||
|
||||
#define ARDUINO_MBEDTLS_DEBUG_LEVEL 0 // Set to 0 to disable debug messsages, 5 to enable all debug messages
|
||||
|
||||
static unsigned int ard_ssl_arc4random(void)
|
||||
{
|
||||
unsigned int res = xTaskGetTickCount();
|
||||
static unsigned int seed = 0xDEADB00B;
|
||||
|
||||
seed = ((seed & 0x007F00FF) << 7) ^
|
||||
((seed & 0x0F80FF00) >> 8) ^ // be sure to stir those low bits
|
||||
(res << 13) ^ (res >> 9); // using the clock too!
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
static void get_random_bytes(void *buf, size_t len)
|
||||
{
|
||||
unsigned int ranbuf;
|
||||
unsigned int *lp;
|
||||
int i, count;
|
||||
count = len / sizeof(unsigned int);
|
||||
lp = (unsigned int *) buf;
|
||||
|
||||
for (i = 0; i < count; i ++) {
|
||||
lp[i] = ard_ssl_arc4random();
|
||||
len -= sizeof(unsigned int);
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
ranbuf = ard_ssl_arc4random();
|
||||
memcpy(&lp[i], &ranbuf, len);
|
||||
}
|
||||
}
|
||||
|
||||
static int my_random(void *p_rng, unsigned char *output, size_t output_len)
|
||||
{
|
||||
p_rng = p_rng;
|
||||
get_random_bytes(output, output_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_verify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
|
||||
{
|
||||
char buf[1024];
|
||||
((void)data);
|
||||
|
||||
mbedtls_x509_crt_info(buf, (sizeof(buf) - 1), "", crt);
|
||||
|
||||
if(ARDUINO_MBEDTLS_DEBUG_LEVEL < 3)
|
||||
return(0);
|
||||
|
||||
printf( "\nVerify requested for (Depth %d):\n", depth );
|
||||
printf( "%s", buf );
|
||||
|
||||
if ((*flags) == 0)
|
||||
printf(" This certificate has no flags\n");
|
||||
else
|
||||
{
|
||||
mbedtls_x509_crt_verify_info(buf, sizeof( buf ), " ! ", *flags);
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void* my_calloc(size_t nelements, size_t elementSize)
|
||||
{
|
||||
size_t size;
|
||||
void *ptr = NULL;
|
||||
|
||||
size = nelements * elementSize;
|
||||
ptr = pvPortMalloc(size);
|
||||
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void my_debug(void *ctx, int level, const char *file, int line, const char *str )
|
||||
{
|
||||
const char *p, *basename;
|
||||
|
||||
ctx = ctx; // Remove unused parameter warning
|
||||
// Extract basename from file
|
||||
for( p = basename = file; *p != '\0'; p++ )
|
||||
if( *p == '/' || *p == '\\' )
|
||||
basename = p + 1;
|
||||
|
||||
printf("%s:%04d: |%d| %s", basename, line, level, str );
|
||||
}
|
||||
|
||||
int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char* rootCABuff, unsigned char* cli_cert, unsigned char* cli_key, unsigned char* pskIdent, unsigned char* psKey, char* SNI_hostname)
|
||||
{
|
||||
int ret = 0;
|
||||
//int timeout;
|
||||
int enable = 1;
|
||||
int keep_idle = 30;
|
||||
mbedtls_x509_crt* cacert = NULL;
|
||||
mbedtls_x509_crt* _cli_crt = NULL;
|
||||
mbedtls_pk_context* _clikey_rsa = NULL;
|
||||
|
||||
do {
|
||||
ssl_client->socket = -1;
|
||||
ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (ssl_client->socket < 0) {
|
||||
printf("ERROR: opening socket failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = ipAddress;
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
|
||||
lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle));
|
||||
if (lwip_connect(ssl_client->socket, ((struct sockaddr *)&serv_addr), sizeof(serv_addr)) < 0) {
|
||||
lwip_close(ssl_client->socket);
|
||||
printf("ERROR: Connect to Server failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
} else {
|
||||
/*/
|
||||
if (lwip_connect(ssl_client->socket, ((struct sockaddr *)&serv_addr), sizeof(serv_addr)) == 0) {
|
||||
timeout = ssl_client->recvTimeout;
|
||||
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
timeout = 30000;
|
||||
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
|
||||
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
|
||||
} else {
|
||||
printf("ERROR: Connect to Server failed!\r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}//*/
|
||||
|
||||
mbedtls_platform_set_calloc_free(my_calloc,vPortFree);
|
||||
|
||||
ssl_client->ssl = (mbedtls_ssl_context *)malloc(sizeof(mbedtls_ssl_context));
|
||||
ssl_client->conf = (mbedtls_ssl_config *)malloc(sizeof(mbedtls_ssl_config));
|
||||
if (( ssl_client->ssl == NULL )||( ssl_client->conf == NULL )) {
|
||||
printf("ERROR: malloc ssl failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
mbedtls_ssl_init(ssl_client->ssl);
|
||||
mbedtls_ssl_config_init(ssl_client->conf);
|
||||
|
||||
if (ARDUINO_MBEDTLS_DEBUG_LEVEL > 0) {
|
||||
mbedtls_ssl_conf_verify(ssl_client->conf, my_verify, NULL);
|
||||
mbedtls_ssl_conf_dbg(ssl_client->conf, my_debug, NULL);
|
||||
mbedtls_debug_set_threshold(ARDUINO_MBEDTLS_DEBUG_LEVEL);
|
||||
}
|
||||
|
||||
if((mbedtls_ssl_config_defaults(ssl_client->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
printf("ERROR: mbedtls ssl config defaults failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_rng(ssl_client->conf, my_random, NULL);
|
||||
|
||||
if (rootCABuff != NULL) {
|
||||
// Configure mbedTLS to use certificate authentication method
|
||||
cacert = (mbedtls_x509_crt *) mbedtls_calloc( sizeof(mbedtls_x509_crt), 1);
|
||||
mbedtls_x509_crt_init(cacert);
|
||||
if (mbedtls_x509_crt_parse(cacert, rootCABuff, (strlen((char*)rootCABuff)) + 1) != 0) {
|
||||
printf("ERROR: mbedtls x509 crt parse failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
mbedtls_ssl_conf_ca_chain(ssl_client->conf, cacert, NULL);
|
||||
mbedtls_ssl_conf_authmode(ssl_client->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
} else if (pskIdent != NULL && psKey != NULL) {
|
||||
// Configure mbedTLS to use PSK authentication method
|
||||
// Check for max length and even number of chars
|
||||
uint16_t pskey_char_len = strlen((char*)psKey);
|
||||
if ( ((pskey_char_len % 2) != 0) || (pskey_char_len > 2*MBEDTLS_PSK_MAX_LEN) ) {
|
||||
printf("ERROR: TLS PSK not in valid hex format or too long \n");
|
||||
return -1;
|
||||
}
|
||||
uint16_t psk_len = pskey_char_len/2;
|
||||
unsigned char psk[MBEDTLS_PSK_MAX_LEN];
|
||||
// Convert PSK from hexadecimal chars to binary
|
||||
int i;
|
||||
for (i = 0; i < pskey_char_len; i = i + 2) {
|
||||
char c = psKey[i];
|
||||
// Convert first 4 bits
|
||||
if (c >= '0' && c <= '9') {
|
||||
c = c - '0';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
c = c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
c = c - 'a' + 10;
|
||||
} else {
|
||||
printf("ERROR: TLS PSK not in valid hex format \n");
|
||||
return -1;
|
||||
}
|
||||
psk[i/2] = c << 4;
|
||||
c = psKey[i+1];
|
||||
// Convert next 4 bits
|
||||
if (c >= '0' && c <= '9') {
|
||||
c = c - '0';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
c = c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
c = c - 'a' + 10;
|
||||
} else {
|
||||
printf("ERROR: TLS PSK not in valid hex format \r\n");
|
||||
return -1;
|
||||
}
|
||||
psk[i/2] |= c;
|
||||
}
|
||||
if (mbedtls_ssl_conf_psk(ssl_client->conf, psk, psk_len, pskIdent, strlen((char*)pskIdent)) != 0) {
|
||||
printf("ERROR: mbedtls conf psk failed! \r\n");
|
||||
}
|
||||
} else {
|
||||
mbedtls_ssl_conf_authmode(ssl_client->conf, MBEDTLS_SSL_VERIFY_NONE);
|
||||
}
|
||||
|
||||
if ((cli_cert != NULL) && (cli_key != NULL)) {
|
||||
_cli_crt = (mbedtls_x509_crt *) mbedtls_calloc( sizeof(mbedtls_x509_crt), 1);
|
||||
if (_cli_crt == NULL) {
|
||||
printf("ERROR: malloc client_crt failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
mbedtls_x509_crt_init(_cli_crt);
|
||||
|
||||
_clikey_rsa = (mbedtls_pk_context *) mbedtls_calloc( sizeof(mbedtls_pk_context), 1);
|
||||
if (_clikey_rsa == NULL) {
|
||||
printf("ERROR: malloc client_rsa failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
mbedtls_pk_init(_clikey_rsa);
|
||||
|
||||
if (mbedtls_x509_crt_parse(_cli_crt, cli_cert, strlen((char*)cli_cert)+1) != 0) {
|
||||
printf("ERROR: mbedtls x509 parse client_crt failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbedtls_pk_parse_key(_clikey_rsa, cli_key, strlen((char*)cli_key)+1, NULL, 0) != 0) {
|
||||
printf("ERROR: mbedtls x509 parse client_rsa failed! \r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
mbedtls_ssl_conf_own_cert(ssl_client->conf, _cli_crt, _clikey_rsa);
|
||||
}
|
||||
|
||||
if((mbedtls_ssl_setup(ssl_client->ssl, ssl_client->conf)) != 0) {
|
||||
printf("ERROR: mbedtls ssl setup failed!\r\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
mbedtls_ssl_set_bio(ssl_client->ssl, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
|
||||
mbedtls_ssl_set_hostname(ssl_client->ssl, SNI_hostname);
|
||||
|
||||
ret = mbedtls_ssl_handshake(ssl_client->ssl);
|
||||
if (ret < 0) {
|
||||
printf("ERROR: mbedtls ssl handshake failed: -0x%04X \r\n", -ret);
|
||||
ret = -1;
|
||||
} else {
|
||||
if (ARDUINO_MBEDTLS_DEBUG_LEVEL > 0) {
|
||||
printf("mbedTLS SSL handshake success \r\n");
|
||||
}
|
||||
}
|
||||
//mbedtls_debug_set_threshold(ARDUINO_MBEDTLS_DEBUG_LEVEL);
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (_clikey_rsa) {
|
||||
mbedtls_pk_free(_clikey_rsa);
|
||||
mbedtls_free(_clikey_rsa);
|
||||
_clikey_rsa = NULL;
|
||||
}
|
||||
|
||||
if (_cli_crt) {
|
||||
mbedtls_x509_crt_free(_cli_crt);
|
||||
mbedtls_free(_cli_crt);
|
||||
_cli_crt = NULL;
|
||||
}
|
||||
|
||||
if (cacert) {
|
||||
mbedtls_x509_crt_free(cacert);
|
||||
mbedtls_free(cacert);
|
||||
cacert = NULL;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (ssl_client->socket >= 0) {
|
||||
mbedtls_net_free((mbedtls_net_context *)&ssl_client->socket);
|
||||
ssl_client->socket = -1;
|
||||
}
|
||||
|
||||
if (ssl_client->ssl != NULL) {
|
||||
mbedtls_ssl_free(ssl_client->ssl);
|
||||
free(ssl_client->ssl);
|
||||
ssl_client->ssl = NULL;
|
||||
}
|
||||
if (ssl_client->conf != NULL) {
|
||||
mbedtls_ssl_config_free(ssl_client->conf);
|
||||
free(ssl_client->conf);
|
||||
ssl_client->conf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ssl_client->socket;
|
||||
}
|
||||
|
||||
void stop_ssl_socket(sslclient_context *ssl_client)
|
||||
{
|
||||
lwip_shutdown(ssl_client->socket, SHUT_RDWR);
|
||||
lwip_close(ssl_client->socket);
|
||||
//mbedtls_net_free((mbedtls_net_context *)&ssl_client->socket);
|
||||
ssl_client->socket = -1;
|
||||
|
||||
if (ssl_client->ssl != NULL) {
|
||||
mbedtls_ssl_free(ssl_client->ssl);
|
||||
free(ssl_client->ssl);
|
||||
ssl_client->ssl = NULL;
|
||||
}
|
||||
if (ssl_client->conf != NULL) {
|
||||
mbedtls_ssl_config_free(ssl_client->conf);
|
||||
free(ssl_client->conf);
|
||||
ssl_client->conf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (ssl_client->ssl != NULL) {
|
||||
ret = mbedtls_ssl_write(ssl_client->ssl, data, len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_ssl_receive(sslclient_context *ssl_client, uint8_t* data, int length, int flag)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t has_backup_recvtimeout = 0;
|
||||
int backup_recv_timeout, recv_timeout;
|
||||
socklen_t len;
|
||||
|
||||
if (ssl_client->ssl == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flag & 0x01) {
|
||||
// peek for 10ms
|
||||
ret = lwip_getsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, &len);
|
||||
if (ret >= 0) {
|
||||
recv_timeout = 100;
|
||||
ret = lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout, sizeof(recv_timeout));
|
||||
if (ret >= 0) {
|
||||
has_backup_recvtimeout = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memset(data, 0, length);
|
||||
ret = mbedtls_ssl_read(ssl_client->ssl, data, length);
|
||||
|
||||
if ((flag & 0x01) && (has_backup_recvtimeout == 1)) {
|
||||
// restore receiving timeout
|
||||
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &backup_recv_timeout, sizeof(recv_timeout));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_ssl_sock_errno(sslclient_context *ssl_client) {
|
||||
int so_error;
|
||||
socklen_t len = sizeof(so_error);
|
||||
lwip_getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||
return so_error;
|
||||
}
|
||||
|
||||
int get_ssl_bytes_avail(sslclient_context *ssl_client) {
|
||||
if (ssl_client->ssl != NULL) {
|
||||
return mbedtls_ssl_get_bytes_avail(ssl_client->ssl);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef ARD_SSL_H
|
||||
#define ARD_SSL_H
|
||||
|
||||
struct mbedtls_ssl_context;
|
||||
struct mbedtls_ssl_config;
|
||||
|
||||
typedef struct {
|
||||
int socket;
|
||||
int recvTimeout;
|
||||
mbedtls_ssl_context *ssl;
|
||||
mbedtls_ssl_config *conf;
|
||||
} sslclient_context;
|
||||
|
||||
int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char* rootCABuff, unsigned char* cli_cert, unsigned char* cli_key, unsigned char* pskIdent, unsigned char* psKey, char* SNI_hostname);
|
||||
|
||||
void stop_ssl_socket(sslclient_context *ssl_client);
|
||||
|
||||
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);
|
||||
|
||||
int get_ssl_receive(sslclient_context *ssl_client, uint8_t* data, int length, int flag);
|
||||
|
||||
int get_ssl_sock_errno(sslclient_context *ssl_client);
|
||||
|
||||
int get_ssl_bytes_avail(sslclient_context *ssl_client);
|
||||
|
||||
#endif
|
||||
@@ -1,71 +0,0 @@
|
||||
// Simple Base64 code
|
||||
// (c) Copyright 2010 MCQN Ltd.
|
||||
// Released under Apache License, version 2.0
|
||||
|
||||
#include "b64.h"
|
||||
|
||||
/* Simple test program
|
||||
#include <stdio.h>
|
||||
void main()
|
||||
{
|
||||
char* in = "amcewen";
|
||||
char out[22];
|
||||
|
||||
b64_encode(in, 15, out, 22);
|
||||
out[21] = '\0';
|
||||
|
||||
printf(out);
|
||||
}
|
||||
*/
|
||||
|
||||
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen)
|
||||
{
|
||||
// Work out if we've got enough space to encode the input
|
||||
// Every 6 bits of input becomes a byte of output
|
||||
if (aOutputLen < (aInputLen*8)/6)
|
||||
{
|
||||
// FIXME Should we return an error here, or just the length
|
||||
return (aInputLen*8)/6;
|
||||
}
|
||||
|
||||
// If we get here we've got enough space to do the encoding
|
||||
|
||||
const char* b64_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (aInputLen == 3)
|
||||
{
|
||||
aOutput[0] = b64_dictionary[aInput[0] >> 2];
|
||||
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
|
||||
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2|(aInput[2]>>6)];
|
||||
aOutput[3] = b64_dictionary[aInput[2]&0x3F];
|
||||
}
|
||||
else if (aInputLen == 2)
|
||||
{
|
||||
aOutput[0] = b64_dictionary[aInput[0] >> 2];
|
||||
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
|
||||
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2];
|
||||
aOutput[3] = '=';
|
||||
}
|
||||
else if (aInputLen == 1)
|
||||
{
|
||||
aOutput[0] = b64_dictionary[aInput[0] >> 2];
|
||||
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4];
|
||||
aOutput[2] = '=';
|
||||
aOutput[3] = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Break the input into 3-byte chunks and process each of them
|
||||
int i;
|
||||
for (i = 0; i < aInputLen/3; i++)
|
||||
{
|
||||
b64_encode(&aInput[i*3], 3, &aOutput[i*4], 4);
|
||||
}
|
||||
if (aInputLen % 3 > 0)
|
||||
{
|
||||
// It doesn't fit neatly into a 3-byte chunk, so process what's left
|
||||
b64_encode(&aInput[i*3], aInputLen % 3, &aOutput[i*4], aOutputLen - (i*4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#ifndef b64_h
|
||||
#define b64_h
|
||||
|
||||
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2011 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
|
||||
*/
|
||||
|
||||
extern "C" void __cxa_pure_virtual(void) ;
|
||||
|
||||
/* We compile with nodefaultlibs, so we need to provide an error
|
||||
* handler for an empty pure virtual function */
|
||||
extern "C" void __cxa_pure_virtual(void) {
|
||||
while(1)
|
||||
;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef ARD_DEBUG_H
|
||||
#define ARD_DEBUG_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef DEBUG_RTL_CORE
|
||||
#define DEBUGV(...) rtl_printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGV
|
||||
#define DEBUGV(...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
void hexdump(void * addr, int len = 16);
|
||||
#else
|
||||
void hexdump(void * addr, int len);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void print_udp_pcb(void);
|
||||
void print_tcp_pcb(void);
|
||||
void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70 !
|
||||
|
||||
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
|
||||
#define panic() __panic_func(__FILE__, __LINE__, __func__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif//ARD_DEBUG_H
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2011 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
|
||||
*/
|
||||
|
||||
#include "api/itoa.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif // __cplusplus
|
||||
|
||||
extern char* ltoa(long value, char *string, int radix)
|
||||
{
|
||||
char tmp[33];
|
||||
char *tp = tmp;
|
||||
long i;
|
||||
unsigned long v;
|
||||
int sign;
|
||||
char *sp;
|
||||
|
||||
if ( string == NULL )
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
if (radix > 36 || radix <= 1)
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
sign = (radix == 10 && value < 0);
|
||||
if (sign)
|
||||
{
|
||||
v = -value;
|
||||
}
|
||||
else
|
||||
{
|
||||
v = (unsigned long)value;
|
||||
}
|
||||
|
||||
while (v || tp == tmp)
|
||||
{
|
||||
i = v % radix;
|
||||
v = v / radix;
|
||||
if (i < 10)
|
||||
*tp++ = i+'0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
sp = string;
|
||||
|
||||
if (sign)
|
||||
*sp++ = '-';
|
||||
while (tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
*sp = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
extern char* ultoa(unsigned long value, char *string, int radix)
|
||||
{
|
||||
char tmp[33];
|
||||
char *tp = tmp;
|
||||
long i;
|
||||
unsigned long v = value;
|
||||
char *sp;
|
||||
|
||||
if ( string == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (radix > 36 || radix <= 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (v || tp == tmp)
|
||||
{
|
||||
i = v % radix;
|
||||
v = v / radix;
|
||||
if (i < 10)
|
||||
*tp++ = i+'0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
sp = string;
|
||||
|
||||
|
||||
while (tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
*sp = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
@@ -1,120 +0,0 @@
|
||||
#ifndef EASYWSCLIENT_H
|
||||
#define EASYWSCLIENT_H
|
||||
#include <platform/platform_stdlib.h>
|
||||
|
||||
/****************Define the debug message level*********************/
|
||||
#define DEBUG_WSCLIENT 1
|
||||
|
||||
#define WSCLIENT_LOG(level, fmt, ...) printf("\n\r[WSCLIENT %s] %s: " fmt "\n", level, __FUNCTION__, ##__VA_ARGS__)
|
||||
#if DEBUG_WSCLIENT == 2
|
||||
#define WSCLIENT_DEBUG(fmt, ...) WSCLIENT_LOG("DEBUG", fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define WSCLIENT_DEBUG(fmt, ...)
|
||||
#endif
|
||||
#if DEBUG_WSCLIENT
|
||||
#define WSCLIENT_ERROR(fmt, ...) WSCLIENT_LOG("ERROR", fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define WSCLIENT_ERROR(fmt, ...)
|
||||
#endif
|
||||
/*******************************************************************/
|
||||
|
||||
/****************Define the structures used*************************/
|
||||
typedef enum{
|
||||
CLOSING,
|
||||
CLOSED,
|
||||
CONNECTING,
|
||||
OPEN
|
||||
} readyStateValues;
|
||||
|
||||
struct wsheader_type{
|
||||
unsigned header_size;
|
||||
int fin;
|
||||
int mask;
|
||||
enum opcode_type {
|
||||
CONTINUATION = 0x0,
|
||||
TEXT_FRAME = 0x1,
|
||||
BINARY_FRAME = 0x2,
|
||||
CLOSE = 8,
|
||||
PING = 9,
|
||||
PONG = 0xa,
|
||||
} opcode;
|
||||
int N0;
|
||||
uint64_t N;
|
||||
uint8_t masking_key[4];
|
||||
};
|
||||
|
||||
struct _wsclient_context;
|
||||
struct _ssl_context;
|
||||
|
||||
struct ssl_fun_ops{
|
||||
int (*memory_set_own)( void * (*malloc_func)( size_t ),void (*free_func)( void * ) );
|
||||
int (*net_connect)( int *fd, const char *host, int port );
|
||||
int (*ssl_init)( struct _ssl_context *ssl );
|
||||
void (*ssl_set_endpoint)( struct _ssl_context *ssl, int endpoint );
|
||||
void (*ssl_set_authmode)( struct _ssl_context *ssl, int authmode );
|
||||
void (*ssl_set_rng)( struct _ssl_context *ssl,
|
||||
int (*f_rng)(void *, unsigned char *, size_t),
|
||||
void *p_rng );
|
||||
void (*ssl_set_bio)( struct _ssl_context *ssl,
|
||||
int (*f_recv)(void *, unsigned char *, size_t), void *p_recv,
|
||||
int (*f_send)(void *, const unsigned char *, size_t), void *p_send );
|
||||
int (*ssl_handshake)( struct _ssl_context *ssl );
|
||||
void (*net_close)( int fd );
|
||||
void (*ssl_free)( struct _ssl_context *ssl );
|
||||
int (*ssl_read)( struct _ssl_context *ssl, unsigned char *buf, size_t len );
|
||||
int (*ssl_write)( struct _ssl_context *ssl, const unsigned char *buf, size_t len );
|
||||
const char *(*ssl_get_ciphersuite)( const struct _ssl_context *ssl );
|
||||
int (*net_recv)( void *ctx, unsigned char *buf, size_t len );
|
||||
int (*net_send)( void *ctx, const unsigned char *buf, size_t len );
|
||||
};
|
||||
|
||||
struct ws_fun_ops{
|
||||
int (*hostname_connect)(struct _wsclient_context *wsclient);
|
||||
void (*client_close)(struct _wsclient_context *wsclient);
|
||||
int (*client_send)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
int (*client_read)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
struct ssl_fun_ops ssl_fun_ops;
|
||||
};
|
||||
|
||||
typedef struct _wsclient_context{
|
||||
char host[128];
|
||||
char path[128];
|
||||
char origin[200];
|
||||
int port;
|
||||
uint8_t use_ssl;
|
||||
int sockfd;
|
||||
readyStateValues readyState;
|
||||
int tx_len;
|
||||
void *ssl;
|
||||
uint8_t *txbuf;
|
||||
uint8_t *rxbuf;
|
||||
uint8_t *receivedData;
|
||||
struct ws_fun_ops fun_ops;
|
||||
}wsclient_context;
|
||||
/*******************************************************************/
|
||||
|
||||
/****************General functions used by wsclient*****************/
|
||||
void ws_get_random_bytes(void *buf, size_t len);
|
||||
void* ws_malloc(unsigned int size);
|
||||
void ws_free(void *buf);
|
||||
int ws_client_handshake(wsclient_context *wsclient);
|
||||
int ws_check_handshake(wsclient_context *wsclient);
|
||||
void ws_sendData(uint8_t type, size_t message_size, uint8_t* message, int useMask, wsclient_context *wsclient);
|
||||
/*******************************************************************/
|
||||
|
||||
/*************Functions used by wsclient without SSL****************/
|
||||
|
||||
int ws_hostname_connect(wsclient_context *wsclient);
|
||||
int ws_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
int ws_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
void ws_client_close(wsclient_context *wsclient);
|
||||
/*******************************************************************/
|
||||
|
||||
/***************Functions used by wsclient with SSL*****************/
|
||||
int wss_hostname_connect(wsclient_context *wsclient);
|
||||
int wss_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
int wss_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
|
||||
void wss_client_close(wsclient_context *wsclient);
|
||||
/*******************************************************************/
|
||||
|
||||
#endif
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lwip_netconf.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/err.h"
|
||||
//#include "lwip/mem.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
/* Get one byte from the 4-byte address */
|
||||
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
|
||||
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
|
||||
#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
|
||||
#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
|
||||
/* These are cast to u16_t, with the intent that they are often arguments
|
||||
* to printf using the U16_F format from cc.h. */
|
||||
#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
|
||||
#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
|
||||
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
|
||||
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
|
||||
|
||||
#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
|
||||
ip4_addr2_16(ipaddr), \
|
||||
ip4_addr3_16(ipaddr), \
|
||||
ip4_addr4_16(ipaddr)
|
||||
|
||||
#define IPSTR "%d.%d.%d.%d"
|
||||
|
||||
extern const char * const tcp_state_str[];
|
||||
/*
|
||||
static const char * const tcp_state_str[] = {
|
||||
"CLOSED",
|
||||
"LISTEN",
|
||||
"SYN_SENT",
|
||||
"SYN_RCVD",
|
||||
"ESTABLISHED",
|
||||
"FIN_WAIT_1",
|
||||
"FIN_WAIT_2",
|
||||
"CLOSE_WAIT",
|
||||
"CLOSING",
|
||||
"LAST_ACK",
|
||||
"TIME_WAIT"
|
||||
};
|
||||
*/
|
||||
/******************************************************************************
|
||||
* FunctionName : debug
|
||||
* Parameters :
|
||||
* Returns :
|
||||
*******************************************************************************/
|
||||
void print_udp_pcb(void)
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
bool prt_none = true;
|
||||
rtl_printf("UDP pcbs:\n");
|
||||
for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
rtl_printf("flg:%02x\t" IPSTR ":%d\t" IPSTR ":%d\trecv:%p\n", pcb->flags, IP2STR(&pcb->local_ip), pcb->local_port, IP2STR(&pcb->remote_ip), pcb->remote_port, pcb->recv );
|
||||
prt_none = false;
|
||||
}
|
||||
if(prt_none) rtl_printf("none\n");
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : debug
|
||||
* Parameters :
|
||||
* Returns :
|
||||
*******************************************************************************/
|
||||
void print_tcp_pcb(void)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
rtl_printf("Active PCB states:\n");
|
||||
bool prt_none = true;
|
||||
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
|
||||
prt_none = false;
|
||||
}
|
||||
if(prt_none) rtl_printf("none\n");
|
||||
rtl_printf("Listen PCB states:\n");
|
||||
prt_none = true;
|
||||
for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
|
||||
prt_none = false;
|
||||
}
|
||||
if(prt_none) rtl_printf("none\n");
|
||||
rtl_printf("TIME-WAIT PCB states:\n");
|
||||
prt_none = true;
|
||||
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
|
||||
prt_none = false;
|
||||
}
|
||||
if(prt_none) rtl_printf("none\n");
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : debug
|
||||
* Parameters :
|
||||
* Returns :
|
||||
*******************************************************************************/
|
||||
#if 0
|
||||
//------------------------------------------------------------------------------
|
||||
void chow_tcp_connection_info(void)
|
||||
{
|
||||
rtl_printf("TCP Server connections:\n");
|
||||
TCP_SERV_CFG * p;
|
||||
TCP_SERV_CONN * ts_conn;
|
||||
bool prt_none = true;
|
||||
for(p = phcfg; p != NULL; p = p->next) {
|
||||
for(ts_conn = p->conn_links; ts_conn != NULL; ts_conn = ts_conn->next) {
|
||||
rtl_printf("%d "IPSTR ":%d %s\n", p->port, ts_conn->remote_ip.b[0], ts_conn->remote_ip.b[1], ts_conn->remote_ip.b[2], ts_conn->remote_ip.b[3], ts_conn->remote_port, tspsrv_srvconn_state_msg(ts_conn->state) );
|
||||
prt_none = false;
|
||||
}
|
||||
}
|
||||
if(prt_none) rtl_printf("none\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -18,87 +18,62 @@
|
||||
*/
|
||||
|
||||
#define ARDUINO_MAIN
|
||||
#include "Arduino.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include "rtl8710b.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "diag.h"
|
||||
extern void HalCpuClkConfig(u8 CpuType);
|
||||
extern void SystemCoreClockUpdate(void);
|
||||
extern void En32KCalibration(void);
|
||||
extern int tcm_heap_freeSpace(void);
|
||||
extern void console_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
osThreadId main_tid = 0;
|
||||
#include <Arduino.h>
|
||||
#include <cmsis_os.h>
|
||||
|
||||
// Weak empty variant initialization function.
|
||||
// May be redefined by variant files.
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() { }
|
||||
|
||||
void initVariant() {}
|
||||
|
||||
// Initialize C library
|
||||
extern "C" void __libc_init_array(void);
|
||||
|
||||
/*
|
||||
* \brief Init Random()
|
||||
* \note Use in void __low_level_init(void) { Init_Rand(); } !
|
||||
*/
|
||||
void Init_Rand(void)
|
||||
{
|
||||
extern u32 _rand_z1, _rand_z2, _rand_z3, _rand_z4, _rand_first;
|
||||
u32 *p = (u32 *)0x1FFFFF00;
|
||||
while(p < (u32 *)0x20000000) _rand_z1 ^= *p++;
|
||||
_rand_z1 ^= (*((u32 *)0x40002018) << 24) ^ (*((u32 *)0x40002118) << 16) ^ (*((u32 *)0x40002218) << 8) ^ *((u32 *)0x40002318);
|
||||
_rand_z2 = ((_rand_z1 & 0x007F00FF) << 7) ^ ((_rand_z1 & 0x0F80FF00) >> 8);
|
||||
_rand_z3 = ((_rand_z2 & 0x007F00FF) << 7) ^ ((_rand_z2 & 0x0F80FF00) >> 8);
|
||||
_rand_z4 = ((_rand_z3 & 0x007F00FF) << 7) ^ ((_rand_z3 & 0x0F80FF00) >> 8);
|
||||
_rand_first = 1;
|
||||
osThreadId main_tid = 0;
|
||||
|
||||
void main_task(const void *arg) {
|
||||
setup();
|
||||
|
||||
for (;;) {
|
||||
loop();
|
||||
if (serialEventRun)
|
||||
serialEventRun();
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief handle sketch
|
||||
*/
|
||||
void main_task( void const *arg )
|
||||
{
|
||||
setup();
|
||||
int main(void) {
|
||||
LT_BANNER();
|
||||
init();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
loop();
|
||||
if (serialEventRun) serialEventRun();
|
||||
yield();
|
||||
}
|
||||
__libc_init_array();
|
||||
|
||||
initVariant();
|
||||
|
||||
osThreadDef(main_task, osPriorityRealtime, 1, 4096 * 4);
|
||||
main_tid = osThreadCreate(osThread(main_task), NULL);
|
||||
|
||||
osKernelStart();
|
||||
|
||||
while (1)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void serialEvent() __attribute__((weak));
|
||||
bool Serial_available() __attribute__((weak));
|
||||
|
||||
/*
|
||||
* \brief Main entry point of Arduino application
|
||||
*/
|
||||
int main( void )
|
||||
{
|
||||
LT_BANNER();
|
||||
init();
|
||||
|
||||
__libc_init_array();
|
||||
|
||||
initVariant();
|
||||
|
||||
osThreadDef(main_task, osPriorityRealtime, 1, 4096*4);
|
||||
main_tid = osThreadCreate(osThread (main_task), NULL);
|
||||
|
||||
osKernelStart();
|
||||
|
||||
while(1);
|
||||
|
||||
return 0;
|
||||
void serialEventRun(void) {
|
||||
if (Serial_available && serialEvent && Serial_available())
|
||||
serialEvent();
|
||||
}
|
||||
|
||||
void wait_for_debug() {
|
||||
while (((CoreDebug->DHCSR) & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
|
||||
asm("nop");
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef __NETBIOS_H__
|
||||
#define __NETBIOS_H__
|
||||
|
||||
/** default port number for "NetBIOS Name service */
|
||||
#define NETBIOS_PORT 137
|
||||
|
||||
/** size of a NetBIOS name */
|
||||
#define NETBIOS_NAME_LEN 16
|
||||
|
||||
#ifndef NET_IF_NUM
|
||||
#define NET_IF_NUM 2
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern char netbios_name[NET_IF_NUM][NETBIOS_NAME_LEN + 1]; // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
|
||||
|
||||
// struct udp_pcb * netbios_pcb(void);
|
||||
void netbios_init(void);
|
||||
bool netbios_set_name(unsigned char interfacenum, char * name); // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
|
||||
bool netbios_off(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NETBIOS_H__ */
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 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
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <diag.h>
|
||||
|
||||
extern void *pvPortMalloc( size_t xWantedSize );
|
||||
extern void *os_zalloc( size_t xWantedSize );
|
||||
extern void vPortFree( void *pv );
|
||||
extern void *pvPortReAlloc( void *pv, size_t xWantedSize );
|
||||
extern size_t xPortGetFreeHeapSize( void );
|
||||
extern size_t xPortGetMinimumEverFreeHeapSize( void );
|
||||
|
||||
extern void *tcm_heap_malloc(int size);
|
||||
extern void *tcm_heap_calloc(int size);
|
||||
extern void tcm_heap_free(void * mem);
|
||||
extern void tcm_heap_dump(void);
|
||||
extern int tcm_heap_freeSpace(void);
|
||||
|
||||
|
||||
__attribute__((noreturn)) void __panic_func(const char* file, int line, const char* func)
|
||||
{
|
||||
DiagPrintf("\r\nPanic: %s, line: %d, %s\r\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
void hexdump(void * ptr, int cnt)
|
||||
{
|
||||
char * p = (char *) ptr;
|
||||
int c = cnt;
|
||||
while(c > 64) {
|
||||
DumpForOneBytes((void *)p, 64);
|
||||
p += 64;
|
||||
c -= 64;
|
||||
}
|
||||
if(c != 0) DumpForOneBytes((void *)p, c);
|
||||
}
|
||||
|
||||
void debug_on(void)
|
||||
{
|
||||
ConfigDebugErr = -1;
|
||||
ConfigDebugInfo = -1;
|
||||
ConfigDebugWarn = -1;
|
||||
CfgSysDebugErr = -1;
|
||||
CfgSysDebugInfo = -1;
|
||||
CfgSysDebugWarn = -1;
|
||||
}
|
||||
|
||||
void sys_info(void) {
|
||||
rtl_printf("\r\nCLK CPU\t\t%d Hz\r\nRAM heap\t%d bytes\r\nTCM heap\t%d bytes\r\n",
|
||||
HalGetCpuClk(), xPortGetFreeHeapSize(), tcm_heap_freeSpace());
|
||||
}
|
||||
|
||||
|
||||
/* void * malloc(size_t size)
|
||||
{
|
||||
void * ret;
|
||||
if((ret = pvPortMalloc(size)) == NULL)
|
||||
ret = tcm_heap_malloc(size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void * zalloc(size_t size)
|
||||
{
|
||||
void * ret;
|
||||
if((ret = pvPortMalloc(size)) == NULL)
|
||||
ret = tcm_heap_calloc(size);
|
||||
else memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *calloc(size_t count, size_t size)
|
||||
{
|
||||
return zalloc(count * size);
|
||||
} */
|
||||
|
||||
void free(void *pv)
|
||||
{
|
||||
vPortFree(pv);
|
||||
}
|
||||
|
||||
void * realloc(void *pv, size_t size)
|
||||
{
|
||||
return pvPortReAlloc(pv, size);
|
||||
}
|
||||
|
||||
/* void *operator new(size_t size)
|
||||
{
|
||||
void * ret;
|
||||
if((ret = zalloc(size)) == NULL) {
|
||||
DiagPrintf("\r\nMEM error!\r\n");
|
||||
while(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *operator new[](size_t size)
|
||||
{
|
||||
void * ret;
|
||||
if((ret = zalloc(size)) == NULL) {
|
||||
DiagPrintf("\r\nMEM error!\r\n");
|
||||
while(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void operator delete(void * ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void * ptr)
|
||||
{
|
||||
free(ptr);
|
||||
} */
|
||||
|
||||
/*
|
||||
extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
|
||||
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));
|
||||
|
||||
void __cxa_pure_virtual(void)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
void __cxa_deleted_virtual(void)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t guard;
|
||||
uint8_t ps;
|
||||
} guard_t;
|
||||
|
||||
extern "C" int __cxa_guard_acquire(__guard* pg)
|
||||
{
|
||||
uint8_t ps = xt_rsil(15);
|
||||
if (reinterpret_cast<guard_t*>(pg)->guard) {
|
||||
xt_wsr_ps(ps);
|
||||
return 0;
|
||||
}
|
||||
reinterpret_cast<guard_t*>(pg)->ps = ps;
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" void __cxa_guard_release(__guard* pg)
|
||||
{
|
||||
reinterpret_cast<guard_t*>(pg)->guard = 1;
|
||||
xt_wsr_ps(reinterpret_cast<guard_t*>(pg)->ps);
|
||||
}
|
||||
|
||||
extern "C" void __cxa_guard_abort(__guard* pg)
|
||||
{
|
||||
xt_wsr_ps(reinterpret_cast<guard_t*>(pg)->ps);
|
||||
}
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
void __throw_bad_function_call()
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
void __throw_length_error(char const*)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
void __throw_bad_alloc()
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
void __throw_logic_error(const char* str)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
void __throw_out_of_range(const char* str)
|
||||
{
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
37
arduino/realtek-ambz/cores/arduino/sdk_extern.h
Normal file
37
arduino/realtek-ambz/cores/arduino/sdk_extern.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define boolean boolean_rtl
|
||||
#include <ameba_soc.h>
|
||||
#include <gpio_api.h>
|
||||
#include <rand.h>
|
||||
#include <rt_lib_rom.h>
|
||||
#include <rtl_lib.h>
|
||||
#include <wait_api.h>
|
||||
#undef boolean
|
||||
|
||||
// stdio.h
|
||||
#define printf rtl_printf
|
||||
#define sprintf rtl_sprintf
|
||||
|
||||
// moved from syscalls.h
|
||||
#define _close __rtl_close
|
||||
#define _fstat __rtl_fstat
|
||||
#define _isatty __rtl_isatty
|
||||
#define _lseek __rtl_lseek
|
||||
#define _open __rtl_open
|
||||
#define _read __rtl_read
|
||||
#define _write __rtl_write
|
||||
#define _sbrk __rtl_sbrk
|
||||
|
||||
#define delay_us wait_us
|
||||
|
||||
extern void wait_us(int us);
|
||||
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
|
||||
extern void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70!
|
||||
extern void SystemCoreClockUpdate(void);
|
||||
16
arduino/realtek-ambz/cores/arduino/sdk_mem.c
Normal file
16
arduino/realtek-ambz/cores/arduino/sdk_mem.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
|
||||
|
||||
#include "sdk_mem.h"
|
||||
|
||||
#include <Arduino.h> // for memset
|
||||
|
||||
void *pvPortZalloc(size_t size) {
|
||||
void *pvReturn = pvPortMalloc(size);
|
||||
if (pvReturn)
|
||||
memset(pvReturn, 0, size);
|
||||
return pvReturn;
|
||||
}
|
||||
|
||||
void *pvPortCalloc(size_t nmemb, size_t size) {
|
||||
return pvPortZalloc(nmemb * size);
|
||||
}
|
||||
19
arduino/realtek-ambz/cores/arduino/sdk_mem.h
Normal file
19
arduino/realtek-ambz/cores/arduino/sdk_mem.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// provide extern functions directly, as callers
|
||||
// generally don't expect needing to include malloc()
|
||||
extern void *pvPortMalloc(size_t xWantedSize);
|
||||
extern void *pvPortZalloc(size_t size);
|
||||
extern void *pvPortCalloc(size_t nmemb, size_t size);
|
||||
extern void *pvPortReAlloc(void *pv, size_t xWantedSize);
|
||||
extern void vPortFree(void *pv);
|
||||
|
||||
#define malloc pvPortMalloc
|
||||
#define zalloc pvPortZalloc
|
||||
#define calloc pvPortCalloc
|
||||
#define realloc pvPortReAlloc
|
||||
#define free vPortFree
|
||||
110
arduino/realtek-ambz/cores/arduino/sdk_os.c
Normal file
110
arduino/realtek-ambz/cores/arduino/sdk_os.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "sdk_os.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <cmsis_os.h>
|
||||
|
||||
uint32_t os_thread_create(void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size) {
|
||||
|
||||
osThreadDef_t thread_def;
|
||||
|
||||
thread_def.pthread = task;
|
||||
thread_def.tpriority = (osPriority)priority;
|
||||
// the underlying freertos implementation on cmsis os divide stack size by 4
|
||||
thread_def.stacksize = stack_size * 4;
|
||||
thread_def.name = "ARDUINO";
|
||||
|
||||
return (uint32_t)osThreadCreate(&thread_def, argument);
|
||||
}
|
||||
|
||||
uint32_t os_thread_get_id(void) {
|
||||
return osThreadGetId();
|
||||
}
|
||||
|
||||
uint32_t os_thread_terminate(uint32_t thread_id) {
|
||||
return (uint32_t)osThreadTerminate(thread_id);
|
||||
}
|
||||
|
||||
uint32_t os_thread_yield(void) {
|
||||
return (uint32_t)osThreadYield();
|
||||
}
|
||||
|
||||
uint32_t os_thread_set_priority(uint32_t thread_id, int priority) {
|
||||
return (uint32_t)osThreadSetPriority(thread_id, (osPriority)priority);
|
||||
}
|
||||
|
||||
int os_thread_get_priority(uint32_t thread_id) {
|
||||
return (int)osThreadGetPriority(thread_id);
|
||||
}
|
||||
|
||||
int32_t os_signal_set(uint32_t thread_id, int32_t signals) {
|
||||
return osSignalSet(thread_id, signals);
|
||||
}
|
||||
|
||||
int32_t os_signal_clear(uint32_t thread_id, int32_t signals) {
|
||||
return osSignalClear(thread_id, signals);
|
||||
}
|
||||
|
||||
os_event_t os_signal_wait(int32_t signals, uint32_t millisec) {
|
||||
|
||||
osEvent evt;
|
||||
os_event_t ret;
|
||||
|
||||
evt = osSignalWait(signals, millisec);
|
||||
ret.status = (uint32_t)evt.status;
|
||||
ret.value.signals = evt.value.signals;
|
||||
ret.def.message_id = evt.def.message_id;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef void (*os_ptimer)(const void *argument);
|
||||
|
||||
uint32_t os_timer_create(void (*callback)(const void *argument), uint8_t isPeriodic, void *argument) {
|
||||
|
||||
osTimerDef_t *pTimerDef;
|
||||
|
||||
pTimerDef = (osTimerDef_t *)malloc(sizeof(osTimerDef_t));
|
||||
pTimerDef->ptimer = callback;
|
||||
pTimerDef->custom = (struct os_timer_custom *)malloc(sizeof(struct os_timer_custom));
|
||||
|
||||
return osTimerCreate(pTimerDef, (isPeriodic ? osTimerPeriodic : osTimerOnce), argument);
|
||||
}
|
||||
|
||||
uint32_t os_timer_start(uint32_t timer_id, uint32_t millisec) {
|
||||
return osTimerStart(timer_id, millisec);
|
||||
}
|
||||
|
||||
uint32_t os_timer_stop(uint32_t timer_id) {
|
||||
return osTimerStop(timer_id);
|
||||
}
|
||||
|
||||
uint32_t os_timer_delete(uint32_t timer_id) {
|
||||
|
||||
osTimerDef_t *pTimerDef;
|
||||
|
||||
pTimerDef = (osTimerDef_t *)pvTimerGetTimerID(timer_id);
|
||||
free(pTimerDef->custom);
|
||||
free(pTimerDef);
|
||||
|
||||
return osTimerDelete(timer_id);
|
||||
}
|
||||
|
||||
uint32_t os_semaphore_create(int32_t count) {
|
||||
return (uint32_t)osSemaphoreCreate(NULL, count);
|
||||
}
|
||||
|
||||
int32_t os_semaphore_wait(uint32_t semaphore_id, uint32_t millisec) {
|
||||
if (osSemaphoreWait((osSemaphoreId)semaphore_id, millisec) == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t os_semaphore_release(uint32_t semaphore_id) {
|
||||
return (uint32_t)osSemaphoreRelease((osSemaphoreId)semaphore_id);
|
||||
}
|
||||
|
||||
uint32_t os_semaphore_delete(uint32_t semaphore_id) {
|
||||
return (uint32_t)osSemaphoreDelete((osSemaphoreId)semaphore_id);
|
||||
}
|
||||
@@ -1,13 +1,4 @@
|
||||
/** @file wiring_os.h */
|
||||
|
||||
/**
|
||||
* @defgroup wiring_os wiring_os
|
||||
* OS realted function for thread, signal, software timer, semaphore
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _WIRING_OS_H_
|
||||
#define _WIRING_OS_H_
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -21,33 +12,33 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
/** function completed; no error or event occurred. */
|
||||
#define OS_OK 0x00
|
||||
#define OS_OK 0x00
|
||||
/** function completed; signal event occurred. */
|
||||
#define OS_EVENT_SIGNAL 0x08
|
||||
#define OS_EVENT_SIGNAL 0x08
|
||||
/** function completed; message event occurred. */
|
||||
#define OS_EVENT_MESSAGE 0x10
|
||||
#define OS_EVENT_MESSAGE 0x10
|
||||
/** function completed; mail event occurred. */
|
||||
#define OS_EVENT_MAIL 0x20
|
||||
#define OS_EVENT_MAIL 0x20
|
||||
/** function completed; timeout occurred. */
|
||||
#define OS_EVENT_TIMEOUT 0x40
|
||||
#define OS_EVENT_TIMEOUT 0x40
|
||||
/** parameter error: a mandatory parameter was missing or specified an incorrect object. */
|
||||
#define OS_ERROR_PARAMETER 0x80
|
||||
#define OS_ERROR_PARAMETER 0x80
|
||||
/** resource not available: a specified resource was not available. */
|
||||
#define OS_ERROR_RESOURCE 0x81
|
||||
#define OS_ERROR_RESOURCE 0x81
|
||||
/** resource not available within given time: a specified resource was not available within the timeout period. */
|
||||
#define OS_ERROR_TIMEOUT_RESOURCE 0xC1
|
||||
/** not allowed in ISR context: the function cannot be called from interrupt service routines. */
|
||||
#define OS_ERROR_ISR 0x82
|
||||
#define OS_ERROR_ISR 0x82
|
||||
/** function called multiple times from ISR with same object. */
|
||||
#define OS_ERROR_ISR_RECURSIVE 0x83
|
||||
#define OS_ERROR_ISR_RECURSIVE 0x83
|
||||
/** system cannot determine priority or thread has illegal priority. */
|
||||
#define OS_ERROR_PRIORITY 0x84
|
||||
#define OS_ERROR_PRIORITY 0x84
|
||||
/** system is out of memory: it was impossible to allocate or reserve memory for the operation. */
|
||||
#define OS_ERROR_NO_MEMORY 0x85
|
||||
#define OS_ERROR_NO_MEMORY 0x85
|
||||
/** value of a parameter is out of range. */
|
||||
#define OS_ERROR_VALUE 0x86
|
||||
#define OS_ERROR_VALUE 0x86
|
||||
/** unspecified RTOS error: run-time error but no other error message fits. */
|
||||
#define OS_ERROR_OS 0xFF
|
||||
#define OS_ERROR_OS 0xFF
|
||||
/** @} */ // end of group os_status
|
||||
|
||||
/**
|
||||
@@ -56,19 +47,19 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
/** priority: idle (lowest) */
|
||||
#define OS_PRIORITY_IDLE (-3)
|
||||
#define OS_PRIORITY_IDLE (-3)
|
||||
/** priority: low */
|
||||
#define OS_PRIORITY_LOW (-2)
|
||||
#define OS_PRIORITY_LOW (-2)
|
||||
/** priority: below normal */
|
||||
#define OS_PRIORITY_BELOW_NORMAL (-1)
|
||||
#define OS_PRIORITY_BELOW_NORMAL (-1)
|
||||
/** priority: normal (default) */
|
||||
#define OS_PRIORITY_NORMAL ( 0)
|
||||
#define OS_PRIORITY_NORMAL (0)
|
||||
/** priority: above normal */
|
||||
#define OS_PRIORITY_ABOVENORMAL (+1)
|
||||
#define OS_PRIORITY_ABOVENORMAL (+1)
|
||||
/** priority: high */
|
||||
#define OS_PRIORITY_HIGH (+2)
|
||||
#define OS_PRIORITY_HIGH (+2)
|
||||
/** priority: realtime (highest) */
|
||||
#define OS_PRIORITY_REALTIME (+3)
|
||||
#define OS_PRIORITY_REALTIME (+3)
|
||||
/** @} */ // end of group os_priority
|
||||
|
||||
#ifndef DEFAULT_STACK_SIZE
|
||||
@@ -87,16 +78,18 @@ extern "C" {
|
||||
* Redefine osEvent in cmsis_os.h
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t status; ///< status code: event or error information
|
||||
union {
|
||||
uint32_t v; ///< message as 32-bit value
|
||||
void *p; ///< message or mail as void pointer
|
||||
int32_t signals; ///< signal flags
|
||||
} value; ///< event value
|
||||
union {
|
||||
void *mail_id; ///< mail id obtained by osMailCreate
|
||||
void *message_id; ///< message id obtained by osMessageCreate
|
||||
} def; ///< event definition
|
||||
uint32_t status; ///< status code: event or error information
|
||||
|
||||
union {
|
||||
uint32_t v; ///< message as 32-bit value
|
||||
void *p; ///< message or mail as void pointer
|
||||
int32_t signals; ///< signal flags
|
||||
} value; ///< event value
|
||||
|
||||
union {
|
||||
void *mail_id; ///< mail id obtained by osMailCreate
|
||||
void *message_id; ///< message id obtained by osMessageCreate
|
||||
} def; ///< event definition
|
||||
} os_event_t;
|
||||
|
||||
/**
|
||||
@@ -108,12 +101,14 @@ typedef struct {
|
||||
* @ingroup thread_management
|
||||
* @brief Create a thread and add it to Active Threads and set it to state READY.
|
||||
*
|
||||
* @param[in] task Function pointer which is the thread body. It should not run into the end of function unless os_thread_terminate is invoked
|
||||
* @param[in] task Function pointer which is the thread body. It should not run into the end of function unless
|
||||
* os_thread_terminate is invoked
|
||||
* @param[in] argument the data pointer which brings to task
|
||||
* @param[in] priority The underlying os is FreeRTOS. It executes tasks with highest priority which are not in idle state.\n
|
||||
* If there are more than 2 tasks to be executed, then they share the time slice.
|
||||
* @param[in] priority The underlying os is FreeRTOS. It executes tasks with highest priority which are not in idle
|
||||
* state.\n If there are more than 2 tasks to be executed, then they share the time slice.
|
||||
* @param[in] stack_size The stack_size is used as memory heap only for this task. \n
|
||||
* The local variables and call stacks would occupy this heap. Please make sure the the stack_size is big enough to avoid curroption
|
||||
* The local variables and call stacks would occupy this heap. Please make sure the the stack_size is big enough to
|
||||
* avoid curroption
|
||||
* @return The thread id which is used in thread operation and signaling.
|
||||
*/
|
||||
extern uint32_t os_thread_create(void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size);
|
||||
@@ -124,7 +119,7 @@ extern uint32_t os_thread_create(void (*task)(const void *argument), void *argum
|
||||
*
|
||||
* @return Current thread id which calls os_thread_get_id
|
||||
*/
|
||||
extern uint32_t os_thread_get_id( void );
|
||||
extern uint32_t os_thread_get_id(void);
|
||||
|
||||
/**
|
||||
* @ingroup thread_management
|
||||
@@ -141,9 +136,10 @@ extern uint32_t os_thread_terminate(uint32_t thread_id);
|
||||
* @ingroup thread_management
|
||||
* @brief Pass control to next thread that is in state \b READY.
|
||||
*
|
||||
* By default the minimal execution unit is 1 millisecond. In a scenario that if a thread with smaller want to handout execution right to a thread with
|
||||
* higher priority immediately without waiting for the ending of current 1 millisecond, then invoke os_thread_yield can transfer exection right to
|
||||
* OS's idle task and check which is the next execution thread.
|
||||
* By default the minimal execution unit is 1 millisecond. In a scenario that if a thread with smaller want to handout
|
||||
* execution right to a thread with higher priority immediately without waiting for the ending of current 1 millisecond,
|
||||
* then invoke os_thread_yield can transfer exection right to OS's idle task and check which is the next execution
|
||||
* thread.
|
||||
*
|
||||
* @return os_status code
|
||||
*/
|
||||
@@ -168,7 +164,7 @@ extern uint32_t os_thread_set_priority(uint32_t thread_id, int priority);
|
||||
*/
|
||||
extern int os_thread_get_priority(uint32_t thread_id);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @defgroup signal_management signal_management
|
||||
* Signaling between threads include set, clear, and wait
|
||||
*/
|
||||
@@ -196,7 +192,7 @@ extern int32_t os_signal_clear(uint32_t thread_id, int32_t signals);
|
||||
/**
|
||||
* @ingroup signal_management
|
||||
* @brief Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread.
|
||||
*
|
||||
*
|
||||
* @param[in] signals the signals to be wait
|
||||
* @param[in] millisec the timeout value if no signal comes in. Fill in 0xFFFFFFFF for infinite wait.
|
||||
* @return os_status code
|
||||
@@ -208,14 +204,14 @@ extern os_event_t os_signal_wait(int32_t signals, uint32_t millisec);
|
||||
* Software timer management include create, start, stop, delete.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
* @brief specify timer type that invoke only once
|
||||
*/
|
||||
#define OS_TIMER_ONCE (0)
|
||||
#define OS_TIMER_ONCE (0)
|
||||
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
* @brief specify timer type that invoke periodically
|
||||
*/
|
||||
#define OS_TIMER_PERIODIC (1)
|
||||
@@ -229,7 +225,7 @@ extern os_event_t os_signal_wait(int32_t signals, uint32_t millisec);
|
||||
* @param[in] argument The argument that is bring into callback function
|
||||
* @return timer id
|
||||
*/
|
||||
extern uint32_t os_timer_create(void (*callback)(void const *argument), uint8_t isPeriodic, void *argument);
|
||||
extern uint32_t os_timer_create(void (*callback)(const void *argument), uint8_t isPeriodic, void *argument);
|
||||
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
@@ -239,7 +235,7 @@ extern uint32_t os_timer_create(void (*callback)(void const *argument), uint8_t
|
||||
* @param[in] millisec The delays after timer starts
|
||||
* @return os_status code
|
||||
*/
|
||||
extern uint32_t os_timer_start (uint32_t timer_id, uint32_t millisec);
|
||||
extern uint32_t os_timer_start(uint32_t timer_id, uint32_t millisec);
|
||||
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
@@ -248,7 +244,7 @@ extern uint32_t os_timer_start (uint32_t timer_id, uint32_t millisec);
|
||||
* @param[in] timer_id The timer id obtained from by os_timer_create
|
||||
* @return os_status code
|
||||
*/
|
||||
extern uint32_t os_timer_stop (uint32_t timer_id);
|
||||
extern uint32_t os_timer_stop(uint32_t timer_id);
|
||||
|
||||
/**
|
||||
* @ingroup timer_management
|
||||
@@ -304,7 +300,3 @@ extern uint32_t os_semaphore_delete(uint32_t semaphore_id);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */ // end of group wiring_os
|
||||
@@ -1,146 +0,0 @@
|
||||
#include "server_drv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include "ard_socket.h"
|
||||
#include "platform_stdlib.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
// Start server TCP on port specified
|
||||
int ServerDrv::startServer(uint16_t port, uint8_t protMode)
|
||||
{
|
||||
int sock;
|
||||
|
||||
sock = start_server(port, protMode);
|
||||
if (sock >= 0) {
|
||||
if(protMode == TCP_MODE)
|
||||
sock_listen(sock, 1);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int ServerDrv::getAvailable(int sock)
|
||||
{
|
||||
return get_available(sock);
|
||||
}
|
||||
|
||||
int ServerDrv::availData(int sock)
|
||||
{
|
||||
int ret;
|
||||
uint8_t c;
|
||||
|
||||
if (sock < 0)
|
||||
return 0;
|
||||
|
||||
if(_available) {
|
||||
return 1;
|
||||
} else {
|
||||
// flag = MSG_PEEK
|
||||
ret = get_receive(sock, &c, 1, 1, &_peer_addr, &_peer_port);
|
||||
if ( ret == 1 ) {
|
||||
_available = true;
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerDrv::getData(int sock, uint8_t *data, uint8_t peek)
|
||||
{
|
||||
int ret = 0;
|
||||
int flag = 0;
|
||||
|
||||
if (peek) {
|
||||
flag |= 1;
|
||||
} else {
|
||||
_available = false;
|
||||
}
|
||||
|
||||
ret = get_receive(sock, data, 1, flag, &_peer_addr, &_peer_port);
|
||||
|
||||
if (ret == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ServerDrv::getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_available = false;
|
||||
|
||||
ret = get_receive(sock, _data, _dataLen, 0, &_peer_addr, &_peer_port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ServerDrv::getLastErrno(int sock)
|
||||
{
|
||||
return get_sock_errno(sock);
|
||||
}
|
||||
|
||||
void ServerDrv::stopClient(int sock)
|
||||
{
|
||||
stop_socket(sock);
|
||||
_available = false;
|
||||
}
|
||||
|
||||
bool ServerDrv::sendData(int sock, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (sock < 0)
|
||||
return false;
|
||||
|
||||
ret = send_data(sock, data, len);
|
||||
|
||||
if (ret <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServerDrv::sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (sock < 0)
|
||||
return false;
|
||||
|
||||
ret = sendto_data(sock, data, len, peer_ip, peer_port);
|
||||
|
||||
if (ret == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ServerDrv::startClient(uint32_t ipAddress, uint16_t port, uint8_t protMode)
|
||||
{
|
||||
int sock;
|
||||
|
||||
sock = start_client(ipAddress, port, protMode);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void ServerDrv::getRemoteData(int sock, uint32_t *ip, uint16_t *port)
|
||||
{
|
||||
// TODO: These data may be outdated?
|
||||
*ip = _peer_addr;
|
||||
*port = _peer_port;
|
||||
}
|
||||
|
||||
int ServerDrv::setSockRecvTimeout(int sock, int timeout)
|
||||
{
|
||||
return set_sock_recv_timeout(sock, timeout);
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
server_drv.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
|
||||
*/
|
||||
|
||||
#ifndef Server_Drv_h
|
||||
#define Server_Drv_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef enum eProtMode {TCP_MODE, UDP_MODE}tProtMode;
|
||||
#define DATA_LENTH 128
|
||||
class ServerDrv
|
||||
{
|
||||
public:
|
||||
int startServer(uint16_t port, uint8_t protMode=TCP_MODE);
|
||||
int getAvailable(int sock);
|
||||
int startClient(uint32_t ipAddress, uint16_t port, uint8_t protMode=TCP_MODE);
|
||||
int getLastErrno(int sock);
|
||||
void stopClient(int sock);
|
||||
bool getData(int sock, uint8_t *data, uint8_t peek = 0);
|
||||
int getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen);
|
||||
|
||||
/* Usually used by TCP */
|
||||
bool sendData(int sock, const uint8_t *data, uint16_t len);
|
||||
|
||||
/* Usually used by UDP */
|
||||
bool sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port);
|
||||
|
||||
int availData(int sock);
|
||||
|
||||
void getRemoteData(int sock, uint32_t *ip, uint16_t *port);
|
||||
|
||||
int setSockRecvTimeout(int sock, int timeout);
|
||||
|
||||
private:
|
||||
bool _available;
|
||||
|
||||
uint32_t _peer_addr;
|
||||
uint16_t _peer_port;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976<at>gmail.com)
|
||||
|
||||
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.
|
||||
@@ -1,146 +0,0 @@
|
||||
# SPIFFS (SPI Flash File System)
|
||||
**V0.3.4**
|
||||
|
||||
Copyright (c) 2013-2016 Peter Andersson (pelleplutt1976 at gmail.com)
|
||||
|
||||
For legal stuff, see [LICENSE](https://github.com/pellepl/spiffs/blob/master/LICENSE). Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won't be held responsible.
|
||||
|
||||
Love to hear feedback though!
|
||||
|
||||
|
||||
## INTRODUCTION
|
||||
|
||||
Spiffs is a file system intended for SPI NOR flash devices on embedded targets.
|
||||
|
||||
Spiffs is designed with following characteristics in mind:
|
||||
- Small (embedded) targets, sparse RAM without heap
|
||||
- Only big areas of data (blocks) can be erased
|
||||
- An erase will reset all bits in block to ones
|
||||
- Writing pulls one to zeroes
|
||||
- Zeroes can only be pulled to ones by erase
|
||||
- Wear leveling
|
||||
|
||||
|
||||
## FEATURES
|
||||
|
||||
What spiffs does:
|
||||
- Specifically designed for low ram usage
|
||||
- Uses statically sized ram buffers, independent of number of files
|
||||
- Posix-like api: open, close, read, write, seek, stat, etc
|
||||
- It can be run on any NOR flash, not only SPI flash - theoretically also on embedded flash of an microprocessor
|
||||
- Multiple spiffs configurations can be run on same target - and even on same SPI flash device
|
||||
- Implements static wear leveling
|
||||
- Built in file system consistency checks
|
||||
|
||||
What spiffs does not:
|
||||
- Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path *tmp/myfile.txt* will create a file called *tmp/myfile.txt* instead of a *myfile.txt* under directory *tmp*.
|
||||
- It is not a realtime stack. One write operation might take much longer than another.
|
||||
- Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128MB is probably a bad idea. This is a side effect of the design goal to use as little ram as possible.
|
||||
- Presently, it does not detect or handle bad blocks.
|
||||
|
||||
|
||||
## MORE INFO
|
||||
|
||||
See the [wiki](https://github.com/pellepl/spiffs/wiki) for configuring, integrating and using spiffs.
|
||||
|
||||
For design, see [docs/TECH_SPEC](https://github.com/pellepl/spiffs/blob/master/docs/TECH_SPEC).
|
||||
|
||||
For a generic spi flash driver, see [this](https://github.com/pellepl/spiflash_driver).
|
||||
|
||||
## HISTORY
|
||||
|
||||
### 0.3.4
|
||||
- Added user callback file func.
|
||||
- Fixed a stat bug with obj id.
|
||||
- SPIFFS_probe_fs added
|
||||
- Add possibility to compile a read-only version of spiffs
|
||||
- Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke)
|
||||
- Exposed SPIFFS_open_by_page_function
|
||||
- Zero-size file cannot be seek #57 (thanks @lishen2)
|
||||
- Add tell and eof functions #54 (thanks @raburton)
|
||||
- Make api string params const #53 (thanks @raburton)
|
||||
- Preserve user_data during mount() #51 (thanks @rojer)
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_set_file_callback_func` - register a callback informing about file events
|
||||
- `SPIFFS_probe_fs` - probe a spi flash trying to figure out size of fs
|
||||
- `SPIFFS_open_by_page` - open a file by page index
|
||||
- `SPIFFS_eof` - checks if end of file is reached
|
||||
- `SPIFFS_tell` - returns current file offset
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_READ_ONLY`
|
||||
- `SPIFFS_USE_MAGIC_LENGTH`
|
||||
|
||||
### 0.3.3
|
||||
**Might not be compatible with 0.3.2 structures. See issue #40**
|
||||
- Possibility to add integer offset to file handles
|
||||
- Truncate function presumes too few free pages #49
|
||||
- Bug in truncate function #48 (thanks @PawelDefee)
|
||||
- Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee)
|
||||
- Update INTEGRATION docs (thanks @PawelDefee)
|
||||
- Fix pointer truncation in 64-bit platforms (thanks @igrr)
|
||||
- Zero-sized files cannot be read #44 (thanks @rojer)
|
||||
- (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2)
|
||||
- Check correct error code in obj_lu_find_free #41 (thanks @lishen2)
|
||||
- Moar comments for SPIFFS_lseek (thanks @igrr)
|
||||
- Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2)
|
||||
- Fixed gc_quick test (thanks @jmattsson)
|
||||
- Add SPIFFS_EXCL flag #36
|
||||
- SPIFFS_close may fail silently if cache is enabled #37
|
||||
- User data in callbacks #34
|
||||
- Ignoring SINGLETON build in cache setup (thanks Luca)
|
||||
- Compilation error fixed #32 (thanks @chotasanjiv)
|
||||
- Align cand_scores (thanks @hefloryd)
|
||||
- Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav)
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_FILEHDL_OFFSET`
|
||||
|
||||
### 0.3.2
|
||||
- Limit cache size if too much cache is given (thanks pgeiem)
|
||||
- New feature - Controlled erase. #23
|
||||
- SPIFFS_rename leaks file descriptors #28 (thanks benpicco)
|
||||
- moved dbg print defines in test framework to params_test.h
|
||||
- lseek should return the resulting offset (thanks hefloryd)
|
||||
- fixed type on dbg ifdefs
|
||||
- silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco)
|
||||
- Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela)
|
||||
- Cache might writethrough too often #16
|
||||
- even moar testrunner updates
|
||||
- Test framework update and some added tests
|
||||
- Some thoughts for next gen
|
||||
- Test sigsevs when having too many sectors #13 (thanks alonewolfx2)
|
||||
- GC might be suboptimal #11
|
||||
- Fix eternal readdir when objheader at last block, last entry
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_gc_quick` - call a nonintrusive gc
|
||||
- `SPIFFS_gc` - call a full-scale intrusive gc
|
||||
|
||||
### 0.3.1
|
||||
- Removed two return warnings, was too triggerhappy on release
|
||||
|
||||
### 0.3.0
|
||||
- Added existing namecheck when creating files
|
||||
- Lots of static analysis bugs #6
|
||||
- Added rename func
|
||||
- Fix SPIFFS_read length when reading beyond file size
|
||||
- Added reading beyond file length testcase
|
||||
- Made build a bit more configurable
|
||||
- Changed name in spiffs from "errno" to "err_code" due to conflicts compiling in mingw
|
||||
- Improved GC checks, fixed an append bug, more robust truncate for very special case
|
||||
- GC checks preempts GC, truncate even less picky
|
||||
- Struct alignment needed for some targets, define in spiffs config #10
|
||||
- Spiffs filesystem magic, definable in config
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_USE_MAGIC` - enable or disable magic check upon mount
|
||||
- `SPIFFS_ALIGNED_OBJECT_INDEX_TABLES` - alignment for certain targets
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_rename` - rename files
|
||||
- `SPIFFS_clearerr` - clears last errno
|
||||
- `SPIFFS_info` - returns info on used and total bytes in fs
|
||||
- `SPIFFS_format` - formats the filesystem
|
||||
- `SPIFFS_mounted` - checks if filesystem is mounted
|
||||
@@ -1,239 +0,0 @@
|
||||
* USING SPIFFS
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
* SPIFFS DESIGN
|
||||
|
||||
Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
|
||||
for bigger targets with much more ram. Nevertheless, many wise thoughts have
|
||||
been borrowed from YAFFS when writing spiffs. Kudos!
|
||||
|
||||
The main complication writing spiffs was that it cannot be assumed the target
|
||||
has a heap. Spiffs must go along only with the work ram buffer given to it.
|
||||
This forces extra implementation on many areas of spiffs.
|
||||
|
||||
|
||||
** SPI flash devices using NOR technology
|
||||
|
||||
Below is a small description of how SPI flashes work internally. This is to
|
||||
give an understanding of the design choices made in spiffs.
|
||||
|
||||
SPI flash devices are physically divided in blocks. On some SPI flash devices,
|
||||
blocks are further divided into sectors. Datasheets sometimes name blocks as
|
||||
sectors and vice versa.
|
||||
|
||||
Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
|
||||
blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes
|
||||
have uniform block sizes, whereas others have non-uniform - the latter meaning
|
||||
that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
|
||||
|
||||
The entire memory is linear and can be read and written in random access.
|
||||
Erasing can only be done block- or sectorwise; or by mass erase.
|
||||
|
||||
SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
|
||||
they fail.
|
||||
|
||||
A clean SPI flash from factory have all bits in entire memory set to one. A
|
||||
mass erase will reset the device to this state. Block or sector erasing will
|
||||
put the all bits in the area given by the sector or block to ones. Writing to a
|
||||
NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op.
|
||||
|
||||
Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
|
||||
|
||||
This way of "write by nand" is used considerably in spiffs.
|
||||
|
||||
Common characteristics of NOR flashes are quick reads, but slow writes.
|
||||
|
||||
And finally, unlike NAND flashes, NOR flashes seem to not need any error
|
||||
correction. They always write correctly I gather.
|
||||
|
||||
|
||||
** Spiffs logical structure
|
||||
|
||||
Some terminology before proceeding. Physical blocks/sectors means sizes stated
|
||||
in the datasheet. Logical blocks and pages is something the integrator choose.
|
||||
|
||||
|
||||
** Blocks and pages
|
||||
|
||||
Spiffs is allocated to a part or all of the memory of the SPI flash device.
|
||||
This area is divided into logical blocks, which in turn are divided into
|
||||
logical pages. The boundary of a logical block must coincide with one or more
|
||||
physical blocks. The sizes for logical blocks and logical pages always remain
|
||||
the same, they are uniform.
|
||||
|
||||
Example: non-uniform flash mapped to spiffs with 128kB logical blocks
|
||||
|
||||
PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB
|
||||
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 1 : 16kB | | Block 1 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 2 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 3 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 4 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 5 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 6 : 64kB | | Block 2 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 7 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 8 : 64kB | | Block 3 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 9 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| ... | | ... |
|
||||
|
||||
A logical block is divided further into a number of logical pages. A page
|
||||
defines the smallest data holding element known to spiffs. Hence, if a file
|
||||
is created being one byte big, it will occupy one page for index and one page
|
||||
for data - it will occupy 2 x size of a logical page on flash.
|
||||
So it seems it is good to select a small page size.
|
||||
|
||||
Each page has a metadata header being normally 5 to 9 bytes. This said, a very
|
||||
small page size will make metadata occupy a lot of the memory on the flash. A
|
||||
page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
|
||||
So it seems it is good to select a big page size.
|
||||
|
||||
Also, spiffs uses a ram buffer being two times the page size. This ram buffer
|
||||
is used for loading and manipulating pages, but it is also used for algorithms
|
||||
to find free file ids, scanning the file system, etc. Having too small a page
|
||||
size means less work buffer for spiffs, ending up in more reads operations and
|
||||
eventually gives a slower file system.
|
||||
|
||||
Choosing the page size for the system involves many factors:
|
||||
- How big is the logical block size
|
||||
- What is the normal size of most files
|
||||
- How much ram can be spent
|
||||
- How much data (vs metadata) must be crammed into the file system
|
||||
- How fast must spiffs be
|
||||
- Other things impossible to find out
|
||||
|
||||
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
|
||||
fret - there is no optimal page size. This varies from how the target will use
|
||||
spiffs. Use the golden rule:
|
||||
|
||||
~~~ Logical Page Size = Logical Block Size / 256 ~~~
|
||||
|
||||
This is a good starting point. The final page size can then be derived through
|
||||
heuristical experimenting for us non-analytical minds.
|
||||
|
||||
|
||||
** Objects, indices and look-ups
|
||||
|
||||
A file, or an object as called in spiffs, is identified by an object id.
|
||||
Another YAFFS rip-off. This object id is a part of the page header. So, all
|
||||
pages know to which object/file they belong - not counting the free pages.
|
||||
|
||||
An object is made up of two types of pages: object index pages and data pages.
|
||||
Data pages contain the data written by user. Index pages contain metadata about
|
||||
the object, more specifically what data pages are part of the object.
|
||||
|
||||
The page header also includes something called a span index. Let's say a file
|
||||
is written covering three data pages. The first data page will then have span
|
||||
index 0, the second span index 1, and the last data page will have span index
|
||||
2. Simple as that.
|
||||
|
||||
Finally, each page header contain flags, telling if the page is used,
|
||||
deleted, finalized, holds index or data, and more.
|
||||
|
||||
Object indices also have span indices, where an object index with span index 0
|
||||
is referred to as the object index header. This page does not only contain
|
||||
references to data pages, but also extra info such as object name, object size
|
||||
in bytes, flags for file or directory, etc.
|
||||
|
||||
If one were to create a file covering three data pages, named e.g.
|
||||
"spandex-joke.txt", given object id 12, it could look like this:
|
||||
|
||||
PAGE 0 <things to be unveiled soon>
|
||||
|
||||
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA]
|
||||
<first data page of joke>
|
||||
|
||||
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA]
|
||||
<second data page of joke>
|
||||
|
||||
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA]
|
||||
<some data belonging to object 545, probably not very amusing>
|
||||
|
||||
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA]
|
||||
<third data page of joke>
|
||||
|
||||
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX]
|
||||
obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE]
|
||||
obj ix: [1 2 4]
|
||||
|
||||
Looking in detail at page 5, the object index header page, the object index
|
||||
array refers to each data page in order, as mentioned before. The index of the
|
||||
object index array correlates with the data page span index.
|
||||
|
||||
entry ix: 0 1 2
|
||||
obj ix: [1 2 4]
|
||||
| | |
|
||||
PAGE 1, DATA, SPAN_IX 0 --------/ | |
|
||||
PAGE 2, DATA, SPAN_IX 1 --------/ |
|
||||
PAGE 4, DATA, SPAN_IX 2 --------/
|
||||
|
||||
Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on
|
||||
ram. We cannot keep a dynamic list on the whereabouts of each object index
|
||||
header so we can find a file fast. There might not even be a heap! But, we do
|
||||
not want to scan all page headers on the flash to find the object index header.
|
||||
|
||||
The first page(s) of each block contains the so called object look-up. These
|
||||
are not normal pages, they do not have a header. Instead, they are arrays
|
||||
pointing out what object-id the rest of all pages in the block belongs to.
|
||||
|
||||
By this look-up, only the first page(s) in each block must to scanned to find
|
||||
the actual page which contains the object index header of the desired object.
|
||||
|
||||
The object lookup is redundant metadata. The assumption is that it presents
|
||||
less overhead reading a full page of data to memory from each block and search
|
||||
that, instead of reading a small amount of data from each page (i.e. the page
|
||||
header) in all blocks. Each read operation from SPI flash normally contains
|
||||
extra data as the read command itself and the flash address. Also, depending on
|
||||
the underlying implementation, other criterions may need to be passed for each
|
||||
read transaction, like mutexes and such.
|
||||
|
||||
The veiled example unveiled would look like this, with some extra pages:
|
||||
|
||||
PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...]
|
||||
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ...
|
||||
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ...
|
||||
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ...
|
||||
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ...
|
||||
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ...
|
||||
PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ...
|
||||
PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ...
|
||||
PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ...
|
||||
PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ...
|
||||
PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ...
|
||||
PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ...
|
||||
PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ...
|
||||
...
|
||||
|
||||
Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
|
||||
pages are deleted, so this is marked both in page header flags and in the look
|
||||
up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
|
||||
|
||||
As a matter of fact, there are two object id's which are special:
|
||||
|
||||
obj id 0 (all bits zeroes) - indicates a deleted page in object look up
|
||||
obj id 0xff.. (all bits ones) - indicates a free page in object look up
|
||||
|
||||
Actually, the object id's have another quirk: if the most significant bit is
|
||||
set, this indicates an object index page. If the most significant bit is zero,
|
||||
this indicates a data page. So to be fully correct, page 0 in above example
|
||||
would look like this:
|
||||
|
||||
PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...]
|
||||
|
||||
where the asterisk means the msb of the object id is set.
|
||||
|
||||
This is another way to speed up the searches when looking for object indices.
|
||||
By looking on the object id's msb in the object lookup, it is also possible
|
||||
to find out whether the page is an object index page or a data page.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
* When mending lost pages, also see if they fit into length specified in object index header
|
||||
|
||||
SPIFFS2 thoughts
|
||||
|
||||
* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id.
|
||||
Eg. object id xor:ed with bit-reversed span index.
|
||||
This should decrease number of actual pages that needs to be visited when looking thru the obj lut.
|
||||
|
||||
* Logical number of each block. When moving stuff in a garbage collected page, the free
|
||||
page is assigned the same number as the garbage collected. Thus, object index pages do not have to
|
||||
be rewritten.
|
||||
|
||||
* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit
|
||||
as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a
|
||||
check is automatically run.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user