mirror of
https://github.com/0xFEEDC0DE64/arduino-esp32.git
synced 2025-06-25 18:01:33 +02:00
Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
77810471ce | |||
be081ac026 | |||
4d3f6caa0d | |||
2db811f7f3 | |||
39836f12df | |||
25fd2d0f3b | |||
278fa0d87a | |||
b37f4069e4 | |||
e602145c2b | |||
6f6ee98188 | |||
1289f4be4b | |||
70f000da71 | |||
884e417a49 | |||
bb7dea1566 | |||
bff9f0b6b1 | |||
72803703fd | |||
0596a2ac86 | |||
fe1fdd2790 | |||
af7e489f01 | |||
5cfff190e9 | |||
7a332864ab | |||
8aa6e2e143 | |||
b5f317010c | |||
f644d9d157 | |||
a15b7e9088 | |||
ce340faf94 | |||
b69b04cb2b | |||
0cbba8a71d | |||
9e1f8cc457 | |||
cfe7e01158 | |||
fcd734a13c | |||
aa030e044c | |||
95d417cd93 | |||
df4eeb3005 | |||
a360064768 | |||
bfde8daf75 | |||
e583a0e879 | |||
7e9afe8c5e | |||
dcb007a485 | |||
bec6f87235 | |||
4ae64c541e | |||
46257c03b3 | |||
0640964879 | |||
e609c78f20 | |||
04963009ee | |||
c3ec91f968 | |||
a30005949a | |||
acefd4bf2e | |||
c700e5694f | |||
0d564d7b1d | |||
44ca2ee976 | |||
af79e18ecb | |||
273196d7e6 | |||
5d2460c74a | |||
259ff80d60 | |||
3902aa4019 | |||
f9d1b24c01 | |||
c7fa251d78 | |||
7b811f9b3a | |||
1fdc660641 | |||
a53c9de09d | |||
b58a3509b8 | |||
01d22c8807 | |||
b70737d276 | |||
233d31bed2 | |||
65c861ad4c | |||
f6a71da378 | |||
b8c9819e7f | |||
1bc1e8c602 | |||
2132d9f809 | |||
14ff311479 | |||
a3ed511884 | |||
9e2888392e | |||
a43682596f | |||
825b6ea79e | |||
deaf339bde | |||
f12df4c719 | |||
85032b226c | |||
e5ea089a7f | |||
96822d783f | |||
4e96bffe0e | |||
ea61563c69 | |||
5be3078b76 | |||
7206b2f397 | |||
3028ec42c7 | |||
145904fb9c | |||
cb8d72fdc7 | |||
1e4bf14a3e | |||
f9f995b283 | |||
c8fe873965 | |||
18d260ec6e | |||
3a8ac27a86 | |||
a6a9a518a7 | |||
02ee799f35 | |||
ce61074802 | |||
339618f8ef | |||
6e4e4c96ee | |||
a0f0bd930c | |||
80c110ece7 | |||
65511b23d3 | |||
14d6f6e7e6 | |||
9db207afbe | |||
a989853d4a | |||
172802b18e | |||
fff1783046 | |||
cb53ec4891 | |||
7d3a67ada0 | |||
d057e544e0 | |||
b05430cfd9 | |||
e346f20aa9 | |||
bdc45e39ef | |||
a7ddf39521 | |||
abb8ea99d5 | |||
30b3eebabc | |||
3222e6490a |
@ -7,6 +7,9 @@ python:
|
||||
os:
|
||||
- linux
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "l/4Dt+KQ/mACtGAHDUsPr66fUte840PZoQ4xpPikqWZI0uARu4l+Ym7+sHinnT6fBqrj8AJeBYGz4nFa8NK4LutZn9mSD40w+sxl0wSV4oHV8rzKe3Cd8+sMG3+o33yWoikMNjSvqa73Q0rm+SgrlInNdZbuAyixL+a2alaWSnGPm4F2xwUGj+S33TOy5P/Xp77CYtCV5S8vzyk/eEdNhoF0GYePJVdfuzCOUjXMyT5OWxORkzzQ7Hnn/Ka/RDfV8Si4HgujLQBrK5q6iPnNBFqBSqilYBepSMn4opnOBpIm0SCgePz7XQEFC83buA7GUcnCnfg38bf+dCwHaODf1d1PmqVRYt2QmfinexXtM4afAtL0iBUDtvrfnXHzwW9w82VeZhpbJSVh9DUQvB0IlsZeCz9J9PUBAi3N+SMX+9l+BomYwRUlPuKY+Ef2JKk9q6mxtUkky5R0daAlVxEhpVdQks1rT+T+NMoDMemxQ3SKEiqAHh6EgHecruszffmZ71uLX9MpERpew0qN+UFiafws+jkTjx+3yF9yut0Hf9sMbeAYzzkGzRqJTUEBJ6B29Cql8M0yRXCNN/8wuuTHhG8esstozga4ZQoIVrq7mEAgup376PTcNfr1+imbbWVQ7lJdYIuDe6OS5V3OX6np11vgK/DbhfyzvQv9Z1zAGnM="
|
||||
|
@ -16,12 +16,15 @@ set(CORE_SRCS
|
||||
cores/esp32/esp32-hal-timer.c
|
||||
cores/esp32/esp32-hal-touch.c
|
||||
cores/esp32/esp32-hal-uart.c
|
||||
cores/esp32/esp32-hal-rmt.c
|
||||
cores/esp32/Esp.cpp
|
||||
cores/esp32/FunctionalInterrupt.cpp
|
||||
cores/esp32/HardwareSerial.cpp
|
||||
cores/esp32/IPAddress.cpp
|
||||
cores/esp32/IPv6Address.cpp
|
||||
cores/esp32/libb64/cdecode.c
|
||||
cores/esp32/libb64/cencode.c
|
||||
cores/esp32/main.cpp
|
||||
cores/esp32/MD5Builder.cpp
|
||||
cores/esp32/Print.cpp
|
||||
cores/esp32/stdlib_noniso.c
|
||||
@ -40,9 +43,11 @@ set(LIBRARY_SRCS
|
||||
libraries/DNSServer/src/DNSServer.cpp
|
||||
libraries/EEPROM/EEPROM.cpp
|
||||
libraries/ESPmDNS/src/ESPmDNS.cpp
|
||||
libraries/FFat/src/FFat.cpp
|
||||
libraries/FS/src/FS.cpp
|
||||
libraries/FS/src/vfs_api.cpp
|
||||
libraries/HTTPClient/src/HTTPClient.cpp
|
||||
libraries/HTTPUpdate/src/HTTPUpdate.cpp
|
||||
libraries/NetBIOS/src/NetBIOS.cpp
|
||||
libraries/Preferences/src/Preferences.cpp
|
||||
libraries/SD_MMC/src/SD_MMC.cpp
|
||||
@ -145,6 +150,8 @@ set(BLE_SRCS
|
||||
libraries/BLE/src/BLEDescriptor.cpp
|
||||
libraries/BLE/src/BLEDescriptorMap.cpp
|
||||
libraries/BLE/src/BLEDevice.cpp
|
||||
libraries/BLE/src/BLEEddystoneTLM.cpp
|
||||
libraries/BLE/src/BLEEddystoneURL.cpp
|
||||
libraries/BLE/src/BLEExceptions.cpp
|
||||
libraries/BLE/src/BLEHIDDevice.cpp
|
||||
libraries/BLE/src/BLERemoteCharacteristic.cpp
|
||||
@ -175,8 +182,10 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
||||
libraries/DNSServer/src
|
||||
libraries/ESP32/src
|
||||
libraries/ESPmDNS/src
|
||||
libraries/FFat/src
|
||||
libraries/FS/src
|
||||
libraries/HTTPClient/src
|
||||
libraries/HTTPUpdate/src
|
||||
libraries/NetBIOS/src
|
||||
libraries/Preferences/src
|
||||
libraries/SD_MMC/src
|
||||
@ -195,6 +204,6 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS cores/esp32/libb64)
|
||||
|
||||
set(COMPONENT_REQUIRES spi_flash mbedtls mdns ethernet)
|
||||
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl)
|
||||
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl bt)
|
||||
|
||||
register_component()
|
||||
|
@ -1,17 +1,7 @@
|
||||
BOOT_APP_BIN_OFFSET := 0xe000
|
||||
BOOT_APP_BIN_ROOT := $(call dequote,$(COMPONENT_PATH))
|
||||
BOOT_APP_BIN_PATH := $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/boot_app0.bin)))
|
||||
|
||||
ifndef CONFIG_PARTITION_TABLE_CUSTOM
|
||||
PARTITION_TABLE_CSV_PATH = $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/$(CONFIG_ARDUHAL_PARTITION_SCHEME).csv)))
|
||||
endif
|
||||
|
||||
BOOT_APP_BIN_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
|
||||
ESPTOOL_ALL_FLASH_ARGS += $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
|
||||
|
||||
CPPFLAGS += -DARDUINO=10800 -DESP32=1 -DARDUINO_ARCH_ESP32=1 -DBOARD_HAS_PSRAM
|
||||
|
||||
boot-app0:
|
||||
@echo "Rebooting to APP0"
|
||||
$(BOOT_APP_BIN_FLASH_CMD)
|
||||
|
||||
|
11
README.md
11
README.md
@ -1,6 +1,4 @@
|
||||
# Arduino core for ESP32 WiFi chip
|
||||
|
||||
[](https://travis-ci.org/espressif/arduino-esp32)
|
||||
# Arduino core for ESP32 WiFi chip [](https://travis-ci.org/espressif/arduino-esp32)
|
||||
|
||||
### Need help or have a question? Join the chat at [](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
@ -12,15 +10,16 @@
|
||||
- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap)
|
||||
|
||||
## Development Status
|
||||
[Latest stable release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
[Latest development release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use:
|
||||
- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM
|
||||
- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation
|
||||
- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output
|
||||
|
||||
## Installation Instructions
|
||||
|
||||
#### [Latest release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
- Using Arduino IDE Boards Manager (preferred)
|
||||
+ [Instructions for Boards Manager](docs/arduino-ide/boards_manager.md)
|
||||
- Using Arduino IDE with the development repository
|
||||
|
911
boards.txt
911
boards.txt
File diff suppressed because it is too large
Load Diff
@ -75,7 +75,6 @@
|
||||
|
||||
#define abs(x) ((x)>0?(x):-(x))
|
||||
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
#define radians(deg) ((deg)*DEG_TO_RAD)
|
||||
#define degrees(rad) ((rad)*RAD_TO_DEG)
|
||||
#define sq(x) ((x)*(x))
|
||||
@ -146,6 +145,9 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "WCharacter.h"
|
||||
#include "WString.h"
|
||||
#include "Stream.h"
|
||||
@ -158,6 +160,12 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||
#include "HardwareSerial.h"
|
||||
#include "Esp.h"
|
||||
|
||||
using std::isinf;
|
||||
using std::isnan;
|
||||
using std::max;
|
||||
using std::min;
|
||||
using ::round;
|
||||
|
||||
uint16_t makeWord(uint16_t w);
|
||||
uint16_t makeWord(byte h, byte l);
|
||||
|
||||
@ -176,12 +184,6 @@ extern "C" void configTzTime(const char* tz,
|
||||
long random(long);
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef _GLIBCXX_VECTOR
|
||||
// arduino is not compatible with std::vector
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#define _min(a,b) ((a)<(b)?(a):(b))
|
||||
#define _max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
|
@ -25,20 +25,12 @@
|
||||
#include <memory>
|
||||
#include <soc/soc.h>
|
||||
#include <soc/efuse_reg.h>
|
||||
|
||||
/* Main header of binary image */
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
uint8_t segment_count;
|
||||
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
|
||||
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
|
||||
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
|
||||
uint32_t entry_addr;
|
||||
uint8_t encrypt_flag; /* encrypt flag */
|
||||
uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */
|
||||
} esp_image_header_t;
|
||||
|
||||
#define ESP_IMAGE_HEADER_MAGIC 0xE9
|
||||
#include <esp_partition.h>
|
||||
extern "C" {
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
}
|
||||
#include <MD5Builder.h>
|
||||
|
||||
/**
|
||||
* User-defined Literals
|
||||
@ -112,9 +104,118 @@ void EspClass::restart(void)
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
uint32_t EspClass::getHeapSize(void)
|
||||
{
|
||||
multi_heap_info_t info;
|
||||
heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
|
||||
return info.total_free_bytes + info.total_allocated_bytes;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeHeap(void)
|
||||
{
|
||||
return esp_get_free_heap_size();
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreeHeap(void)
|
||||
{
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocHeap(void)
|
||||
{
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getPsramSize(void)
|
||||
{
|
||||
multi_heap_info_t info;
|
||||
heap_caps_get_info(&info, MALLOC_CAP_SPIRAM);
|
||||
return info.total_free_bytes + info.total_allocated_bytes;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreePsram(void)
|
||||
{
|
||||
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreePsram(void)
|
||||
{
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocPsram(void)
|
||||
{
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
static uint32_t sketchSize(sketchSize_t response) {
|
||||
esp_image_metadata_t data;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) return 0;
|
||||
const esp_partition_pos_t running_pos = {
|
||||
.offset = running->address,
|
||||
.size = running->size,
|
||||
};
|
||||
data.start_addr = running_pos.offset;
|
||||
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
|
||||
if (response) {
|
||||
return running_pos.size - data.image_len;
|
||||
} else {
|
||||
return data.image_len;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::getSketchSize () {
|
||||
return sketchSize(SKETCH_SIZE_TOTAL);
|
||||
}
|
||||
|
||||
String EspClass::getSketchMD5()
|
||||
{
|
||||
static String result;
|
||||
if (result.length()) {
|
||||
return result;
|
||||
}
|
||||
uint32_t lengthLeft = getSketchSize();
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) {
|
||||
log_e("Partition could not be found");
|
||||
|
||||
return String();
|
||||
}
|
||||
const size_t bufSize = SPI_FLASH_SEC_SIZE;
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
|
||||
uint32_t offset = 0;
|
||||
if(!buf.get()) {
|
||||
log_e("Not enough memory to allocate buffer");
|
||||
|
||||
return String();
|
||||
}
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
while( lengthLeft > 0) {
|
||||
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
|
||||
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
|
||||
log_e("Could not read buffer from flash");
|
||||
|
||||
return String();
|
||||
}
|
||||
md5.add(buf.get(), readBytes);
|
||||
lengthLeft -= readBytes;
|
||||
offset += readBytes;
|
||||
}
|
||||
md5.calculate();
|
||||
result = md5.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeSketchSpace () {
|
||||
const esp_partition_t* _partition = esp_ota_get_next_update_partition(NULL);
|
||||
if(!_partition){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _partition->size;
|
||||
}
|
||||
|
||||
uint8_t EspClass::getChipRevision(void)
|
||||
|
@ -50,13 +50,30 @@ typedef enum {
|
||||
FM_UNKNOWN = 0xff
|
||||
} FlashMode_t;
|
||||
|
||||
typedef enum {
|
||||
SKETCH_SIZE_TOTAL = 0,
|
||||
SKETCH_SIZE_FREE = 1
|
||||
} sketchSize_t;
|
||||
|
||||
class EspClass
|
||||
{
|
||||
public:
|
||||
EspClass() {}
|
||||
~EspClass() {}
|
||||
void restart();
|
||||
uint32_t getFreeHeap();
|
||||
|
||||
//Internal RAM
|
||||
uint32_t getHeapSize(); //total heap size
|
||||
uint32_t getFreeHeap(); //available heap
|
||||
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
|
||||
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once
|
||||
|
||||
//SPI RAM
|
||||
uint32_t getPsramSize();
|
||||
uint32_t getFreePsram();
|
||||
uint32_t getMinFreePsram();
|
||||
uint32_t getMaxAllocPsram();
|
||||
|
||||
uint8_t getChipRevision();
|
||||
uint8_t getCpuFreqMHz(){ return CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; }
|
||||
uint32_t getCycleCount();
|
||||
@ -72,6 +89,10 @@ public:
|
||||
uint32_t magicFlashChipSpeed(uint8_t byte);
|
||||
FlashMode_t magicFlashChipMode(uint8_t byte);
|
||||
|
||||
uint32_t getSketchSize();
|
||||
String getSketchMD5();
|
||||
uint32_t getFreeSketchSpace();
|
||||
|
||||
bool flashEraseSector(uint32_t sector);
|
||||
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
|
||||
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
|
||||
|
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* FunctionalInterrupt.cpp
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#include "FunctionalInterrupt.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional);
|
||||
}
|
||||
|
||||
void IRAM_ATTR interruptFunctional(void* arg)
|
||||
{
|
||||
InterruptArgStructure* localArg = (InterruptArgStructure*)arg;
|
||||
if (localArg->interruptFunction)
|
||||
{
|
||||
localArg->interruptFunction();
|
||||
}
|
||||
}
|
||||
|
||||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)
|
||||
{
|
||||
// use the local interrupt routine which takes the ArgStructure as argument
|
||||
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void cleanupFunctional(void* arg)
|
||||
{
|
||||
delete (InterruptArgStructure*)arg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
20
cores/esp32/FunctionalInterrupt.h
Normal file
20
cores/esp32/FunctionalInterrupt.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* FunctionalInterrupt.h
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct InterruptArgStructure {
|
||||
std::function<void(void)> interruptFunction;
|
||||
};
|
||||
|
||||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
|
||||
|
||||
|
||||
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */
|
@ -3,8 +3,25 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "HardwareSerial.h"
|
||||
|
||||
#ifndef RX1
|
||||
#define RX1 9
|
||||
#endif
|
||||
|
||||
#ifndef TX1
|
||||
#define TX1 10
|
||||
#endif
|
||||
|
||||
#ifndef RX2
|
||||
#define RX2 16
|
||||
#endif
|
||||
|
||||
#ifndef TX2
|
||||
#define TX2 17
|
||||
#endif
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
HardwareSerial Serial(0);
|
||||
HardwareSerial Serial1(1);
|
||||
@ -13,7 +30,7 @@ HardwareSerial Serial2(2);
|
||||
|
||||
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
|
||||
|
||||
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert)
|
||||
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
|
||||
{
|
||||
if(0 > _uart_nr || _uart_nr > 2) {
|
||||
log_e("Serial number is invalid, please use 0, 1 or 2");
|
||||
@ -27,14 +44,33 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
|
||||
txPin = 1;
|
||||
}
|
||||
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = 9;
|
||||
txPin = 10;
|
||||
rxPin = RX1;
|
||||
txPin = TX1;
|
||||
}
|
||||
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = 16;
|
||||
txPin = 17;
|
||||
rxPin = RX2;
|
||||
txPin = TX2;
|
||||
}
|
||||
|
||||
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert);
|
||||
|
||||
if(!baud) {
|
||||
time_t startMillis = millis();
|
||||
unsigned long detectedBaudRate = 0;
|
||||
while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
|
||||
yield();
|
||||
}
|
||||
|
||||
end();
|
||||
|
||||
if(detectedBaudRate) {
|
||||
delay(100); // Give some time...
|
||||
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, 256, invert);
|
||||
} else {
|
||||
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible");
|
||||
_uart = NULL;
|
||||
}
|
||||
}
|
||||
_uart = uartBegin(_uart_nr, baud, config, rxPin, txPin, 256, invert);
|
||||
}
|
||||
|
||||
void HardwareSerial::end()
|
||||
@ -46,6 +82,10 @@ void HardwareSerial::end()
|
||||
_uart = 0;
|
||||
}
|
||||
|
||||
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
|
||||
return uartResizeRxBuffer(_uart, new_size);
|
||||
}
|
||||
|
||||
void HardwareSerial::setDebugOutput(bool en)
|
||||
{
|
||||
if(_uart == 0) {
|
||||
|
@ -22,6 +22,24 @@
|
||||
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support)
|
||||
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
|
||||
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
|
||||
Modified 13 October 2018 by Jeroen Döll (add baudrate detection)
|
||||
Baudrate detection example usage (detection on Serial1):
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
Serial.println();
|
||||
|
||||
Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms
|
||||
|
||||
unsigned long detectedBaudRate = Serial1.baudRate();
|
||||
if(detectedBaudRate) {
|
||||
Serial.printf("Detected baudrate is %lu\n", detectedBaudRate);
|
||||
} else {
|
||||
Serial.println("No baudrate detected, Serial1 will not work!");
|
||||
}
|
||||
}
|
||||
|
||||
Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201
|
||||
*/
|
||||
|
||||
#ifndef HardwareSerial_h
|
||||
@ -37,7 +55,7 @@ class HardwareSerial: public Stream
|
||||
public:
|
||||
HardwareSerial(int uart_nr);
|
||||
|
||||
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false);
|
||||
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL);
|
||||
void end();
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
@ -70,6 +88,7 @@ public:
|
||||
uint32_t baudRate();
|
||||
operator bool() const;
|
||||
|
||||
size_t setRxBufferSize(size_t);
|
||||
void setDebugOutput(bool);
|
||||
|
||||
protected:
|
||||
|
@ -63,7 +63,7 @@ size_t Print::printf(const char *format, ...)
|
||||
len = vsnprintf(temp, len+1, format, arg);
|
||||
write((uint8_t*)temp, len);
|
||||
va_end(arg);
|
||||
if(len > 64){
|
||||
if(len >= sizeof(loc_buf)){
|
||||
delete[] temp;
|
||||
}
|
||||
return len;
|
||||
|
@ -882,6 +882,12 @@ float String::toFloat(void) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
double String::toDouble(void) const {
|
||||
if (buffer) {
|
||||
return atof(buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char String::equalsConstantTime(const String &s2) const {
|
||||
// To avoid possible time-based attacks present function
|
||||
|
@ -260,6 +260,7 @@ public:
|
||||
// parsing/conversion
|
||||
long toInt(void) const;
|
||||
float toFloat(void) const;
|
||||
double toDouble(void) const;
|
||||
|
||||
protected:
|
||||
char *buffer; // the actual char array
|
||||
|
1
cores/esp32/apps/sntp/sntp.h
Normal file
1
cores/esp32/apps/sntp/sntp.h
Normal file
@ -0,0 +1 @@
|
||||
#include "lwip/apps/sntp.h"
|
@ -37,8 +37,7 @@ extern "C" {
|
||||
*/
|
||||
String base64::encode(uint8_t * data, size_t length)
|
||||
{
|
||||
// base64 needs more size then the source data
|
||||
size_t size = ((length * 1.6f) + 1);
|
||||
size_t size = base64_encode_expected_len(length) + 1;
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
|
@ -74,6 +74,7 @@ typedef void (*voidFuncPtrArg)(void*);
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void* arg;
|
||||
bool functional;
|
||||
} InterruptHandle_t;
|
||||
static InterruptHandle_t __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,};
|
||||
|
||||
@ -238,16 +239,26 @@ static void IRAM_ATTR __onPinInterrupt()
|
||||
}
|
||||
}
|
||||
|
||||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
|
||||
extern void cleanupFunctional(void* arg);
|
||||
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional)
|
||||
{
|
||||
static bool interrupt_initialized = false;
|
||||
|
||||
|
||||
if(!interrupt_initialized) {
|
||||
interrupt_initialized = true;
|
||||
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);
|
||||
}
|
||||
|
||||
// if new attach without detach remove old info
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
|
||||
{
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc;
|
||||
__pinInterruptHandlers[pin].arg = arg;
|
||||
__pinInterruptHandlers[pin].functional = functional;
|
||||
|
||||
esp_intr_disable(gpio_intr_handle);
|
||||
if(esp_intr_get_cpu(gpio_intr_handle)) { //APP_CPU
|
||||
GPIO.pin[pin].int_ena = 1;
|
||||
@ -258,15 +269,26 @@ extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * ar
|
||||
esp_intr_enable(gpio_intr_handle);
|
||||
}
|
||||
|
||||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
|
||||
{
|
||||
__attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) {
|
||||
__attachInterruptArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type);
|
||||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __detachInterrupt(uint8_t pin)
|
||||
{
|
||||
esp_intr_disable(gpio_intr_handle);
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
|
||||
{
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = NULL;
|
||||
__pinInterruptHandlers[pin].arg = NULL;
|
||||
__pinInterruptHandlers[pin].arg = false;
|
||||
|
||||
GPIO.pin[pin].int_ena = 0;
|
||||
GPIO.pin[pin].int_type = 0;
|
||||
esp_intr_enable(gpio_intr_handle);
|
||||
@ -277,6 +299,6 @@ extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pi
|
||||
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
|
||||
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")));
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtr handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
|
||||
|
||||
|
@ -79,7 +79,7 @@ void digitalWrite(uint8_t pin, uint8_t val);
|
||||
int digitalRead(uint8_t pin);
|
||||
|
||||
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void), void * arg, int mode);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode);
|
||||
void detachInterrupt(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -36,17 +36,21 @@
|
||||
/* Stickbreaker ISR mode debug support
|
||||
|
||||
ENABLE_I2C_DEBUG_BUFFER
|
||||
Enable debug interrupt history buffer, setting this define will result in 1544 bytes of RAM
|
||||
being used whenever CORE_DEBUG_LEVEL is higher than WARNING. Unless you are debugging
|
||||
a problem in the I2C subsystem I would recommend you leave it commented out.
|
||||
Enable debug interrupt history buffer, fifoTx history buffer.
|
||||
Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL
|
||||
is higher than WARNING. Unless you are debugging a problem in the I2C subsystem,
|
||||
I would recommend you leave it commented out.
|
||||
*/
|
||||
|
||||
//#define ENABLE_I2C_DEBUG_BUFFER
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
#define INTBUFFMAX 64
|
||||
#define FIFOMAX 512
|
||||
static uint32_t intBuff[INTBUFFMAX][3][2];
|
||||
static uint32_t intPos[2]= {0,0};
|
||||
static uint16_t fifoBuffer[FIFOMAX];
|
||||
static uint16_t fifoPos = 0;
|
||||
#endif
|
||||
|
||||
// start from tools/sdk/include/soc/soc/i2c_struct.h
|
||||
@ -154,7 +158,7 @@ typedef union {
|
||||
|
||||
// individual dq element
|
||||
typedef struct {
|
||||
uint8_t *data; // datapointer for read/write buffer
|
||||
uint8_t *data; // data pointer for read/write buffer
|
||||
uint16_t length; // size of data buffer
|
||||
uint16_t position; // current position for next char in buffer (<length)
|
||||
uint16_t cmdBytesNeeded; // used to control number of I2C_COMMAND_t blocks added to queue
|
||||
@ -179,9 +183,10 @@ struct i2c_struct_t {
|
||||
I2C_DATA_QUEUE_t * dq;
|
||||
uint16_t queueCount; // number of dq entries in queue.
|
||||
uint16_t queuePos; // current queue that still has or needs data (out/in)
|
||||
uint16_t errorByteCnt; // count of bytes moved (both in and out)
|
||||
int16_t errorByteCnt; // byte pos where error happened, -1 devId, 0..(length-1) data
|
||||
uint16_t errorQueue; // errorByteCnt is in this queue,(for error locus)
|
||||
uint32_t exitCode;
|
||||
uint32_t debugFlags;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -197,16 +202,16 @@ enum {
|
||||
#define I2C_MUTEX_UNLOCK()
|
||||
|
||||
static i2c_t _i2c_bus_array[2] = {
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0},
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0},
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
|
||||
};
|
||||
#else
|
||||
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS)
|
||||
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock)
|
||||
|
||||
static i2c_t _i2c_bus_array[2] = {
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0},
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0},
|
||||
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0}
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -218,6 +223,228 @@ static i2c_t _i2c_bus_array[2] = {
|
||||
* ack_exp - This bit is to set an expected ACK value for the transmitter.
|
||||
* ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no.
|
||||
* */
|
||||
|
||||
|
||||
/* Stickbreaker ISR mode debug support
|
||||
*/
|
||||
static void IRAM_ATTR i2cDumpCmdQueue(i2c_t *i2c)
|
||||
{
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
static const char * const cmdName[] ={"RSTART","WRITE","READ","STOP","END"};
|
||||
uint8_t i=0;
|
||||
while(i<16) {
|
||||
I2C_COMMAND_t c;
|
||||
c.val=i2c->dev->command[i].val;
|
||||
log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'),
|
||||
cmdName[c.op_code],
|
||||
c.ack_val,
|
||||
c.ack_exp,
|
||||
c.ack_en,
|
||||
c.byte_num);
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Stickbreaker ISR mode debug support
|
||||
*/
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
|
||||
static void i2cDumpDqData(i2c_t * i2c)
|
||||
{
|
||||
#if defined (ENABLE_I2C_DEBUG_BUFFER)
|
||||
uint16_t a=0;
|
||||
char buff[140];
|
||||
I2C_DATA_QUEUE_t *tdq;
|
||||
int digits=0,lenDigits=0;
|
||||
a = i2c->queueCount;
|
||||
while(a>0) {
|
||||
digits++;
|
||||
a /= 10;
|
||||
}
|
||||
while(a<i2c->queueCount) { // find maximum number of len decimal digits for formatting
|
||||
if (i2c->dq[a].length > lenDigits ) lenDigits = i2c->dq[a].length;
|
||||
a++;
|
||||
}
|
||||
a=0;
|
||||
while(lenDigits>0){
|
||||
a++;
|
||||
lenDigits /= 10;
|
||||
}
|
||||
lenDigits = a;
|
||||
a = 0;
|
||||
while(a<i2c->queueCount) {
|
||||
tdq=&i2c->dq[a];
|
||||
char buf1[10],buf2[10];
|
||||
sprintf(buf1,"%0*d",lenDigits,tdq->length);
|
||||
sprintf(buf2,"%0*d",lenDigits,tdq->position);
|
||||
log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a,
|
||||
(tdq->ctrl.addr>0x100)?"10":"7",
|
||||
(tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1),
|
||||
(tdq->ctrl.mode)?'R':'W',
|
||||
(tdq->ctrl.stop)?"STOP":"",
|
||||
tdq->data,
|
||||
buf1,buf2,
|
||||
tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent
|
||||
);
|
||||
uint16_t offset = 0;
|
||||
while(offset<tdq->length) {
|
||||
memset(buff,' ',140);
|
||||
buff[139]='\0';
|
||||
uint16_t i = 0,j;
|
||||
j=sprintf(buff,"0x%04x: ",offset);
|
||||
while((i<32)&&(offset < tdq->length)) {
|
||||
char ch = tdq->data[offset];
|
||||
sprintf((char*)&buff[(i*3)+41],"%02x ",ch);
|
||||
if((ch<32)||(ch>126)) {
|
||||
ch='.';
|
||||
}
|
||||
j+=sprintf((char*)&buff[j],"%c",ch);
|
||||
buff[j]=' ';
|
||||
i++;
|
||||
offset++;
|
||||
}
|
||||
log_i("%s",buff);
|
||||
}
|
||||
a++;
|
||||
}
|
||||
#else
|
||||
log_i("Debug Buffer not Enabled");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
||||
static void i2cDumpI2c(i2c_t * i2c)
|
||||
{
|
||||
log_e("i2c=%p",i2c);
|
||||
log_i("dev=%p date=%p",i2c->dev,i2c->dev->date);
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
log_i("lock=%p",i2c->lock);
|
||||
#endif
|
||||
log_i("num=%d",i2c->num);
|
||||
log_i("mode=%d",i2c->mode);
|
||||
log_i("stage=%d",i2c->stage);
|
||||
log_i("error=%d",i2c->error);
|
||||
log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0);
|
||||
log_i("intr_handle=%p",i2c->intr_handle);
|
||||
log_i("dq=%p",i2c->dq);
|
||||
log_i("queueCount=%d",i2c->queueCount);
|
||||
log_i("queuePos=%d",i2c->queuePos);
|
||||
log_i("errorByteCnt=%d",i2c->errorByteCnt);
|
||||
log_i("errorQueue=%d",i2c->errorQueue);
|
||||
log_i("debugFlags=0x%08X",i2c->debugFlags);
|
||||
if(i2c->dq) {
|
||||
i2cDumpDqData(i2c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
|
||||
static void i2cDumpInts(uint8_t num)
|
||||
{
|
||||
#if defined (ENABLE_I2C_DEBUG_BUFFER)
|
||||
uint32_t b;
|
||||
log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num);
|
||||
for(uint32_t a=1; a<=INTBUFFMAX; a++) {
|
||||
b=(a+intPos[num])%INTBUFFMAX;
|
||||
if(intBuff[b][0][num]!=0) {
|
||||
log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
log_i("Debug Buffer not Enabled");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/
|
||||
uint32_t slave_rw: 1; /*when in slave mode 1:master read slave 0: master write slave.*/
|
||||
uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/
|
||||
uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/
|
||||
uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/
|
||||
uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/
|
||||
uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/
|
||||
uint32_t reserved7: 1;
|
||||
uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/
|
||||
uint32_t reserved14: 4;
|
||||
uint32_t tx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/
|
||||
uint32_t scl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/
|
||||
uint32_t reserved27: 1;
|
||||
uint32_t scl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/
|
||||
uint32_t reserved31: 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} status_reg;
|
||||
|
||||
status_reg sr;
|
||||
sr.val= i2c->dev->status_reg.val;
|
||||
|
||||
log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
static void i2cDumpFifo(i2c_t * i2c){
|
||||
char buf[64];
|
||||
uint16_t k = 0;
|
||||
uint16_t i = fifoPos+1;
|
||||
i %=FIFOMAX;
|
||||
while((fifoBuffer[i]==0)&&(i!=fifoPos)){
|
||||
i++;
|
||||
i %=FIFOMAX;
|
||||
}
|
||||
if(i != fifoPos){// actual data
|
||||
do{
|
||||
if(fifoBuffer[i] & 0x8000){ // address byte
|
||||
if(fifoBuffer[i] & 0x100) { // read
|
||||
if(fifoBuffer[i] & 0x1) { // valid read dev id
|
||||
k+= sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1);
|
||||
} else { // invalid read dev id
|
||||
k+= sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff));
|
||||
}
|
||||
} else { // write
|
||||
if(fifoBuffer[i] & 0x1) { // bad write dev id
|
||||
k+= sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff));
|
||||
} else { // good Write
|
||||
k+= sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1);
|
||||
}
|
||||
}
|
||||
} else k += sprintf(&buf[k],"% 4X ",fifoBuffer[i]);
|
||||
|
||||
i++;
|
||||
i %= FIFOMAX;
|
||||
bool outBuffer=false;
|
||||
if( fifoBuffer[i] & 0x8000){
|
||||
outBuffer=true;
|
||||
k=0;
|
||||
}
|
||||
if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf);
|
||||
outBuffer = false;
|
||||
if(k>50) {
|
||||
k=sprintf(buf,"-> ");
|
||||
}
|
||||
}while( i!= fifoPos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
if( trigger ){
|
||||
log_i("%s",locus);
|
||||
if(trigger & 1) i2cDumpI2c(i2c);
|
||||
if(trigger & 2) i2cDumpInts(i2c->num);
|
||||
if(trigger & 4) i2cDumpCmdQueue(i2c);
|
||||
if(trigger & 8) i2cDumpStatus(i2c);
|
||||
if(trigger & 16) i2cDumpFifo(i2c);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// end of debug support routines
|
||||
|
||||
static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check)
|
||||
{
|
||||
I2C_COMMAND_t cmd;
|
||||
@ -229,43 +456,24 @@ static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uin
|
||||
cmd.op_code = op_code;
|
||||
i2c->dev->command[index].val = cmd.val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Stickbreaker ISR mode debug support
|
||||
*/
|
||||
void IRAM_ATTR dumpCmdQueue(i2c_t *i2c)
|
||||
{
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
uint8_t i=0;
|
||||
while(i<16) {
|
||||
I2C_COMMAND_t c;
|
||||
c.val=i2c->dev->command[i].val;
|
||||
log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'),
|
||||
c.op_code,
|
||||
c.ack_val,
|
||||
c.ack_exp,
|
||||
c.ack_en,
|
||||
c.byte_num);
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Stickbreaker ISR mode support
|
||||
*/
|
||||
static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
|
||||
{
|
||||
/* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs
|
||||
*/
|
||||
uint16_t cmdIdx = 0;
|
||||
uint16_t qp = i2c->queuePos;
|
||||
uint16_t qp = i2c->queuePos; // all queues before queuePos have been completely processed,
|
||||
// so look start by checking the 'current queue' so see if it needs commands added to
|
||||
// hardware peripheral command list. walk through each queue entry until all queues have been
|
||||
// checked
|
||||
bool done;
|
||||
bool needMoreCmds = false;
|
||||
bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ
|
||||
bool ena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ
|
||||
|
||||
while(!needMoreCmds&&(qp < i2c->queueCount)) { // check if more possible cmds
|
||||
if(i2c->dq[qp].ctrl.stopCmdSent) {
|
||||
if(i2c->dq[qp].ctrl.stopCmdSent) {// marks that all required cmds[] have been added to peripheral
|
||||
qp++;
|
||||
} else {
|
||||
needMoreCmds=true;
|
||||
@ -282,7 +490,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
|
||||
// (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs
|
||||
i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false);
|
||||
tdq->ctrl.startCmdSent=1;
|
||||
done = (cmdIdx>14);
|
||||
}
|
||||
|
||||
//CMD WRITE ADDRESS
|
||||
@ -341,11 +548,11 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
|
||||
tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data
|
||||
|
||||
if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop
|
||||
if(tdq->ctrl.stop) { //send a stop
|
||||
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
|
||||
done = cmdIdx > 14;
|
||||
tdq->ctrl.stopCmdSent = 1;
|
||||
} else { // dummy a stop because this is a restart
|
||||
if(!tdq->ctrl.stopCmdSent){
|
||||
if(tdq->ctrl.stop) { //send a stop
|
||||
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
|
||||
done = cmdIdx > 14;
|
||||
}
|
||||
tdq->ctrl.stopCmdSent = 1;
|
||||
}
|
||||
}
|
||||
@ -370,7 +577,7 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
|
||||
i--;
|
||||
} else { // unable to stretch, fatal
|
||||
log_e("invalid CMD[] layout Stretch Failed");
|
||||
dumpCmdQueue(i2c);
|
||||
i2cDumpCmdQueue(i2c);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
@ -409,8 +616,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
|
||||
}
|
||||
}
|
||||
|
||||
/* Stickbreaker ISR mode support
|
||||
*/
|
||||
static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
|
||||
{
|
||||
/*
|
||||
@ -421,6 +626,7 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
|
||||
uint16_t a=i2c->queuePos; // currently executing dq,
|
||||
bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
|
||||
uint8_t cnt;
|
||||
bool rxQueueEncountered = false;
|
||||
while((a < i2c->queueCount) && !full) {
|
||||
I2C_DATA_QUEUE_t *tdq = &i2c->dq[a];
|
||||
cnt=0;
|
||||
@ -449,24 +655,38 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
|
||||
i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF;
|
||||
cnt++;
|
||||
tdq->ctrl.addrSent=1; // 7bit lowbyte sent
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
uint16_t a = 0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8);
|
||||
fifoBuffer[fifoPos++] = a;
|
||||
fifoPos %= FIFOMAX;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
|
||||
// add write data to fifo
|
||||
//21NOV2017 might want to look into using local capacity counter instead of reading status_reg
|
||||
// a double while loop, like emptyRxFifo()
|
||||
if(tdq->ctrl.mode==0) { // write
|
||||
if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode!
|
||||
while((!full)&&(tdq->position < tdq->length)) {
|
||||
uint32_t moveCnt = 32 - i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo?
|
||||
while(( moveCnt>0)&&(tdq->position < tdq->length)) {
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
fifoBuffer[fifoPos++] = tdq->data[tdq->position];
|
||||
fifoPos %= FIFOMAX;
|
||||
#endif
|
||||
i2c->dev->fifo_data.val = tdq->data[tdq->position++];
|
||||
cnt++;
|
||||
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
|
||||
moveCnt--;
|
||||
|
||||
}
|
||||
}
|
||||
} else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it
|
||||
if( ! rxQueueEncountered ) {
|
||||
rxQueueEncountered = true;
|
||||
if(a > i2c->queuePos){
|
||||
i2c->queuePos = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
|
||||
// update debug buffer tx counts
|
||||
@ -474,65 +694,78 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
|
||||
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16);
|
||||
|
||||
#endif
|
||||
|
||||
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
|
||||
if(!full) {
|
||||
a++; // check next buffer for tx
|
||||
a++; // check next buffer for address tx, or write data
|
||||
}
|
||||
}
|
||||
|
||||
if(a >= i2c->queueCount ) { // disable IRQ, the next dq will re-enable it
|
||||
// (a >= i2c->queueCount) means no more data is available
|
||||
if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued
|
||||
i2c->dev->int_ena.tx_fifo_empty= 0;
|
||||
}
|
||||
|
||||
i2c->dev->int_clr.tx_fifo_empty=1;
|
||||
}
|
||||
|
||||
/* Stickbreaker ISR mode support
|
||||
*/
|
||||
|
||||
static void IRAM_ATTR emptyRxFifo(i2c_t * i2c)
|
||||
{
|
||||
uint32_t d, cnt=0, moveCnt;
|
||||
|
||||
moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read
|
||||
|
||||
while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move
|
||||
I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut
|
||||
if(tdq->ctrl.mode == 1){ // read command
|
||||
if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq
|
||||
// part of these reads go into the next dq
|
||||
moveCnt = (tdq->length - tdq->position);
|
||||
}
|
||||
} else {// error
|
||||
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
|
||||
return;
|
||||
}
|
||||
while(moveCnt > 0) {
|
||||
d = i2c->dev->fifo_data.val;
|
||||
moveCnt--;
|
||||
cnt++;
|
||||
tdq->data[tdq->position++] = (d&0xFF);
|
||||
}
|
||||
if(tdq->position >= tdq->length ){ // inc queuePos until next READ command or end of queue
|
||||
i2c->queuePos++;
|
||||
while((i2c->queuePos < i2c->queueCount)&&(i2c->dq[i2c->queuePos].ctrl.mode !=1)){
|
||||
i2c->queuePos++;
|
||||
}
|
||||
if(i2c->queuePos < i2c->queueCount){ // found new place to store rx data
|
||||
tdq = &i2c->dq[i2c->queuePos]; // update shortcut
|
||||
// see if any more chars showed up while empting Fifo.
|
||||
moveCnt = i2c->dev->status_reg.rx_fifo_cnt;
|
||||
|
||||
I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut
|
||||
|
||||
while((tdq->position >= tdq->length)&&( i2c->queuePos < i2c->queueCount)){ // find were to store
|
||||
i2c->queuePos++;
|
||||
tdq = &i2c->dq[i2c->queuePos];
|
||||
}
|
||||
|
||||
if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it!
|
||||
log_e("no Storage location for %d",moveCnt);
|
||||
// discard
|
||||
while(moveCnt>0){
|
||||
d = i2c->dev->fifo_data.val;
|
||||
moveCnt--;
|
||||
cnt++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(tdq->ctrl.mode == 1){ // read command
|
||||
if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq
|
||||
// part of these reads go into the next dq
|
||||
// part of these reads go into the next dq
|
||||
moveCnt = (tdq->length - tdq->position);
|
||||
}
|
||||
} else {// error
|
||||
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
|
||||
// discard
|
||||
while(moveCnt>0){
|
||||
d = i2c->dev->fifo_data.val;
|
||||
moveCnt--;
|
||||
cnt++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while(moveCnt > 0) { // store data
|
||||
d = i2c->dev->fifo_data.val;
|
||||
moveCnt--;
|
||||
cnt++;
|
||||
tdq->data[tdq->position++] = (d&0xFF);
|
||||
}
|
||||
|
||||
moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there?
|
||||
}
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
// update Debug rxCount
|
||||
cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&&0xffFF;
|
||||
cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&0xffFF;
|
||||
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal)
|
||||
{
|
||||
@ -578,16 +811,17 @@ static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
|
||||
{
|
||||
/* i2c_update_error_byte_cnt 07/18/2018
|
||||
Only called after an error has occurred, so, most of the time this function is never used.
|
||||
This function obliterates the need to interrupt monitor each byte transferred, at high bitrates
|
||||
the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated.
|
||||
it update errorByteCnt, errorQueue.
|
||||
it updates errorByteCnt, errorQueue.
|
||||
*/
|
||||
static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
|
||||
{
|
||||
uint16_t a=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo.
|
||||
uint16_t bc = 0;
|
||||
int16_t bc = 0;
|
||||
I2C_DATA_QUEUE_t *tdq;
|
||||
i2c->errorByteCnt = 0;
|
||||
while( a < i2c->queueCount){ // add up all bytes loaded into fifo's
|
||||
@ -596,40 +830,34 @@ static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
|
||||
i2c->errorByteCnt += tdq->position;
|
||||
a++;
|
||||
}
|
||||
// log_v("errorByteCnt=%d",i2c->errorByteCnt);
|
||||
// now errorByteCnt contains total bytes moved into and out of FIFO's
|
||||
// but, there may still be bytes waiting in Fifo's
|
||||
i2c->errorByteCnt -= i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out;
|
||||
i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received
|
||||
// now walk thru DQ again, find which byte is 'current'
|
||||
bool done = false;
|
||||
bc = i2c->errorByteCnt;
|
||||
i2c->errorQueue = 0;
|
||||
while(( i2c->errorQueue < i2c->queueCount)&&( !done )){
|
||||
while( i2c->errorQueue < i2c->queueCount ){
|
||||
tdq = &i2c->dq[i2c->errorQueue];
|
||||
if(bc>0){ // not found yet
|
||||
if( tdq->ctrl.addrSent >= bc){ // in address
|
||||
done = true;
|
||||
continue;
|
||||
bc = -1; // in address
|
||||
break;
|
||||
} else {
|
||||
bc -= tdq->ctrl.addrSent;
|
||||
if( tdq->position >= bc) { // data nak
|
||||
done = true;
|
||||
continue;
|
||||
if( tdq->length > bc) { // data nak
|
||||
break;
|
||||
} else { // count down
|
||||
bc -= tdq->position;
|
||||
bc -= tdq->length;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done= true;
|
||||
continue;
|
||||
}
|
||||
} else break;
|
||||
|
||||
i2c->errorQueue++;
|
||||
}
|
||||
// log_v("errorByteCnt=%d errorQueue=%d",i2c->errorByteCnt,i2c->errorQueue);
|
||||
|
||||
i2c->errorByteCnt = bc;
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR i2c_isr_handler_default(void* arg)
|
||||
{
|
||||
@ -637,17 +865,14 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
|
||||
uint32_t activeInt = p_i2c->dev->int_status.val&0x7FF;
|
||||
|
||||
if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
uint32_t raw = p_i2c->dev->int_raw.val;
|
||||
#endif
|
||||
p_i2c->dev->int_ena.val = 0;
|
||||
p_i2c->dev->int_clr.val = 0x1FFF;
|
||||
log_v("eject raw=%p, int=%p",raw,activeInt);
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
uint32_t raw = p_i2c->dev->int_raw.val;
|
||||
log_w("eject raw=%p, int=%p",raw,activeInt);
|
||||
#endif
|
||||
return;
|
||||
}else if(!activeInt){ //spurious interrupt, possibly bus relate 20180711
|
||||
log_v("r=0x%x s=0x%x %d",p_i2c->dev->int_raw.val,p_i2c->dev->int_status.val,p_i2c->stage);
|
||||
}
|
||||
|
||||
while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change
|
||||
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
@ -690,8 +915,7 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
|
||||
if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service
|
||||
if (p_i2c->mode == I2C_MASTER) {
|
||||
i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data
|
||||
log_v("AcK Err errorByteCnt=%d, errorQueue=%d queuepos=%d",p_i2c->errorByteCnt,p_i2c->errorQueue, p_i2c->queuePos);
|
||||
if(p_i2c->errorByteCnt <= p_i2c->dq[p_i2c->errorQueue].ctrl.addrReq) { // address
|
||||
if(p_i2c->errorByteCnt < 0 ) { // address
|
||||
i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true);
|
||||
} else {
|
||||
i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data
|
||||
@ -750,14 +974,16 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
|
||||
p_i2c->dev->int_ena.val = p_i2c->dev->int_ena.val & (~activeInt);
|
||||
}
|
||||
|
||||
activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened
|
||||
// activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened
|
||||
// 01AUG2018 if another interrupt happened, the OS will schedule another interrupt
|
||||
// this is the source of spurious interrupts
|
||||
}
|
||||
}
|
||||
|
||||
/* Stickbreaker added for ISR 11/2017
|
||||
functional with Silicon date=0x16042000
|
||||
*/
|
||||
static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, EventGroupHandle_t event)
|
||||
static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, bool dataOnly, EventGroupHandle_t event)
|
||||
{
|
||||
// need to grab a MUTEX for exclusive Queue,
|
||||
// what about if ISR is running?
|
||||
@ -772,10 +998,21 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u
|
||||
dqx.position = 0;
|
||||
dqx.cmdBytesNeeded = dataLen;
|
||||
dqx.ctrl.val = 0;
|
||||
if( dataOnly) {
|
||||
/* special case to add a queue data only element.
|
||||
START and devAddr will not be sent, this dq element can have a STOP.
|
||||
allows multiple buffers to be used for one transaction.
|
||||
sequence: normal transaction(sendStop==false), [dataonly(sendStop==false)],dataOnly(sendStop==true)
|
||||
*** Currently only works with WRITE, final byte NAK an READ will cause a fail between dq buffer elements. (in progress 30JUL2018)
|
||||
*/
|
||||
dqx.ctrl.startCmdSent = 1; // mark as already sent
|
||||
dqx.ctrl.addrCmdSent = 1;
|
||||
} else {
|
||||
dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address
|
||||
}
|
||||
dqx.ctrl.addr = i2cDeviceAddr;
|
||||
dqx.ctrl.mode = mode;
|
||||
dqx.ctrl.stop= sendStop;
|
||||
dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address
|
||||
dqx.queueEvent = event;
|
||||
|
||||
if(event) { // an eventGroup exist, so, initialize it
|
||||
@ -808,7 +1045,7 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u
|
||||
|
||||
i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event)
|
||||
{
|
||||
return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,event);
|
||||
return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event);
|
||||
}
|
||||
|
||||
i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event)
|
||||
@ -822,16 +1059,15 @@ i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr,
|
||||
// this is the Industry Standard specification.
|
||||
|
||||
if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read
|
||||
i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,event);
|
||||
i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,false,event);
|
||||
if(err==I2C_ERROR_OK) {
|
||||
return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,event);
|
||||
return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,false,event);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event);
|
||||
return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event);
|
||||
}
|
||||
// Stickbreaker
|
||||
|
||||
i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
{
|
||||
@ -847,6 +1083,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
if(i2c == NULL) {
|
||||
return I2C_ERROR_DEV;
|
||||
}
|
||||
if(i2c->debugFlags & 0xff000000) i2cTriggerDumps(i2c,(i2c->debugFlags>>24),"before ProcQueue");
|
||||
if (i2c->dev->status_reg.bus_busy) { // return error, let TwoWire() handle resetting the hardware.
|
||||
/* if multi master then this if should be changed to this 03/12/2018
|
||||
if(multiMaster){// try to let the bus clear by its self
|
||||
@ -876,6 +1113,8 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
intBuff[i][2][i2c->num] = 0;
|
||||
}
|
||||
intPos[i2c->num] = 0;
|
||||
fifoPos = 0;
|
||||
memset(fifoBuffer,0,FIFOMAX);
|
||||
#endif
|
||||
// EventGroup is used to signal transmission completion from ISR
|
||||
// not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request
|
||||
@ -909,7 +1148,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
if(tdq->ctrl.addrReq ==2) { // 10bit address
|
||||
taddr =((tdq->ctrl.addr >> 7) & 0xFE)
|
||||
|tdq->ctrl.mode;
|
||||
taddr = (taddr <<8) || (tdq->ctrl.addr&0xFF);
|
||||
taddr = (taddr <<8) | (tdq->ctrl.addr&0xFF);
|
||||
} else { // 7bit address
|
||||
taddr = ((tdq->ctrl.addr<<1)&0xFE)
|
||||
|tdq->ctrl.mode;
|
||||
@ -950,31 +1189,31 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
// receives the TRANS_START
|
||||
|
||||
|
||||
|
||||
i2c->dev->int_ena.val =
|
||||
uint32_t interruptsEnabled =
|
||||
I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit
|
||||
I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END
|
||||
I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR
|
||||
I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit
|
||||
// I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos
|
||||
I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit
|
||||
I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled
|
||||
I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list
|
||||
I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) unhandled
|
||||
I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo()
|
||||
I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo()
|
||||
|
||||
|
||||
i2c->dev->int_ena.val = interruptsEnabled;
|
||||
|
||||
if(!i2c->intr_handle) { // create ISR for either peripheral
|
||||
// log_i("create ISR %d",i2c->num);
|
||||
uint32_t ret = 0;
|
||||
uint32_t flags = ESP_INTR_FLAG_EDGE | //< Edge-triggered interrupt
|
||||
ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled
|
||||
ESP_INTR_FLAG_LOWMED; //< Low and medium prio interrupts. These can be handled in C.
|
||||
|
||||
uint32_t flags = ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled
|
||||
ESP_INTR_FLAG_LOWMED | //< Low and medium prio interrupts. These can be handled in C.
|
||||
ESP_INTR_FLAG_SHARED; //< Reduce resource requirements, Share interrupts
|
||||
|
||||
if(i2c->num) {
|
||||
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
|
||||
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
|
||||
} else {
|
||||
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
|
||||
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
|
||||
}
|
||||
|
||||
if(ret!=ESP_OK) {
|
||||
@ -983,7 +1222,6 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
return I2C_ERROR_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
//hang until it completes.
|
||||
|
||||
// how many ticks should it take to transfer totalBytes through the I2C hardware,
|
||||
@ -1052,7 +1290,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
i2c->dev->int_ena.val =0;
|
||||
i2c->dev->int_clr.val = 0x1FFF;
|
||||
i2c_update_error_byte_cnt(i2c);
|
||||
if(i2c->errorByteCnt == 0) { // Bus Busy no bytes Moved
|
||||
if((i2c->errorByteCnt == 0)&&(i2c->errorQueue==0)) { // Bus Busy no bytes Moved
|
||||
reason = I2C_ERROR_BUSY;
|
||||
eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE;
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
@ -1103,6 +1341,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
|
||||
}
|
||||
b++;
|
||||
}
|
||||
if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue");
|
||||
|
||||
I2C_MUTEX_UNLOCK();
|
||||
return reason;
|
||||
@ -1117,33 +1356,32 @@ static void i2cReleaseISR(i2c_t * i2c)
|
||||
}
|
||||
|
||||
static bool i2cCheckLineState(int8_t sda, int8_t scl){
|
||||
if(sda < 0 || scl < 0){
|
||||
return true;//return true since there is nothing to do
|
||||
if(sda < 0 || scl < 0){
|
||||
return false;//return false since there is nothing to do
|
||||
}
|
||||
// if the bus is not 'clear' try the recommended recovery sequence, START, 9 Clocks, STOP
|
||||
// if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles
|
||||
digitalWrite(sda, HIGH);
|
||||
digitalWrite(scl, HIGH);
|
||||
pinMode(sda, PULLUP|OPEN_DRAIN|OUTPUT|INPUT);
|
||||
pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT|INPUT);
|
||||
pinMode(sda, PULLUP|OPEN_DRAIN|INPUT);
|
||||
pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT);
|
||||
|
||||
if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state
|
||||
log_w("invalid state sda=%d, scl=%d\n", digitalRead(sda), digitalRead(scl));
|
||||
digitalWrite(sda, HIGH);
|
||||
log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, digitalRead(sda), scl, digitalRead(scl));
|
||||
digitalWrite(scl, HIGH);
|
||||
delayMicroseconds(5);
|
||||
digitalWrite(sda, LOW);
|
||||
for(uint8_t a=0; a<9; a++) {
|
||||
delayMicroseconds(5);
|
||||
digitalWrite(scl, LOW);
|
||||
delayMicroseconds(5);
|
||||
digitalWrite(scl, HIGH);
|
||||
if(digitalRead(sda)){ // bus recovered
|
||||
log_d("Recovered after %d Cycles",a+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
delayMicroseconds(5);
|
||||
digitalWrite(sda, HIGH);
|
||||
}
|
||||
|
||||
if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state
|
||||
log_e("Bus Invalid State, TwoWire() Can't init");
|
||||
log_e("Bus Invalid State, TwoWire() Can't init sda=%d, scl=%d",digitalRead(sda),digitalRead(scl));
|
||||
return false; // bus is busy
|
||||
}
|
||||
return true;
|
||||
@ -1311,6 +1549,8 @@ i2c_err_t i2cFlush(i2c_t * i2c)
|
||||
if(i2c==NULL) {
|
||||
return I2C_ERROR_DEV;
|
||||
}
|
||||
i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH");
|
||||
|
||||
// need to grab a MUTEX for exclusive Queue,
|
||||
// what out if ISR is running?
|
||||
i2c_err_t rc=I2C_ERROR_OK;
|
||||
@ -1425,91 +1665,27 @@ uint32_t i2cGetFrequency(i2c_t * i2c)
|
||||
}
|
||||
|
||||
|
||||
/* Stickbreaker ISR mode debug support
|
||||
*/
|
||||
void i2cDumpDqData(i2c_t * i2c)
|
||||
{
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
uint16_t a=0;
|
||||
char buff[140];
|
||||
I2C_DATA_QUEUE_t *tdq;
|
||||
while(a<i2c->queueCount) {
|
||||
tdq=&i2c->dq[a];
|
||||
log_e("[%d] %sbit %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,
|
||||
(tdq->ctrl.addr>0x100)?"10":"7",
|
||||
(tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1),
|
||||
(tdq->ctrl.mode)?'R':'W',
|
||||
(tdq->ctrl.stop)?"STOP":"",
|
||||
tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0);
|
||||
uint16_t offset = 0;
|
||||
while(offset<tdq->length) {
|
||||
memset(buff,' ',140);
|
||||
buff[139]='\0';
|
||||
uint16_t i = 0,j;
|
||||
j=sprintf(buff,"0x%04x: ",offset);
|
||||
while((i<32)&&(offset < tdq->length)) {
|
||||
char ch = tdq->data[offset];
|
||||
sprintf((char*)&buff[(i*3)+41],"%02x ",ch);
|
||||
if((ch<32)||(ch>126)) {
|
||||
ch='.';
|
||||
}
|
||||
j+=sprintf((char*)&buff[j],"%c",ch);
|
||||
buff[j]=' ';
|
||||
i++;
|
||||
offset++;
|
||||
}
|
||||
log_e("%s",buff);
|
||||
}
|
||||
a++;
|
||||
uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){
|
||||
if(i2c != NULL) {
|
||||
i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits);
|
||||
return i2c->debugFlags;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void i2cDumpI2c(i2c_t * i2c)
|
||||
{
|
||||
log_e("i2c=%p",i2c);
|
||||
log_e("dev=%p date=%p",i2c->dev,i2c->dev->date);
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
log_e("lock=%p",i2c->lock);
|
||||
#endif
|
||||
log_e("num=%d",i2c->num);
|
||||
log_e("mode=%d",i2c->mode);
|
||||
log_e("stage=%d",i2c->stage);
|
||||
log_e("error=%d",i2c->error);
|
||||
log_e("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0);
|
||||
log_e("intr_handle=%p",i2c->intr_handle);
|
||||
log_e("dq=%p",i2c->dq);
|
||||
log_e("queueCount=%d",i2c->queueCount);
|
||||
log_e("queuePos=%d",i2c->queuePos);
|
||||
log_e("errorByteCnt=%d",i2c->errorByteCnt);
|
||||
log_e("errorQueue=%d",i2c->errorQueue);
|
||||
if(i2c->dq) {
|
||||
i2cDumpDqData(i2c);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
uint32_t i2cGetStatus(i2c_t * i2c){
|
||||
if(i2c != NULL){
|
||||
return i2c->dev->status_reg.val;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void i2cDumpInts(uint8_t num)
|
||||
{
|
||||
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
|
||||
|
||||
uint32_t b;
|
||||
log_e("%u row count INTR TX RX",num);
|
||||
for(uint32_t a=1; a<=INTBUFFMAX; a++) {
|
||||
b=(a+intPos[num])%INTBUFFMAX;
|
||||
if(intBuff[b][0][num]!=0) {
|
||||
log_e("[%02d] 0x%04x 0x%04x 0x%04x 0x%04x 0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
log_i("Debug Buffer not Enabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* todo
|
||||
22JUL18
|
||||
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads
|
||||
transactions are present in the same queue, and an error occurs, abort all succeeding unserviced transactions
|
||||
with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values
|
||||
can be re-queued and processed independently.
|
||||
30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks,
|
||||
*/
|
||||
|
||||
|
@ -48,6 +48,7 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b
|
||||
i2c_err_t i2cFlush(i2c_t *i2c);
|
||||
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
|
||||
uint32_t i2cGetFrequency(i2c_t * i2c);
|
||||
uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral
|
||||
|
||||
//Functions below should be used only if well understood
|
||||
//Might be deprecated and removed in future
|
||||
@ -62,8 +63,17 @@ i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr,
|
||||
i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
|
||||
|
||||
//stickbreaker debug support
|
||||
void i2cDumpInts(uint8_t num);
|
||||
void i2cDumpI2c(i2c_t *i2c);
|
||||
uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits);
|
||||
// Debug actions have 3 currently defined locus
|
||||
// 0xXX------ : at entry of ProcQueue
|
||||
// 0x--XX---- : at exit of ProcQueue
|
||||
// 0x------XX : at entry of Flush
|
||||
//
|
||||
// bit 0 causes DumpI2c to execute
|
||||
// bit 1 causes DumpInts to execute
|
||||
// bit 2 causes DumpCmdqueue to execute
|
||||
// bit 3 causes DumpStatus to execute
|
||||
// bit 4 causes DumpFifo to execute
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -79,38 +79,50 @@ int log_printf(const char *fmt, ...);
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_v(format, ...)
|
||||
#define isr_log_v(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_d(format, ...)
|
||||
#define isr_log_d(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
||||
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_i(format, ...)
|
||||
#define isr_log_i(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
|
||||
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_w(format, ...)
|
||||
#define isr_log_w(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_e(format, ...)
|
||||
#define isr_log_e(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
|
||||
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_n(format, ...)
|
||||
#define isr_log_n(format, ...)
|
||||
#endif
|
||||
|
||||
#include "esp_log.h"
|
||||
@ -121,12 +133,22 @@ int log_printf(const char *fmt, ...);
|
||||
#undef ESP_LOGI
|
||||
#undef ESP_LOGD
|
||||
#undef ESP_LOGV
|
||||
#undef ESP_EARLY_LOGE
|
||||
#undef ESP_EARLY_LOGW
|
||||
#undef ESP_EARLY_LOGI
|
||||
#undef ESP_EARLY_LOGD
|
||||
#undef ESP_EARLY_LOGV
|
||||
|
||||
#define ESP_LOGE(tag, ...) log_e(__VA_ARGS__)
|
||||
#define ESP_LOGW(tag, ...) log_w(__VA_ARGS__)
|
||||
#define ESP_LOGI(tag, ...) log_i(__VA_ARGS__)
|
||||
#define ESP_LOGD(tag, ...) log_d(__VA_ARGS__)
|
||||
#define ESP_LOGV(tag, ...) log_v(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGE(tag, ...) isr_log_e(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGW(tag, ...) isr_log_w(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGI(tag, ...) isr_log_i(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGD(tag, ...) isr_log_d(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGV(tag, ...) isr_log_v(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -21,7 +21,9 @@
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
#include "esp_bt.h"
|
||||
#endif //CONFIG_BT_ENABLED
|
||||
#include <sys/time.h>
|
||||
#include "esp32-hal.h"
|
||||
|
||||
|
821
cores/esp32/esp32-hal-rmt.c
Normal file
821
cores/esp32/esp32-hal-rmt.c
Normal file
@ -0,0 +1,821 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp8266-compat.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
#include "esp32-hal-rmt.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
|
||||
#include "soc/rmt_struct.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
/**
|
||||
* Internal macros
|
||||
*/
|
||||
#define MAX_CHANNELS 8
|
||||
#define MAX_DATA_PER_CHANNEL 64
|
||||
#define MAX_DATA_PER_ITTERATION 62
|
||||
#define _ABS(a) (a>0?a:-a)
|
||||
#define _LIMIT(a,b) (a>b?b:a)
|
||||
#define __INT_TX_END (1)
|
||||
#define __INT_RX_END (2)
|
||||
#define __INT_ERROR (4)
|
||||
#define __INT_THR_EVNT (1<<24)
|
||||
|
||||
#define _INT_TX_END(channel) (__INT_TX_END<<(channel*3))
|
||||
#define _INT_RX_END(channel) (__INT_RX_END<<(channel*3))
|
||||
#define _INT_ERROR(channel) (__INT_ERROR<<(channel*3))
|
||||
#define _INT_THR_EVNT(channel) ((__INT_THR_EVNT)<<(channel))
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
# define RMT_MUTEX_LOCK(channel)
|
||||
# define RMT_MUTEX_UNLOCK(channel)
|
||||
#else
|
||||
# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS)
|
||||
# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel])
|
||||
#endif /* CONFIG_DISABLE_HAL_LOCKS */
|
||||
|
||||
#define _RMT_INTERNAL_DEBUG
|
||||
#ifdef _RMT_INTERNAL_DEBUG
|
||||
# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1);
|
||||
# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0);
|
||||
#else
|
||||
# define DEBUG_INTERRUPT_START(pin)
|
||||
# define DEBUG_INTERRUPT_END(pin)
|
||||
#endif /* _RMT_INTERNAL_DEBUG */
|
||||
|
||||
/**
|
||||
* Typedefs for internal stuctures, enums
|
||||
*/
|
||||
typedef enum {
|
||||
E_NO_INTR = 0,
|
||||
E_TX_INTR = 1,
|
||||
E_TXTHR_INTR = 2,
|
||||
E_RX_INTR = 4,
|
||||
} intr_mode_t;
|
||||
|
||||
typedef enum {
|
||||
E_INACTIVE = 0,
|
||||
E_FIRST_HALF = 1,
|
||||
E_LAST_DATA = 2,
|
||||
E_END_TRANS = 4,
|
||||
E_SET_CONTI = 8,
|
||||
} transaction_state_t;
|
||||
|
||||
struct rmt_obj_s
|
||||
{
|
||||
bool allocated;
|
||||
EventGroupHandle_t events;
|
||||
int pin;
|
||||
int channel;
|
||||
bool tx_not_rx;
|
||||
int buffers;
|
||||
int data_size;
|
||||
uint32_t* data_ptr;
|
||||
intr_mode_t intr_mode;
|
||||
transaction_state_t tx_state;
|
||||
rmt_rx_data_cb_t cb;
|
||||
bool data_alloc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal variables for channel descriptors
|
||||
*/
|
||||
static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = {
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal variables for driver data
|
||||
*/
|
||||
static intr_handle_t intr_handle;
|
||||
|
||||
static bool periph_enabled = false;
|
||||
|
||||
static xSemaphoreHandle g_rmt_block_lock = NULL;
|
||||
|
||||
/**
|
||||
* Internal method (private) declarations
|
||||
*/
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx);
|
||||
|
||||
static bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
static void IRAM_ATTR _rmt_isr(void* arg);
|
||||
|
||||
static rmt_obj_t* _rmtAllocate(int pin, int from, int size);
|
||||
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx);
|
||||
|
||||
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel);
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch);
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch);
|
||||
|
||||
|
||||
/**
|
||||
* Public method definitions
|
||||
*/
|
||||
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high)
|
||||
{
|
||||
if (!rmt || low > 0xFFFF || high > 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
RMT.carrier_duty_ch[channel].low = low;
|
||||
RMT.carrier_duty_ch[channel].low = high;
|
||||
RMT.conf_ch[channel].conf0.carrier_en = carrier_en;
|
||||
RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level)
|
||||
{
|
||||
if (!rmt || filter_level > 0xFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_filter_thres = filter_level;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_en = filter_en;
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value)
|
||||
{
|
||||
if (!rmt || value > 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
RMT.conf_ch[channel].conf0.idle_thres = value;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool rmtDeinit(rmt_obj_t *rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if (rmt != &(g_rmt_objects[rmt->channel])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t from = rmt->channel;
|
||||
size_t to = rmt->buffers + rmt->channel;
|
||||
size_t i;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(g_rmt_objlocks[from] != NULL) {
|
||||
vSemaphoreDelete(g_rmt_objlocks[from]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_rmt_objects[from].data_alloc) {
|
||||
free(g_rmt_objects[from].data_ptr);
|
||||
}
|
||||
|
||||
for (i = from; i < to; i++) {
|
||||
g_rmt_objects[i].allocated = false;
|
||||
}
|
||||
|
||||
g_rmt_objects[from].channel = 0;
|
||||
g_rmt_objects[from].buffers = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int channel = rmt->channel;
|
||||
int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers;
|
||||
|
||||
if (size > allocated_size) {
|
||||
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
// setup interrupt handler if not yet installed for half and full tx
|
||||
if (!intr_handle) {
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
|
||||
}
|
||||
|
||||
rmt->data_size = size - MAX_DATA_PER_ITTERATION;
|
||||
rmt->data_ptr = ((uint32_t*)data) + MAX_DATA_PER_ITTERATION;
|
||||
rmt->intr_mode = E_TX_INTR | E_TXTHR_INTR;
|
||||
rmt->tx_state = E_SET_CONTI | E_FIRST_HALF;
|
||||
|
||||
// init the tx limit for interruption
|
||||
RMT.tx_lim_ch[channel].limit = half_tx_nr+2;
|
||||
// reset memory pointer
|
||||
RMT.conf_ch[channel].conf1.apb_mem_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.apb_mem_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 0;
|
||||
|
||||
// set the tx end mark
|
||||
RMTMEM.chan[channel].data32[MAX_DATA_PER_ITTERATION].val = 0;
|
||||
|
||||
// clear and enable both Tx completed and half tx event
|
||||
RMT.int_clr.val = _INT_TX_END(channel);
|
||||
RMT.int_clr.val = _INT_THR_EVNT(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_TX_END(channel);
|
||||
RMT.int_ena.val |= _INT_THR_EVNT(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
// start the transation
|
||||
return _rmtSendOnce(rmt, data, MAX_DATA_PER_ITTERATION);
|
||||
} else {
|
||||
// use one-go mode if data fits one buffer
|
||||
return _rmtSendOnce(rmt, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
|
||||
for (i=0; i<size; i++) {
|
||||
data[i] = *rmt_mem_ptr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtBeginReceive(rmt_obj_t* rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtReceiveCompleted(rmt_obj_t* rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (RMT.int_raw.val&_INT_RX_END(channel)) {
|
||||
// RX end flag
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb)
|
||||
{
|
||||
if (!rmt && !cb) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
rmt->intr_mode = E_RX_INTR;
|
||||
rmt->tx_state = E_FIRST_HALF;
|
||||
rmt->cb = cb;
|
||||
// allocate internally two buffers which would alternate
|
||||
if (!rmt->data_alloc) {
|
||||
rmt->data_ptr = (uint32_t*)malloc(2*MAX_DATA_PER_CHANNEL*(rmt->buffers)*sizeof(uint32_t));
|
||||
rmt->data_size = MAX_DATA_PER_CHANNEL*rmt->buffers;
|
||||
rmt->data_alloc = true;
|
||||
}
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_RX_END(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventFlag) {
|
||||
xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL);
|
||||
rmt->events = eventFlag;
|
||||
}
|
||||
|
||||
if (data && size>0) {
|
||||
rmt->data_ptr = (uint32_t*)data;
|
||||
rmt->data_size = size;
|
||||
}
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
rmt->intr_mode = E_RX_INTR;
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_RX_END(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
// wait for data if requested so
|
||||
if (waitForData && eventFlag) {
|
||||
uint32_t flags = xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL,
|
||||
pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout);
|
||||
if (flags & RMT_FLAG_ERROR) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float rmtSetTick(rmt_obj_t* rmt, float tick)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
divider field span from 1 (smallest), 2, 3, ... , 0xFF, 0x00 (highest)
|
||||
* rmt tick from 1/80M -> 12.5ns (1x) div_cnt = 0x01
|
||||
3.2 us (256x) div_cnt = 0x00
|
||||
* rmt tick for 1 MHz -> 1us (1x) div_cnt = 0x01
|
||||
256us (256x) div_cnt = 0x00
|
||||
*/
|
||||
int apb_div = _LIMIT(tick/12.5, 256);
|
||||
int ref_div = _LIMIT(tick/1000, 256);
|
||||
|
||||
float apb_tick = 12.5 * apb_div;
|
||||
float ref_tick = 1000.0 * ref_div;
|
||||
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) {
|
||||
RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF;
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 1;
|
||||
return apb_tick;
|
||||
} else {
|
||||
RMT.conf_ch[channel].conf0.div_cnt = ref_div & 0xFF;
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 0;
|
||||
return ref_tick;
|
||||
}
|
||||
}
|
||||
|
||||
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize)
|
||||
{
|
||||
int buffers = memsize;
|
||||
rmt_obj_t* rmt;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
// create common block mutex for protecting allocs from multiple threads
|
||||
if (!g_rmt_block_lock) {
|
||||
g_rmt_block_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
// lock
|
||||
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
|
||||
|
||||
for (i=0; i<MAX_CHANNELS; i++) {
|
||||
for (j=0; j<buffers && i+j < MAX_CHANNELS; j++) {
|
||||
// if the space is ocupied break and continue on other channel
|
||||
if (g_rmt_objects[i+j].allocated) {
|
||||
i += j; // continue searching from latter channel
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == buffers) {
|
||||
// found a space in channel descriptors
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAX_CHANNELS || i+j >= MAX_CHANNELS || j != buffers) {
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
return NULL;
|
||||
}
|
||||
rmt = _rmtAllocate(pin, i, buffers);
|
||||
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
|
||||
size_t channel = i;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(g_rmt_objlocks[channel] == NULL) {
|
||||
g_rmt_objlocks[channel] = xSemaphoreCreateMutex();
|
||||
if(g_rmt_objlocks[channel] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
rmt->pin = pin;
|
||||
rmt->tx_not_rx = tx_not_rx;
|
||||
rmt->buffers =buffers;
|
||||
rmt->channel = channel;
|
||||
_initPin(pin, channel, tx_not_rx);
|
||||
|
||||
// Initialize the registers in default mode:
|
||||
// - no carrier, filter
|
||||
// - timebase tick of 1us
|
||||
// - idle threshold set to 0x8000 (max pulse width + 1)
|
||||
RMT.conf_ch[channel].conf0.div_cnt = 1;
|
||||
RMT.conf_ch[channel].conf0.mem_size = buffers;
|
||||
RMT.conf_ch[channel].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[channel].conf0.carrier_out_lv = 0;
|
||||
RMT.conf_ch[channel].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[channel].conf0.idle_thres = 0x80;
|
||||
RMT.conf_ch[channel].conf1.rx_en = 0;
|
||||
RMT.conf_ch[channel].conf1.tx_conti_mode = 0;
|
||||
RMT.conf_ch[channel].conf1.ref_cnt_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_en = 0;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_thres = 0;
|
||||
RMT.conf_ch[channel].conf1.idle_out_lv = 0; // signal level for idle
|
||||
RMT.conf_ch[channel].conf1.idle_out_en = 1; // enable idle
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 0; // base clock
|
||||
RMT.apb_conf.fifo_mask = 1;
|
||||
|
||||
if (tx_not_rx) {
|
||||
// RMT.conf_ch[channel].conf1.rx_en = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
} else {
|
||||
// RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
}
|
||||
|
||||
// install interrupt if at least one channel is active
|
||||
if (!intr_handle) {
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
|
||||
}
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return rmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private methods definitions
|
||||
*/
|
||||
bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
RMT.apb_conf.fifo_mask = 1;
|
||||
if (data && size>0) {
|
||||
size_t i;
|
||||
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
|
||||
for (i = 0; i < size; i++) {
|
||||
*rmt_mem_ptr++ = data[i].val;
|
||||
}
|
||||
// tx end mark
|
||||
RMTMEM.chan[channel].data32[size].val = 0;
|
||||
}
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.tx_start = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static rmt_obj_t* _rmtAllocate(int pin, int from, int size)
|
||||
{
|
||||
size_t i;
|
||||
// setup how many buffers shall we use
|
||||
g_rmt_objects[from].buffers = size;
|
||||
|
||||
for (i=0; i<size; i++) {
|
||||
// mark the block of channels as used
|
||||
g_rmt_objects[i+from].allocated = true;
|
||||
}
|
||||
return &(g_rmt_objects[from]);
|
||||
}
|
||||
|
||||
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx)
|
||||
{
|
||||
if (!periph_enabled) {
|
||||
periph_enabled = true;
|
||||
periph_module_enable( PERIPH_RMT_MODULE );
|
||||
}
|
||||
if (tx_not_rx) {
|
||||
pinMode(pin, OUTPUT);
|
||||
pinMatrixOutAttach(pin, RMT_SIG_OUT0_IDX + channel, 0, 0);
|
||||
} else {
|
||||
pinMode(pin, INPUT);
|
||||
pinMatrixInAttach(pin, RMT_SIG_IN0_IDX + channel, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void IRAM_ATTR _rmt_isr(void* arg)
|
||||
{
|
||||
int intr_val = RMT.int_st.val;
|
||||
size_t ch;
|
||||
for (ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
|
||||
if (intr_val & _INT_RX_END(ch)) {
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_RX_END(ch);
|
||||
RMT.int_ena.val &= ~_INT_RX_END(ch);
|
||||
|
||||
if ((g_rmt_objects[ch].intr_mode) & E_RX_INTR) {
|
||||
if (g_rmt_objects[ch].events) {
|
||||
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_RX_DONE);
|
||||
}
|
||||
if (g_rmt_objects[ch].data_ptr && g_rmt_objects[ch].data_size > 0) {
|
||||
size_t i;
|
||||
uint32_t * data = g_rmt_objects[ch].data_ptr;
|
||||
// in case of callback, provide switching between memories
|
||||
if (g_rmt_objects[ch].cb) {
|
||||
if (g_rmt_objects[ch].tx_state & E_FIRST_HALF) {
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
} else {
|
||||
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
|
||||
data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) {
|
||||
*data++ = RMTMEM.chan[ch].data32[i].val;
|
||||
}
|
||||
if (g_rmt_objects[ch].cb) {
|
||||
// actually received data ptr
|
||||
uint32_t * data = g_rmt_objects[ch].data_ptr;
|
||||
(g_rmt_objects[ch].cb)(data, _rmt_get_mem_len(ch));
|
||||
|
||||
// restart the reception
|
||||
RMT.conf_ch[ch].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.rx_en = 1;
|
||||
RMT.int_ena.val |= _INT_RX_END(ch);
|
||||
} else {
|
||||
// if not callback provide, expect only one Rx
|
||||
g_rmt_objects[ch].intr_mode &= ~E_RX_INTR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Report error and disable Rx interrupt
|
||||
log_e("Unexpected Rx interrupt!\n"); // TODO: eplace messages with log_X
|
||||
RMT.int_ena.val &= ~_INT_RX_END(ch);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (intr_val & _INT_ERROR(ch)) {
|
||||
digitalWrite(2, 1);
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_ERROR(ch);
|
||||
RMT.int_ena.val &= ~_INT_ERROR(ch);
|
||||
// report error
|
||||
log_e("RMT Error %d!\n", ch);
|
||||
if (g_rmt_objects[ch].events) {
|
||||
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_ERROR);
|
||||
}
|
||||
// reset memory
|
||||
RMT.conf_ch[ch].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_rd_rst = 0;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 0;
|
||||
}
|
||||
|
||||
if (intr_val & _INT_TX_END(ch)) {
|
||||
|
||||
RMT.int_clr.val = _INT_TX_END(ch);
|
||||
_rmt_tx_mem_second(ch);
|
||||
}
|
||||
|
||||
if (intr_val & _INT_THR_EVNT(ch)) {
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_THR_EVNT(ch);
|
||||
|
||||
// initial setup of continuous mode
|
||||
if (g_rmt_objects[ch].tx_state & E_SET_CONTI) {
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 1;
|
||||
g_rmt_objects[ch].intr_mode &= ~E_SET_CONTI;
|
||||
}
|
||||
_rmt_tx_mem_first(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch)
|
||||
{
|
||||
DEBUG_INTERRUPT_START(4)
|
||||
uint32_t* data = g_rmt_objects[ch].data_ptr;
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
int i;
|
||||
|
||||
RMT.tx_lim_ch[ch].limit = half_tx_nr+2;
|
||||
RMT.int_clr.val = _INT_THR_EVNT(ch);
|
||||
RMT.int_ena.val |= _INT_THR_EVNT(ch);
|
||||
|
||||
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
|
||||
|
||||
if (data) {
|
||||
int remaining_size = g_rmt_objects[ch].data_size;
|
||||
// will the remaining data occupy the entire halfbuffer
|
||||
if (remaining_size > half_tx_nr) {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
|
||||
}
|
||||
g_rmt_objects[ch].data_size -= half_tx_nr;
|
||||
g_rmt_objects[ch].data_ptr += half_tx_nr;
|
||||
} else {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
if (i < remaining_size) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
|
||||
}
|
||||
}
|
||||
g_rmt_objects[ch].data_ptr = NULL;
|
||||
|
||||
}
|
||||
} else if ((!(g_rmt_objects[ch].tx_state & E_LAST_DATA)) &&
|
||||
(!(g_rmt_objects[ch].tx_state & E_END_TRANS))) {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
|
||||
}
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0;
|
||||
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
} else {
|
||||
log_d("RMT Tx finished %d!\n", ch);
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
RMT.int_ena.val &= ~_INT_TX_END(ch);
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
g_rmt_objects[ch].intr_mode = E_NO_INTR;
|
||||
g_rmt_objects[ch].tx_state = E_INACTIVE;
|
||||
}
|
||||
DEBUG_INTERRUPT_END(4);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch)
|
||||
{
|
||||
DEBUG_INTERRUPT_START(2);
|
||||
uint32_t* data = g_rmt_objects[ch].data_ptr;
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
int i;
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
|
||||
if (data) {
|
||||
int remaining_size = g_rmt_objects[ch].data_size;
|
||||
|
||||
// will the remaining data occupy the entire halfbuffer
|
||||
if (remaining_size > half_tx_nr) {
|
||||
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
|
||||
for (i = 1; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[i].val = data[i];
|
||||
}
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
// turn off the treshold interrupt
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
g_rmt_objects[ch].data_size -= half_tx_nr;
|
||||
g_rmt_objects[ch].data_ptr += half_tx_nr;
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
|
||||
for (i = 1; i < half_tx_nr; i++) {
|
||||
if (i < remaining_size) {
|
||||
RMTMEM.chan[ch].data32[i].val = data[i];
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
|
||||
}
|
||||
}
|
||||
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
g_rmt_objects[ch].data_ptr = NULL;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
|
||||
}
|
||||
RMTMEM.chan[ch].data32[i].val = 0;
|
||||
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
}
|
||||
DEBUG_INTERRUPT_END(2);
|
||||
}
|
||||
|
||||
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel)
|
||||
{
|
||||
int block_num = RMT.conf_ch[channel].conf0.mem_size;
|
||||
int item_block_len = block_num * 64;
|
||||
volatile rmt_item32_t* data = RMTMEM.chan[channel].data32;
|
||||
int idx;
|
||||
for(idx = 0; idx < item_block_len; idx++) {
|
||||
if(data[idx].duration0 == 0) {
|
||||
return idx;
|
||||
} else if(data[idx].duration1 == 0) {
|
||||
return idx + 1;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
137
cores/esp32/esp32-hal-rmt.h
Normal file
137
cores/esp32/esp32-hal-rmt.h
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 MAIN_ESP32_HAL_RMT_H_
|
||||
#define MAIN_ESP32_HAL_RMT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// notification flags
|
||||
#define RMT_FLAG_TX_DONE (1)
|
||||
#define RMT_FLAG_RX_DONE (2)
|
||||
#define RMT_FLAG_ERROR (4)
|
||||
#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR)
|
||||
|
||||
struct rmt_obj_s;
|
||||
|
||||
typedef enum {
|
||||
RMT_MEM_64 = 1,
|
||||
RMT_MEM_128 = 2,
|
||||
RMT_MEM_192 = 3,
|
||||
RMT_MEM_256 = 4,
|
||||
RMT_MEM_320 = 5,
|
||||
RMT_MEM_384 = 6,
|
||||
RMT_MEM_448 = 7,
|
||||
RMT_MEM_512 = 8,
|
||||
} rmt_reserve_memsize_t;
|
||||
|
||||
typedef struct rmt_obj_s rmt_obj_t;
|
||||
|
||||
typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len);
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t duration0 :15;
|
||||
uint32_t level0 :1;
|
||||
uint32_t duration1 :15;
|
||||
uint32_t level1 :1;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
} rmt_data_t;
|
||||
|
||||
/**
|
||||
* Initialize the object
|
||||
*
|
||||
*/
|
||||
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize);
|
||||
|
||||
/**
|
||||
* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds
|
||||
* return the real actual tick value in ns
|
||||
*/
|
||||
float rmtSetTick(rmt_obj_t* rmt, float tick);
|
||||
|
||||
/**
|
||||
* Sending data in one-go mode or continual mode
|
||||
* (more data being send while updating buffers in interrupts)
|
||||
*
|
||||
*/
|
||||
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Initiates async receive, event flag indicates data received
|
||||
*
|
||||
*/
|
||||
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Initiates async receive with automatic buffering
|
||||
* and callback with data from ISR
|
||||
*
|
||||
*/
|
||||
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb);
|
||||
|
||||
|
||||
/* Additional interface */
|
||||
|
||||
/**
|
||||
* Start reception
|
||||
*
|
||||
*/
|
||||
bool rmtBeginReceive(rmt_obj_t* rmt);
|
||||
|
||||
/**
|
||||
* Checks if reception completes
|
||||
*
|
||||
*/
|
||||
bool rmtReceiveCompleted(rmt_obj_t* rmt);
|
||||
|
||||
/**
|
||||
* Reads the data for particular channel
|
||||
*
|
||||
*/
|
||||
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Setting threshold for Rx completed
|
||||
*/
|
||||
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value);
|
||||
|
||||
/**
|
||||
* Setting carrier
|
||||
*/
|
||||
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high);
|
||||
|
||||
/**
|
||||
* Setting input filter
|
||||
*/
|
||||
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level);
|
||||
|
||||
|
||||
// TODO:
|
||||
// * uninstall interrupt when all channels are deinit
|
||||
// * send only-conti mode with circular-buffer
|
||||
// * put sanity checks to some macro or inlines
|
||||
// * doxy comments
|
||||
// * error reporting
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAIN_ESP32_HAL_RMT_H_ */
|
@ -13,13 +13,13 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "apps/sntp/sntp.h"
|
||||
#include "lwip/apps/sntp.h"
|
||||
|
||||
static void setTimeZone(long offset, int daylight)
|
||||
{
|
||||
char cst[16] = {0};
|
||||
char cdt[16] = "DST";
|
||||
char tz[32] = {0};
|
||||
char cst[17] = {0};
|
||||
char cdt[17] = "DST";
|
||||
char tz[33] = {0};
|
||||
|
||||
if(offset % 3600){
|
||||
sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
|
||||
|
@ -80,7 +80,7 @@ static void IRAM_ATTR _uart_isr(void *arg)
|
||||
uart->dev->int_clr.rxfifo_full = 1;
|
||||
uart->dev->int_clr.frm_err = 1;
|
||||
uart->dev->int_clr.rxfifo_tout = 1;
|
||||
while(uart->dev->status.rxfifo_cnt) {
|
||||
while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
|
||||
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
||||
@ -240,6 +240,26 @@ void uartEnd(uart_t* uart)
|
||||
uartDetachTx(uart);
|
||||
}
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
if(uart->queue != NULL) {
|
||||
uint8_t c;
|
||||
while(xQueueReceive(uart->queue, &c, 0));
|
||||
vQueueDelete(uart->queue);
|
||||
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
|
||||
if(uart->queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
return new_size;
|
||||
}
|
||||
|
||||
uint32_t uartAvailable(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->queue == NULL) {
|
||||
@ -313,7 +333,7 @@ void uartFlush(uart_t* uart)
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
while(uart->dev->status.txfifo_cnt);
|
||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||
|
||||
//Due to hardware issue, we can not use fifo_rst to reset uart fifo.
|
||||
//See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
|
||||
@ -435,3 +455,65 @@ int log_printf(const char *format, ...)
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two.
|
||||
* This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
|
||||
*/
|
||||
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
|
||||
{
|
||||
while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
|
||||
if(flg) return 0;
|
||||
ets_delay_us(1000);
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is
|
||||
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
|
||||
* rounded to the closed real baudrate.
|
||||
*/
|
||||
unsigned long
|
||||
uartDetectBaudrate(uart_t *uart)
|
||||
{
|
||||
static bool uartStateDetectingBaudrate = false;
|
||||
|
||||
if(!uartStateDetectingBaudrate) {
|
||||
uart->dev->auto_baud.glitch_filt = 0x08;
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uart->dev->auto_baud.en = 1;
|
||||
uartStateDetectingBaudrate = true;
|
||||
}
|
||||
|
||||
unsigned long divisor = uartBaudrateDetect(uart, true);
|
||||
if (!divisor) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uartStateDetectingBaudrate = false; // Initialize for the next round
|
||||
|
||||
unsigned long baudrate = UART_CLK_FREQ / divisor;
|
||||
|
||||
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
|
||||
|
||||
size_t i;
|
||||
for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate
|
||||
{
|
||||
if (baudrate <= default_rates[i])
|
||||
{
|
||||
if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) {
|
||||
i--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return default_rates[i];
|
||||
}
|
||||
|
@ -67,9 +67,13 @@ void uartFlush(uart_t* uart);
|
||||
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
|
||||
uint32_t uartGetBaudRate(uart_t* uart);
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
|
||||
|
||||
void uartSetDebug(uart_t* uart);
|
||||
int uartGetDebug();
|
||||
|
||||
unsigned long uartDetectBaudrate(uart_t *uart);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -57,6 +57,7 @@ void yield(void);
|
||||
#include "esp32-hal-spi.h"
|
||||
#include "esp32-hal-i2c.h"
|
||||
#include "esp32-hal-ledc.h"
|
||||
#include "esp32-hal-rmt.h"
|
||||
#include "esp32-hal-sigmadelta.h"
|
||||
#include "esp32-hal-timer.h"
|
||||
#include "esp32-hal-bt.h"
|
||||
|
@ -7,13 +7,10 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
@ -68,12 +65,6 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4) {
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
|
@ -1,24 +1,32 @@
|
||||
Please fill the info fields, it helps to get you faster support ;)
|
||||
Make your question, not a Statement, inclusive. Include all pertinent information:
|
||||
|
||||
If you have a Guru Meditation Error, please decode it:
|
||||
What you are trying to do.
|
||||
Describe your system( Hardware, computer, O/S, core version, environment)
|
||||
Describe what is failing
|
||||
Show the shortest possible code that will duplicate the error
|
||||
Show the EXACT error message(it doesn't work is not enough)
|
||||
Then if someone is interested and knowledgeable you might get a answer. All of this work on your part shows us that you have worked to solve YOUR problem. The more complete your issue posting is, the more likely someone will volunteer their time to help you.
|
||||
|
||||
If you have a Guru Meditation Error or Backtrace, ***please decode it***:
|
||||
https://github.com/me-no-dev/EspExceptionDecoder
|
||||
|
||||
----------------------------- Remove above -----------------------------
|
||||
|
||||
|
||||
### Hardware:
|
||||
Board: ?ESP32 Dev Module?
|
||||
Board: ?ESP32 Dev Module? ?node32? ?ttgo_lora?
|
||||
Core Installation/update date: ?11/jul/2017?
|
||||
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
|
||||
Flash Frequency: ?40Mhz?
|
||||
PSRAM enabled: ?no?
|
||||
Upload Speed: ?115200?
|
||||
|
||||
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
|
||||
|
||||
### Description:
|
||||
Describe your problem here
|
||||
|
||||
|
||||
### Sketch:
|
||||
### Sketch: (leave the backquotes for [code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/))
|
||||
```cpp
|
||||
|
||||
//Change the code below by your sketch
|
||||
|
@ -8,8 +8,6 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
|
||||
- Enter ```https://dl.espressif.com/dl/package_esp32_index.json``` into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
|
||||
- Open Boards Manager from Tools > Board menu and install *esp32* platform (and don't forget to select your ESP32 board from Tools > Board menu after installation).
|
||||
|
||||
#### [Latest stable release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
Stable release link: `https://dl.espressif.com/dl/package_esp32_index.json`
|
||||
|
||||
#### [Latest development release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
Development release link: `https://dl.espressif.com/dl/package_esp32_dev_index.json`
|
||||
|
@ -22,14 +22,15 @@ Installation instructions for Debian / Ubuntu OS
|
||||
|
||||
|
||||
|
||||
- If you have Arduino.app installed to /Applications/, modify the installation as follows, beginning at `mkdir -p ~/Arduino...`:
|
||||
- If you have Arduino installed to ~/, modify the installation as follows, beginning at `mkdir -p ~/Arduino/hardware`:
|
||||
|
||||
```bash
|
||||
cd /Applications/Arduino_*/Contents/java/hardware/
|
||||
```bash
|
||||
cd ~/Arduino/hardware
|
||||
mkdir -p espressif && \
|
||||
cd espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
python2 get.py```
|
||||
python2 get.py
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ Installation instructions for Mac OS
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
python get.py
|
||||
python get.py
|
||||
```
|
||||
Where `~/Documents/Arduino` represents your sketch book location as per "Arduino" > "Preferences" > "Sketchbook location" (in the IDE once started). Adjust the command above accordingly if necessary!
|
||||
|
||||
@ -20,6 +20,8 @@ Installation instructions for Mac OS
|
||||
```xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun```
|
||||
|
||||
```xcode-select --install```
|
||||
|
||||
- Try `python3` instead of `python` if you get the error: `IOError: [Errno socket error] [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)` when running `python get.py`
|
||||
|
||||
- Restart Arduino IDE
|
||||
|
||||
|
@ -8,6 +8,7 @@ extern "C" {
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/prot/ethernet.h"
|
||||
#include <esp_err.h>
|
||||
#include <esp_wifi.h>
|
||||
}
|
||||
@ -15,7 +16,7 @@ extern "C" {
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
typedef struct {
|
||||
struct tcpip_api_call call;
|
||||
struct tcpip_api_call_data call;
|
||||
udp_pcb * pcb;
|
||||
const ip_addr_t *addr;
|
||||
uint16_t port;
|
||||
@ -24,7 +25,7 @@ typedef struct {
|
||||
err_t err;
|
||||
} udp_api_call_t;
|
||||
|
||||
static err_t _udp_connect_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_connect_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_connect(msg->pcb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -35,11 +36,11 @@ static err_t _udp_connect(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port
|
||||
msg.pcb = pcb;
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_disconnect_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = 0;
|
||||
udp_disconnect(msg->pcb);
|
||||
@ -49,10 +50,10 @@ static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
|
||||
static void _udp_disconnect(struct udp_pcb *pcb){
|
||||
udp_api_call_t msg;
|
||||
msg.pcb = pcb;
|
||||
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call_data*)&msg);
|
||||
}
|
||||
|
||||
static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_remove_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = 0;
|
||||
udp_remove(msg->pcb);
|
||||
@ -62,10 +63,10 @@ static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
|
||||
static void _udp_remove(struct udp_pcb *pcb){
|
||||
udp_api_call_t msg;
|
||||
msg.pcb = pcb;
|
||||
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call_data*)&msg);
|
||||
}
|
||||
|
||||
static err_t _udp_bind_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_bind_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_bind(msg->pcb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -76,11 +77,11 @@ static err_t _udp_bind(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port){
|
||||
msg.pcb = pcb;
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_sendto_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_sendto_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_sendto(msg->pcb, msg->pb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -92,11 +93,11 @@ static err_t _udp_sendto(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
msg.pb = pb;
|
||||
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_sendto_if_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_sendto_if_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_sendto_if(msg->pcb, msg->pb, msg->addr, msg->port, msg->netif);
|
||||
return msg->err;
|
||||
@ -109,7 +110,7 @@ static err_t _udp_sendto_if(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_
|
||||
msg.port = port;
|
||||
msg.pb = pb;
|
||||
msg.netif = netif;
|
||||
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ static void _udp_task(void *pvParameters){
|
||||
if(xQueueReceive(_udp_queue, &e, portMAX_DELAY) == pdTRUE){
|
||||
if(!e->pb){
|
||||
free((void*)(e));
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
AsyncUDP::_s_recv(e->arg, e->pcb, e->pb, e->addr, e->port, e->netif);
|
||||
free((void*)(e));
|
||||
@ -292,19 +293,23 @@ AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr,
|
||||
_remoteIp.type = raddr->type;
|
||||
_localIp.type = _remoteIp.type;
|
||||
|
||||
eth_hdr* eth = NULL;
|
||||
udp_hdr* udphdr = reinterpret_cast<udp_hdr*>(_data - UDP_HLEN);
|
||||
_localPort = ntohs(udphdr->dest);
|
||||
_remotePort = ntohs(udphdr->src);
|
||||
|
||||
|
||||
if (_remoteIp.type == IPADDR_TYPE_V4) {
|
||||
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN - SIZEOF_ETH_HDR);
|
||||
struct ip_hdr * iphdr = (struct ip_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN);
|
||||
_localIp.u_addr.ip4.addr = iphdr->dest.addr;
|
||||
_remoteIp.u_addr.ip4.addr = iphdr->src.addr;
|
||||
} else {
|
||||
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN - SIZEOF_ETH_HDR);
|
||||
struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN);
|
||||
memcpy(&_localIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
|
||||
memcpy(&_remoteIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->src.addr, 16);
|
||||
}
|
||||
memcpy(_remoteMac, eth->src.addr, 6);
|
||||
|
||||
struct netif * netif = NULL;
|
||||
void * nif = NULL;
|
||||
@ -415,6 +420,11 @@ uint16_t AsyncUDPPacket::remotePort()
|
||||
return _remotePort;
|
||||
}
|
||||
|
||||
void AsyncUDPPacket::remoteMac(uint8_t * mac)
|
||||
{
|
||||
memcpy(mac, _remoteMac, 6);
|
||||
}
|
||||
|
||||
bool AsyncUDPPacket::isIPv6()
|
||||
{
|
||||
return _localIp.type == IPADDR_TYPE_V6;
|
||||
@ -452,16 +462,24 @@ size_t AsyncUDPPacket::send(AsyncUDPMessage &message)
|
||||
return write(message.data(), message.length());
|
||||
}
|
||||
|
||||
AsyncUDP::AsyncUDP()
|
||||
{
|
||||
bool AsyncUDP::_init(){
|
||||
if(_pcb){
|
||||
return true;
|
||||
}
|
||||
_pcb = udp_new();
|
||||
_connected = false;
|
||||
_handler = NULL;
|
||||
if(!_pcb){
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
//_lock = xSemaphoreCreateMutex();
|
||||
udp_recv(_pcb, &_udp_recv, (void *) this);
|
||||
return true;
|
||||
}
|
||||
|
||||
AsyncUDP::AsyncUDP()
|
||||
{
|
||||
_pcb = NULL;
|
||||
_connected = false;
|
||||
_handler = NULL;
|
||||
}
|
||||
|
||||
AsyncUDP::~AsyncUDP()
|
||||
@ -483,8 +501,7 @@ void AsyncUDP::close()
|
||||
_udp_disconnect(_pcb);
|
||||
}
|
||||
_connected = false;
|
||||
_pcb->multicast_ip.type = IPADDR_TYPE_V4;
|
||||
_pcb->multicast_ip.u_addr.ip4.addr = 0;
|
||||
//todo: unjoin multicast group
|
||||
}
|
||||
UDP_MUTEX_UNLOCK();
|
||||
}
|
||||
@ -495,7 +512,7 @@ bool AsyncUDP::connect(const ip_addr_t *addr, uint16_t port)
|
||||
log_e("failed to start task");
|
||||
return false;
|
||||
}
|
||||
if(_pcb == NULL) {
|
||||
if(!_init()) {
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
@ -516,7 +533,7 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
|
||||
log_e("failed to start task");
|
||||
return false;
|
||||
}
|
||||
if(_pcb == NULL) {
|
||||
if(!_init()) {
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
@ -534,57 +551,53 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX)
|
||||
{
|
||||
struct netif * netif = NULL;
|
||||
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
|
||||
void * nif = NULL;
|
||||
esp_err_t err = tcpip_adapter_get_netif(tcpip_if, &nif);
|
||||
if (err) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
netif = (struct netif *)nif;
|
||||
}
|
||||
|
||||
if (addr->type == IPADDR_TYPE_V4) {
|
||||
if(join){
|
||||
if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(join){
|
||||
if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if)
|
||||
{
|
||||
if(!ip_addr_ismulticast(addr)) {
|
||||
return false;
|
||||
}
|
||||
ip_addr_t multicast_if_addr;
|
||||
uint8_t mode;
|
||||
if(esp_wifi_get_mode((wifi_mode_t*)&mode)){
|
||||
mode = WIFI_MODE_NULL;
|
||||
}
|
||||
|
||||
if(addr->type == IPADDR_TYPE_V6){
|
||||
multicast_if_addr.type = IPADDR_TYPE_V6;
|
||||
|
||||
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
|
||||
if(tcpip_adapter_get_ip6_linklocal(tcpip_if, &multicast_if_addr.u_addr.ip6)){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mld6_joingroup(&(multicast_if_addr.u_addr.ip6), &(addr->u_addr.ip6))) {
|
||||
return false;
|
||||
}
|
||||
} else if(addr->type == IPADDR_TYPE_V4){
|
||||
tcpip_adapter_ip_info_t ifIpInfo;
|
||||
|
||||
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
|
||||
if(tcpip_adapter_get_ip_info(tcpip_if, &ifIpInfo)){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
multicast_if_addr.type = IPADDR_TYPE_V4;
|
||||
multicast_if_addr.u_addr.ip4.addr = ifIpInfo.ip.addr;
|
||||
|
||||
if (igmp_joingroup((const ip4_addr *)&multicast_if_addr.u_addr.ip4, (const ip4_addr *)&addr->u_addr.ip4)!= ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (joinMulticastGroup(addr, true, tcpip_if)!= ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!listen(&multicast_if_addr, port)) {
|
||||
if(!listen(NULL, port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -592,9 +605,7 @@ bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl
|
||||
_pcb->mcast_ttl = ttl;
|
||||
_pcb->remote_port = port;
|
||||
ip_addr_copy(_pcb->remote_ip, *addr);
|
||||
if(addr->type == IPADDR_TYPE_V4){
|
||||
ip_addr_copy(_pcb->multicast_ip, multicast_if_addr);
|
||||
}
|
||||
//ip_addr_copy(_pcb->remote_ip, ip_addr_any_type);
|
||||
UDP_MUTEX_UNLOCK();
|
||||
|
||||
return true;
|
||||
@ -604,7 +615,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
|
||||
{
|
||||
if(!_pcb) {
|
||||
UDP_MUTEX_LOCK();
|
||||
_pcb = udp_new_ip_type(addr->type);
|
||||
_pcb = udp_new();
|
||||
UDP_MUTEX_UNLOCK();
|
||||
if(_pcb == NULL) {
|
||||
return 0;
|
||||
@ -619,7 +630,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload);
|
||||
memcpy(dst, data, len);
|
||||
UDP_MUTEX_LOCK();
|
||||
if(tcpip_if != TCPIP_ADAPTER_IF_MAX){
|
||||
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
|
||||
void * nif = NULL;
|
||||
tcpip_adapter_get_netif((tcpip_adapter_if_t)tcpip_if, &nif);
|
||||
if(!nif){
|
||||
|
@ -53,6 +53,7 @@ protected:
|
||||
uint16_t _localPort;
|
||||
ip_addr_t _remoteIp;
|
||||
uint16_t _remotePort;
|
||||
uint8_t _remoteMac[6];
|
||||
uint8_t *_data;
|
||||
size_t _len;
|
||||
size_t _index;
|
||||
@ -74,6 +75,7 @@ public:
|
||||
IPAddress remoteIP();
|
||||
IPv6Address remoteIPv6();
|
||||
uint16_t remotePort();
|
||||
void remoteMac(uint8_t * mac);
|
||||
|
||||
size_t send(AsyncUDPMessage &message);
|
||||
|
||||
@ -95,6 +97,7 @@ protected:
|
||||
bool _connected;
|
||||
AuPacketHandlerFunction _handler;
|
||||
|
||||
bool _init();
|
||||
void _recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif);
|
||||
|
||||
public:
|
||||
@ -109,9 +112,9 @@ public:
|
||||
bool listen(const IPv6Address addr, uint16_t port);
|
||||
bool listen(uint16_t port);
|
||||
|
||||
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
|
||||
bool connect(const ip_addr_t *addr, uint16_t port);
|
||||
bool connect(const IPAddress addr, uint16_t port);
|
||||
|
Submodule libraries/BLE updated: 7951347ed6...b232e7f5f0
@ -40,11 +40,117 @@
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
const char * _spp_server_name = "ESP32_SPP_SERVER";
|
||||
const char * _spp_server_name = "ESP32SPP";
|
||||
|
||||
#define QUEUE_SIZE 256
|
||||
#define RX_QUEUE_SIZE 512
|
||||
#define TX_QUEUE_SIZE 32
|
||||
static uint32_t _spp_client = 0;
|
||||
static xQueueHandle _spp_queue = NULL;
|
||||
static xQueueHandle _spp_rx_queue = NULL;
|
||||
static xQueueHandle _spp_tx_queue = NULL;
|
||||
static SemaphoreHandle_t _spp_tx_done = NULL;
|
||||
static TaskHandle_t _spp_task_handle = NULL;
|
||||
static EventGroupHandle_t _spp_event_group = NULL;
|
||||
static boolean secondConnectionAttempt;
|
||||
static esp_spp_cb_t * custom_spp_callback = NULL;
|
||||
|
||||
#define SPP_RUNNING 0x01
|
||||
#define SPP_CONNECTED 0x02
|
||||
#define SPP_CONGESTED 0x04
|
||||
|
||||
typedef struct {
|
||||
size_t len;
|
||||
uint8_t data[];
|
||||
} spp_packet_t;
|
||||
|
||||
static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){
|
||||
if(!data || !len){
|
||||
log_w("No data provided");
|
||||
return ESP_OK;
|
||||
}
|
||||
spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t) + len);
|
||||
if(!packet){
|
||||
log_e("SPP TX Packet Malloc Failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
packet->len = len;
|
||||
memcpy(packet->data, data, len);
|
||||
if (xQueueSend(_spp_tx_queue, &packet, portMAX_DELAY) != pdPASS) {
|
||||
log_e("SPP TX Queue Send Failed!");
|
||||
free(packet);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const uint16_t SPP_TX_MAX = 330;
|
||||
static uint8_t _spp_tx_buffer[SPP_TX_MAX];
|
||||
static uint16_t _spp_tx_buffer_len = 0;
|
||||
|
||||
static bool _spp_send_buffer(){
|
||||
if((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED)){
|
||||
esp_err_t err = esp_spp_write(_spp_client, _spp_tx_buffer_len, _spp_tx_buffer);
|
||||
if(err != ESP_OK){
|
||||
log_e("SPP Write Failed! [0x%X]", err);
|
||||
return false;
|
||||
}
|
||||
_spp_tx_buffer_len = 0;
|
||||
if(xSemaphoreTake(_spp_tx_done, portMAX_DELAY) != pdTRUE){
|
||||
log_e("SPP Ack Failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _spp_tx_task(void * arg){
|
||||
spp_packet_t *packet = NULL;
|
||||
size_t len = 0, to_send = 0;
|
||||
uint8_t * data = NULL;
|
||||
for (;;) {
|
||||
if(_spp_tx_queue && xQueueReceive(_spp_tx_queue, &packet, portMAX_DELAY) == pdTRUE && packet){
|
||||
if(packet->len <= (SPP_TX_MAX - _spp_tx_buffer_len)){
|
||||
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, packet->data, packet->len);
|
||||
_spp_tx_buffer_len+=packet->len;
|
||||
free(packet);
|
||||
packet = NULL;
|
||||
if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){
|
||||
_spp_send_buffer();
|
||||
}
|
||||
} else {
|
||||
len = packet->len;
|
||||
data = packet->data;
|
||||
to_send = SPP_TX_MAX - _spp_tx_buffer_len;
|
||||
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, data, to_send);
|
||||
_spp_tx_buffer_len = SPP_TX_MAX;
|
||||
data += to_send;
|
||||
len -= to_send;
|
||||
_spp_send_buffer();
|
||||
while(len >= SPP_TX_MAX){
|
||||
memcpy(_spp_tx_buffer, data, SPP_TX_MAX);
|
||||
_spp_tx_buffer_len = SPP_TX_MAX;
|
||||
data += SPP_TX_MAX;
|
||||
len -= SPP_TX_MAX;
|
||||
_spp_send_buffer();
|
||||
}
|
||||
if(len){
|
||||
memcpy(_spp_tx_buffer, data, len);
|
||||
_spp_tx_buffer_len += len;
|
||||
free(packet);
|
||||
packet = NULL;
|
||||
if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){
|
||||
_spp_send_buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_e("Something went horribly wrong");
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
_spp_task_handle = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
|
||||
{
|
||||
@ -54,86 +160,153 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
|
||||
log_i("ESP_SPP_INIT_EVT");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name);
|
||||
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
|
||||
break;
|
||||
|
||||
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
||||
if (!_spp_client){
|
||||
_spp_client = param->open.handle;
|
||||
} else {
|
||||
secondConnectionAttempt = true;
|
||||
esp_spp_disconnect(param->open.handle);
|
||||
}
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
|
||||
log_i("ESP_SPP_SRV_OPEN_EVT");
|
||||
break;
|
||||
|
||||
case ESP_SPP_CLOSE_EVT://Client connection closed
|
||||
if(secondConnectionAttempt) {
|
||||
secondConnectionAttempt = false;
|
||||
} else {
|
||||
_spp_client = 0;
|
||||
}
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
|
||||
log_i("ESP_SPP_CLOSE_EVT");
|
||||
break;
|
||||
|
||||
case ESP_SPP_CONG_EVT://connection congestion status changed
|
||||
if(param->cong.cong){
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
|
||||
} else {
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE");
|
||||
break;
|
||||
|
||||
case ESP_SPP_WRITE_EVT://write operation completed
|
||||
if(param->write.cong){
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
xSemaphoreGive(_spp_tx_done);//we can try to send another packet
|
||||
log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"FREE");
|
||||
break;
|
||||
|
||||
case ESP_SPP_DATA_IND_EVT://connection received data
|
||||
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
|
||||
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
|
||||
//ets_printf("r:%u\n", param->data_ind.len);
|
||||
|
||||
if (_spp_rx_queue != NULL){
|
||||
for (int i = 0; i < param->data_ind.len; i++){
|
||||
if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){
|
||||
log_e("RX Full! Discarding %u bytes", param->data_ind.len - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
//should maybe delete those.
|
||||
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
|
||||
log_i("ESP_SPP_DISCOVERY_COMP_EVT");
|
||||
break;
|
||||
case ESP_SPP_OPEN_EVT://Client connection open
|
||||
log_i("ESP_SPP_OPEN_EVT");
|
||||
break;
|
||||
case ESP_SPP_CLOSE_EVT://Client connection closed
|
||||
_spp_client = 0;
|
||||
log_i("ESP_SPP_CLOSE_EVT");
|
||||
break;
|
||||
case ESP_SPP_START_EVT://server started
|
||||
log_i("ESP_SPP_START_EVT");
|
||||
break;
|
||||
case ESP_SPP_CL_INIT_EVT://client initiated a connection
|
||||
log_i("ESP_SPP_CL_INIT_EVT");
|
||||
break;
|
||||
case ESP_SPP_DATA_IND_EVT://connection received data
|
||||
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
|
||||
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
|
||||
|
||||
if (_spp_queue != NULL){
|
||||
for (int i = 0; i < param->data_ind.len; i++)
|
||||
xQueueSend(_spp_queue, param->data_ind.data + i, (TickType_t)0);
|
||||
} else {
|
||||
log_e("SerialQueueBT ERROR");
|
||||
}
|
||||
break;
|
||||
case ESP_SPP_CONG_EVT://connection congestion status changed
|
||||
log_i("ESP_SPP_CONG_EVT");
|
||||
break;
|
||||
case ESP_SPP_WRITE_EVT://write operation completed
|
||||
log_v("ESP_SPP_WRITE_EVT");
|
||||
break;
|
||||
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
||||
_spp_client = param->open.handle;
|
||||
log_i("ESP_SPP_SRV_OPEN_EVT");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(custom_spp_callback)(*custom_spp_callback)(event, param);
|
||||
}
|
||||
|
||||
static bool _init_bt(const char *deviceName)
|
||||
{
|
||||
if(!_spp_event_group){
|
||||
_spp_event_group = xEventGroupCreate();
|
||||
if(!_spp_event_group){
|
||||
log_e("SPP Event Group Create Failed!");
|
||||
return false;
|
||||
}
|
||||
xEventGroupClearBits(_spp_event_group, 0xFFFFFF);
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
if (_spp_rx_queue == NULL){
|
||||
_spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
|
||||
if (_spp_rx_queue == NULL){
|
||||
log_e("RX Queue Create Failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_spp_tx_queue == NULL){
|
||||
_spp_tx_queue = xQueueCreate(TX_QUEUE_SIZE, sizeof(spp_packet_t*)); //initialize the queue
|
||||
if (_spp_tx_queue == NULL){
|
||||
log_e("TX Queue Create Failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(_spp_tx_done == NULL){
|
||||
_spp_tx_done = xSemaphoreCreateBinary();
|
||||
if (_spp_tx_done == NULL){
|
||||
log_e("TX Semaphore Create Failed");
|
||||
return false;
|
||||
}
|
||||
xSemaphoreTake(_spp_tx_done, 0);
|
||||
}
|
||||
|
||||
if(!_spp_task_handle){
|
||||
xTaskCreate(_spp_tx_task, "spp_tx", 4096, NULL, 2, &_spp_task_handle);
|
||||
if(!_spp_task_handle){
|
||||
log_e("Network Event Task Start Failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!btStarted() && !btStart()){
|
||||
log_e("%s initialize controller failed\n", __func__);
|
||||
log_e("initialize controller failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
|
||||
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){
|
||||
if (esp_bluedroid_init()) {
|
||||
log_e("%s initialize bluedroid failed\n", __func__);
|
||||
log_e("initialize bluedroid failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){
|
||||
if (esp_bluedroid_enable()) {
|
||||
log_e("%s enable bluedroid failed\n", __func__);
|
||||
log_e("enable bluedroid failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){
|
||||
log_e("%s spp register failed\n", __func__);
|
||||
log_e("spp register failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK){
|
||||
log_e("%s spp init failed\n", __func__);
|
||||
log_e("spp init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
_spp_queue = xQueueCreate(QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
|
||||
if (_spp_queue == NULL){
|
||||
log_e("%s Queue creation error\n", __func__);
|
||||
return false;
|
||||
}
|
||||
esp_bt_dev_set_device_name(deviceName);
|
||||
|
||||
// the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack
|
||||
@ -142,7 +315,7 @@ static bool _init_bt(const char *deviceName)
|
||||
cod.minor = 0b000100;
|
||||
cod.service = 0b00000010110;
|
||||
if (esp_bt_gap_set_cod(cod, ESP_BT_INIT_COD) != ESP_OK) {
|
||||
log_e("%s set cod failed\n", __func__);
|
||||
log_e("set cod failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -152,10 +325,39 @@ static bool _init_bt(const char *deviceName)
|
||||
static bool _stop_bt()
|
||||
{
|
||||
if (btStarted()){
|
||||
if(_spp_client)
|
||||
esp_spp_disconnect(_spp_client);
|
||||
esp_spp_deinit();
|
||||
esp_bluedroid_disable();
|
||||
esp_bluedroid_deinit();
|
||||
btStop();
|
||||
}
|
||||
_spp_client = 0;
|
||||
if(_spp_task_handle){
|
||||
vTaskDelete(_spp_task_handle);
|
||||
_spp_task_handle = NULL;
|
||||
}
|
||||
if(_spp_event_group){
|
||||
vEventGroupDelete(_spp_event_group);
|
||||
_spp_event_group = NULL;
|
||||
}
|
||||
if(_spp_rx_queue){
|
||||
vQueueDelete(_spp_rx_queue);
|
||||
//ToDo: clear RX queue when in packet mode
|
||||
_spp_rx_queue = NULL;
|
||||
}
|
||||
if(_spp_tx_queue){
|
||||
spp_packet_t *packet = NULL;
|
||||
while(xQueueReceive(_spp_tx_queue, &packet, 0) == pdTRUE){
|
||||
free(packet);
|
||||
}
|
||||
vQueueDelete(_spp_tx_queue);
|
||||
_spp_tx_queue = NULL;
|
||||
}
|
||||
if (_spp_tx_done) {
|
||||
vSemaphoreDelete(_spp_tx_done);
|
||||
_spp_tx_done = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -184,60 +386,39 @@ bool BluetoothSerial::begin(String localName)
|
||||
|
||||
int BluetoothSerial::available(void)
|
||||
{
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
if (_spp_rx_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
return uxQueueMessagesWaiting(_spp_queue);
|
||||
return uxQueueMessagesWaiting(_spp_rx_queue);
|
||||
}
|
||||
|
||||
int BluetoothSerial::peek(void)
|
||||
{
|
||||
if (available()){
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
if (xQueuePeek(_spp_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
uint8_t c;
|
||||
if (_spp_rx_queue && xQueuePeek(_spp_rx_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool BluetoothSerial::hasClient(void)
|
||||
{
|
||||
if (_spp_client)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return _spp_client > 0;
|
||||
}
|
||||
|
||||
int BluetoothSerial::read(void)
|
||||
{
|
||||
if (available()){
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
if (xQueueReceive(_spp_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
if (_spp_rx_queue && xQueueReceive(_spp_rx_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t BluetoothSerial::write(uint8_t c)
|
||||
{
|
||||
if (!_spp_client){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buffer[1];
|
||||
buffer[0] = c;
|
||||
esp_err_t err = esp_spp_write(_spp_client, 1, buffer);
|
||||
return (err == ESP_OK) ? 1 : 0;
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
|
||||
@ -245,18 +426,12 @@ size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
|
||||
if (!_spp_client){
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_spp_write(_spp_client, size, (uint8_t *)buffer);
|
||||
return (err == ESP_OK) ? size : 0;
|
||||
return (_spp_queue_packet((uint8_t *)buffer, size) == ESP_OK) ? size : 0;
|
||||
}
|
||||
|
||||
void BluetoothSerial::flush()
|
||||
{
|
||||
if (_spp_client){
|
||||
int qsize = available();
|
||||
uint8_t buffer[qsize];
|
||||
esp_spp_write(_spp_client, qsize, buffer);
|
||||
}
|
||||
while(read() >= 0){}
|
||||
}
|
||||
|
||||
void BluetoothSerial::end()
|
||||
@ -264,4 +439,10 @@ void BluetoothSerial::end()
|
||||
_stop_bt();
|
||||
}
|
||||
|
||||
esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback)
|
||||
{
|
||||
custom_spp_callback = callback;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Stream.h"
|
||||
#include <esp_spp_api.h>
|
||||
|
||||
class BluetoothSerial: public Stream
|
||||
{
|
||||
@ -38,6 +39,7 @@ class BluetoothSerial: public Stream
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
void flush();
|
||||
void end(void);
|
||||
esp_err_t register_callback(esp_spp_cb_t * callback);
|
||||
|
||||
private:
|
||||
String local_name;
|
||||
|
@ -36,12 +36,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,7 @@ void setup(){
|
||||
reset occurs.
|
||||
*/
|
||||
Serial.println("Going to sleep now");
|
||||
Serial.flush();
|
||||
esp_deep_sleep_start();
|
||||
Serial.println("This will never be printed");
|
||||
}
|
||||
|
@ -26,12 +26,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
97
libraries/ESP32/examples/FreeRTOS/FreeRTOS.ino
Normal file
97
libraries/ESP32/examples/FreeRTOS/FreeRTOS.ino
Normal file
@ -0,0 +1,97 @@
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define ARDUINO_RUNNING_CORE 0
|
||||
#else
|
||||
#define ARDUINO_RUNNING_CORE 1
|
||||
#endif
|
||||
|
||||
#ifndef LED_BUILTIN
|
||||
#define LED_BUILTIN 13
|
||||
#endif
|
||||
|
||||
// define two tasks for Blink & AnalogRead
|
||||
void TaskBlink( void *pvParameters );
|
||||
void TaskAnalogReadA3( void *pvParameters );
|
||||
|
||||
// the setup function runs once when you press reset or power the board
|
||||
void setup() {
|
||||
|
||||
// initialize serial communication at 115200 bits per second:
|
||||
Serial.begin(115200);
|
||||
|
||||
// Now set up two tasks to run independently.
|
||||
xTaskCreatePinnedToCore(
|
||||
TaskBlink
|
||||
, "TaskBlink" // A name just for humans
|
||||
, 1024 // This stack size can be checked & adjusted by reading the Stack Highwater
|
||||
, NULL
|
||||
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
|
||||
, NULL
|
||||
, ARDUINO_RUNNING_CORE);
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
TaskAnalogReadA3
|
||||
, "AnalogReadA3"
|
||||
, 1024 // Stack size
|
||||
, NULL
|
||||
, 1 // Priority
|
||||
, NULL
|
||||
, ARDUINO_RUNNING_CORE);
|
||||
|
||||
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Empty. Things are done in Tasks.
|
||||
}
|
||||
|
||||
/*--------------------------------------------------*/
|
||||
/*---------------------- Tasks ---------------------*/
|
||||
/*--------------------------------------------------*/
|
||||
|
||||
void TaskBlink(void *pvParameters) // This is a task.
|
||||
{
|
||||
(void) pvParameters;
|
||||
|
||||
/*
|
||||
Blink
|
||||
Turns on an LED on for one second, then off for one second, repeatedly.
|
||||
|
||||
If you want to know what pin the on-board LED is connected to on your ESP32 model, check
|
||||
the Technical Specs of your board.
|
||||
*/
|
||||
|
||||
// initialize digital LED_BUILTIN on pin 13 as an output.
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
for (;;) // A Task shall never return or exit.
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
|
||||
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
|
||||
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
|
||||
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
|
||||
}
|
||||
}
|
||||
|
||||
void TaskAnalogReadA3(void *pvParameters) // This is a task.
|
||||
{
|
||||
(void) pvParameters;
|
||||
|
||||
/*
|
||||
AnalogReadSerial
|
||||
Reads an analog input on pin A3, prints the result to the serial monitor.
|
||||
Graphical representation is available using serial plotter (Tools > Serial Plotter menu)
|
||||
Attach the center pin of a potentiometer to pin A3, and the outside pins to +5V and ground.
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// read the input on analog pin A3:
|
||||
int sensorValueA3 = analogRead(A3);
|
||||
// print out the value you read:
|
||||
Serial.println(sensorValueA3);
|
||||
vTaskDelay(10); // one tick delay (15ms) in between reads for stability
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#include <Arduino.h>
|
||||
#include <FunctionalInterrupt.h>
|
||||
|
||||
#define BUTTON1 16
|
||||
#define BUTTON2 17
|
||||
|
||||
class Button
|
||||
{
|
||||
public:
|
||||
Button(uint8_t reqPin) : PIN(reqPin){
|
||||
pinMode(PIN, INPUT_PULLUP);
|
||||
attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
|
||||
};
|
||||
~Button() {
|
||||
detachInterrupt(PIN);
|
||||
}
|
||||
|
||||
void IRAM_ATTR isr() {
|
||||
numberKeyPresses += 1;
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
void checkPressed() {
|
||||
if (pressed) {
|
||||
Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses);
|
||||
pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t PIN;
|
||||
volatile uint32_t numberKeyPresses;
|
||||
volatile bool pressed;
|
||||
};
|
||||
|
||||
Button button1(BUTTON1);
|
||||
Button button2(BUTTON2);
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
button1.checkPressed();
|
||||
button2.checkPressed();
|
||||
}
|
61
libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino
Normal file
61
libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino
Normal file
@ -0,0 +1,61 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
rmt_data_t my_data[256];
|
||||
rmt_data_t data[256];
|
||||
|
||||
rmt_obj_t* rmt_send = NULL;
|
||||
rmt_obj_t* rmt_recv = NULL;
|
||||
|
||||
static EventGroupHandle_t events;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
|
||||
{
|
||||
Serial.println("init sender failed\n");
|
||||
}
|
||||
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
|
||||
{
|
||||
Serial.println("init receiver failed\n");
|
||||
}
|
||||
|
||||
float realTick = rmtSetTick(rmt_send, 100);
|
||||
printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Init data
|
||||
int i;
|
||||
for (i=0; i<255; i++) {
|
||||
data[i].val = 0x80010001 + ((i%13)<<16) + 13-(i%13);
|
||||
}
|
||||
data[255].val = 0;
|
||||
|
||||
// Start receiving
|
||||
rmtReadAsync(rmt_recv, my_data, 100, events, false, 0);
|
||||
|
||||
// Send in continous mode
|
||||
rmtWrite(rmt_send, data, 100);
|
||||
|
||||
// Wait for data
|
||||
xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY);
|
||||
|
||||
// Printout the received data plus the original values
|
||||
for (i=0; i<60; i++)
|
||||
{
|
||||
Serial.printf("%08x=%08x ", my_data[i], data[i] );
|
||||
if (!((i+1)%4)) Serial.println("\n");
|
||||
}
|
||||
Serial.println("\n");
|
||||
|
||||
delay(2000);
|
||||
}
|
204
libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino
Normal file
204
libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino
Normal file
@ -0,0 +1,204 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
//
|
||||
// Note: This example uses a FrSKY device communication
|
||||
// using XJT D12 protocol
|
||||
//
|
||||
// ; 0 bit = 6us low/10us high
|
||||
// ; 1 bit = 14us low/10us high
|
||||
// ;
|
||||
// ; --------+ +----------+ +----------+
|
||||
// ; | | | | |
|
||||
// ; | 0 | | 1 | |
|
||||
// ; | | | | |
|
||||
// ; | | | | |
|
||||
// ; +-------+ +-----------------+ +---------
|
||||
// ;
|
||||
// ; | 6us 10us | 14us 10us |
|
||||
// ; |-------|----------|-----------------|----------|--------
|
||||
// ; | 16us | 24us |
|
||||
|
||||
// Typedef of received frame
|
||||
//
|
||||
// ; 0x00 - Sync, 0x7E (sync header ID)
|
||||
// ; 0x01 - Rx ID, 0x?? (receiver ID number, 0x00-0x??)
|
||||
// ; 0x02 - Flags 1, 0x?? (used for failsafe and binding)
|
||||
// ; 0x03 - Flags 2, 0x00 (reserved)
|
||||
// ; 0x04-0x06, Channels 1/9 and 2/10
|
||||
// ; 0x07-0x09, Channels 3/11 and 4/12
|
||||
// ; 0x0A-0x0C, Channels 5/13 and 6/14
|
||||
// ; 0x0D-0x0F, Channels 7/15 and 8/16
|
||||
// ; 0x10 - 0x00, always zero
|
||||
// ; 0x11 - CRC-16 High
|
||||
// ; 0x12 - CRC-16 Low
|
||||
// ; 0x13 - Tail, 0x7E (tail ID)
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t head;//0x7E
|
||||
uint8_t rxid;//Receiver Number
|
||||
uint8_t flags;//Range:0x20, Bind:0x01
|
||||
uint8_t reserved0;//0x00
|
||||
union {
|
||||
struct {
|
||||
uint8_t ch0_l;
|
||||
uint8_t ch0_h:4;
|
||||
uint8_t ch1_l:4;
|
||||
uint8_t ch1_h;
|
||||
};
|
||||
uint8_t bytes[3];
|
||||
} channels[4];
|
||||
uint8_t reserved1;//0x00
|
||||
uint8_t crc_h;
|
||||
uint8_t crc_l;
|
||||
uint8_t tail;//0x7E
|
||||
};
|
||||
uint8_t buffer[20];
|
||||
} xjt_packet_t;
|
||||
|
||||
#define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11)
|
||||
|
||||
rmt_obj_t* rmt_recv = NULL;
|
||||
|
||||
static uint32_t *s_channels;
|
||||
static uint32_t channels[16];
|
||||
static uint8_t xjt_flags = 0x0;
|
||||
static uint8_t xjt_rxid = 0x0;
|
||||
|
||||
static bool xjtReceiveBit(size_t index, bool bit){
|
||||
static xjt_packet_t xjt;
|
||||
static uint8_t xjt_bit_index = 8;
|
||||
static uint8_t xht_byte_index = 0;
|
||||
static uint8_t xht_ones = 0;
|
||||
|
||||
if(!index){
|
||||
xjt_bit_index = 8;
|
||||
xht_byte_index = 0;
|
||||
xht_ones = 0;
|
||||
}
|
||||
|
||||
if(xht_byte_index > 19){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
if(bit){
|
||||
xht_ones++;
|
||||
if(xht_ones > 5 && xht_byte_index && xht_byte_index < 19){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
//add bit
|
||||
xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index);
|
||||
} else if(xht_ones == 5 && xht_byte_index && xht_byte_index < 19){
|
||||
xht_ones = 0;
|
||||
//skip bit
|
||||
return true;
|
||||
} else {
|
||||
xht_ones = 0;
|
||||
//add bit
|
||||
xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index);
|
||||
}
|
||||
if ((!xjt_bit_index) || (xjt_bit_index==1 && xht_byte_index==19) ) {
|
||||
xjt_bit_index = 8;
|
||||
if(!xht_byte_index && xjt.buffer[0] != 0x7E){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
xht_byte_index++;
|
||||
if(xht_byte_index == 20){
|
||||
//done
|
||||
if(xjt.buffer[19] != 0x7E){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
//check crc?
|
||||
|
||||
xjt_flags = xjt.flags;
|
||||
xjt_rxid = xjt.rxid;
|
||||
for(int i=0; i<4; i++){
|
||||
uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8);
|
||||
ch0 = ((ch0 * 2) + 2452) / 3;
|
||||
uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4);
|
||||
ch1 = ((ch1 * 2) + 2452) / 3;
|
||||
uint8_t c0n = i*2;
|
||||
if(xjt.channels[i].ch0_h & 0x8){
|
||||
c0n += 8;
|
||||
}
|
||||
uint8_t c1n = i*2+1;
|
||||
if(xjt.channels[i].ch1_h & 0x80){
|
||||
c1n += 8;
|
||||
}
|
||||
s_channels[c0n] = ch0;
|
||||
s_channels[c1n] = ch1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels){
|
||||
size_t chan = 0;
|
||||
bool valid = true;
|
||||
rmt_data_t* it = NULL;
|
||||
|
||||
if (!channels) {
|
||||
log_e("Please provide data block for storing channel info");
|
||||
return;
|
||||
}
|
||||
s_channels = channels;
|
||||
|
||||
it = &items[0];
|
||||
for(size_t i = 0; i<len; i++){
|
||||
|
||||
if(!valid){
|
||||
break;
|
||||
}
|
||||
it = &items[i];
|
||||
if(XJT_VALID(it)){
|
||||
if(it->duration1 >= 5 && it->duration1 <= 8){
|
||||
valid = xjtReceiveBit(i, false);
|
||||
} else if(it->duration1 >= 13 && it->duration1 <= 16){
|
||||
valid = xjtReceiveBit(i, true);
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
} else if(!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) {
|
||||
valid = xjtReceiveBit(i, false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void receive_data(uint32_t *data, size_t len)
|
||||
{
|
||||
parseRmt((rmt_data_t*) data, len, channels);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize the channel to capture up to 192 items
|
||||
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
|
||||
{
|
||||
Serial.println("init receiver failed\n");
|
||||
}
|
||||
|
||||
// Setup 1us tick
|
||||
float realTick = rmtSetTick(rmt_recv, 1000);
|
||||
Serial.printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
// Ask to start reading
|
||||
rmtRead(rmt_recv, receive_data);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// printout some of the channels
|
||||
Serial.printf("%04x %04x %04x %04x\n", channels[0], channels[1], channels[2], channels[3]);
|
||||
delay(500);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#define NR_OF_LEDS 8*4
|
||||
#define NR_OF_ALL_BITS 24*NR_OF_LEDS
|
||||
|
||||
//
|
||||
// Note: This example uses Neopixel LED board, 32 LEDs chained one
|
||||
// after another, each RGB LED has its 24 bit value
|
||||
// for color configuration (8b for each color)
|
||||
//
|
||||
// Bits encoded as pulses as follows:
|
||||
//
|
||||
// "0":
|
||||
// +-------+ +--
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// ---| |--------------|
|
||||
// + + +
|
||||
// | 0.4us | 0.85 0us |
|
||||
//
|
||||
// "1":
|
||||
// +-------------+ +--
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// ---+ +-------+
|
||||
// | 0.8us | 0.4us |
|
||||
|
||||
rmt_data_t led_data[NR_OF_ALL_BITS];
|
||||
|
||||
rmt_obj_t* rmt_send = NULL;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
|
||||
{
|
||||
Serial.println("init sender failed\n");
|
||||
}
|
||||
|
||||
float realTick = rmtSetTick(rmt_send, 100);
|
||||
Serial.printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
}
|
||||
|
||||
int color[] = { 0x55, 0x11, 0x77 }; // RGB value
|
||||
int led_index = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Init data with only one led ON
|
||||
int led, col, bit;
|
||||
int i=0;
|
||||
for (led=0; led<NR_OF_LEDS; led++) {
|
||||
for (col=0; col<3; col++ ) {
|
||||
for (bit=0; bit<8; bit++){
|
||||
if ( (color[col] & (1<<(8-bit))) && (led == led_index) ) {
|
||||
led_data[i].level0 = 1;
|
||||
led_data[i].duration0 = 8;
|
||||
led_data[i].level1 = 0;
|
||||
led_data[i].duration1 = 4;
|
||||
} else {
|
||||
led_data[i].level0 = 1;
|
||||
led_data[i].duration0 = 4;
|
||||
led_data[i].level1 = 0;
|
||||
led_data[i].duration1 = 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// make the led travel in the pannel
|
||||
if ((++led_index)>=NR_OF_LEDS) {
|
||||
led_index = 0;
|
||||
}
|
||||
|
||||
// Send the data
|
||||
rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS);
|
||||
|
||||
delay(100);
|
||||
}
|
@ -6,7 +6,7 @@ hw_timer_t *timer = NULL;
|
||||
|
||||
void IRAM_ATTR resetModule() {
|
||||
ets_printf("reboot\n");
|
||||
esp_restart_noos();
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
@ -43,6 +43,14 @@ License (MIT license):
|
||||
#include <functional>
|
||||
#include "esp_wifi.h"
|
||||
|
||||
// Add quotes around defined value
|
||||
#ifdef __IN_ECLIPSE__
|
||||
#define STR_EXPAND(tok) #tok
|
||||
#define STR(tok) STR_EXPAND(tok)
|
||||
#else
|
||||
#define STR(tok) tok
|
||||
#endif
|
||||
|
||||
static void _on_sys_event(system_event_t *event){
|
||||
mdns_handle_system_event(NULL, event);
|
||||
}
|
||||
@ -82,7 +90,7 @@ void MDNSResponder::setInstanceName(String name) {
|
||||
|
||||
void MDNSResponder::enableArduino(uint16_t port, bool auth){
|
||||
mdns_txt_item_t arduTxtData[4] = {
|
||||
{(char*)"board" ,(char*)ARDUINO_VARIANT},
|
||||
{(char*)"board" ,(char*)STR(ARDUINO_VARIANT)},
|
||||
{(char*)"tcp_check" ,(char*)"no"},
|
||||
{(char*)"ssh_upload" ,(char*)"no"},
|
||||
{(char*)"auth_upload" ,(char*)"no"}
|
||||
|
181
libraries/FFat/examples/FFat_Test/FFat_Test.ino
Normal file
181
libraries/FFat/examples/FFat_Test/FFat_Test.ino
Normal file
@ -0,0 +1,181 @@
|
||||
#include "FS.h"
|
||||
#include "FFat.h"
|
||||
|
||||
// You only need to format FFat the first time you run a test
|
||||
#define FORMAT_FFAT true
|
||||
|
||||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if(!root){
|
||||
Serial.println("- failed to open directory");
|
||||
return;
|
||||
}
|
||||
if(!root.isDirectory()){
|
||||
Serial.println(" - not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
if(file.isDirectory()){
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if(levels){
|
||||
listDir(fs, file.name(), levels -1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char * path){
|
||||
Serial.printf("Reading file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if(!file || file.isDirectory()){
|
||||
Serial.println("- failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("- read from file:");
|
||||
while(file.available()){
|
||||
Serial.write(file.read());
|
||||
}
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char * path, const char * message){
|
||||
Serial.printf("Writing file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if(file.print(message)){
|
||||
Serial.println("- file written");
|
||||
} else {
|
||||
Serial.println("- frite failed");
|
||||
}
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char * path, const char * message){
|
||||
Serial.printf("Appending to file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if(file.print(message)){
|
||||
Serial.println("- message appended");
|
||||
} else {
|
||||
Serial.println("- append failed");
|
||||
}
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char * path1, const char * path2){
|
||||
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("- file renamed");
|
||||
} else {
|
||||
Serial.println("- rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char * path){
|
||||
Serial.printf("Deleting file: %s\r\n", path);
|
||||
if(fs.remove(path)){
|
||||
Serial.println("- file deleted");
|
||||
} else {
|
||||
Serial.println("- delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char * path){
|
||||
Serial.printf("Testing file I/O with %s\r\n", path);
|
||||
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
Serial.print("- writing" );
|
||||
uint32_t start = millis();
|
||||
for(i=0; i<2048; i++){
|
||||
if ((i & 0x001F) == 0x001F){
|
||||
Serial.print(".");
|
||||
}
|
||||
file.write(buf, 512);
|
||||
}
|
||||
Serial.println("");
|
||||
uint32_t end = millis() - start;
|
||||
Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
|
||||
file.close();
|
||||
|
||||
file = fs.open(path);
|
||||
start = millis();
|
||||
end = start;
|
||||
i = 0;
|
||||
if(file && !file.isDirectory()){
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
Serial.print("- reading" );
|
||||
while(len){
|
||||
size_t toRead = len;
|
||||
if(toRead > 512){
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
if ((i++ & 0x001F) == 0x001F){
|
||||
Serial.print(".");
|
||||
}
|
||||
len -= toRead;
|
||||
}
|
||||
Serial.println("");
|
||||
end = millis() - start;
|
||||
Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("- failed to open file for reading");
|
||||
}
|
||||
}
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
if (FORMAT_FFAT) FFat.format();
|
||||
if(!FFat.begin()){
|
||||
Serial.println("FFat Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("Total space: %10lu\n", FFat.totalBytes());
|
||||
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
|
||||
listDir(FFat, "/", 0);
|
||||
writeFile(FFat, "/hello.txt", "Hello ");
|
||||
appendFile(FFat, "/hello.txt", "World!\r\n");
|
||||
readFile(FFat, "/hello.txt");
|
||||
renameFile(FFat, "/hello.txt", "/foo.txt");
|
||||
readFile(FFat, "/foo.txt");
|
||||
deleteFile(FFat, "/foo.txt");
|
||||
testFileIO(FFat, "/test.txt");
|
||||
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
|
||||
deleteFile(FFat, "/test.txt");
|
||||
Serial.println( "Test complete" );
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
}
|
9
libraries/FFat/library.properties
Normal file
9
libraries/FFat/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=FFat
|
||||
version=1.0
|
||||
author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=ESP32 FAT on Flash File System
|
||||
paragraph=
|
||||
category=Data Storage
|
||||
url=
|
||||
architectures=esp32
|
144
libraries/FFat/src/FFat.cpp
Normal file
144
libraries/FFat/src/FFat.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "vfs_api.h"
|
||||
extern "C" {
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "diskio.h"
|
||||
#include "diskio_wl.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
}
|
||||
#include "FFat.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
F_Fat::F_Fat(FSImplPtr impl)
|
||||
: FS(impl)
|
||||
{}
|
||||
|
||||
const esp_partition_t *check_ffat_partition(const char* label)
|
||||
{
|
||||
const esp_partition_t* ck_part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, label);
|
||||
if (!ck_part) {
|
||||
log_e("No FAT partition found with label %s", label);
|
||||
return NULL;
|
||||
}
|
||||
return ck_part;
|
||||
}
|
||||
|
||||
bool F_Fat::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles, const char * partitionLabel)
|
||||
{
|
||||
if(_wl_handle){
|
||||
log_w("Already Mounted!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!check_ffat_partition(partitionLabel)) return false;
|
||||
|
||||
esp_vfs_fat_mount_config_t conf = {
|
||||
.format_if_mount_failed = formatOnFail,
|
||||
.max_files = maxOpenFiles
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount(basePath, partitionLabel, &conf, &_wl_handle);
|
||||
if(err){
|
||||
log_e("Mounting FFat partition failed! Error: %d", err);
|
||||
return false;
|
||||
}
|
||||
_impl->mountpoint(basePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
void F_Fat::end()
|
||||
{
|
||||
if(_wl_handle){
|
||||
esp_err_t err = esp_vfs_fat_spiflash_unmount(_impl->mountpoint(), _wl_handle);
|
||||
if(err){
|
||||
log_e("Unmounting FFat partition failed! Error: %d", err);
|
||||
return;
|
||||
}
|
||||
_wl_handle = NULL;
|
||||
_impl->mountpoint(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool F_Fat::format(bool full_wipe, char* partitionLabel)
|
||||
{
|
||||
esp_err_t result;
|
||||
if(_wl_handle){
|
||||
log_w("Already Mounted!");
|
||||
return false;
|
||||
}
|
||||
wl_handle_t temp_handle;
|
||||
// Attempt to mount to see if there is already data
|
||||
const esp_partition_t *ffat_partition = check_ffat_partition(partitionLabel);
|
||||
if (!ffat_partition) return false;
|
||||
result = wl_mount(ffat_partition, &temp_handle);
|
||||
|
||||
if (result == ESP_OK) {
|
||||
// Wipe disk- quick just wipes the FAT. Full zeroes the whole disk
|
||||
uint32_t wipe_size = full_wipe ? wl_size(temp_handle) : 16384;
|
||||
wl_erase_range(temp_handle, 0, wipe_size);
|
||||
wl_unmount(temp_handle);
|
||||
}
|
||||
// Now do a mount with format_if_fail (which it will)
|
||||
esp_vfs_fat_mount_config_t conf = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 1
|
||||
};
|
||||
result = esp_vfs_fat_spiflash_mount("/format_ffat", partitionLabel, &conf, &temp_handle);
|
||||
esp_vfs_fat_spiflash_unmount("/format_ffat", temp_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t F_Fat::totalBytes()
|
||||
{
|
||||
FATFS *fs;
|
||||
DWORD free_clust, tot_sect, sect_size;
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
|
||||
char drv[3] = {(char)(48+pdrv), ':', 0};
|
||||
FRESULT res = f_getfree(drv, &free_clust, &fs);
|
||||
tot_sect = (fs->n_fatent - 2) * fs->csize;
|
||||
sect_size = CONFIG_WL_SECTOR_SIZE;
|
||||
return tot_sect * sect_size;
|
||||
}
|
||||
|
||||
size_t F_Fat::freeBytes()
|
||||
{
|
||||
|
||||
FATFS *fs;
|
||||
DWORD free_clust, free_sect, sect_size;
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
|
||||
char drv[3] = {(char)(48+pdrv), ':', 0};
|
||||
FRESULT res = f_getfree(drv, &free_clust, &fs);
|
||||
free_sect = free_clust * fs->csize;
|
||||
sect_size = CONFIG_WL_SECTOR_SIZE;
|
||||
return free_sect * sect_size;
|
||||
}
|
||||
|
||||
bool F_Fat::exists(const char* path)
|
||||
{
|
||||
File f = open(path, "r");
|
||||
return (f == true) && !f.isDirectory();
|
||||
}
|
||||
|
||||
bool F_Fat::exists(const String& path)
|
||||
{
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
|
||||
F_Fat FFat = F_Fat(FSImplPtr(new VFSImpl()));
|
47
libraries/FFat/src/FFat.h
Normal file
47
libraries/FFat/src/FFat.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 _FFAT_H_
|
||||
#define _FFAT_H_
|
||||
|
||||
#include "FS.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#define FFAT_WIPE_QUICK 0
|
||||
#define FFAT_WIPE_FULL 1
|
||||
#define FFAT_PARTITION_LABEL "ffat"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
class F_Fat : public FS
|
||||
{
|
||||
public:
|
||||
F_Fat(FSImplPtr impl);
|
||||
bool begin(bool formatOnFail=false, const char * basePath="/ffat", uint8_t maxOpenFiles=10, const char * partitionLabel = (char*)FFAT_PARTITION_LABEL);
|
||||
bool format(bool full_wipe = FFAT_WIPE_QUICK, char* partitionLabel = (char*)FFAT_PARTITION_LABEL);
|
||||
size_t totalBytes();
|
||||
size_t freeBytes();
|
||||
void end();
|
||||
bool exists(const char* path);
|
||||
bool exists(const String& path);
|
||||
|
||||
private:
|
||||
wl_handle_t _wl_handle;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern fs::F_Fat FFat;
|
||||
|
||||
#endif /* _FFAT_H_ */
|
@ -0,0 +1,147 @@
|
||||
/**
|
||||
BasicHTTPSClient.ino
|
||||
|
||||
Created on: 14.10.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
// This is GandiStandardSSLCA2.pem, the root Certificate Authority that signed
|
||||
// the server certifcate for the demo server https://jigsaw.w3.org in this
|
||||
// example. This certificate is valid until Sep 11 23:59:59 2024 GMT
|
||||
const char* rootCACertificate = \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIF6TCCA9GgAwIBAgIQBeTcO5Q4qzuFl8umoZhQ4zANBgkqhkiG9w0BAQwFADCB\n" \
|
||||
"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \
|
||||
"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \
|
||||
"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQw\n" \
|
||||
"OTEyMDAwMDAwWhcNMjQwOTExMjM1OTU5WjBfMQswCQYDVQQGEwJGUjEOMAwGA1UE\n" \
|
||||
"CBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMQ4wDAYDVQQKEwVHYW5kaTEgMB4GA1UE\n" \
|
||||
"AxMXR2FuZGkgU3RhbmRhcmQgU1NMIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" \
|
||||
"DwAwggEKAoIBAQCUBC2meZV0/9UAPPWu2JSxKXzAjwsLibmCg5duNyj1ohrP0pIL\n" \
|
||||
"m6jTh5RzhBCf3DXLwi2SrCG5yzv8QMHBgyHwv/j2nPqcghDA0I5O5Q1MsJFckLSk\n" \
|
||||
"QFEW2uSEEi0FXKEfFxkkUap66uEHG4aNAXLy59SDIzme4OFMH2sio7QQZrDtgpbX\n" \
|
||||
"bmq08j+1QvzdirWrui0dOnWbMdw+naxb00ENbLAb9Tr1eeohovj0M1JLJC0epJmx\n" \
|
||||
"bUi8uBL+cnB89/sCdfSN3tbawKAyGlLfOGsuRTg/PwSWAP2h9KK71RfWJ3wbWFmV\n" \
|
||||
"XooS/ZyrgT5SKEhRhWvzkbKGPym1bgNi7tYFAgMBAAGjggF1MIIBcTAfBgNVHSME\n" \
|
||||
"GDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUs5Cn2MmvTs1hPJ98\n" \
|
||||
"rV1/Qf1pMOowDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD\n" \
|
||||
"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCIGA1UdIAQbMBkwDQYLKwYBBAGy\n" \
|
||||
"MQECAhowCAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNl\n" \
|
||||
"cnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNy\n" \
|
||||
"bDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRy\n" \
|
||||
"dXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZ\n" \
|
||||
"aHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAWGf9\n" \
|
||||
"crJq13xhlhl+2UNG0SZ9yFP6ZrBrLafTqlb3OojQO3LJUP33WbKqaPWMcwO7lWUX\n" \
|
||||
"zi8c3ZgTopHJ7qFAbjyY1lzzsiI8Le4bpOHeICQW8owRc5E69vrOJAKHypPstLbI\n" \
|
||||
"FhfFcvwnQPYT/pOmnVHvPCvYd1ebjGU6NSU2t7WKY28HJ5OxYI2A25bUeo8tqxyI\n" \
|
||||
"yW5+1mUfr13KFj8oRtygNeX56eXVlogMT8a3d2dIhCe2H7Bo26y/d7CQuKLJHDJd\n" \
|
||||
"ArolQ4FCR7vY4Y8MDEZf7kYzawMUgtN+zY+vkNaOJH1AQrRqahfGlZfh8jjNp+20\n" \
|
||||
"J0CT33KpuMZmYzc4ZCIwojvxuch7yPspOqsactIGEk72gtQjbz7Dk+XYtsDe3CMW\n" \
|
||||
"1hMwt6CaDixVBgBwAc/qOR2A24j3pSC4W/0xJmmPLQphgzpHphNULB7j7UTKvGof\n" \
|
||||
"KA5R2d4On3XNDgOVyvnFqSot/kGkoUeuDcL5OWYzSlvhhChZbH2UF3bkRYKtcCD9\n" \
|
||||
"0m9jqNf6oDP6N8v3smWe2lBvP+Sn845dWDKXcCMu5/3EFZucJ48y7RetWIExKREa\n" \
|
||||
"m9T8bJUox04FB6b9HbwZ4ui3uRGKLXASUoWNjDNKD/yZkuBjcNqllEdjB+dYxzFf\n" \
|
||||
"BT02Vf6Dsuimrdfp5gJ0iHRc2jTbkNJtUQoj1iM=\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
// Not sure if WiFiClientSecure checks the validity date of the certificate.
|
||||
// Setting clock just to be sure...
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Serial.print(F("Waiting for NTP time sync: "));
|
||||
time_t nowSecs = time(nullptr);
|
||||
while (nowSecs < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
Serial.print(F("."));
|
||||
yield();
|
||||
nowSecs = time(nullptr);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&nowSecs, &timeinfo);
|
||||
Serial.print(F("Current time: "));
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
// wait for WiFi connection
|
||||
Serial.print("Waiting for WiFi to connect...");
|
||||
while ((WiFiMulti.run() != WL_CONNECTED)) {
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println(" connected");
|
||||
|
||||
setClock();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
WiFiClientSecure *client = new WiFiClientSecure;
|
||||
if(client) {
|
||||
client -> setCACert(rootCACertificate);
|
||||
|
||||
{
|
||||
// Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is
|
||||
HTTPClient https;
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = https.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("[HTTPS] Unable to connect\n");
|
||||
}
|
||||
|
||||
// End extra scoping block
|
||||
}
|
||||
|
||||
delete client;
|
||||
} else {
|
||||
Serial.println("Unable to create client");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Waiting 10s before the next round...");
|
||||
delay(10000);
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*|----------------------------------------------------------|*/
|
||||
/*|WORKING EXAMPLE FOR HTTP/HTTPS CONNECTION |*/
|
||||
/*|TESTED BOARDS: Devkit v1 DOIT, Devkitc v4 |*/
|
||||
/*|CORE: June 2018 |*/
|
||||
/*|----------------------------------------------------------|*/
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "esp_wpa2.h"
|
||||
#include <Wire.h>
|
||||
#define EAP_IDENTITY "identity" //if connecting from another corporation, use identity@organisation.domain in Eduroam
|
||||
#define EAP_PASSWORD "password" //your Eduroam password
|
||||
const char* ssid = "eduroam"; // Eduroam SSID
|
||||
int counter = 0;
|
||||
const char* test_root_ca= \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
|
||||
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
|
||||
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
|
||||
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
|
||||
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
|
||||
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
|
||||
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
|
||||
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
|
||||
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
|
||||
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
|
||||
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
|
||||
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
|
||||
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
|
||||
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
Serial.println();
|
||||
Serial.print("Connecting to network: ");
|
||||
Serial.println(ssid);
|
||||
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
|
||||
WiFi.mode(WIFI_STA); //init wifi mode
|
||||
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
|
||||
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
|
||||
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
|
||||
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
|
||||
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
|
||||
WiFi.begin(ssid); //connect to wifi
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //after 30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address set: ");
|
||||
Serial.println(WiFi.localIP()); //print LAN IP
|
||||
}
|
||||
void loop() {
|
||||
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
|
||||
counter = 0; //reset counter
|
||||
Serial.println("Wifi is still connected with IP: ");
|
||||
Serial.println(WiFi.localIP()); //inform user about his IP address
|
||||
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.print("Connecting to website: ");
|
||||
HTTPClient http;
|
||||
http.begin("https://arduino.php5.sk/rele/rele1.txt", test_root_ca); //HTTPS example connection
|
||||
//http.begin("http://www.arduino.php5.sk/rele/rele1.txt"); //HTTP example connection
|
||||
//if uncomment HTTP example, you can comment root CA certificate too!
|
||||
int httpCode = http.GET();
|
||||
if(httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
//file found at server --> on unsucessful connection code will be -1
|
||||
if(httpCode == HTTP_CODE_OK) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
}else{
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
http.end();
|
||||
delay(2000);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
name=HTTPClient
|
||||
version=1.1
|
||||
version=1.2
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=http Client for ESP32
|
||||
|
@ -22,17 +22,23 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Adapted in October 2018
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
|
||||
#include <StreamString.h>
|
||||
#include <base64.h>
|
||||
|
||||
#include "HTTPClient.h"
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits
|
||||
{
|
||||
public:
|
||||
@ -78,6 +84,7 @@ protected:
|
||||
const char* _clicert;
|
||||
const char* _clikey;
|
||||
};
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
/**
|
||||
* constructor
|
||||
@ -91,8 +98,8 @@ HTTPClient::HTTPClient()
|
||||
*/
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
if(_tcp) {
|
||||
_tcp->stop();
|
||||
if(_client) {
|
||||
_client->stop();
|
||||
}
|
||||
if(_currentHeaders) {
|
||||
delete[] _currentHeaders;
|
||||
@ -107,9 +114,81 @@ void HTTPClient::clear()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* parsing the url for all needed parameters
|
||||
* @param client Client&
|
||||
* @param url String
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String url) {
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
#endif
|
||||
|
||||
_client = &client;
|
||||
|
||||
// check for : (http: or https:)
|
||||
int index = url.indexOf(':');
|
||||
if(index < 0) {
|
||||
log_d("failed to parse protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
String protocol = url.substring(0, index);
|
||||
if(protocol != "http" && protocol != "https") {
|
||||
log_d("unknown protocol '%s'", protocol.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_port = (protocol == "https" ? 443 : 80);
|
||||
return beginInternal(url, protocol.c_str());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* directly supply all needed parameters
|
||||
* @param client Client&
|
||||
* @param host String
|
||||
* @param port uint16_t
|
||||
* @param uri String
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https)
|
||||
{
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
#endif
|
||||
|
||||
_client = &client;
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
_uri = uri;
|
||||
_protocol = (https ? "https" : "http");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool HTTPClient::begin(String url, const char* CAcert)
|
||||
{
|
||||
_transportTraits.reset(nullptr);
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
_port = 443;
|
||||
if (!beginInternal(url, "https")) {
|
||||
return false;
|
||||
@ -125,8 +204,12 @@ bool HTTPClient::begin(String url, const char* CAcert)
|
||||
*/
|
||||
bool HTTPClient::begin(String url)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
_transportTraits.reset(nullptr);
|
||||
_port = 80;
|
||||
if (!beginInternal(url, "http")) {
|
||||
return begin(url, (const char*)NULL);
|
||||
@ -134,6 +217,7 @@ bool HTTPClient::begin(String url)
|
||||
_transportTraits = TransportTraitsPtr(new TransportTraits());
|
||||
return true;
|
||||
}
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
{
|
||||
@ -182,8 +266,15 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -195,6 +286,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri)
|
||||
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -210,6 +307,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
|
||||
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -222,37 +325,60 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
|
||||
_transportTraits = TransportTraitsPtr(new TLSTraits(CAcert, cli_cert, cli_key));
|
||||
return true;
|
||||
}
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
/**
|
||||
* end
|
||||
* called after the payload is handled
|
||||
*/
|
||||
void HTTPClient::end(void)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* disconnect
|
||||
* close the TCP socket
|
||||
*/
|
||||
void HTTPClient::disconnect()
|
||||
{
|
||||
if(connected()) {
|
||||
if(_tcp->available() > 0) {
|
||||
log_d("still data in buffer (%d), clean up.", _tcp->available());
|
||||
_tcp->flush();
|
||||
if(_client->available() > 0) {
|
||||
log_d("still data in buffer (%d), clean up.\n", _client->available());
|
||||
while(_client->available() > 0) {
|
||||
_client->read();
|
||||
}
|
||||
}
|
||||
|
||||
if(_reuse && _canReuse) {
|
||||
log_d("tcp keep open for reuse");
|
||||
log_d("tcp keep open for reuse\n");
|
||||
} else {
|
||||
log_d("tcp stop");
|
||||
_tcp->stop();
|
||||
log_d("tcp stop\n");
|
||||
_client->stop();
|
||||
_client = nullptr;
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
_transportTraits.reset(nullptr);
|
||||
_tcpDeprecated.reset(nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
log_v("tcp is closed");
|
||||
log_d("tcp is closed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* connected
|
||||
* @return connected status
|
||||
*/
|
||||
bool HTTPClient::connected()
|
||||
{
|
||||
if(_tcp) {
|
||||
return ((_tcp->available() > 0) || _tcp->connected());
|
||||
if(_client) {
|
||||
return ((_client->available() > 0) || _client->connected());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -309,8 +435,8 @@ void HTTPClient::setAuthorization(const char * auth)
|
||||
void HTTPClient::setTimeout(uint16_t timeout)
|
||||
{
|
||||
_tcpTimeout = timeout;
|
||||
if(connected() && !_secure) {
|
||||
_tcp->setTimeout(timeout);
|
||||
if(connected()) {
|
||||
_client->setTimeout((timeout + 500) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +524,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
|
||||
|
||||
// send Payload if needed
|
||||
if(payload && size > 0) {
|
||||
if(_tcp->write(&payload[0], size) != size) {
|
||||
if(_client->write(&payload[0], size) != size) {
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
@ -477,7 +603,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
int bytesRead = stream->readBytes(buff, readBytes);
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = _tcp->write((const uint8_t *) buff, bytesRead);
|
||||
int bytesWrite = _client->write((const uint8_t *) buff, bytesRead);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
// are all Bytes a writen to stream ?
|
||||
@ -485,11 +611,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
log_d("short write, asked for %d but got %d retry...", bytesRead, bytesWrite);
|
||||
|
||||
// check for write error
|
||||
if(_tcp->getWriteError()) {
|
||||
log_d("stream write error %d", _tcp->getWriteError());
|
||||
if(_client->getWriteError()) {
|
||||
log_d("stream write error %d", _client->getWriteError());
|
||||
|
||||
//reset write error for retry
|
||||
_tcp->clearWriteError();
|
||||
_client->clearWriteError();
|
||||
}
|
||||
|
||||
// some time for the stream
|
||||
@ -498,7 +624,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
int leftBytes = (readBytes - bytesWrite);
|
||||
|
||||
// retry to send the missed bytes
|
||||
bytesWrite = _tcp->write((const uint8_t *) (buff + bytesWrite), leftBytes);
|
||||
bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
if(bytesWrite != leftBytes) {
|
||||
@ -510,8 +636,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
}
|
||||
|
||||
// check for write error
|
||||
if(_tcp->getWriteError()) {
|
||||
log_d("stream write error %d", _tcp->getWriteError());
|
||||
if(_client->getWriteError()) {
|
||||
log_d("stream write error %d", _client->getWriteError());
|
||||
free(buff);
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
@ -561,8 +687,8 @@ int HTTPClient::getSize(void)
|
||||
*/
|
||||
WiFiClient& HTTPClient::getStream(void)
|
||||
{
|
||||
if (connected() && !_secure) {
|
||||
return *_tcp;
|
||||
if (connected()) {
|
||||
return *_client;
|
||||
}
|
||||
|
||||
log_w("getStream: not connected");
|
||||
@ -577,7 +703,7 @@ WiFiClient& HTTPClient::getStream(void)
|
||||
WiFiClient* HTTPClient::getStreamPtr(void)
|
||||
{
|
||||
if(connected()) {
|
||||
return _tcp.get();
|
||||
return _client;
|
||||
}
|
||||
|
||||
log_w("getStreamPtr: not connected");
|
||||
@ -617,7 +743,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
if(!connected()) {
|
||||
return returnError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
}
|
||||
String chunkHeader = _tcp->readStringUntil('\n');
|
||||
String chunkHeader = _client->readStringUntil('\n');
|
||||
|
||||
if(chunkHeader.length() <= 0) {
|
||||
return returnError(HTTPC_ERROR_READ_TIMEOUT);
|
||||
@ -654,7 +780,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
|
||||
// read trailing \r\n at the end of the chunk
|
||||
char buf[2];
|
||||
auto trailing_seq_len = _tcp->readBytes((uint8_t*)buf, 2);
|
||||
auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2);
|
||||
if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') {
|
||||
return returnError(HTTPC_ERROR_READ_TIMEOUT);
|
||||
}
|
||||
@ -822,38 +948,46 @@ bool HTTPClient::connect(void)
|
||||
|
||||
if(connected()) {
|
||||
log_d("already connected, try reuse!");
|
||||
while(_tcp->available() > 0) {
|
||||
_tcp->read();
|
||||
while(_client->available() > 0) {
|
||||
_client->read();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_transportTraits) {
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(!_client) {
|
||||
_tcpDeprecated = _transportTraits->create();
|
||||
_client = _tcpDeprecated.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_client) {
|
||||
log_d("HTTPClient::begin was not called or returned error");
|
||||
return false;
|
||||
}
|
||||
|
||||
_tcp = _transportTraits->create();
|
||||
|
||||
// set Timeout for WiFiClient and for Stream::readBytesUntil() and Stream::readStringUntil()
|
||||
_client->setTimeout((_tcpTimeout + 500) / 1000);
|
||||
|
||||
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
|
||||
log_d("transport level verify failed");
|
||||
_tcp->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_tcp->connect(_host.c_str(), _port)) {
|
||||
if(!_client->connect(_host.c_str(), _port)) {
|
||||
log_d("failed connect to %s:%u", _host.c_str(), _port);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_d(" connected to %s:%u", _host.c_str(), _port);
|
||||
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
setTimeout(_tcpTimeout);
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if (_tcpDeprecated && !_transportTraits->verify(*_client, _host.c_str())) {
|
||||
log_d("transport level verify failed");
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
#ifdef ESP8266
|
||||
_tcp->setNoDelay(true);
|
||||
_client->setNoDelay(true);
|
||||
#endif
|
||||
*/
|
||||
return connected();
|
||||
@ -907,7 +1041,7 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
|
||||
header += _headers + "\r\n";
|
||||
|
||||
return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length());
|
||||
return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -928,9 +1062,9 @@ int HTTPClient::handleHeaderResponse()
|
||||
unsigned long lastDataTime = millis();
|
||||
|
||||
while(connected()) {
|
||||
size_t len = _tcp->available();
|
||||
size_t len = _client->available();
|
||||
if(len > 0) {
|
||||
String headerLine = _tcp->readStringUntil('\n');
|
||||
String headerLine = _client->readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
lastDataTime = millis();
|
||||
@ -1026,7 +1160,7 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
|
||||
while(connected() && (len > 0 || len == -1)) {
|
||||
|
||||
// get available data size
|
||||
size_t sizeAvailable = _tcp->available();
|
||||
size_t sizeAvailable = _client->available();
|
||||
|
||||
if(sizeAvailable) {
|
||||
|
||||
@ -1041,9 +1175,13 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
|
||||
if(readBytes > buff_size) {
|
||||
readBytes = buff_size;
|
||||
}
|
||||
|
||||
// stop if no more reading
|
||||
if (readBytes == 0)
|
||||
break;
|
||||
|
||||
// read data
|
||||
int bytesRead = _tcp->readBytes(buff, readBytes);
|
||||
int bytesRead = _client->readBytes(buff, readBytes);
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = stream->write(buff, bytesRead);
|
||||
@ -1124,7 +1262,7 @@ int HTTPClient::returnError(int error)
|
||||
log_w("error(%d): %s", error, errorToString(error).c_str());
|
||||
if(connected()) {
|
||||
log_d("tcp stop");
|
||||
_tcp->stop();
|
||||
_client->stop();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
|
@ -27,6 +27,8 @@
|
||||
#ifndef HTTPClient_H_
|
||||
#define HTTPClient_H_
|
||||
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
@ -117,8 +119,10 @@ typedef enum {
|
||||
HTTPC_TE_CHUNKED
|
||||
} transferEncoding_t;
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
#endif
|
||||
|
||||
class HTTPClient
|
||||
{
|
||||
@ -126,11 +130,20 @@ 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);
|
||||
|
||||
@ -181,6 +194,7 @@ protected:
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char* expectedProtocol);
|
||||
void disconnect();
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
@ -189,8 +203,12 @@ protected:
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcp;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient* _client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
|
72
libraries/HTTPUpdate/examples/httpUpdate/httpUpdate.ino
Normal file
72
libraries/HTTPUpdate/examples/httpUpdate/httpUpdate.ino
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
httpUpdate.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, "http://server/file.bin");
|
||||
// Or:
|
||||
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("HTTP_UPDATE_NO_UPDATES");
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("HTTP_UPDATE_OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
httpUpdateSPIFFS.ino
|
||||
|
||||
Created on: 05.12.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
Serial.println("Update SPIFFS...");
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.updateSpiffs(client, "http://server/spiffs.bin");
|
||||
if (ret == HTTP_UPDATE_OK) {
|
||||
Serial.println("Update sketch...");
|
||||
ret = httpUpdate.update(client, "http://server/file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("HTTP_UPDATE_NO_UPDATES");
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("HTTP_UPDATE_OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
httpUpdateSecure.ino
|
||||
|
||||
Created on: 16.10.2018 as an adaptation of the ESP8266 version of httpUpdate.ino
|
||||
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
|
||||
|
||||
Serial.print(F("Waiting for NTP time sync: "));
|
||||
time_t now = time(nullptr);
|
||||
while (now < 8 * 3600 * 2) {
|
||||
yield();
|
||||
delay(500);
|
||||
Serial.print(F("."));
|
||||
now = time(nullptr);
|
||||
}
|
||||
|
||||
Serial.println(F(""));
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Serial.print(F("Current time: "));
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is lets-encrypt-x3-cross-signed.pem
|
||||
*/
|
||||
const char* rootCACertificate = \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
|
||||
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
|
||||
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
|
||||
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
|
||||
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
|
||||
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
|
||||
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
|
||||
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
|
||||
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
|
||||
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
|
||||
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
|
||||
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
|
||||
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
|
||||
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
|
||||
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
|
||||
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
|
||||
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
|
||||
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
|
||||
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
|
||||
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
|
||||
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
|
||||
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
|
||||
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
|
||||
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
|
||||
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
setClock();
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setCACert(rootCACertificate);
|
||||
|
||||
// Reading data over SSL may be slow, use an adequate timeout
|
||||
client.setTimeout(12000);
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, HIGH);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, "https://server/file.bin");
|
||||
// Or:
|
||||
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 443, "file.bin");
|
||||
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("HTTP_UPDATE_NO_UPDATES");
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("HTTP_UPDATE_OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
42
libraries/HTTPUpdate/keywords.txt
Normal file
42
libraries/HTTPUpdate/keywords.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESP8266httpUpdate
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
ESP8266httpUpdate KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HTTPUpdateResult KEYWORD1 DATA_TYPE
|
||||
ESPhttpUpdate KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
rebootOnUpdate KEYWORD2
|
||||
update KEYWORD2
|
||||
updateSpiffs KEYWORD2
|
||||
getLastError KEYWORD2
|
||||
getLastErrorString KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTP_UE_TOO_LESS_SPACE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_NOT_REPORT_SIZE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FILE_NOT_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FORBIDDEN LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_WRONG_HTTP_CODE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FAULTY_MD5 LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_VERIFY_HEADER_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_FOR_WRONG_FLASH LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_NO_UPDATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_OK LITERAL1 RESERVED_WORD_2
|
9
libraries/HTTPUpdate/library.properties
Normal file
9
libraries/HTTPUpdate/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=HTTPUpdate
|
||||
version=1.3
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=Http Update for ESP32
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=https://github.com/Links2004/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/ESP8266httpUpdate
|
||||
architectures=esp32
|
417
libraries/HTTPUpdate/src/HTTPUpdate.cpp
Normal file
417
libraries/HTTPUpdate/src/HTTPUpdate.cpp
Normal file
@ -0,0 +1,417 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.cpp based om ESP8266HTTPUpdate.cpp
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 Http Updater.
|
||||
*
|
||||
* 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 "HTTPUpdate.h"
|
||||
#include <StreamString.h>
|
||||
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h> // get running partition
|
||||
|
||||
// To do extern "C" uint32_t _SPIFFS_start;
|
||||
// To do extern "C" uint32_t _SPIFFS_end;
|
||||
|
||||
HTTPUpdate::HTTPUpdate(void)
|
||||
: _httpClientTimeout(8000), _ledPin(-1)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdate::HTTPUpdate(int httpClientTimeout)
|
||||
: _httpClientTimeout(httpClientTimeout), _ledPin(-1)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdate::~HTTPUpdate(void)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, url))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, url))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
|
||||
const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, host, port, uri))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* return error code as int
|
||||
* @return int error code
|
||||
*/
|
||||
int HTTPUpdate::getLastError(void)
|
||||
{
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
* return error code as String
|
||||
* @return String error
|
||||
*/
|
||||
String HTTPUpdate::getLastErrorString(void)
|
||||
{
|
||||
|
||||
if(_lastError == 0) {
|
||||
return String(); // no error
|
||||
}
|
||||
|
||||
// error from Update class
|
||||
if(_lastError > 0) {
|
||||
StreamString error;
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
return String("Update error: ") + error;
|
||||
}
|
||||
|
||||
// error from http client
|
||||
if(_lastError > -100) {
|
||||
return String("HTTP error: ") + HTTPClient::errorToString(_lastError);
|
||||
}
|
||||
|
||||
switch(_lastError) {
|
||||
case HTTP_UE_TOO_LESS_SPACE:
|
||||
return "Not Enough space";
|
||||
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
|
||||
return "Server Did Not Report Size";
|
||||
case HTTP_UE_SERVER_FILE_NOT_FOUND:
|
||||
return "File Not Found (404)";
|
||||
case HTTP_UE_SERVER_FORBIDDEN:
|
||||
return "Forbidden (403)";
|
||||
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
|
||||
return "Wrong HTTP Code";
|
||||
case HTTP_UE_SERVER_FAULTY_MD5:
|
||||
return "Wrong MD5";
|
||||
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
|
||||
return "Verify Bin Header Failed";
|
||||
case HTTP_UE_BIN_FOR_WRONG_FLASH:
|
||||
return "New Binary Does Not Fit Flash Size";
|
||||
case HTTP_UE_NO_PARTITION:
|
||||
return "Partition Could Not be Found";
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
|
||||
String getSketchSHA256() {
|
||||
const size_t HASH_LEN = 32; // SHA-256 digest length
|
||||
|
||||
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
|
||||
// get sha256 digest for running partition
|
||||
if(esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256) == 0) {
|
||||
char buffer[2 * HASH_LEN + 1];
|
||||
|
||||
for(size_t index = 0; index < HASH_LEN; index++) {
|
||||
uint8_t nibble = (sha_256[index] & 0xf0) >> 4;
|
||||
buffer[2 * index] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
|
||||
nibble = sha_256[index] & 0x0f;
|
||||
buffer[2 * index + 1] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
}
|
||||
|
||||
buffer[2 * HASH_LEN] = '\0';
|
||||
|
||||
return String(buffer);
|
||||
} else {
|
||||
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param http HTTPClient *
|
||||
* @param currentVersion const char *
|
||||
* @return HTTPUpdateResult
|
||||
*/
|
||||
HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs)
|
||||
{
|
||||
|
||||
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
|
||||
|
||||
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
|
||||
http.useHTTP10(true);
|
||||
http.setTimeout(_httpClientTimeout);
|
||||
http.setUserAgent("ESP32-http-Update");
|
||||
http.addHeader("Cache-Control", "no-cache");
|
||||
http.addHeader("x-ESP32-STA-MAC", WiFi.macAddress());
|
||||
http.addHeader("x-ESP32-AP-MAC", WiFi.softAPmacAddress());
|
||||
http.addHeader("x-ESP32-free-space", String(ESP.getFreeSketchSpace()));
|
||||
http.addHeader("x-ESP32-sketch-size", String(ESP.getSketchSize()));
|
||||
String sketchMD5 = ESP.getSketchMD5();
|
||||
if(sketchMD5.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-md5", sketchMD5);
|
||||
}
|
||||
// Add also a SHA256
|
||||
String sketchSHA256 = getSketchSHA256();
|
||||
if(sketchSHA256.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-sha256", sketchSHA256);
|
||||
}
|
||||
http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize()));
|
||||
http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion());
|
||||
|
||||
if(spiffs) {
|
||||
http.addHeader("x-ESP32-mode", "spiffs");
|
||||
} else {
|
||||
http.addHeader("x-ESP32-mode", "sketch");
|
||||
}
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
http.addHeader("x-ESP32-version", currentVersion);
|
||||
}
|
||||
|
||||
const char * headerkeys[] = { "x-MD5" };
|
||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
|
||||
|
||||
// track these headers
|
||||
http.collectHeaders(headerkeys, headerkeyssize);
|
||||
|
||||
|
||||
int code = http.GET();
|
||||
int len = http.getSize();
|
||||
|
||||
if(code <= 0) {
|
||||
log_e("HTTP error: %s\n", http.errorToString(code).c_str());
|
||||
_lastError = code;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
log_d("Header read fin.\n");
|
||||
log_d("Server header:\n");
|
||||
log_d(" - code: %d\n", code);
|
||||
log_d(" - len: %d\n", len);
|
||||
|
||||
if(http.hasHeader("x-MD5")) {
|
||||
log_d(" - MD5: %s\n", http.header("x-MD5").c_str());
|
||||
}
|
||||
|
||||
log_d("ESP32 info:\n");
|
||||
log_d(" - free Space: %d\n", ESP.getFreeSketchSpace());
|
||||
log_d(" - current Sketch Size: %d\n", ESP.getSketchSize());
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
log_d(" - current version: %s\n", currentVersion.c_str() );
|
||||
}
|
||||
|
||||
switch(code) {
|
||||
case HTTP_CODE_OK: ///< OK (Start Update)
|
||||
if(len > 0) {
|
||||
bool startUpdate = true;
|
||||
if(spiffs) {
|
||||
const esp_partition_t* _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
|
||||
if(!_partition){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > _partition->size) {
|
||||
log_e("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
} else {
|
||||
int sketchFreeSpace = ESP.getFreeSketchSpace();
|
||||
if(!sketchFreeSpace){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > sketchFreeSpace) {
|
||||
log_e("FreeSketchSpace to low (%d) needed: %d\n", sketchFreeSpace, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!startUpdate) {
|
||||
_lastError = HTTP_UE_TOO_LESS_SPACE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
} else {
|
||||
|
||||
WiFiClient * tcp = http.getStreamPtr();
|
||||
|
||||
// To do? WiFiUDP::stopAll();
|
||||
// To do? WiFiClient::stopAllExcept(tcp);
|
||||
|
||||
delay(100);
|
||||
|
||||
int command;
|
||||
|
||||
if(spiffs) {
|
||||
command = U_SPIFFS;
|
||||
log_d("runUpdate spiffs...\n");
|
||||
} else {
|
||||
command = U_FLASH;
|
||||
log_d("runUpdate flash...\n");
|
||||
}
|
||||
|
||||
if(!spiffs) {
|
||||
/* To do
|
||||
uint8_t buf[4];
|
||||
if(tcp->peekBytes(&buf[0], 4) != 4) {
|
||||
log_e("peekBytes magic header failed\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
|
||||
// check for valid first magic byte
|
||||
// if(buf[0] != 0xE9) {
|
||||
if(tcp->peek() != 0xE9) {
|
||||
log_e("Magic header does not start with 0xE9\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
|
||||
}
|
||||
/* To do
|
||||
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
||||
|
||||
// check if new bin fits to SPI flash
|
||||
if(bin_flash_size > ESP.getFlashChipRealSize()) {
|
||||
log_e("New binary does not fit SPI Flash size\n");
|
||||
_lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
}
|
||||
if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
log_d("Update ok\n");
|
||||
http.end();
|
||||
|
||||
if(_rebootOnUpdate && !spiffs) {
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("Update failed\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("Content-Length was 0 or wasn't set by Server?!\n");
|
||||
}
|
||||
break;
|
||||
case HTTP_CODE_NOT_MODIFIED:
|
||||
///< Not Modified (No updates)
|
||||
ret = HTTP_UPDATE_NO_UPDATES;
|
||||
break;
|
||||
case HTTP_CODE_NOT_FOUND:
|
||||
_lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_FORBIDDEN:
|
||||
_lastError = HTTP_UE_SERVER_FORBIDDEN;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
default:
|
||||
_lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("HTTP Code is (%d)\n", code);
|
||||
break;
|
||||
}
|
||||
|
||||
http.end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* write Update to flash
|
||||
* @param in Stream&
|
||||
* @param size uint32_t
|
||||
* @param md5 String
|
||||
* @return true if Update ok
|
||||
*/
|
||||
bool HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command)
|
||||
{
|
||||
|
||||
StreamString error;
|
||||
|
||||
if(!Update.begin(size, command, _ledPin, _ledOn)) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(md5.length()) {
|
||||
if(!Update.setMD5(md5.c_str())) {
|
||||
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
|
||||
log_e("Update.setMD5 failed! (%s)\n", md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// To do: the SHA256 could be checked if the server sends it
|
||||
|
||||
if(Update.writeStream(in) != size) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Update.end()) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
HTTPUpdate httpUpdate;
|
||||
#endif
|
101
libraries/HTTPUpdate/src/HTTPUpdate.h
Normal file
101
libraries/HTTPUpdate/src/HTTPUpdate.h
Normal file
@ -0,0 +1,101 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.h based on ESP8266HTTPUpdate.h
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 Http Updater.
|
||||
*
|
||||
* 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 ___HTTP_UPDATE_H___
|
||||
#define ___HTTP_UPDATE_H___
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Update.h>
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
#define HTTP_UE_TOO_LESS_SPACE (-100)
|
||||
#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101)
|
||||
#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102)
|
||||
#define HTTP_UE_SERVER_FORBIDDEN (-103)
|
||||
#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104)
|
||||
#define HTTP_UE_SERVER_FAULTY_MD5 (-105)
|
||||
#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106)
|
||||
#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107)
|
||||
#define HTTP_UE_NO_PARTITION (-108)
|
||||
|
||||
enum HTTPUpdateResult {
|
||||
HTTP_UPDATE_FAILED,
|
||||
HTTP_UPDATE_NO_UPDATES,
|
||||
HTTP_UPDATE_OK
|
||||
};
|
||||
|
||||
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
|
||||
|
||||
class HTTPUpdate
|
||||
{
|
||||
public:
|
||||
HTTPUpdate(void);
|
||||
HTTPUpdate(int httpClientTimeout);
|
||||
~HTTPUpdate(void);
|
||||
|
||||
void rebootOnUpdate(bool reboot)
|
||||
{
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
void setLedPin(int ledPin = -1, uint8_t ledOn = HIGH)
|
||||
{
|
||||
_ledPin = ledPin;
|
||||
_ledOn = ledOn;
|
||||
}
|
||||
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
||||
const String& currentVersion = "");
|
||||
|
||||
t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
|
||||
int getLastError(void);
|
||||
String getLastErrorString(void);
|
||||
|
||||
protected:
|
||||
t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
|
||||
bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH);
|
||||
|
||||
int _lastError;
|
||||
bool _rebootOnUpdate = true;
|
||||
private:
|
||||
int _httpClientTimeout;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
extern HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
|
||||
#endif /* ___HTTP_UPDATE_H___ */
|
@ -468,3 +468,13 @@ size_t Preferences::getBytes(const char* key, void * buf, size_t maxLen){
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Preferences::freeEntries() {
|
||||
nvs_stats_t nvs_stats;
|
||||
esp_err_t err = nvs_get_stats(NULL, &nvs_stats);
|
||||
if(err){
|
||||
log_e("Failed to get nvs statistics");
|
||||
return 0;
|
||||
}
|
||||
return nvs_stats.free_entries;
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ class Preferences {
|
||||
size_t getString(const char* key, char* value, size_t maxLen);
|
||||
String getString(const char* key, String defaultValue = String());
|
||||
size_t getBytes(const char* key, void * buf, size_t maxLen);
|
||||
size_t freeEntries();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@ extern "C" {
|
||||
#include "diskio.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
#include "esp_vfs.h"
|
||||
//#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
char CRC7(const char* data, int length);
|
||||
unsigned short CRC16(const char* data, int length);
|
||||
|
@ -50,12 +50,13 @@ bool SDMMCFS::begin(const char * mountpoint, bool mode1bit)
|
||||
.init = &sdmmc_host_init,
|
||||
.set_bus_width = &sdmmc_host_set_bus_width,
|
||||
.get_bus_width = &sdmmc_host_get_slot_width,
|
||||
.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode,
|
||||
.set_card_clk = &sdmmc_host_set_card_clk,
|
||||
.do_transaction = &sdmmc_host_do_transaction,
|
||||
.deinit = &sdmmc_host_deinit,
|
||||
.io_int_enable = sdmmc_host_io_int_enable,
|
||||
.io_int_wait = sdmmc_host_io_int_wait,
|
||||
.command_timeout_ms = 0,
|
||||
.io_int_enable = &sdmmc_host_io_int_enable,
|
||||
.io_int_wait = &sdmmc_host_io_int_wait,
|
||||
.command_timeout_ms = 0
|
||||
};
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#ifdef BOARD_HAS_1BIT_SDMMC
|
||||
|
@ -214,6 +214,11 @@ void SPIClass::writeBytes(uint8_t * data, uint32_t size)
|
||||
spiEndTransaction(_spi);
|
||||
}
|
||||
|
||||
void SPIClass::transfer(uint8_t * data, uint32_t size)
|
||||
{
|
||||
transferBytes(data, data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data void *
|
||||
* @param size uint32_t
|
||||
|
@ -65,10 +65,11 @@ public:
|
||||
|
||||
void beginTransaction(SPISettings settings);
|
||||
void endTransaction(void);
|
||||
|
||||
void transfer(uint8_t * data, uint32_t size);
|
||||
uint8_t transfer(uint8_t data);
|
||||
uint16_t transfer16(uint16_t data);
|
||||
uint32_t transfer32(uint32_t data);
|
||||
|
||||
void transferBytes(uint8_t * data, uint8_t * out, uint32_t size);
|
||||
void transferBits(uint32_t data, uint32_t * out, uint8_t bits);
|
||||
|
||||
|
@ -41,7 +41,7 @@ class UpdateClass {
|
||||
Call this to check the space needed for the update
|
||||
Will return false if there is not enough space
|
||||
*/
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW);
|
||||
|
||||
/*
|
||||
Writes a buffer to the flash and increments the address
|
||||
@ -174,6 +174,9 @@ class UpdateClass {
|
||||
|
||||
String _target_md5;
|
||||
MD5Builder _md5;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
extern UpdateClass Update;
|
||||
|
@ -88,6 +88,10 @@ void UpdateClass::_reset() {
|
||||
_progress = 0;
|
||||
_size = 0;
|
||||
_command = U_FLASH;
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // off
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateClass::canRollBack(){
|
||||
@ -106,12 +110,15 @@ bool UpdateClass::rollBack(){
|
||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||
}
|
||||
|
||||
bool UpdateClass::begin(size_t size, int command) {
|
||||
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
if(_size > 0){
|
||||
log_w("already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
_ledPin = ledPin;
|
||||
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||
|
||||
_reset();
|
||||
_error = 0;
|
||||
|
||||
@ -315,16 +322,32 @@ size_t UpdateClass::writeStream(Stream &data) {
|
||||
if (_progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
while(remaining()) {
|
||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||
}
|
||||
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
if(bytesToRead > remaining()) {
|
||||
bytesToRead = remaining();
|
||||
}
|
||||
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
delay(100);
|
||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
_abort(UPDATE_ERROR_STREAM);
|
||||
return written;
|
||||
}
|
||||
}
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||
}
|
||||
_bufferLen += toRead;
|
||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||
return written;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
FSWebServer - Example WebServer with SPIFFS backend for esp8266
|
||||
FSWebServer - Example WebServer with FS backend for esp8266/esp32
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the WebServer library for Arduino environment.
|
||||
|
||||
@ -26,14 +26,21 @@
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <SPIFFS.h>
|
||||
|
||||
#define FILESYSTEM SPIFFS
|
||||
#define FORMAT_FILESYSTEM true
|
||||
#define DBG_OUTPUT_PORT Serial
|
||||
|
||||
#if FILESYSTEM == FFat
|
||||
#include <FFat.h>
|
||||
#endif
|
||||
#if FILESYSTEM == SPIFFS
|
||||
#include <SPIFFS.h>
|
||||
#endif
|
||||
|
||||
const char* ssid = "wifi-ssid";
|
||||
const char* password = "wifi-password";
|
||||
const char* host = "esp32fs";
|
||||
|
||||
WebServer server(80);
|
||||
//holds the current upload
|
||||
File fsUploadFile;
|
||||
@ -84,7 +91,7 @@ String getContentType(String filename) {
|
||||
|
||||
bool exists(String path){
|
||||
bool yes = false;
|
||||
File file = SPIFFS.open(path, "r");
|
||||
File file = FILESYSTEM.open(path, "r");
|
||||
if(!file.isDirectory()){
|
||||
yes = true;
|
||||
}
|
||||
@ -103,7 +110,7 @@ bool handleFileRead(String path) {
|
||||
if (exists(pathWithGz)) {
|
||||
path += ".gz";
|
||||
}
|
||||
File file = SPIFFS.open(path, "r");
|
||||
File file = FILESYSTEM.open(path, "r");
|
||||
server.streamFile(file, contentType);
|
||||
file.close();
|
||||
return true;
|
||||
@ -122,7 +129,7 @@ void handleFileUpload() {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
|
||||
fsUploadFile = SPIFFS.open(filename, "w");
|
||||
fsUploadFile = FILESYSTEM.open(filename, "w");
|
||||
filename = String();
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
|
||||
@ -149,7 +156,7 @@ void handleFileDelete() {
|
||||
if (!exists(path)) {
|
||||
return server.send(404, "text/plain", "FileNotFound");
|
||||
}
|
||||
SPIFFS.remove(path);
|
||||
FILESYSTEM.remove(path);
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
}
|
||||
@ -166,7 +173,7 @@ void handleFileCreate() {
|
||||
if (exists(path)) {
|
||||
return server.send(500, "text/plain", "FILE EXISTS");
|
||||
}
|
||||
File file = SPIFFS.open(path, "w");
|
||||
File file = FILESYSTEM.open(path, "w");
|
||||
if (file) {
|
||||
file.close();
|
||||
} else {
|
||||
@ -186,7 +193,7 @@ void handleFileList() {
|
||||
DBG_OUTPUT_PORT.println("handleFileList: " + path);
|
||||
|
||||
|
||||
File root = SPIFFS.open(path);
|
||||
File root = FILESYSTEM.open(path);
|
||||
path = String();
|
||||
|
||||
String output = "[";
|
||||
@ -212,9 +219,10 @@ void setup(void) {
|
||||
DBG_OUTPUT_PORT.begin(115200);
|
||||
DBG_OUTPUT_PORT.print("\n");
|
||||
DBG_OUTPUT_PORT.setDebugOutput(true);
|
||||
SPIFFS.begin();
|
||||
if (FORMAT_FILESYSTEM) FILESYSTEM.format();
|
||||
FILESYSTEM.begin();
|
||||
{
|
||||
File root = SPIFFS.open("/");
|
||||
File root = FILESYSTEM.open("/");
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
String fileName = file.name();
|
||||
@ -267,7 +275,7 @@ void setup(void) {
|
||||
}, handleFileUpload);
|
||||
|
||||
//called when the url is not defined here
|
||||
//use it to load content from SPIFFS
|
||||
//use it to load content from FILESYSTEM
|
||||
server.onNotFound([]() {
|
||||
if (!handleFileRead(server.uri())) {
|
||||
server.send(404, "text/plain", "FileNotFound");
|
||||
|
53
libraries/WebServer/examples/PathArgServer/PathArgServer.ino
Normal file
53
libraries/WebServer/examples/PathArgServer/PathArgServer.ino
Normal file
@ -0,0 +1,53 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
const char *ssid = "........";
|
||||
const char *password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp32")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", []() {
|
||||
server.send(200, "text/plain", "hello from esp32!");
|
||||
});
|
||||
|
||||
server.on("/users/{}", []() {
|
||||
String user = server.pathArg(0);
|
||||
server.send(200, "text/plain", "User: '" + user + "'");
|
||||
});
|
||||
|
||||
server.on("/users/{}/devices/{}", []() {
|
||||
String user = server.pathArg(0);
|
||||
String device = server.pathArg(1);
|
||||
server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
@ -509,6 +509,11 @@ void WebServer::_streamFileCore(const size_t fileSize, const String & fileName,
|
||||
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) {
|
||||
|
@ -97,6 +97,7 @@ public:
|
||||
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
|
||||
|
@ -1,6 +1,9 @@
|
||||
#ifndef REQUESTHANDLER_H
|
||||
#define REQUESTHANDLER_H
|
||||
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
virtual ~RequestHandler() { }
|
||||
@ -14,6 +17,15 @@ public:
|
||||
|
||||
private:
|
||||
RequestHandler* _next = nullptr;
|
||||
|
||||
protected:
|
||||
std::vector<String> pathArgs;
|
||||
|
||||
public:
|
||||
const String& pathArg(unsigned int i) {
|
||||
assert(i < pathArgs.size());
|
||||
return pathArgs[i];
|
||||
}
|
||||
};
|
||||
|
||||
#endif //REQUESTHANDLER_H
|
||||
|
@ -15,16 +15,55 @@ public:
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
{
|
||||
int numParams = 0, start = 0;
|
||||
do {
|
||||
start = _uri.indexOf("{}", start);
|
||||
if (start > 0) {
|
||||
numParams++;
|
||||
start += 2;
|
||||
}
|
||||
} while (start > 0);
|
||||
pathArgs.resize(numParams);
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
if (_uri == requestUri)
|
||||
return true;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
|
93
libraries/WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino
Normal file
93
libraries/WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it.
|
||||
|
||||
Steps:
|
||||
1. Connect to the access point "yourAp"
|
||||
2. Point your web browser to http://192.168.4.1/H to turn the LED on or http://192.168.4.1/L to turn it off
|
||||
OR
|
||||
Run raw TCP "GET /H" and "GET /L" on PuTTY terminal with 192.168.4.1 as IP address and 80 as port
|
||||
|
||||
Created for arduino-esp32 on 04 July, 2018
|
||||
by Elochukwu Ifediora (fedy0)
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiAP.h>
|
||||
|
||||
#define LED_BUILTIN 2 // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
|
||||
|
||||
// Set these to your desired credentials.
|
||||
const char *ssid = "yourAP";
|
||||
const char *password = "yourPassword";
|
||||
|
||||
WiFiServer server(80);
|
||||
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
|
||||
// You can remove the password parameter if you want the AP to be open.
|
||||
WiFi.softAP(ssid, password);
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(myIP);
|
||||
server.begin();
|
||||
|
||||
Serial.println("Server started");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
WiFiClient client = server.available(); // listen for incoming clients
|
||||
|
||||
if (client) { // if you get a client,
|
||||
Serial.println("New Client."); // print a message out the serial port
|
||||
String currentLine = ""; // make a String to hold incoming data from the client
|
||||
while (client.connected()) { // loop while the client's connected
|
||||
if (client.available()) { // if there's bytes to read from the client,
|
||||
char c = client.read(); // read a byte, then
|
||||
Serial.write(c); // print it out the serial monitor
|
||||
if (c == '\n') { // if the byte is a newline character
|
||||
|
||||
// if the current line is blank, you got two newline characters in a row.
|
||||
// that's the end of the client HTTP request, so send a response:
|
||||
if (currentLine.length() == 0) {
|
||||
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
|
||||
// and a content-type so the client knows what's coming, then a blank line:
|
||||
client.println("HTTP/1.1 200 OK");
|
||||
client.println("Content-type:text/html");
|
||||
client.println();
|
||||
|
||||
// the content of the HTTP response follows the header:
|
||||
client.print("Click <a href=\"/H\">here</a> to turn ON the LED.<br>");
|
||||
client.print("Click <a href=\"/L\">here</a> to turn OFF the LED.<br>");
|
||||
|
||||
// The HTTP response ends with another blank line:
|
||||
client.println();
|
||||
// break out of the while loop:
|
||||
break;
|
||||
} else { // if you got a newline, then clear currentLine:
|
||||
currentLine = "";
|
||||
}
|
||||
} else if (c != '\r') { // if you got anything else but a carriage return character,
|
||||
currentLine += c; // add it to the end of the currentLine
|
||||
}
|
||||
|
||||
// Check to see if the client request was "GET /H" or "GET /L":
|
||||
if (currentLine.endsWith("GET /H")) {
|
||||
digitalWrite(LED_BUILTIN, HIGH); // GET /H turns the LED on
|
||||
}
|
||||
if (currentLine.endsWith("GET /L")) {
|
||||
digitalWrite(LED_BUILTIN, LOW); // GET /L turns the LED off
|
||||
}
|
||||
}
|
||||
}
|
||||
// close the connection:
|
||||
client.stop();
|
||||
Serial.println("Client Disconnected.");
|
||||
}
|
||||
}
|
43
libraries/WiFi/examples/WiFiClientEnterprise/README.md
Normal file
43
libraries/WiFi/examples/WiFiClientEnterprise/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# ESP32-Eduroam
|
||||
* Eduroam wifi connection with university login identity
|
||||
* Working under Eduroam networks worldwide
|
||||
* Methods: PEAP + MsCHAPv2
|
||||
|
||||
# Format
|
||||
* IDENTITY = youridentity --> if connecting from different university, use youridentity@youruniversity.domain format
|
||||
* PASSWORD = yourpassword
|
||||
|
||||
# Usage
|
||||
* Change IDENTITY
|
||||
* Change password
|
||||
* Upload sketch and enjoy!
|
||||
* After sucessful assign of IP address, board will connect to HTTP page on internet to verify your authentification
|
||||
* Board will auto reconnect to Eduroam if it lost connection
|
||||
|
||||
# Tested locations
|
||||
|University|Board|Method|Result|
|
||||
|-------------|-------------| -----|------|
|
||||
|Technical University in Košice (Slovakia)|ESP32 Devkit v1|PEAP + MsCHAPv2|Working|
|
||||
|Technical University in Košice (Slovakia)|ESP32 Devmodule v4|PEAP + MsCHAPv2|Working on 6th attempt in loop|
|
||||
|Slovak Technical University in Bratislava (Slovakia)|ESP32 Devkit v1|PEAP + MsCHAPv2|Working|
|
||||
|University of Antwerp (Belgium)|Lolin32|PEAP + MsCHAPv2|Working|
|
||||
|UPV Universitat Politècnica de València (Spain)|ESP32 Devmodule v4|PEAP + MsCHAPv2|Working|
|
||||
|Local Zeroshell powered network|ESP32 Devkit v1|PEAP + MsCHAPv2|*Not working*|
|
||||
|Hasselt University (Belgium)|xxx|PEAP + MsCHAPv2|Working with fix sketch|
|
||||
|Universidad de Granada (Spain)|Lolin D32 Pro|PEAP + MsCHAPv2|Working|
|
||||
|Universidad de Granada (Spain)|Lolin D32|PEAP + MsCHAPv2|Working|
|
||||
|Universidade Federal de Santa Catarina (Brazil)|xxx|EAP-TTLS + MsCHAPv2|Working|
|
||||
|University of Central Florida (Orlando, Florida)|ESP32 Built-in OLED – Heltec WiFi Kit 32|PEAP + MsCHAPv2|Working|
|
||||
|Université de Montpellier (France)|NodeMCU-32S|PEAP + MsCHAPv2|Working|
|
||||
|
||||
# Common errors - Switch to Debug mode for Serial monitor prints
|
||||
|Error|Appearance|Solution|
|
||||
|-------------|-------------|-------------|
|
||||
|wifi: Set status to INIT|Frequent|Hold EN button for few seconds|
|
||||
|HANDSHAKE_TIMEOUT|Rare|Bug was found under Zeroshell RADIUS authentization - Unsucessful connection|
|
||||
|AUTH_EXPIRE|Common|In the case of weak wifi network signal, this error is quite common, bring your device closer to AP|
|
||||
|ASSOC_EXPIRE|Rare|-|
|
||||
# Sucessful connection example
|
||||

|
||||
# Unsucessful connection example
|
||||

|
@ -1,12 +1,10 @@
|
||||
//Sketch edited by: Martin Chlebovec
|
||||
//Personal website: https://arduino.php5.sk
|
||||
#include "esp_wpa2.h"
|
||||
#include <WiFi.h>
|
||||
#define EAP_IDENTITY "login@university.domain" //eduroam login --> identity@youruniversity.domain
|
||||
#include <WiFi.h> //Wifi library
|
||||
#include "esp_wpa2.h" //wpa2 library for connections to Enterprise networks
|
||||
#define EAP_IDENTITY "login" //if connecting from another corporation, use identity@organisation.domain in Eduroam
|
||||
#define EAP_PASSWORD "password" //your Eduroam password
|
||||
String line; //variable for response
|
||||
const char* ssid = "eduroam"; // Eduroam SSID
|
||||
const char* host = "arduino.php5.sk"; //external server domain for HTTP connection after authentification
|
||||
int counter = 0;
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
@ -14,50 +12,58 @@ void setup() {
|
||||
Serial.print("Connecting to network: ");
|
||||
Serial.println(ssid);
|
||||
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
|
||||
WiFi.mode(WIFI_STA); //init wifi mode
|
||||
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
|
||||
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username
|
||||
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
|
||||
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
|
||||
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config to default (fixed for 2018 and Arduino 1.8.5+)
|
||||
esp_wifi_sta_wpa2_ent_enable(&config); //set config to enable function (fixed for 2018 and Arduino 1.8.5+)
|
||||
WiFi.begin(ssid); //connect to Eduroam function
|
||||
WiFi.setHostname("ESP32Name"); //set Hostname for your device - not neccesary
|
||||
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
|
||||
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
|
||||
WiFi.begin(ssid); //connect to wifi
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //after 30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address set: ");
|
||||
Serial.println(WiFi.localIP()); //print LAN IP
|
||||
|
||||
}
|
||||
void loop() {
|
||||
delay(5000);
|
||||
if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
|
||||
WiFi.begin(ssid);
|
||||
delay(500);
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
|
||||
counter = 0; //reset counter
|
||||
Serial.println("Wifi is still connected with IP: ");
|
||||
Serial.println(WiFi.localIP()); //inform user about his IP address
|
||||
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.print("Connecting to website: ");
|
||||
Serial.println(host);
|
||||
WiFiClient client;
|
||||
if (!client.connect(host, 80)) { // HTTP connection on port 80
|
||||
Serial.println("Connection lost! - Failed response");
|
||||
}
|
||||
String url = "/rele/rele1.txt"; //read .txt file
|
||||
Serial.print("Requesting URL: ");
|
||||
Serial.println(url);
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
|
||||
unsigned long timeout = millis();
|
||||
while (client.available() == 0) {
|
||||
if (millis() - timeout > 5000) {
|
||||
Serial.println("Client timed out! - retry");
|
||||
if (client.connect(host, 80)) {
|
||||
String url = "/rele/rele1.txt";
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: ESP32\r\n" + "Connection: close\r\n\r\n");
|
||||
|
||||
while (client.connected()) {
|
||||
String line = client.readStringUntil('\n');
|
||||
if (line == "\r") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(client.available()) {
|
||||
line = client.readStringUntil('\n');
|
||||
Serial.println(line);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("End connection");
|
||||
client.stop();
|
||||
String line = client.readStringUntil('\n');
|
||||
Serial.println(line);
|
||||
}else{
|
||||
Serial.println("Connection unsucessful");
|
||||
}
|
||||
}
|
||||
|
@ -3,34 +3,35 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* WiFi Events
|
||||
|
||||
SYSTEM_EVENT_WIFI_READY < ESP32 WiFi ready
|
||||
SYSTEM_EVENT_SCAN_DONE < ESP32 finish scanning AP
|
||||
SYSTEM_EVENT_STA_START < ESP32 station start
|
||||
SYSTEM_EVENT_STA_STOP < ESP32 station stop
|
||||
SYSTEM_EVENT_STA_CONNECTED < ESP32 station connected to AP
|
||||
SYSTEM_EVENT_STA_DISCONNECTED < ESP32 station disconnected from AP
|
||||
SYSTEM_EVENT_STA_AUTHMODE_CHANGE < the auth mode of AP connected by ESP32 station changed
|
||||
SYSTEM_EVENT_STA_GOT_IP < ESP32 station got IP from connected AP
|
||||
SYSTEM_EVENT_STA_LOST_IP < ESP32 station lost IP and the IP is reset to 0
|
||||
SYSTEM_EVENT_STA_WPS_ER_SUCCESS < ESP32 station wps succeeds in enrollee mode
|
||||
SYSTEM_EVENT_STA_WPS_ER_FAILED < ESP32 station wps fails in enrollee mode
|
||||
SYSTEM_EVENT_STA_WPS_ER_TIMEOUT < ESP32 station wps timeout in enrollee mode
|
||||
SYSTEM_EVENT_STA_WPS_ER_PIN < ESP32 station wps pin code in enrollee mode
|
||||
SYSTEM_EVENT_AP_START < ESP32 soft-AP start
|
||||
SYSTEM_EVENT_AP_STOP < ESP32 soft-AP stop
|
||||
SYSTEM_EVENT_AP_STACONNECTED < a station connected to ESP32 soft-AP
|
||||
SYSTEM_EVENT_AP_STADISCONNECTED < a station disconnected from ESP32 soft-AP
|
||||
SYSTEM_EVENT_AP_PROBEREQRECVED < Receive probe request packet in soft-AP interface
|
||||
SYSTEM_EVENT_GOT_IP6 < ESP32 station or ap or ethernet interface v6IP addr is preferred
|
||||
SYSTEM_EVENT_ETH_START < ESP32 ethernet start
|
||||
SYSTEM_EVENT_ETH_STOP < ESP32 ethernet stop
|
||||
SYSTEM_EVENT_ETH_CONNECTED < ESP32 ethernet phy link up
|
||||
SYSTEM_EVENT_ETH_DISCONNECTED < ESP32 ethernet phy link down
|
||||
SYSTEM_EVENT_ETH_GOT_IP < ESP32 ethernet got IP from connected AP
|
||||
SYSTEM_EVENT_MAX
|
||||
0 SYSTEM_EVENT_WIFI_READY < ESP32 WiFi ready
|
||||
1 SYSTEM_EVENT_SCAN_DONE < ESP32 finish scanning AP
|
||||
2 SYSTEM_EVENT_STA_START < ESP32 station start
|
||||
3 SYSTEM_EVENT_STA_STOP < ESP32 station stop
|
||||
4 SYSTEM_EVENT_STA_CONNECTED < ESP32 station connected to AP
|
||||
5 SYSTEM_EVENT_STA_DISCONNECTED < ESP32 station disconnected from AP
|
||||
6 SYSTEM_EVENT_STA_AUTHMODE_CHANGE < the auth mode of AP connected by ESP32 station changed
|
||||
7 SYSTEM_EVENT_STA_GOT_IP < ESP32 station got IP from connected AP
|
||||
8 SYSTEM_EVENT_STA_LOST_IP < ESP32 station lost IP and the IP is reset to 0
|
||||
9 SYSTEM_EVENT_STA_WPS_ER_SUCCESS < ESP32 station wps succeeds in enrollee mode
|
||||
10 SYSTEM_EVENT_STA_WPS_ER_FAILED < ESP32 station wps fails in enrollee mode
|
||||
11 SYSTEM_EVENT_STA_WPS_ER_TIMEOUT < ESP32 station wps timeout in enrollee mode
|
||||
12 SYSTEM_EVENT_STA_WPS_ER_PIN < ESP32 station wps pin code in enrollee mode
|
||||
13 SYSTEM_EVENT_AP_START < ESP32 soft-AP start
|
||||
14 SYSTEM_EVENT_AP_STOP < ESP32 soft-AP stop
|
||||
15 SYSTEM_EVENT_AP_STACONNECTED < a station connected to ESP32 soft-AP
|
||||
16 SYSTEM_EVENT_AP_STADISCONNECTED < a station disconnected from ESP32 soft-AP
|
||||
17 SYSTEM_EVENT_AP_STAIPASSIGNED < ESP32 soft-AP assign an IP to a connected station
|
||||
18 SYSTEM_EVENT_AP_PROBEREQRECVED < Receive probe request packet in soft-AP interface
|
||||
19 SYSTEM_EVENT_GOT_IP6 < ESP32 station or ap or ethernet interface v6IP addr is preferred
|
||||
20 SYSTEM_EVENT_ETH_START < ESP32 ethernet start
|
||||
21 SYSTEM_EVENT_ETH_STOP < ESP32 ethernet stop
|
||||
22 SYSTEM_EVENT_ETH_CONNECTED < ESP32 ethernet phy link up
|
||||
23 SYSTEM_EVENT_ETH_DISCONNECTED < ESP32 ethernet phy link down
|
||||
24 SYSTEM_EVENT_ETH_GOT_IP < ESP32 ethernet got IP from connected AP
|
||||
25 SYSTEM_EVENT_MAX
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
@ -43,18 +44,84 @@ void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
Serial.printf("[WiFi-event] event: %d\n", event);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
Serial.println("WiFi lost connection");
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_WIFI_READY:
|
||||
Serial.println("WiFi interface ready");
|
||||
break;
|
||||
case SYSTEM_EVENT_SCAN_DONE:
|
||||
Serial.println("Completed scan for access points");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_START:
|
||||
Serial.println("WiFi client started");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_STOP:
|
||||
Serial.println("WiFi clients stopped");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
Serial.println("Connected to access point");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
Serial.println("Disconnected from WiFi access point");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
|
||||
Serial.println("Authentication mode of access point has changed");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
Serial.print("Obtained IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_LOST_IP:
|
||||
Serial.println("Lost IP address and IP address is reset to 0");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
|
||||
Serial.println("WiFi Protected Setup (WPS): succeeded in enrollee mode");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_FAILED:
|
||||
Serial.println("WiFi Protected Setup (WPS): failed in enrollee mode");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
|
||||
Serial.println("WiFi Protected Setup (WPS): timeout in enrollee mode");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_PIN:
|
||||
Serial.println("WiFi Protected Setup (WPS): pin code in enrollee mode");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_START:
|
||||
Serial.println("WiFi access point started");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STOP:
|
||||
Serial.println("WiFi access point stopped");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STACONNECTED:
|
||||
Serial.println("Client connected");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STADISCONNECTED:
|
||||
Serial.println("Client disconnected");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STAIPASSIGNED:
|
||||
Serial.println("Assigned IP address to client");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_PROBEREQRECVED:
|
||||
Serial.println("Received probe request");
|
||||
break;
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
Serial.println("IPv6 is preferred");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
Serial.println("Ethernet started");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_STOP:
|
||||
Serial.println("Ethernet stopped");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||
Serial.println("Ethernet connected");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
Serial.println("Ethernet disconnected");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||
Serial.println("Obtained IP address");
|
||||
break;
|
||||
}}
|
||||
|
||||
void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
{
|
||||
@ -72,7 +139,7 @@ void setup()
|
||||
|
||||
delay(1000);
|
||||
|
||||
// Examples of diffrent ways to register wifi events
|
||||
// Examples of different ways to register wifi events
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
|
||||
WiFiEventId_t eventID = WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){
|
||||
|
@ -37,7 +37,7 @@ extern "C" {
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event_loop.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include "apps/dhcpserver_options.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
}
|
||||
|
||||
|
||||
@ -94,25 +94,28 @@ bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel,
|
||||
|
||||
if(!WiFi.enableAP(true)) {
|
||||
// enable AP failed
|
||||
log_e("enable AP first!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ssid || *ssid == 0 || strlen(ssid) > 31) {
|
||||
// fail SSID too long or missing!
|
||||
if(!ssid || *ssid == 0) {
|
||||
// fail SSID missing
|
||||
log_e("SSID missing!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(passphrase && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) {
|
||||
// fail passphrase to long or short!
|
||||
if(passphrase && (strlen(passphrase) > 0 && strlen(passphrase) < 8)) {
|
||||
// fail passphrase too short
|
||||
log_e("passphrase too short!");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_wifi_start();
|
||||
|
||||
wifi_config_t conf;
|
||||
strcpy(reinterpret_cast<char*>(conf.ap.ssid), ssid);
|
||||
strlcpy(reinterpret_cast<char*>(conf.ap.ssid), ssid, sizeof(conf.ap.ssid));
|
||||
conf.ap.channel = channel;
|
||||
conf.ap.ssid_len = strlen(ssid);
|
||||
conf.ap.ssid_len = strlen(reinterpret_cast<char *>(conf.ap.ssid));
|
||||
conf.ap.ssid_hidden = ssid_hidden;
|
||||
conf.ap.max_connection = max_connection;
|
||||
conf.ap.beacon_interval = 100;
|
||||
@ -122,7 +125,7 @@ bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel,
|
||||
*conf.ap.password = 0;
|
||||
} else {
|
||||
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
|
||||
strcpy(reinterpret_cast<char*>(conf.ap.password), passphrase);
|
||||
strlcpy(reinterpret_cast<char*>(conf.ap.password), passphrase, sizeof(conf.ap.password));
|
||||
}
|
||||
|
||||
wifi_config_t conf_current;
|
||||
|
@ -240,6 +240,7 @@ int WiFiClient::setSocketOption(int option, char* value, size_t len)
|
||||
|
||||
int WiFiClient::setTimeout(uint32_t seconds)
|
||||
{
|
||||
Client::setTimeout(seconds * 1000);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = seconds;
|
||||
tv.tv_usec = 0;
|
||||
@ -397,7 +398,8 @@ int WiFiClient::peek()
|
||||
|
||||
int WiFiClient::available()
|
||||
{
|
||||
if(!_connected) {
|
||||
if(!_rxBuffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int res = _rxBuffer->available();
|
||||
@ -438,6 +440,8 @@ uint8_t WiFiClient::connected()
|
||||
if (_connected) {
|
||||
uint8_t dummy;
|
||||
int res = recv(fd(), &dummy, 0, MSG_DONTWAIT);
|
||||
// avoid unused var warning by gcc
|
||||
(void)res;
|
||||
switch (errno) {
|
||||
case EWOULDBLOCK:
|
||||
case ENOENT: //caused by vfs
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Client.h"
|
||||
#undef min
|
||||
#undef max
|
||||
#include <memory>
|
||||
|
||||
class WiFiClientSocketHandle;
|
||||
|
@ -46,9 +46,6 @@ extern "C" {
|
||||
} //extern "C"
|
||||
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#include <vector>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
@ -99,7 +96,7 @@ static bool _start_network_event_task(){
|
||||
}
|
||||
}
|
||||
if(!_network_event_task_handle){
|
||||
xTaskCreatePinnedToCore(_network_event_task, "network_event", 4096, NULL, 2, &_network_event_task_handle, ARDUINO_RUNNING_CORE);
|
||||
xTaskCreatePinnedToCore(_network_event_task, "network_event", 4096, NULL, ESP_TASKD_EVENT_PRIO - 1, &_network_event_task_handle, ARDUINO_RUNNING_CORE);
|
||||
if(!_network_event_task_handle){
|
||||
log_e("Network Event Task Start Failed!");
|
||||
return false;
|
||||
@ -156,8 +153,8 @@ static bool espWiFiStart(bool persistent){
|
||||
return false;
|
||||
}
|
||||
_esp_wifi_started = true;
|
||||
system_event_t event;
|
||||
event.event_id = SYSTEM_EVENT_WIFI_READY;
|
||||
system_event_t event;
|
||||
event.event_id = SYSTEM_EVENT_WIFI_READY;
|
||||
WiFiGenericClass::_eventCallback(nullptr, &event);
|
||||
|
||||
return true;
|
||||
@ -496,7 +493,7 @@ bool WiFiGenericClass::mode(wifi_mode_t m)
|
||||
} else if(cm && !m){
|
||||
return espWiFiStop();
|
||||
}
|
||||
|
||||
|
||||
esp_err_t err;
|
||||
err = esp_wifi_set_mode(m);
|
||||
if(err){
|
||||
|
@ -60,13 +60,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
|
||||
uint8_t bestBSSID[6];
|
||||
int32_t bestChannel = 0;
|
||||
|
||||
DEBUG_WIFI_MULTI("[WIFI] scan done\n");
|
||||
delay(0);
|
||||
log_i("[WIFI] scan done");
|
||||
|
||||
if(scanResult <= 0) {
|
||||
DEBUG_WIFI_MULTI("[WIFI] no networks found\n");
|
||||
log_e("[WIFI] no networks found");
|
||||
} else {
|
||||
DEBUG_WIFI_MULTI("[WIFI] %d networks found\n", scanResult);
|
||||
log_i("[WIFI] %d networks found", scanResult);
|
||||
for(int8_t i = 0; i < scanResult; ++i) {
|
||||
|
||||
String ssid_scan;
|
||||
@ -96,24 +95,18 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
|
||||
}
|
||||
|
||||
if(known) {
|
||||
DEBUG_WIFI_MULTI(" ---> ");
|
||||
log_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 {
|
||||
DEBUG_WIFI_MULTI(" ");
|
||||
log_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) ? ' ' : '*');
|
||||
}
|
||||
|
||||
DEBUG_WIFI_MULTI(" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c\n", 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) ? ' ' : '*');
|
||||
delay(0);
|
||||
}
|
||||
}
|
||||
|
||||
// clean up ram
|
||||
WiFi.scanDelete();
|
||||
|
||||
DEBUG_WIFI_MULTI("\n\n");
|
||||
delay(0);
|
||||
|
||||
if(bestNetwork.ssid) {
|
||||
DEBUG_WIFI_MULTI("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channal: %d (%d)\n", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
|
||||
log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channal: %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();
|
||||
@ -125,37 +118,33 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
|
||||
status = WiFi.status();
|
||||
}
|
||||
|
||||
IPAddress ip;
|
||||
uint8_t * mac;
|
||||
switch(status) {
|
||||
case 3:
|
||||
ip = WiFi.localIP();
|
||||
mac = WiFi.BSSID();
|
||||
DEBUG_WIFI_MULTI("[WIFI] Connecting done.\n");
|
||||
DEBUG_WIFI_MULTI("[WIFI] SSID: %s\n", WiFi.SSID());
|
||||
DEBUG_WIFI_MULTI("[WIFI] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
DEBUG_WIFI_MULTI("[WIFI] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
DEBUG_WIFI_MULTI("[WIFI] Channel: %d\n", WiFi.channel());
|
||||
log_i("[WIFI] Connecting done.");
|
||||
log_d("[WIFI] SSID: %s", WiFi.SSID().c_str());
|
||||
log_d("[WIFI] IP: %s", WiFi.localIP().toString().c_str());
|
||||
log_d("[WIFI] MAC: %s", WiFi.BSSIDstr().c_str());
|
||||
log_d("[WIFI] Channel: %d", WiFi.channel());
|
||||
break;
|
||||
case 1:
|
||||
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed AP not found.\n");
|
||||
log_e("[WIFI] Connecting Failed AP not found.");
|
||||
break;
|
||||
case 4:
|
||||
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed.\n");
|
||||
log_e("[WIFI] Connecting Failed.");
|
||||
break;
|
||||
default:
|
||||
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed (%d).\n", status);
|
||||
log_e("[WIFI] Connecting Failed (%d).", status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DEBUG_WIFI_MULTI("[WIFI] no matching wifi found!\n");
|
||||
log_e("[WIFI] no matching wifi found!");
|
||||
}
|
||||
} else {
|
||||
// start scan
|
||||
DEBUG_WIFI_MULTI("[WIFI] delete old wifi config...\n");
|
||||
log_d("[WIFI] delete old wifi config...");
|
||||
WiFi.disconnect();
|
||||
|
||||
DEBUG_WIFI_MULTI("[WIFI] start scan\n");
|
||||
log_d("[WIFI] start scan");
|
||||
// scan wifi async mode
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
@ -172,34 +161,34 @@ bool WiFiMulti::APlistAdd(const char* ssid, const char *passphrase)
|
||||
|
||||
if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
|
||||
// fail SSID to long or missing!
|
||||
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] no ssid or ssid to long\n");
|
||||
log_e("[WIFI][APlistAdd] no ssid or ssid to long");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(passphrase && strlen(passphrase) > 63) {
|
||||
// fail passphrase to long!
|
||||
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] passphrase to long\n");
|
||||
log_e("[WIFI][APlistAdd] passphrase to long");
|
||||
return false;
|
||||
}
|
||||
|
||||
newAP.ssid = strdup(ssid);
|
||||
|
||||
if(!newAP.ssid) {
|
||||
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.ssid == 0\n");
|
||||
log_e("[WIFI][APlistAdd] fail newAP.ssid == 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(passphrase && *passphrase != 0x00) {
|
||||
newAP.passphrase = strdup(passphrase);
|
||||
if(!newAP.passphrase) {
|
||||
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.passphrase == 0\n");
|
||||
log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
|
||||
free(newAP.ssid);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
APlist.push_back(newAP);
|
||||
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] add SSID: %s\n", newAP.ssid);
|
||||
log_i("[WIFI][APlistAdd] add SSID: %s", newAP.ssid);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,20 +27,8 @@
|
||||
#define WIFICLIENTMULTI_H_
|
||||
|
||||
#include "WiFi.h"
|
||||
#undef min
|
||||
#undef max
|
||||
#include <vector>
|
||||
|
||||
#ifdef DEBUG_ESP_WIFI
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_WIFI_MULTI(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_WIFI_MULTI
|
||||
#define DEBUG_WIFI_MULTI(...)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char * ssid;
|
||||
char * passphrase;
|
||||
|
@ -683,8 +683,10 @@ void WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) {
|
||||
smartconfig_status_t status = (smartconfig_status_t) st;
|
||||
log_d("Status: %s", sc_status_strings[st % 5]);
|
||||
if (status == SC_STATUS_GETTING_SSID_PSWD) {
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
smartconfig_type_t * type = (smartconfig_type_t *)result;
|
||||
log_d("Type: %s", sc_type_strings[*type % 3]);
|
||||
#endif
|
||||
} else if (status == SC_STATUS_LINK) {
|
||||
wifi_sta_config_t *sta_conf = reinterpret_cast<wifi_sta_config_t *>(result);
|
||||
log_d("SSID: %s", (char *)(sta_conf->ssid));
|
||||
@ -694,8 +696,10 @@ void WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) {
|
||||
_smartConfigDone = true;
|
||||
} else if (status == SC_STATUS_LINK_OVER) {
|
||||
if(result){
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
ip4_addr_t * ip = (ip4_addr_t *)result;
|
||||
log_d("Sender IP: " IPSTR, IP2STR(ip));
|
||||
#endif
|
||||
}
|
||||
WiFi.stopSmartConfig();
|
||||
}
|
||||
|
67
libraries/WiFiClientSecure/README.md
Normal file
67
libraries/WiFiClientSecure/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
WiFiClientSecure
|
||||
================
|
||||
|
||||
The WiFiClientSecure class implements support for secure connections using TLS (SSL).
|
||||
It inherits from WiFiClient and thus implements a superset of that class' interface.
|
||||
There are three ways to establish a secure connection using the WiFiClientSecure class:
|
||||
using a root certificate authority (CA) cert, using a root CA cert plus a client cert and key,
|
||||
and using a pre-shared key (PSK).
|
||||
|
||||
Using a root certificate authority cert
|
||||
---------------------------------------
|
||||
This method authenticates the server and negotiates an encrypted connection.
|
||||
It is the same functionality as implemented in your web browser when you connect to HTTPS sites.
|
||||
|
||||
If you are accessing your own server:
|
||||
- Generate a root certificate for your own certificate authority
|
||||
- Generate a cert & private key using your root certificate ("self-signed cert") for your server
|
||||
If you are accessing a public server:
|
||||
- Obtain the cert of the public CA that signed that server's cert
|
||||
Then:
|
||||
- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your
|
||||
CA or of the public CA
|
||||
- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate
|
||||
presented by the server, and then negotiates encryption for the connection
|
||||
|
||||
Please see the WiFiClientSecure example.
|
||||
|
||||
Using a root CA cert and client cert/keys
|
||||
-----------------------------------------
|
||||
This method authenticates the server and additionally also authenticates
|
||||
the client to the server, then negotiates an encrypted connection.
|
||||
|
||||
- Follow steps above
|
||||
- Using your root CA generate cert/key for your client
|
||||
- Register the keys with the server you will be accessing so the server can authenticate your client
|
||||
- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your
|
||||
CA or of the public CA, this is used to authenticate the server
|
||||
- In WiFiClientSecure use setCertificate, and setPrivateKey (or the appropriate connect method) to
|
||||
set your client's cert & key, this will be used to authenticate your client to the server
|
||||
- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate
|
||||
presented by the server, it will use the cert/key to authenticate your client to the server, and
|
||||
it will then negotiate encryption for the connection
|
||||
|
||||
Using Pre-Shared Keys (PSK)
|
||||
---------------------------
|
||||
|
||||
TLS supports authentication and encryption using a pre-shared key (i.e. a key that both client and
|
||||
server know) as an alternative to the public key cryptography commonly used on the web for HTTPS.
|
||||
PSK is starting to be used for MQTT, e.g. in mosquitto, to simplify the set-up and avoid having to
|
||||
go through the whole CA, cert, and private key process.
|
||||
|
||||
A pre-shared key is a binary string of up to 32 bytes and is commonly represented in hex form. In
|
||||
addition to the key, clients can also present an id and typically the server allows a different key
|
||||
to be associated with each client id. In effect this is very similar to username and password pairs,
|
||||
except that unlike a password the key is not directly transmitted to the server, thus a connection to a
|
||||
malicious server does not divulge the password. Plus the server is also authenticated to the client.
|
||||
|
||||
To use PSK:
|
||||
- Generate a random hex string (generating an MD5 or SHA for some file is one way to do this)
|
||||
- Come up with a string id for your client and configure your server to accept the id/key pair
|
||||
- In WiFiClientSecure use setPreSharedKey (or the appropriate connect method) to
|
||||
set the id/key combo
|
||||
- When WiFiClientSecure connects to the target server it uses the id/key combo to authenticate the
|
||||
server (it must prove that it has the key too), authenticate the client and then negotiate
|
||||
encryption for the connection
|
||||
|
||||
Please see the WiFiClientPSK example.
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Wifi secure connection example for ESP32 using a pre-shared key (PSK)
|
||||
This is useful with MQTT servers instead of using a self-signed cert, tested with mosquitto.
|
||||
Running on TLS 1.2 using mbedTLS
|
||||
|
||||
To test run a test server using: openssl s_server -accept 8443 -psk 1a2b3c4d -nocert
|
||||
It will show the http request made, but there's no easy way to send a reply back...
|
||||
|
||||
2017 - Evandro Copercini - Apache 2.0 License.
|
||||
2018 - Adapted for PSK by Thorsten von Eicken
|
||||
*/
|
||||
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#if 0
|
||||
const char* ssid = "your-ssid"; // your network SSID (name of wifi network)
|
||||
const char* password = "your-password"; // your network password
|
||||
#else
|
||||
const char* ssid = "test"; // your network SSID (name of wifi network)
|
||||
const char* password = "securetest"; // your network password
|
||||
#endif
|
||||
|
||||
//const char* server = "server.local"; // Server hostname
|
||||
const IPAddress server = IPAddress(192, 168, 0, 14); // Server IP address
|
||||
const int port = 8443; // server's port (8883 for MQTT)
|
||||
|
||||
const char* pskIdent = "Client_identity"; // PSK identity (sometimes called key hint)
|
||||
const char* psKey = "1a2b3c4d"; // PSK Key (must be hex string without 0x)
|
||||
|
||||
WiFiClientSecure client;
|
||||
|
||||
void setup() {
|
||||
//Initialize serial and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
|
||||
Serial.print("Attempting to connect to SSID: ");
|
||||
Serial.println(ssid);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
// attempt to connect to Wifi network:
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
// wait 1 second for re-trying
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
client.setPreSharedKey(pskIdent, psKey);
|
||||
|
||||
Serial.println("\nStarting connection to server...");
|
||||
if (!client.connect(server, port))
|
||||
Serial.println("Connection failed!");
|
||||
else {
|
||||
Serial.println("Connected to server!");
|
||||
// Make a HTTP request:
|
||||
client.println("GET /a/check HTTP/1.0");
|
||||
client.print("Host: ");
|
||||
client.println(server);
|
||||
client.println("Connection: close");
|
||||
client.println();
|
||||
|
||||
while (client.connected()) {
|
||||
String line = client.readStringUntil('\n');
|
||||
if (line == "\r") {
|
||||
Serial.println("headers received");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if there are incoming bytes available
|
||||
// from the server, read them and print them:
|
||||
while (client.available()) {
|
||||
char c = client.read();
|
||||
Serial.write(c);
|
||||
}
|
||||
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// do nothing
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*|----------------------------------------------------------|*/
|
||||
/*|WORKING EXAMPLE FOR HTTPS CONNECTION |*/
|
||||
/*|TESTED BOARDS: Devkit v1 DOIT, Devkitc v4 |*/
|
||||
/*|CORE: June 2018 |*/
|
||||
/*|----------------------------------------------------------|*/
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include "esp_wpa2.h"
|
||||
#include <Wire.h>
|
||||
#define EAP_IDENTITY "identity" //if connecting from another corporation, use identity@organisation.domain in Eduroam
|
||||
#define EAP_PASSWORD "password" //your Eduroam password
|
||||
const char* ssid = "eduroam"; // Eduroam SSID
|
||||
const char* host = "arduino.php5.sk"; //external server domain for HTTP connection after authentification
|
||||
int counter = 0;
|
||||
const char* test_root_ca= \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
|
||||
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
|
||||
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
|
||||
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
|
||||
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
|
||||
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
|
||||
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
|
||||
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
|
||||
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
|
||||
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
|
||||
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
|
||||
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
|
||||
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
|
||||
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
// You can use x.509 client certificates if you want
|
||||
//const char* test_client_key = ""; //to verify the client
|
||||
//const char* test_client_cert = ""; //to verify the client
|
||||
WiFiClientSecure client;
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
Serial.println();
|
||||
Serial.print("Connecting to network: ");
|
||||
Serial.println(ssid);
|
||||
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
|
||||
WiFi.mode(WIFI_STA); //init wifi mode
|
||||
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
|
||||
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
|
||||
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
|
||||
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
|
||||
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
|
||||
WiFi.begin(ssid); //connect to wifi
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //after 30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
client.setCACert(test_root_ca);
|
||||
//client.setCertificate(test_client_key); // for client verification
|
||||
//client.setPrivateKey(test_client_cert); // for client verification
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address set: ");
|
||||
Serial.println(WiFi.localIP()); //print LAN IP
|
||||
}
|
||||
void loop() {
|
||||
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
|
||||
counter = 0; //reset counter
|
||||
Serial.println("Wifi is still connected with IP: ");
|
||||
Serial.println(WiFi.localIP()); //inform user about his IP address
|
||||
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.print("Connecting to website: ");
|
||||
Serial.println(host);
|
||||
if (client.connect(host, 443)) {
|
||||
String url = "/rele/rele1.txt";
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: ESP32\r\n" + "Connection: close\r\n\r\n");
|
||||
while (client.connected()) {
|
||||
String header = client.readStringUntil('\n');
|
||||
Serial.println(header);
|
||||
if (header == "\r") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String line = client.readStringUntil('\n');
|
||||
Serial.println(line);
|
||||
}else{
|
||||
Serial.println("Connection unsucessful");
|
||||
}
|
||||
delay(5000);
|
||||
}
|
@ -35,10 +35,12 @@ WiFiClientSecure::WiFiClientSecure()
|
||||
sslclient = new sslclient_context;
|
||||
ssl_init(sslclient);
|
||||
sslclient->socket = -1;
|
||||
|
||||
sslclient->handshake_timeout = 120000;
|
||||
_CA_cert = NULL;
|
||||
_cert = NULL;
|
||||
_private_key = NULL;
|
||||
_pskIdent = NULL;
|
||||
_psKey = NULL;
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
@ -50,6 +52,7 @@ WiFiClientSecure::WiFiClientSecure(int sock)
|
||||
sslclient = new sslclient_context;
|
||||
ssl_init(sslclient);
|
||||
sslclient->socket = sock;
|
||||
sslclient->handshake_timeout = 120000;
|
||||
|
||||
if (sock >= 0) {
|
||||
_connected = true;
|
||||
@ -58,6 +61,8 @@ WiFiClientSecure::WiFiClientSecure(int sock)
|
||||
_CA_cert = NULL;
|
||||
_cert = NULL;
|
||||
_private_key = NULL;
|
||||
_pskIdent = NULL;
|
||||
_psKey = NULL;
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
@ -88,11 +93,15 @@ void WiFiClientSecure::stop()
|
||||
|
||||
int WiFiClientSecure::connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
if (_pskIdent && _psKey)
|
||||
return connect(ip, port, _pskIdent, _psKey);
|
||||
return connect(ip, port, _CA_cert, _cert, _private_key);
|
||||
}
|
||||
|
||||
int WiFiClientSecure::connect(const char *host, uint16_t port)
|
||||
{
|
||||
if (_pskIdent && _psKey)
|
||||
return connect(host, port, _pskIdent, _psKey);
|
||||
return connect(host, port, _CA_cert, _cert, _private_key);
|
||||
}
|
||||
|
||||
@ -103,7 +112,24 @@ int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *_CA_cert,
|
||||
|
||||
int WiFiClientSecure::connect(const char *host, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key)
|
||||
{
|
||||
int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key);
|
||||
int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key, NULL, NULL);
|
||||
_lastError = ret;
|
||||
if (ret < 0) {
|
||||
log_e("start_ssl_client: %d", ret);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
_connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) {
|
||||
return connect(ip.toString().c_str(), port,_pskIdent, _psKey);
|
||||
}
|
||||
|
||||
int WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) {
|
||||
log_v("start_ssl_client with PSK");
|
||||
int ret = start_ssl_client(sslclient, host, port, NULL, NULL, NULL, _pskIdent, _psKey);
|
||||
_lastError = ret;
|
||||
if (ret < 0) {
|
||||
log_e("start_ssl_client: %d", ret);
|
||||
@ -130,13 +156,6 @@ size_t WiFiClientSecure::write(uint8_t data)
|
||||
int WiFiClientSecure::read()
|
||||
{
|
||||
uint8_t data = -1;
|
||||
|
||||
if(_peek >= 0){
|
||||
data = _peek;
|
||||
_peek = -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
int res = read(&data, 1);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
@ -160,7 +179,8 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size)
|
||||
int WiFiClientSecure::read(uint8_t *buf, size_t size)
|
||||
{
|
||||
int peeked = 0;
|
||||
if ((!buf && size) || (_peek < 0 && !available())) {
|
||||
int avail = available();
|
||||
if ((!buf && size) || avail <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if(!size){
|
||||
@ -170,7 +190,8 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size)
|
||||
buf[0] = _peek;
|
||||
_peek = -1;
|
||||
size--;
|
||||
if(!size || !available()){
|
||||
avail--;
|
||||
if(!size || !avail){
|
||||
return 1;
|
||||
}
|
||||
buf++;
|
||||
@ -180,23 +201,23 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size)
|
||||
int res = get_ssl_receive(sslclient, buf, size);
|
||||
if (res < 0) {
|
||||
stop();
|
||||
return res;
|
||||
return peeked?peeked:res;
|
||||
}
|
||||
return res + peeked;
|
||||
}
|
||||
|
||||
int WiFiClientSecure::available()
|
||||
{
|
||||
int peeked = (_peek >= 0);
|
||||
if (!_connected) {
|
||||
return 0;
|
||||
return peeked;
|
||||
}
|
||||
int res = data_to_read(sslclient);
|
||||
if (res < 0 ) {
|
||||
if (res < 0) {
|
||||
stop();
|
||||
} else if(_peek >= 0) {
|
||||
res += 1;
|
||||
return peeked?peeked:res;
|
||||
}
|
||||
return res;
|
||||
return res+peeked;
|
||||
}
|
||||
|
||||
uint8_t WiFiClientSecure::connected()
|
||||
@ -222,6 +243,11 @@ void WiFiClientSecure::setPrivateKey (const char *private_key)
|
||||
_private_key = private_key;
|
||||
}
|
||||
|
||||
void WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) {
|
||||
_pskIdent = pskIdent;
|
||||
_psKey = psKey;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
|
||||
{
|
||||
if (!sslclient)
|
||||
@ -230,6 +256,52 @@ bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
|
||||
return verify_ssl_fingerprint(sslclient, fp, domain_name);
|
||||
}
|
||||
|
||||
char *WiFiClientSecure::_streamLoad(Stream& stream, size_t size) {
|
||||
static char *dest = nullptr;
|
||||
if(dest) {
|
||||
free(dest);
|
||||
}
|
||||
dest = (char*)malloc(size);
|
||||
if (!dest) {
|
||||
return nullptr;
|
||||
}
|
||||
if (size != stream.readBytes(dest, size)) {
|
||||
free(dest);
|
||||
dest = nullptr;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure::loadCACert(Stream& stream, size_t size) {
|
||||
char *dest = _streamLoad(stream, size);
|
||||
bool ret = false;
|
||||
if (dest) {
|
||||
setCACert(dest);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size) {
|
||||
char *dest = _streamLoad(stream, size);
|
||||
bool ret = false;
|
||||
if (dest) {
|
||||
setCertificate(dest);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) {
|
||||
char *dest = _streamLoad(stream, size);
|
||||
bool ret = false;
|
||||
if (dest) {
|
||||
setPrivateKey(dest);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int WiFiClientSecure::lastError(char *buf, const size_t size)
|
||||
{
|
||||
if (!_lastError) {
|
||||
@ -240,3 +312,8 @@ int WiFiClientSecure::lastError(char *buf, const size_t size)
|
||||
snprintf(buf, size, "%s", error_buf);
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
void WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout)
|
||||
{
|
||||
sslclient->handshake_timeout = handshake_timeout * 1000;
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ protected:
|
||||
const char *_CA_cert;
|
||||
const char *_cert;
|
||||
const char *_private_key;
|
||||
const char *_pskIdent; // identity for PSK cipher suites
|
||||
const char *_psKey; // key in hex for PSK cipher suites
|
||||
|
||||
public:
|
||||
WiFiClientSecure *next;
|
||||
@ -45,6 +47,8 @@ public:
|
||||
int connect(const char *host, uint16_t port);
|
||||
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);
|
||||
int peek();
|
||||
size_t write(uint8_t data);
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
@ -55,10 +59,15 @@ public:
|
||||
void stop();
|
||||
uint8_t connected();
|
||||
int lastError(char *buf, const size_t size);
|
||||
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);
|
||||
|
||||
operator bool()
|
||||
{
|
||||
@ -84,6 +93,9 @@ public:
|
||||
return sslclient->socket = -1;
|
||||
}
|
||||
|
||||
private:
|
||||
char *_streamLoad(Stream& stream, size_t size);
|
||||
|
||||
//friend class WiFiServer;
|
||||
using Print::write;
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ void ssl_init(sslclient_context *ssl_client)
|
||||
}
|
||||
|
||||
|
||||
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key)
|
||||
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey)
|
||||
{
|
||||
char buf[512];
|
||||
int ret, flags, timeout;
|
||||
@ -116,6 +116,36 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p
|
||||
if (ret < 0) {
|
||||
return handle_error(ret);
|
||||
}
|
||||
} else if (pskIdent != NULL && psKey != NULL) {
|
||||
log_v("Setting up PSK");
|
||||
// convert PSK from hex to binary
|
||||
if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) {
|
||||
log_e("pre-shared key not valid hex or too long");
|
||||
return -1;
|
||||
}
|
||||
unsigned char psk[MBEDTLS_PSK_MAX_LEN];
|
||||
size_t psk_len = strlen(psKey)/2;
|
||||
for (int j=0; j<strlen(psKey); j+= 2) {
|
||||
char c = psKey[j];
|
||||
if (c >= '0' && c <= '9') c -= '0';
|
||||
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
|
||||
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
|
||||
else return -1;
|
||||
psk[j/2] = c<<4;
|
||||
c = psKey[j+1];
|
||||
if (c >= '0' && c <= '9') c -= '0';
|
||||
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
|
||||
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
|
||||
else return -1;
|
||||
psk[j/2] |= c;
|
||||
}
|
||||
// set mbedtls config
|
||||
ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len,
|
||||
(const unsigned char *)pskIdent, strlen(pskIdent));
|
||||
if (ret != 0) {
|
||||
log_e("mbedtls_ssl_conf_psk returned %d", ret);
|
||||
return handle_error(ret);
|
||||
}
|
||||
} else {
|
||||
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE);
|
||||
log_i("WARNING: Use certificates for a more secure communication!");
|
||||
@ -158,12 +188,14 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p
|
||||
mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL );
|
||||
|
||||
log_v("Performing the SSL/TLS handshake...");
|
||||
|
||||
unsigned long handshake_start_time=millis();
|
||||
while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
return handle_error(ret);
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
if((millis()-handshake_start_time)>ssl_client->handshake_timeout)
|
||||
return -1;
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,11 +23,13 @@ typedef struct sslclient_context {
|
||||
mbedtls_x509_crt ca_cert;
|
||||
mbedtls_x509_crt client_cert;
|
||||
mbedtls_pk_context client_key;
|
||||
|
||||
unsigned long handshake_timeout;
|
||||
} sslclient_context;
|
||||
|
||||
|
||||
void ssl_init(sslclient_context *ssl_client);
|
||||
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey);
|
||||
void stop_ssl_socket(sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
int data_to_read(sslclient_context *ssl_client);
|
||||
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=Wire
|
||||
version=1.0
|
||||
version=1.0.1
|
||||
author=Hristo Gochkov
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards.
|
||||
|
@ -58,7 +58,7 @@ TwoWire::~TwoWire()
|
||||
}
|
||||
}
|
||||
|
||||
void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
{
|
||||
if(sdaPin < 0) { // default param passed
|
||||
if(num == 0) {
|
||||
@ -70,7 +70,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
} else {
|
||||
if(sda==-1) {
|
||||
log_e("no Default SDA Pin for Second Peripheral");
|
||||
return; //no Default pin for Second Peripheral
|
||||
return false; //no Default pin for Second Peripheral
|
||||
} else {
|
||||
sdaPin = sda; // reuse prior pin
|
||||
}
|
||||
@ -87,7 +87,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
} else {
|
||||
if(scl == -1) {
|
||||
log_e("no Default SCL Pin for Second Peripheral");
|
||||
return; //no Default pin for Second Peripheral
|
||||
return false; //no Default pin for Second Peripheral
|
||||
} else {
|
||||
sclPin = scl; // reuse prior pin
|
||||
}
|
||||
@ -98,10 +98,11 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
scl = sclPin;
|
||||
i2c = i2cInit(num, sdaPin, sclPin, frequency);
|
||||
if(!i2c) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
flush();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@ -145,6 +146,7 @@ void TwoWire::beginTransmission(uint16_t address)
|
||||
txAddress = address;
|
||||
txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true)
|
||||
txLength = txQueued;
|
||||
last_error = I2C_ERROR_OK;
|
||||
}
|
||||
|
||||
/*stickbreaker isr
|
||||
@ -152,14 +154,15 @@ void TwoWire::beginTransmission(uint16_t address)
|
||||
uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransaction(), Wire.write()
|
||||
{
|
||||
if(transmitting == 1) {
|
||||
last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop);
|
||||
rxIndex = 0;
|
||||
rxLength = rxQueued;
|
||||
rxQueued = 0;
|
||||
txQueued = 0; // the SendStop=true will restart all Queueing
|
||||
if(last_error == I2C_ERROR_CONTINUE){
|
||||
// txlength is howmany bytes in txbuffer have been use
|
||||
last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop);
|
||||
if(last_error == I2C_ERROR_CONTINUE){
|
||||
txQueued = txLength;
|
||||
} else if( last_error == I2C_ERROR_OK){
|
||||
rxIndex = 0;
|
||||
rxLength = rxQueued;
|
||||
rxQueued = 0;
|
||||
txQueued = 0; // the SendStop=true will restart all Queueing
|
||||
}
|
||||
} else {
|
||||
last_error = I2C_ERROR_NO_BEGIN;
|
||||
@ -168,7 +171,7 @@ uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransactio
|
||||
txIndex = 0;
|
||||
txLength = 0;
|
||||
transmitting = 0;
|
||||
return last_error;
|
||||
return (last_error == I2C_ERROR_CONTINUE)?I2C_ERROR_OK:last_error; // Don't return Continue for compatibility.
|
||||
}
|
||||
|
||||
/* @stickBreaker 11/2017 fix for ReSTART timeout, ISR
|
||||
@ -189,12 +192,19 @@ uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop)
|
||||
|
||||
last_error = readTransmission(address, &rxBuffer[cnt], size, sendStop, &cnt);
|
||||
rxIndex = 0;
|
||||
rxLength = rxQueued;
|
||||
rxQueued = 0;
|
||||
txQueued = 0; // the SendStop=true will restart all Queueing
|
||||
if(last_error != I2C_ERROR_OK){
|
||||
|
||||
rxLength = cnt;
|
||||
|
||||
if( last_error != I2C_ERROR_CONTINUE){ // not a buffered ReSTART operation
|
||||
// so this operation actually moved data, queuing is done.
|
||||
rxQueued = 0;
|
||||
txQueued = 0; // the SendStop=true will restart all Queueing or error condition
|
||||
}
|
||||
|
||||
if(last_error != I2C_ERROR_OK){ // ReSTART on read does not return any data
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
@ -202,6 +212,7 @@ size_t TwoWire::write(uint8_t data)
|
||||
{
|
||||
if(transmitting) {
|
||||
if(txLength >= I2C_BUFFER_LENGTH) {
|
||||
last_error = I2C_ERROR_MEMORY;
|
||||
return 0;
|
||||
}
|
||||
txBuffer[txIndex] = data;
|
||||
@ -209,20 +220,19 @@ size_t TwoWire::write(uint8_t data)
|
||||
txLength = txIndex;
|
||||
return 1;
|
||||
}
|
||||
last_error = I2C_ERROR_NO_BEGIN; // no begin, not transmitting
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t TwoWire::write(const uint8_t *data, size_t quantity)
|
||||
{
|
||||
if(transmitting) {
|
||||
for(size_t i = 0; i < quantity; ++i) {
|
||||
if(!write(data[i])) {
|
||||
return i;
|
||||
}
|
||||
for(size_t i = 0; i < quantity; ++i) {
|
||||
if(!write(data[i])) {
|
||||
return i;
|
||||
}
|
||||
return quantity;
|
||||
}
|
||||
return 0;
|
||||
return quantity;
|
||||
|
||||
}
|
||||
|
||||
int TwoWire::available(void)
|
||||
@ -306,11 +316,6 @@ uint8_t TwoWire::endTransmission(void)
|
||||
return endTransmission(true);
|
||||
}
|
||||
|
||||
uint8_t TwoWire::endTransmission(uint8_t sendStop)
|
||||
{
|
||||
return endTransmission(static_cast<bool>(sendStop));
|
||||
}
|
||||
|
||||
/* stickbreaker Nov2017 better error reporting
|
||||
*/
|
||||
uint8_t TwoWire::lastError()
|
||||
@ -353,14 +358,13 @@ char * TwoWire::getErrorText(uint8_t err)
|
||||
|
||||
/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging
|
||||
*/
|
||||
void TwoWire::dumpInts()
|
||||
{
|
||||
i2cDumpInts(num);
|
||||
|
||||
uint32_t TwoWire::setDebugFlags( uint32_t setBits, uint32_t resetBits){
|
||||
return i2cDebug(i2c,setBits,resetBits);
|
||||
}
|
||||
|
||||
void TwoWire::dumpI2C()
|
||||
{
|
||||
i2cDumpI2c(i2c);
|
||||
bool TwoWire::busy(void){
|
||||
return ((i2cGetStatus(i2c) & 16 )==16);
|
||||
}
|
||||
|
||||
TwoWire Wire = TwoWire(0);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user