From 90e9fee101c9fc1d43ab64a02a773b86d61b80f4 Mon Sep 17 00:00:00 2001 From: kazu Date: Sat, 22 Sep 2018 15:44:26 +0200 Subject: [PATCH] many changes --- Debug.h | 52 ++- Platforms.h | 34 +- data/Color.h | 99 +++++ data/RingBuffer.h | 63 +-- ext/audio/VS1003.h | 135 +++++-- ext/led/WS2812B.h | 972 +++++++++++++++++++++++++++++++++++---------- io/SoftSPI.h | 60 +-- net/DNS.h | 69 ++++ net/HTTP.h | 33 +- net/IP.h | 121 ++++-- net/TCP.h | 419 +++++++++++++------ 11 files changed, 1581 insertions(+), 476 deletions(-) create mode 100644 data/Color.h create mode 100644 net/DNS.h diff --git a/Debug.h b/Debug.h index 7a27ac7..637ab96 100644 --- a/Debug.h +++ b/Debug.h @@ -2,7 +2,9 @@ #define DEBUG_H #include +#include "Platforms.h" +/* extern "C" { #include "ets_sys.h" #include "c_types.h" @@ -21,26 +23,11 @@ extern "C" { #include "driver/uart.h" } - -void hexdump(const uint8_t* buf, uint8_t len) { - for (int i = 0; i < len; ++i) { - os_printf("%02x ", buf[i]); - } - os_printf("\n"); -} - -#ifdef DEBUG - - #define debug(str) os_printf(str) - #define debugMod(module, str) os_printf("[%s] %s\n", module, str) - #define debugMod1(module, str, val) os_printf("[%s] ", module); os_printf(str, val); os_printf("\n"); - #define debugMod2(module, str, v1, v2) os_printf("[%s] ", module); os_printf(str, v1, v2); os_printf("\n"); - #define debugMod3(module, str, v1, v2, v3) os_printf("[%s] ", module); os_printf(str, v1, v2, v3); os_printf("\n"); - #define IF_DEBUG(a) a - #define debugShow(buf, len) hexdump(buf,len) +*/ -#else + +#if (!defined(DEBUG)) #define debug(str) #define debugMod(module, str) @@ -50,6 +37,35 @@ void hexdump(const uint8_t* buf, uint8_t len) { #define IF_DEBUG(a) #define debugShow(a, b) +#elif (PLATFORM == WEMOS_D1_MINI) || (PLATFORM == NODE_MCU) + + #define debug(str) os_printf(str) + #define debugMod(module, str) os_printf("[%s] %s\n", module, str) + #define debugMod1(module, str, val) os_printf("[%s] ", module); os_printf(str, val); os_printf("\n"); + #define debugMod2(module, str, v1, v2) os_printf("[%s] ", module); os_printf(str, v1, v2); os_printf("\n"); + #define debugMod3(module, str, v1, v2, v3) os_printf("[%s] ", module); os_printf(str, v1, v2, v3); os_printf("\n"); + #define IF_DEBUG(a) a + #define debugShow(buf, len) hexdump(buf,len) + + void hexdump(const uint8_t* buf, uint8_t len) { + for (int i = 0; i < len; ++i) { + os_printf("%02x ", buf[i]); + } + os_printf("\n"); + } + +#elif (PLATFORM == WROOM32_DEVKIT) + + #define debug(str) ESP_LOGI("", str); + #define debugMod(module, str) ESP_LOGI(module, str); + #define debugMod1(module, str, val) ESP_LOGI(module, str, val); + #define debugMod2(module, str, v1, v2) ESP_LOGI(module, str, v1, v2); + #define debugMod3(module, str, v1, v2, v3) ESP_LOGI(module, str, v1, v2, v3); + #define IF_DEBUG(a) a + #define debugShow(buf, len) hexdump(buf,len) + +#else + #error "unsupported platform" #endif #endif // DEBUG_H diff --git a/Platforms.h b/Platforms.h index d6af7d6..d7f0800 100644 --- a/Platforms.h +++ b/Platforms.h @@ -5,8 +5,38 @@ // https://blog.anto.io/wp-content/uploads/2016/12/esp32.png -#define NODE32S 32 +// https://i.pinimg.com/originals/c6/57/83/c657835e84aaf91832a770ea0d7d0767.jpg +#define WROOM32_DEVKIT 32 +#define ESP8266 (PLATFORM == WEMOS_D1_MINI) || (PLATFORM == NODE_MCU) +#define ESP32 (PLATFORM == WROOM32_DEVKIT) -#define PLATFORM NODE32S + +#if (PLATFORM == WROOM32_DEVKIT) + #define DELAY_US(us) ets_delay_us(us) + #define IN_FLASH +#endif + +#if (PLATFORM == WEMOS_D1_MINI) || (PLATFORM == NODE_MCU) + #define DELAY_US(us) os_delay_us(us) + #define IN_FLASH ICACHE_FLASH_ATTR +#endif + +#define min(a,b) (ab) ? (a) : (b) + +//template inline T min(const T a, const T b) { +// return (a inline T max(const T a, const T b) { +// return (a>b) ? (a) : (b); +//} + +#if ESP8266 + #define sprintf os_sprintf +#elif ESP32 + #include + #include +#endif diff --git a/data/Color.h b/data/Color.h new file mode 100644 index 0000000..bb7878b --- /dev/null +++ b/data/Color.h @@ -0,0 +1,99 @@ +struct Color { + + uint8_t r; + uint8_t g; + uint8_t b; + + /** no-init */ + Color() { + ; + } + +private: + + /** Hidden ctor. RGB */ + Color(const uint8_t r, const uint8_t g, const uint8_t b) : r(r), g(g), b(b) { + ; + } + +public: + + /** get X-percent [0:100] of this color = darker/brighter */ + Color inline getPercent(const int percent) const { + return Color(r*percent/100, g*percent/100, b*percent/100); + } + + /** mix the two given colors */ + static Color mix(const Color c1, const Color c2, int percentC1) { + return Color( + ((c1.r * percentC1) + (c2.r * (100-percentC1))) / 100, + ((c1.g * percentC1) + (c2.g * (100-percentC1))) / 100, + ((c1.b * percentC1) + (c2.b * (100-percentC1))) / 100 + ); + } + + static inline Color fromRGB(const uint8_t r, const uint8_t g, const uint8_t b) { + return Color(r,g,b); + } + + static inline Color fromHSV(const uint8_t h, const uint8_t s, const uint8_t v) { + Color c; c.setHSV(h,s,v); return c; + } + + void setHSV(const uint8_t h, const uint8_t s, const uint8_t v) { + + uint8_t region, remainder, p, q, t; + + region = h / 43; + remainder = (h - (region * 43)) * 6; + + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 8))) >> 8; + t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; + + switch (region) { + case 0: + r = v; g = t; b = p; + break; + case 1: + r = q; g = v; b = p; + break; + case 2: + r = p; g = v; b = t; + break; + case 3: + r = p; g = q; b = v; + break; + case 4: + r = t; g = p; b = v; + break; + default: + r = v; g = p; b = q; + break; + } + + } + + void setRGB(const uint8_t r, const uint8_t g, const uint8_t b) { + this->r = r; + this->g = g; + this->b = b; + } + + void setOff() { + this->r = 0; + this->g = 0; + this->b = 0; + } + + + /** add two colors */ + Color operator + (const Color o) const { + return Color(r+o.r, g+o.g, b+o.g); + } + + Color operator * (const float f) const { + return Color(r*f, g*f, b*f); + } + +}; diff --git a/data/RingBuffer.h b/data/RingBuffer.h index a6b1c25..22a7474 100644 --- a/data/RingBuffer.h +++ b/data/RingBuffer.h @@ -2,6 +2,7 @@ #define RINGBUFFER_H #include "../Debug.h" +#include "../Platforms.h" typedef void (*RingBufferFullCallback)(); @@ -16,7 +17,7 @@ private: volatile size_t head; volatile size_t tail; - volatile mutable size_t used; + volatile size_t used; public: @@ -34,13 +35,13 @@ public: init(); } - /** add one value to the buffer. blocks until space is available */ - void addBlocking(const Scalar value) { - while (used == size) {os_delay_us(1000);} - data[head] = value; - head = (head + 1) % size; - ++used; - } +// /** add one value to the buffer. blocks until space is available */ +// void addBlocking(const Scalar value) { +// while (used == size) {os_delay_us(1000);} +// data[head] = value; +// head = (head + 1) % size; +// ++used; +// } /** add one value to the buffer, if there is enough space */ void addOrIgnore(const Scalar value) { @@ -59,23 +60,23 @@ public: } - /** add multiple values to the buffer. blocks until space is available */ - void addBlocking(const Scalar* value, const size_t len) { - debugMod1(NAME, "addBlocking(%d)", len); - for (size_t i = 0; i < len; ++i) { - addBlocking(value[i]); - } - debugMod1(NAME, "used: %d", used); - } +// /** add multiple values to the buffer. blocks until space is available */ +// void addBlocking(const Scalar* value, const size_t len) { +// debugMod1(NAME, "addBlocking(%d)", len); +// for (size_t i = 0; i < len; ++i) { +// addBlocking(value[i]); +// } +// debugMod1(NAME, "used: %d", used); +// } - /** add multiple values to the buffer, if there is enough space */ - void addOrIgnore(const Scalar* value, const size_t len) { - debugMod1(NAME, "addOrIgnore(%d)", len); - for (size_t i = 0; i < len; ++i) { - addOrIgnore(value[i]); - } - debugMod1(NAME, "used: %d", used); - } +// /** add multiple values to the buffer, if there is enough space */ +// void addOrIgnore(const Scalar* value, const size_t len) { +// debugMod1(NAME, "addOrIgnore(%d)", len); +// for (size_t i = 0; i < len; ++i) { +// addOrIgnore(value[i]); +// } +// debugMod1(NAME, "used: %d", used); +// } /** add multiple values to the buffer, if there is enough space */ void addOrCallback(const Scalar* value, const size_t len, RingBufferFullCallback cb) { @@ -86,6 +87,18 @@ public: debugMod1(NAME, "used: %d", used); } + /** add as many entries as possible */ + size_t addOrIgnore(const Scalar* value, const size_t len) { + const size_t toAdd = min(len, getNumFree()); + for (size_t i = 0; i < toAdd; ++i) { + data[head] = value[i]; + head = (head + 1) % size; + } + used += toAdd; + debugMod3(NAME, "addOrIgnore(%d) added %d bytes. used: %d", len, toAdd, used); + return toAdd; + } + // /** anything available? */ // bool hasNext() const { // return used != 0; @@ -99,7 +112,7 @@ public: return res; } - const Scalar& getConst() const { + const Scalar& getConst() { const uint8_t idx = tail; tail = (tail + 1) % size; --used; diff --git a/ext/audio/VS1003.h b/ext/audio/VS1003.h index 6908243..add16ba 100644 --- a/ext/audio/VS1003.h +++ b/ext/audio/VS1003.h @@ -1,13 +1,60 @@ #ifndef VS1003_H #define VS1003_H -#include "../../io/fastGPIO.h" + #include "../../io/SoftSPI.h" #include "../../Debug.h" -//#include "IPlayable.h" -//#include "AudioData.h" -#include "../../data/ChunkBuffer.h" +#include "../../Platforms.h" +#if (PLATFORM == NODE_MCU) || (PLATFORM == WEMOS_D1_MINI) + + #include "../../io/fastGPIO.h" + + #define SPI_INIT() spi::init(); + #define SPI_WRITE_8(byte) spi::writeByte(byte) + #define SPI_WRITE_16(word) spi::writeWord(word) + #define SPI_READ_8(byte) spi::readByte(byte) + #define SPI_READ_16(word) spi::readWord(word) + #define SPI_READ_WRITE_8(byte) spi::readWriteByte(byte) + #define SPI_READ_WRITE_16(word) spi::readWriteWord(word) + #define SPI_DELAY() //os_delay_us(10) + + #define CFG_DREG() GPIO16_INPUT_SET + #define GET_DREQ() GPIO16_IN // PIN D0 + + #define MP3_CTRL_DESELECT() spi::chipDeselect(); + #define MP3_CTRL_SELECT() spi::chipSelect(); + + #define CFG_DATA_SELECT() GPIO5_OUTPUT_SET + #define MP3_DATA_DESELECT() GPIO5_H + #define MP3_DATA_SELECT() GPIO5_L + +#elif (PLATFORM == WROOM32_DEVKIT) + + #include "driver/gpio.h" + + #define SPI_INIT() spi::init(); + #define SPI_WRITE_8(byte) spi::writeByte(byte) + #define SPI_WRITE_16(word) spi::writeWord(word) + #define SPI_READ_8(byte) spi::readByte(byte) + #define SPI_READ_16(word) spi::readWord(word) + #define SPI_READ_WRITE_8(byte) spi::readWriteByte(byte) + #define SPI_READ_WRITE_16(word) spi::readWriteWord(word) + #define SPI_DELAY() //os_delay_us(10) + + #define CFG_DREG() gpio_set_direction(GPIO_NUM_26, GPIO_MODE_INPUT) + #define GET_DREQ() gpio_get_level(GPIO_NUM_26) + + #define MP3_CTRL_DESELECT() spi::chipDeselect(); + #define MP3_CTRL_SELECT() spi::chipSelect(); + + #define CFG_DATA_SELECT() gpio_set_direction(GPIO_NUM_33, GPIO_MODE_OUTPUT) + #define MP3_DATA_DESELECT() gpio_set_level(GPIO_NUM_33, 1) + #define MP3_DATA_SELECT() gpio_set_level(GPIO_NUM_33, 0) + +#else + #error "no platform" +#endif /** * control a VS1003 audio chip attached to the SPI port @@ -18,24 +65,6 @@ private: static constexpr const char* NAME = "VS1003"; - //SoftSPI spi; - #define SPI_INIT() spi::init(); - #define SPI_WRITE_8(byte) spi::writeByte(byte) - #define SPI_WRITE_16(word) spi::writeWord(word) - #define SPI_READ_8(byte) spi::readByte(byte) - #define SPI_READ_16(word) spi::readWord(word) - #define SPI_READ_WRITE_8(byte) spi::readWriteByte(byte) - #define SPI_READ_WRITE_16(word) spi::readWriteWord(word) - //#define SPI_DELAY() os_delay_us(10) - #define SPI_DELAY() //os_delay_us(10) - - #define GET_DREQ() GPIO16_IN // PIN D0 - - #define MP3_CTRL_DESELECT() spi::chipDeselect(); - #define MP3_CTRL_SELECT() spi::chipSelect(); - #define MP3_DATA_DESELECT() GPIO5_H - #define MP3_DATA_SELECT() GPIO5_L - enum OP { WRITE = 0x2, READ = 0x3, @@ -107,8 +136,8 @@ public: SPI_INIT(); - GPIO5_OUTPUT_SET; // Pin D1 - XDCS - GPIO16_INPUT_SET; // Pin D0 - DREQ + CFG_DATA_SELECT(); // XDCS + CFG_DREG(); // DREQ this->state = PlayState::STOPPED; @@ -134,6 +163,14 @@ public: return (255 - (values & 0xFF)); } + void dbg() { + debugMod1(NAME, "mode: %d", readRegister(Register::SCI_MODE)); + debugMod1(NAME, "vol: %d", readRegister(Register::SCI_VOL)); + debugMod1(NAME, "status: %d", readRegister(Register::SCI_STATUS)); + debugMod1(NAME, "clk: %d", readRegister(Register::SCI_CLOCKF)); + + } + // void play(AudioData ad) { // this->playable = ad; // this->state = PlayState::PLAYING; @@ -145,24 +182,25 @@ public: // this->state = PlayState::PLAYING; // } - void ICACHE_FLASH_ATTR record(RecordSource source) { + /* + void IN_FLASH record(RecordSource source) { state = PlayState::RECORDING; - writeRegister(Register::SCI_CLOCKF, 0x4430); /* 2.0x 12.288MHz */ - os_delay_us(100); + writeRegister(Register::SCI_CLOCKF, 0x4430); // 2.0x 12.288MHz + DELAY_US(100); - writeRegister(Register::SCI_AICTRL0, 12); /* Div -> 12=8kHz 8=12kHz 6=16kHz */ - os_delay_us(100); + writeRegister(Register::SCI_AICTRL0, 12); // Div -> 12=8kHz 8=12kHz 6=16kHz + DELAY_US(100); - writeRegister(Register::SCI_AICTRL1, 0); /* Auto gain */ - os_delay_us(100); + writeRegister(Register::SCI_AICTRL1, 0); // Auto gain + DELAY_US(100); setVolume(255); os_printf("mode: %d", readRegister(Register::SCI_MODE)); writeRegister(Register::SCI_MODE, readRegister(Register::SCI_MODE) | (int)Mode::SM_ADPCM | (int)Mode::SM_RESET); - os_delay_us(100); + DELAY_US(100); os_printf("mode: %d", readRegister(Register::SCI_MODE)); @@ -174,11 +212,12 @@ public: // } awaitDREQ(); - os_delay_us(100); + DELAY_US(100); } + */ - PlayState ICACHE_FLASH_ATTR getState() const { + PlayState IN_FLASH getState() const { return this->state; } @@ -187,7 +226,7 @@ public: this->state = PlayState::PLAYING; } - void ICACHE_FLASH_ATTR run() { + size_t IN_FLASH run() { if(this->state == PlayState::PLAYING) { if (playBuf.empty()) { //uint8_t stopSequence[4] = {0}; @@ -196,11 +235,13 @@ public: } else { //const ChunkBuffer::Data chunk = playBuf.get(32); //playChunk32(chunk.ptr, chunk.size); - sendPlayBuffer(); + return sendPlayBuffer(); } } + return 0; } + /* uint32_t getRecordData(uint8_t* buffer, uint32_t maxLength) { uint32_t available = this->getNumAvailableRecordBytes(); @@ -237,6 +278,15 @@ public: + } + */ + + uint16_t getSampleRate() const { + return readRegister(Register::SCI_AUDATA) & 0xFFFE; // exclude last bit (mono/stereo) + } + + bool isStereo() const { + return readRegister(Register::SCI_AUDATA) & 1; } PlayBuf& getPlayBuffer() { @@ -255,7 +305,7 @@ private: return (uint32_t)readRegister(Register::SCI_HDAT1) * sizeof(uint16_t); } - void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const { + void IN_FLASH playChunk32(const uint8_t* buffer, const uint32_t len) const { debugMod1(NAME, "playChunk32() len:%d", len); MP3_CTRL_DESELECT(); MP3_DATA_SELECT(); @@ -267,10 +317,10 @@ private: MP3_DATA_DESELECT(); } - void ICACHE_FLASH_ATTR sendPlayBuffer() { + size_t IN_FLASH sendPlayBuffer() { MP3_CTRL_DESELECT(); MP3_DATA_SELECT(); - int written = 0; + size_t written = 0; while(canConsume() && !playBuf.empty()) { const uint8_t byte = playBuf.get(); if (byte == -1) {break;} @@ -279,6 +329,7 @@ private: } if (written) {debugMod1(NAME, "sendPlayBuffer(): %d bytes", written);} MP3_DATA_DESELECT(); + return written; } /** @@ -342,11 +393,11 @@ private: debugMod(NAME, "reset()"); writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS - os_delay_us(1); + DELAY_US(1); awaitDREQ(); writeRegister(SCI_CLOCKF, 0x9800); setVolume(0); - os_delay_us(1); + DELAY_US(1); MP3_DATA_SELECT(); SPI_WRITE_8(0); @@ -355,7 +406,7 @@ private: SPI_WRITE_8(0); MP3_DATA_DESELECT(); - os_delay_us(100); + DELAY_US(100); MP3_CTRL_DESELECT(); debugMod(NAME, "reset() complete"); diff --git a/ext/led/WS2812B.h b/ext/led/WS2812B.h index bd67a5b..fb299d6 100644 --- a/ext/led/WS2812B.h +++ b/ext/led/WS2812B.h @@ -1,122 +1,646 @@ #ifndef WS2812B_H #define WS2812B_H -#include "../../io/fastGPIO.h" -struct Color { +#include "../../data/Color.h" +#include "../../Platforms.h" - uint8_t r; - uint8_t g; - uint8_t b; - /** no-init */ - Color() { - ; - } -private: - /** Hidden ctor. RGB */ - Color(const uint8_t r, const uint8_t g, const uint8_t b) : r(r), g(g), b(b) { - ; - } +#if ESP8266 -public: + #include "../../io/fastGPIO.h" - /** get X-percent [0:100] of this color = darker/brighter */ - Color inline getPercent(const int percent) const { - return Color(r*percent/100, g*percent/100, b*percent/100); - } + template class WS2812B { - /** mix the two given colors */ - static Color mix(const Color c1, const Color c2, int percentC1) { - return Color( - ((c1.r * percentC1) + (c2.r * (100-percentC1))) / 100, - ((c1.g * percentC1) + (c2.g * (100-percentC1))) / 100, - ((c1.b * percentC1) + (c2.b * (100-percentC1))) / 100 - ); - } + #define LED_SET_PIN_TO_OUTPUT GPIO5_OUTPUT_SET + #define LED_SET_PIN_H GPIO5_H + #define LED_SET_PIN_L GPIO5_L - static inline Color fromRGB(const uint8_t r, const uint8_t g, const uint8_t b) { - return Color(r,g,b); - } + //#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) ) - static inline Color fromHSV(const uint8_t h, const uint8_t s, const uint8_t v) { - Color c; c.setHSV(h,s,v); return c; - } + /** color-value for each attached LED */ + Color colors[numLEDs]; - void setHSV(const uint8_t h, const uint8_t s, const uint8_t v) { + /** enable/disable each led */ + bool enabled[numLEDs] = {true}; - uint8_t region, remainder, p, q, t; + public: - region = h / 43; - remainder = (h - (region * 43)) * 6; - - p = (v * (255 - s)) >> 8; - q = (v * (255 - ((s * remainder) >> 8))) >> 8; - t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; - - switch (region) { - case 0: - r = v; g = t; b = p; - break; - case 1: - r = q; g = v; b = p; - break; - case 2: - r = p; g = v; b = t; - break; - case 3: - r = p; g = q; b = v; - break; - case 4: - r = t; g = p; b = v; - break; - default: - r = v; g = p; b = q; - break; + /** ctor */ + WS2812B() { + init(); } + void init() { + LED_SET_PIN_TO_OUTPUT; + } + + /** set the color for the given LED */ + void setColor(const uint8_t idx, const Color rgb) { + colors[idx] = rgb; + } + + /** set the color for all LEDs */ + void setColor(const Color rgb) { + for (int idx = 0; idx < numLEDs; ++idx) { + colors[idx] = rgb; + } + } + + /** enable/disable the given LED */ + void setEnabled(const uint8_t idx, const bool en) { + enabled[idx] = en; + } + + /** enable/disable all LEDs */ + void setEnabled(const bool en) { + for (int idx = 0; idx < numLEDs; ++idx) { + enabled[idx] = en; + } + } + + /** is the given LED enabled? */ + bool isEnabled(const uint8_t idx) const { + return enabled[idx]; + } + + Color& getColor(const uint8_t idx) { + return colors[idx]; + } + + /** flush configured changes */ + void flush() { + + LED_SET_PIN_TO_OUTPUT; + + ets_intr_lock(); + + // process each LED + for (uint8_t i = 0; i < numLEDs; ++i) { + + // send each LEDs 24-bit GRB data + if (enabled[i]) { + const Color rgb = colors[i]; + sendByte(rgb.g); + sendByte(rgb.r); + sendByte(rgb.b); + } else { + sendByte(0); + sendByte(0); + sendByte(0); + } + + } + + ets_intr_unlock(); + + reset(); + + } + + private: + + inline void sendByte(uint8_t b) { + // for (uint8_t i = 0; i < 8; ++i) { + // if (b & 0b10000000) { + // send1(); + // } else { + // send0(); + // } + // b <<= 1; + // } + if (b & 0b10000000) {send1();} else {send0();} + if (b & 0b01000000) {send1();} else {send0();} + if (b & 0b00100000) {send1();} else {send0();} + if (b & 0b00010000) {send1();} else {send0();} + if (b & 0b00001000) {send1();} else {send0();} + if (b & 0b00000100) {send1();} else {send0();} + if (b & 0b00000010) {send1();} else {send0();} + if (b & 0b00000001) {send1();} else {send0();} + } + + __attribute__((always_inline)) inline void send1() { // 800ns high, 450ns low + LED_SET_PIN_H; + delay800(); + LED_SET_PIN_L; + delay100(); + } + + __attribute__((always_inline)) inline void send0() { // 400ns high, 850ns low + LED_SET_PIN_H; + delay100(); + LED_SET_PIN_L; + delay800(); + } + + __attribute__((always_inline)) inline void reset() { + LED_SET_PIN_L; + os_delay_us(50); + //delayMicroseconds(50); + } + + + //#pragma GCC optimize 0 + + __attribute__((always_inline)) inline void delay50() { + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + } + + + // 100 ns delay. @80 MHz one nop = 12,5ns + __attribute__((always_inline)) inline void delay100() { + delay50(); + delay50(); + } + + /* + __attribute__((always_inline)) inline void delay200() { + delay100(); + delay100(); + } + + __attribute__((always_inline)) inline void delay400() { + delay100(); + delay100(); + delay100(); + delay100(); + } + + __attribute__((always_inline)) inline void delay500() { + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + } + + __attribute__((always_inline)) inline void delay600() { + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + } + */ + + __attribute__((always_inline)) inline void delay800() { + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + } + + + // #pragma GCC reset_options + + + + // inline void delayNS(const uint16_t ns) { + // const uint16_t ticks = ns / NS_PER_TICK / 2; + // for (uint16_t i = 0; i < ticks; ++i) {asm("nop");} + // } + + }; + +#elif ESP32aaa + + //#include + #include + #include + #include + #include + + template class WS2812B { + + static constexpr const char* TAG = "WS2812"; + static constexpr rmt_channel_t chan = RMT_CHANNEL_0; + + //#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0) + //#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0) + //#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + //#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + + static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer! + //static constexpr int numItems = 256;//(numLEDs*3)*8; + //static constexpr int numItems = (numLEDs*3)*8; + rmt_item32_t items[numBits+1]; // + one entry has two bits + 0-terminator (just like within strings) + + /** enable/disable each led */ + bool enabled[numLEDs] = {true}; + + /** color-value for each attached LED */ + Color colors[numLEDs]; + + public: + + /** ctor */ + WS2812B() { + init(); + } + + void init() { + + ESP_LOGI(TAG, "init()"); + + rmt_config_t cfg; + cfg.rmt_mode = RMT_MODE_TX; + cfg.channel = chan; + cfg.gpio_num = outPin; + cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;// + cfg.clk_div = 8; + cfg.tx_config.loop_en = false; + cfg.tx_config.carrier_en = false; + cfg.tx_config.idle_output_en = true; + cfg.tx_config.idle_level = (rmt_idle_level_t)0; + cfg.tx_config.carrier_freq_hz = 10000; + cfg.tx_config.carrier_level = (rmt_carrier_level_t)1; + cfg.tx_config.carrier_duty_percent = 50; + + ESP_ERROR_CHECK(rmt_config(&cfg)); + ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0)); + + // setup constant values once + for (int idx = 0; idx < numBits; ++idx) { + items[idx].duration0 = 0; + items[idx].level0 = 1; + items[idx].duration1 = 0; + items[idx].level1 = 0; + } + items[numBits].val = 0; // 0 terminator + + ESP_LOGI(TAG, "init OK!"); + + } + + /** set the color for the given LED */ + void setColor(const uint8_t idx, const Color rgb) { + colors[idx] = rgb; + } + + /** set the color for all LEDs */ + void setColor(const Color rgb) { + for (int idx = 0; idx < numLEDs; ++idx) { + colors[idx] = rgb; + } + } + + /** enable/disable the given LED */ + void setEnabled(const uint8_t idx, const bool en) { + enabled[idx] = en; + } + + /** enable/disable all LEDs */ + void setEnabled(const bool en) { + for (int idx = 0; idx < numLEDs; ++idx) { + enabled[idx] = en; + } + } + + /** is the given LED enabled? */ + bool isEnabled(const uint8_t idx) const { + return enabled[idx]; + } + + Color& getColor(const uint8_t idx) { + return colors[idx]; + } + + int nextLED; + + /** flush configured changes */ + IRAM_ATTR void flush() { + + nextLED = 0; + ESP_LOGI(TAG, "sending %d LEDs", numLEDs); + + // important! otherwise often interrupted within + portDISABLE_INTERRUPTS(); + + waitForTX(); + + const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator + + // add null terminator + items[toWrite-1].val = 0; + + // process each LED + for (int i = 0; i < numLEDs; i+=1) { + + int idx = 0; + if (enabled[i]) { + emplaceByte(colors[i].g, idx); + emplaceByte(colors[i].r, idx); + emplaceByte(colors[i].b, idx); + } else { + emplaceByte(0, idx); + emplaceByte(0, idx); + emplaceByte(0, idx); + } + + // wait for sending to complete using the RMT Unit's status + waitForTX(); + + ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0)); + ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); + + // reset for next round + //idx = 0; + + } + + //waitForTX(); + + portENABLE_INTERRUPTS(); + + } + + + + private: + + IRAM_ATTR void waitForTX() { + + uint32_t status; // status while sending: 16797696 ?? + for (int i = 0; i < 2000; ++i) { + rmt_get_status(this->chan, &status); + if (status == 0) {break;} + //if (status != 16797696) {break;} + //if ( (status & (1<<24)) == 0 ) {break;} // somewhat OK + } + + + +// for (int i = 0; i < 1000; ++i) { +// asm volatile("nop"); +// } + + } + + /** send one whole byte */ + inline void emplaceByte(const uint8_t b, int& idx) { + if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);} + } + + /** emplace a 0 (short 1 pulse, long 0 pulse) */ + inline void emplace0(int& idx) { + items[idx].duration0 = 2;//1; + //items[idx].level0 = 1; + items[idx].duration1 = 8;//7; + //items[idx].level1 = 0; + ++idx; + } + + /** emplace a 1 (long 1 pulse, short 0 pulse) */ + inline void emplace1(int& idx) { + items[idx].duration0 = 7;//6; + //items[idx].level0 = 1; + items[idx].duration1 = 3;//2; + //items[idx].level1 = 0; + ++idx; + } + + + }; + +#elif ESP32 + + #include + #include + #include + #include "driver/timer.h" + + static constexpr timer_group_t grp = TIMER_GROUP_0; + static constexpr timer_idx_t idx = TIMER_0; + + template class WS2812B; + + static IRAM_ATTR void myISR(void* arg) { + printf("interrupt\n"); + //WS2812B* leds = (WS2812B*) arg; + //timer_pause(grp, idx); + //leds->flushOLD(); } - void setRGB(const uint8_t r, const uint8_t g, const uint8_t b) { - this->r = r; - this->g = g; - this->b = b; - } + template class WS2812B { - void setOff() { - this->r = 0; - this->g = 0; - this->b = 0; - } + static constexpr const char* TAG = "WS2812"; + + /** enable/disable each led */ + bool enabled[numLEDs] = {true}; + + /** color-value for each attached LED */ + Color colors[numLEDs]; + + public: + + /** ctor */ + WS2812B() { + init(); + } + + void init() { + + ESP_LOGI(TAG, "init()"); + + gpio_set_direction(outPin, GPIO_MODE_OUTPUT); + + } + + /** set the color for the given LED */ + void setColor(const uint8_t idx, const Color rgb) { + colors[idx] = rgb; + } + + /** set the color for all LEDs */ + void setColor(const Color rgb) { + for (int idx = 0; idx < numLEDs; ++idx) { + colors[idx] = rgb; + } + } + + /** enable/disable the given LED */ + void setEnabled(const uint8_t idx, const bool en) { + enabled[idx] = en; + } + + /** enable/disable all LEDs */ + void setEnabled(const bool en) { + for (int idx = 0; idx < numLEDs; ++idx) { + enabled[idx] = en; + } + } + + /** is the given LED enabled? */ + bool isEnabled(const uint8_t idx) const { + return enabled[idx]; + } + + Color& getColor(const uint8_t idx) { + return colors[idx]; + } - /** add two colors */ - Color operator + (const Color o) const { - return Color(r+o.r, g+o.g, b+o.g); - } + timer_config_t config; - Color operator * (const float f) const { - return Color(r*f, g*f, b*f); - } + /** flush configured changes */ + IRAM_ATTR void flush() { -}; + printf("flush()\n"); -template class WS2812B { + config.alarm_en = 1; + config.auto_reload = 1; + config.counter_dir = TIMER_COUNT_UP; + config.divider = 2; + config.intr_type = TIMER_INTR_LEVEL; + config.counter_en = TIMER_PAUSE; - #define LED_SET_PIN_TO_OUTPUT GPIO5_OUTPUT_SET - #define LED_SET_PIN_H GPIO5_H - #define LED_SET_PIN_L GPIO5_L - //#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) ) - /** color-value for each attached LED */ - Color colors[numLEDs]; + ESP_ERROR_CHECK(timer_init(grp, idx, &config)); + + ESP_ERROR_CHECK(timer_set_counter_value(grp, idx, 0)); + ESP_ERROR_CHECK(timer_set_alarm_value(grp, idx, 2)); + ESP_ERROR_CHECK(timer_enable_intr(grp, idx)); + ESP_ERROR_CHECK(timer_isr_register(grp, idx, myISR, (void*)this, ESP_INTR_FLAG_IRAM, NULL)); + + printf("start()\n"); + ESP_ERROR_CHECK(timer_start(grp, idx)); + + } + + + + IRAM_ATTR void flushOLD() { + + ESP_LOGI(TAG, "sending %d LEDs", numLEDs); + + // important! otherwise often interrupted within + portDISABLE_INTERRUPTS(); + + //portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED; + //portENTER_CRITICAL(&myMutex); + + + // process each LED + for (int i = 0; i < numLEDs; ++i) { + + portDISABLE_INTERRUPTS(); + const volatile Color c = colors[i]; + + if (enabled[i]) { + sendByte(c.g); + sendByte(c.r); + sendByte(c.b); + } else { + sendByte(0); + sendByte(0); + sendByte(0); + } + + } + + //portEXIT_CRITICAL(&myMutex); + + portENABLE_INTERRUPTS(); + + } + + private: + + /** send one whole byte */ + inline void sendByte(const uint8_t b) { + if (b & 0b10000000) {send1();} else {send0();} + if (b & 0b01000000) {send1();} else {send0();} + if (b & 0b00100000) {send1();} else {send0();} + if (b & 0b00010000) {send1();} else {send0();} + if (b & 0b00001000) {send1();} else {send0();} + if (b & 0b00000100) {send1();} else {send0();} + if (b & 0b00000010) {send1();} else {send0();} + if (b & 0b00000001) {send1();} else {send0();} + } + + /** send a 0 (short 1 pulse, long 0 pulse) */ + inline void send0() { + gpio_set_level(outPin, 1); + delayShort(); + gpio_set_level(outPin, 0); + delayLong(); + } + + /** send a 1 (long 1 pulse, short 0 pulse) */ + inline void send1() { + gpio_set_level(outPin, 1); + delayLong(); + gpio_set_level(outPin, 0); + delayShort(); + } + + inline void delayShort() { + volatile int i; + i = 0; i = 1; i = 2; i = 3; + (void) i; + } + + inline void delayLong() { + volatile int j; + j = 0; j = 1; j = 2; j = 3; + j = 4; j = 5; j = 6; j = 7; + j = 8; j = 9; j = 1; j = 2; + (void) j; +// for (size_t i = 0; i < 16; ++i) { +// asm volatile("nop"); +// } + } + + + }; + +#elif othertest + +//#include +#include +#include +#include +#include + +template class WS2812B { + + static constexpr const char* TAG = "WS2812"; + static constexpr rmt_channel_t chan = RMT_CHANNEL_0; + + //#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0) + //#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0) + //#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + //#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + + static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer! + //static constexpr int numItems = 256;//(numLEDs*3)*8; + //static constexpr int numItems = (numLEDs*3)*8; + rmt_item32_t items[numBits+1]; // + one entry has two bits + 0-terminator (just like within strings) /** enable/disable each led */ bool enabled[numLEDs] = {true}; + /** color-value for each attached LED */ + Color colors[numLEDs]; + public: /** ctor */ @@ -125,7 +649,39 @@ template class WS2812B { } void init() { - LED_SET_PIN_TO_OUTPUT; + + ESP_LOGI(TAG, "init()"); + + rmt_config_t cfg; + cfg.rmt_mode = RMT_MODE_TX; + cfg.channel = chan; + cfg.gpio_num = outPin; + cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;// + cfg.clk_div = 8; + cfg.tx_config.loop_en = false; + cfg.tx_config.carrier_en = false; + cfg.tx_config.idle_output_en = true; + cfg.tx_config.idle_level = (rmt_idle_level_t)0; + cfg.tx_config.carrier_freq_hz = 10000; + cfg.tx_config.carrier_level = (rmt_carrier_level_t)1; + cfg.tx_config.carrier_duty_percent = 50; + + ESP_ERROR_CHECK(rmt_config(&cfg)); + + // disable when using custom ISR + //ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0)); + + // setup constant values once + for (int idx = 0; idx < numBits; ++idx) { + items[idx].duration0 = 0; + items[idx].level0 = 1; + items[idx].duration1 = 0; + items[idx].level1 = 0; + } + items[numBits].val = 0; // 0 terminator + + ESP_LOGI(TAG, "init OK!"); + } /** set the color for the given LED */ @@ -158,149 +714,163 @@ template class WS2812B { } Color& getColor(const uint8_t idx) { - return colors[idx]; + return colors[idx]; } + int nextLED; + /** flush configured changes */ + /* void flush() { - LED_SET_PIN_TO_OUTPUT; + nextLED = 0; + ESP_LOGI(TAG, "sending %d LEDs", numLEDs); - ets_intr_lock(); + // important! otherwise often interrupted within + portDISABLE_INTERRUPTS(); - // process each LED - for (uint8_t i = 0; i < numLEDs; ++i) { + for (int i = 0; i < 1024; ++i) { + asm volatile("nop"); + } - // send each LEDs 24-bit GRB data - if (enabled[i]) { - const Color rgb = colors[i]; - sendByte(rgb.g); - sendByte(rgb.r); - sendByte(rgb.b); - } else { - sendByte(0); - sendByte(0); - sendByte(0); - } + //waitForTX(); + + const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator + + // add null terminator + items[toWrite-1].val = 0; + + // process each LED + for (int i = 0; i < numLEDs; i+=2) { + + enqueueNext(); + + // add null terminator + //items[idx].val = 0; + + // wait for sending to complete using the RMT Unit's status + waitForTX(); + + ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0)); + ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); + + // reset for next round + //idx = 0; } - ets_intr_unlock(); + //waitForTX(); - reset(); + portENABLE_INTERRUPTS(); + + } + */ + + static constexpr int toWrite = 8+8+8+1; + rmt_isr_handle_t isrHandle; + + void flush() { + + nextLED = 0; + ESP_LOGI(TAG, "sending %d LEDs", numLEDs); + + enqueueNext(); + + ESP_ERROR_CHECK(rmt_isr_register(onISR, this, ESP_INTR_FLAG_LEVEL1, &isrHandle)); + ESP_ERROR_CHECK(rmt_set_tx_thr_intr_en(this->chan, true, toWrite/2)); + //ESP_ERROR_CHECK(rmt_set_tx_intr_en(this->chan, true)); + + printf("_start\n"); + ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); } - private: +private: - inline void sendByte(uint8_t b) { -// for (uint8_t i = 0; i < 8; ++i) { -// if (b & 0b10000000) { -// send1(); -// } else { -// send0(); -// } -// b <<= 1; -// } - if (b & 0b10000000) {send1();} else {send0();} - if (b & 0b01000000) {send1();} else {send0();} - if (b & 0b00100000) {send1();} else {send0();} - if (b & 0b00010000) {send1();} else {send0();} - if (b & 0b00001000) {send1();} else {send0();} - if (b & 0b00000100) {send1();} else {send0();} - if (b & 0b00000010) {send1();} else {send0();} - if (b & 0b00000001) {send1();} else {send0();} + static IRAM_ATTR void onISR(void* arg) { + printf(".\n"); + WS2812B* led = (WS2812B*) arg; + if (led->nextLED < numLEDs) { + led->enqueueNext(); + } else { + rmt_tx_stop(led->chan); + } } - __attribute__((always_inline)) inline void send1() { // 800ns high, 450ns low - LED_SET_PIN_H; - delay800(); - LED_SET_PIN_L; - delay100(); + void IRAM_ATTR enqueueNext() { + + printf("e\n"); + int idx = 0; + + if (enabled[nextLED]) { + emplaceByte(colors[nextLED].g, idx); + emplaceByte(colors[nextLED].r, idx); + emplaceByte(colors[nextLED].b, idx); + } else { + emplaceByte(0, idx); + emplaceByte(0, idx); + emplaceByte(0, idx); + } + + ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0)); + + ++nextLED; + } - __attribute__((always_inline)) inline void send0() { // 400ns high, 850ns low - LED_SET_PIN_H; - delay100(); - LED_SET_PIN_L; - delay800(); + void waitForTX() { + + uint32_t status; // status while sending: 16797696 ?? + for (int i = 0; i < 2000; ++i) { + rmt_get_status(this->chan, &status); + if (status == 0) {break;} + //if (status != 16797696) {break;} + //if ( (status & (1<<24)) == 0 ) {break;} // somewhat OK + } + + + +// for (int i = 0; i < 1000; ++i) { +// asm volatile("nop"); +// } + } - __attribute__((always_inline)) inline void reset() { - LED_SET_PIN_L; - os_delay_us(50); - //delayMicroseconds(50); + /** send one whole byte */ + inline void emplaceByte(const uint8_t b, int& idx) { + if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);} + if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);} } - - //#pragma GCC optimize 0 - - __attribute__((always_inline)) inline void delay50() { - asm volatile("nop"); - asm volatile("nop"); - asm volatile("nop"); - asm volatile("nop"); + /** emplace a 0 (short 1 pulse, long 0 pulse) */ + inline void emplace0(int& idx) { + items[idx].duration0 = 2;//1; + //items[idx].level0 = 1; + items[idx].duration1 = 8;//7; + //items[idx].level1 = 0; + ++idx; } - - // 100 ns delay. @80 MHz one nop = 12,5ns - __attribute__((always_inline)) inline void delay100() { - delay50(); - delay50(); + /** emplace a 1 (long 1 pulse, short 0 pulse) */ + inline void emplace1(int& idx) { + items[idx].duration0 = 7;//6; + //items[idx].level0 = 1; + items[idx].duration1 = 3;//2; + //items[idx].level1 = 0; + ++idx; } - /* - __attribute__((always_inline)) inline void delay200() { - delay100(); - delay100(); - } - - __attribute__((always_inline)) inline void delay400() { - delay100(); - delay100(); - delay100(); - delay100(); - } - - __attribute__((always_inline)) inline void delay500() { - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - } - - __attribute__((always_inline)) inline void delay600() { - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - } -*/ - - __attribute__((always_inline)) inline void delay800() { - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - delay100(); - } - - - // #pragma GCC reset_options - - - - // inline void delayNS(const uint16_t ns) { - // const uint16_t ticks = ns / NS_PER_TICK / 2; - // for (uint16_t i = 0; i < ticks; ++i) {asm("nop");} - // } }; + + +#endif + #endif // WS2812B_H diff --git a/io/SoftSPI.h b/io/SoftSPI.h index da4726f..69fa790 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -3,6 +3,7 @@ //#include "IO.h" #include "../Platforms.h" +#include "../Debug.h" #if (PLATFORM == NODE_MCU) || (PLATFORM == WEMOS_D1_MINI) @@ -28,31 +29,43 @@ #define SPI_FAST -#elif (PLATFORM == NODE32S) +#elif (PLATFORM == WROOM32_DEVKIT) - #define SPI_CS_OUTPUT GPIO15_OUTPUT_SET - #define SPI_MOSI_OUTPUT GPIO13_OUTPUT_SET - #define SPI_MISO_INPUT GPIO12_INPUT_SET - #define SPI_MISO_NO_PULLUP GPIO12_INPUT_PULLUP_UNSET - #define SPI_CLK_OUTPUT GPIO14_OUTPUT_SET + #include "driver/gpio.h" + #include - #define SPI_CS_LO GPIO15_L - #define SPI_CS_HI GPIO15_H + #define SPI_PIN_MISO GPIO_NUM_35 + #define SPI_PIN_MOSI GPIO_NUM_32 + #define SPI_PIN_CLK GPIO_NUM_27 + #define SPI_PIN_CS GPIO_NUM_25 - #define SPI_CLK_LO GPIO14_L - #define SPI_CLK_HI GPIO14_H + #define SPI_CS_OUTPUT gpio_set_direction(SPI_PIN_CS, GPIO_MODE_OUTPUT) + #define SPI_MOSI_OUTPUT gpio_set_direction(SPI_PIN_MOSI, GPIO_MODE_OUTPUT) + #define SPI_MISO_INPUT gpio_set_direction(SPI_PIN_MISO, GPIO_MODE_INPUT) + #define SPI_MISO_NO_PULLUP //gpio_set_pull_mode(SPI_PIN_MISO, GPIO_FLOATING) /// ?????? + #define SPI_CLK_OUTPUT gpio_set_direction(SPI_PIN_CLK, GPIO_MODE_OUTPUT) - #define SPI_MOSI_LO GPIO13_L - #define SPI_MOSI_HI GPIO13_H + #define SPI_CS_LO gpio_set_level(SPI_PIN_CS, 0) + #define SPI_CS_HI gpio_set_level(SPI_PIN_CS, 1) - #define SPI_MISO_READ GPIO12_IN + #define SPI_CLK_LO gpio_set_level(SPI_PIN_CLK, 0) + #define SPI_CLK_HI gpio_set_level(SPI_PIN_CLK, 1) + + #define SPI_MOSI_LO gpio_set_level(SPI_PIN_MOSI, 0) + #define SPI_MOSI_HI gpio_set_level(SPI_PIN_MOSI, 1) + + #define SPI_MISO_READ gpio_get_level(SPI_PIN_MISO) #define SPI_FAST +#else + #error "not supported" #endif namespace spi { + static constexpr const char* NAME = "SoftSPI"; + // MTDI GPIO12 MISO (DIN) D6 // MTCK GPIO13 MOSI (DOUT) D7 // MTMS GPIO14 CLOCK D5 @@ -86,14 +99,14 @@ namespace spi { __asm__ __volatile__("nop"); } static inline void waitLong() { - os_delay_us(1); + DELAY_US(1); } #else static inline void wait() { - os_delay_us(2); + DELAY_US(2); } static inline void waitLong() { - os_delay_us(50); + DELAY_US(50); } #endif @@ -150,7 +163,8 @@ namespace spi { - static void ICACHE_FLASH_ATTR init() { + static void IN_FLASH init() { + debugMod(NAME, "init()"); SPI_MISO_INPUT; SPI_MISO_NO_PULLUP; // D6: MISO SPI_MOSI_OUTPUT; // D7: MOSI SPI_CLK_OUTPUT; // D5: CLK @@ -159,7 +173,7 @@ namespace spi { /** read 16 bits */ - static uint16_t ICACHE_FLASH_ATTR readWord() { + static uint16_t IN_FLASH readWord() { return (readBit() << 15) | (readBit() << 14) | @@ -180,7 +194,7 @@ namespace spi { } /** read 8 bits */ - static uint8_t ICACHE_FLASH_ATTR readByte() { + static uint8_t IN_FLASH readByte() { return (readBit() << 7) | (readBit() << 6) | @@ -194,7 +208,7 @@ namespace spi { - static void ICACHE_FLASH_ATTR writeWord(const uint16_t word) { + static void IN_FLASH writeWord(const uint16_t word) { writeBit(word & BIT(15)); writeBit(word & BIT(14)); writeBit(word & BIT(13)); @@ -213,7 +227,7 @@ namespace spi { writeBit(word & BIT( 0)); } - static void ICACHE_FLASH_ATTR writeByte(const uint8_t byte) { + static void IN_FLASH writeByte(const uint8_t byte) { writeBit(byte & BIT( 7)); writeBit(byte & BIT( 6)); writeBit(byte & BIT( 5)); @@ -224,7 +238,7 @@ namespace spi { writeBit(byte & BIT( 0)); } - static uint16 ICACHE_FLASH_ATTR readWriteWord(const uint16_t word) { + static uint16_t IN_FLASH readWriteWord(const uint16_t word) { return (readWriteBit(word & BIT(15)) << 15) | (readWriteBit(word & BIT(14)) << 14) | @@ -244,7 +258,7 @@ namespace spi { (readWriteBit(word & BIT( 0)) << 0); } - static uint8 ICACHE_FLASH_ATTR readWriteByte(const uint8_t byte) { + static uint8_t IN_FLASH readWriteByte(const uint8_t byte) { return (readWriteBit(byte & BIT( 7)) << 7) | (readWriteBit(byte & BIT( 6)) << 6) | diff --git a/net/DNS.h b/net/DNS.h new file mode 100644 index 0000000..f729100 --- /dev/null +++ b/net/DNS.h @@ -0,0 +1,69 @@ +#ifndef DNS_H +#define DNS_H + +#include "../Platforms.h" +#include "../Debug.h" + +typedef void (*DNSResolveCallback)(const char* name, const ip_addr_t* ip, void* arg); + +#if ESP8266 + class DNS { + + static constexpr const char* NAME = "DNS"; + + espconn dns; + ip_addr_t ip; + + DNSResolveCallback callback; + void* arg; + + public: + + /** resolve hostname. creates callback */ + void resolveHost(const char* c, DNSResolveCallback callback, void* arg) { + debugMod1(NAME, "dns lookup: %s", c); + this->callback = callback; + this->arg = arg; + dns.reverse = (void*) this; + espconn_gethostbyname(&dns, c, &ip, _onHostResolved); + } + + static void _onHostResolved(const char* name, const ip_addr_t* ipaddr, void* arg) { + debugMod1(NAME, "dns resolved: %s", c); + DNS* dns = (DNS*) arg; + dns->callback(name, ipaddr, dns->arg); + } + + }; +#elif ESP32 + + #include + + class DNS { + + static constexpr const char* NAME = "DNS"; + ip_addr_t ip; + DNSResolveCallback callback; + void* arg; + + public: + + /** resolve hostname. creates callback */ + void resolveHost(const char* host, DNSResolveCallback callback, void* arg) { + debugMod1(NAME, "dns lookup: %s", host); + this->callback = callback; + this->arg = arg; + dns_gethostbyname(host, &ip, _onHostResolved, this); + } + + static void _onHostResolved(const char* name, const ip_addr_t* ipaddr, void* arg) { + debugMod1(NAME, "dns resolved: %s", name); + DNS* dns = (DNS*) arg; + dns->callback(name, ipaddr, dns->arg); + } + + }; + +#endif + +#endif // DNS_H diff --git a/net/HTTP.h b/net/HTTP.h index 3588cac..d283ae8 100644 --- a/net/HTTP.h +++ b/net/HTTP.h @@ -1,7 +1,11 @@ #ifndef HTTP_H #define HTTP_H + +#include "../Platforms.h" + #include "TCP.h" +#include "DNS.h" #include "../Debug.h" class HTTPClient; @@ -10,13 +14,13 @@ typedef void (*HTTPCBodyDataCallback)(HTTPClient* httpc, const uint8_t* data, ui typedef void (*HTTPCConnectCallback)(HTTPClient* httpc); typedef void (*HTTPCDisconnectCallback)(HTTPClient* httpc); - class HTTPClient { static constexpr const char* NAME = "HTTPC"; TCP tcp; - espconn dns; + DNS dns; + ip_addr_t ip; char host[64]; char query[128]; @@ -70,7 +74,7 @@ public: debugMod3(NAME, "connect(%s) -> host: '%s' query: '%s'", url, this->host, this->query); - resolveHost(this->host); + dns.resolveHost(this->host, _onHostResolved, (void*)this); } @@ -80,6 +84,10 @@ public: numBreaks = 0; } + void consumed(uint16_t len) { + tcp.consumed(len); + } + private: /** from TCP: connected */ @@ -130,26 +138,23 @@ private: private: - /** resolve hostname. creates callback */ - void resolveHost(const char* c) { - debugMod1(NAME, "dns lookup: %s", c); - dns.reverse = (void*) this; - espconn_gethostbyname(&dns, c, &ip, _onHostResolved); - } + /** send the request header */ void sendRequest() { char buf[128]; - const int len = os_sprintf(buf, "GET %s HTTP/1.1\r\nicy-metadata:1\r\n\r\n", this->query); - debugMod(NAME, buf); + const int len = sprintf(buf, "GET %s HTTP/1.1\r\n\r\n", this->query); + //const int len = sprintf(buf, "GET %s HTTP/1.1\r\nicy-metadata:1\r\n\r\n", this->query); + printf(buf); + //debugMod(NAME, (const char*) buf); tcp.send((const uint8_t*)buf, len); } - static void _onHostResolved(const char* name, ip_addr_t* ipaddr, void* arg ) { - HTTPClient* http = (HTTPClient*) ((espconn*)arg)->reverse; + static void _onHostResolved(const char* name, const ip_addr_t* ipaddr, void* arg ) { + HTTPClient* http = (HTTPClient*) arg; if (ipaddr) { //debugMod1(NAME, "dns lookup returned: %s", tmp.toString()); - http->tcp.connect(IP(*ipaddr), 80); + http->tcp.connect(IP4(*ipaddr), 80); } else { debugMod1(NAME, "dns lookup failed: %s", name); } diff --git a/net/IP.h b/net/IP.h index 6e98b96..f23030e 100644 --- a/net/IP.h +++ b/net/IP.h @@ -1,46 +1,105 @@ #ifndef IP_H #define IP_H +#include "../Platforms.h" + #define Port uint16_t -struct IP { +#if ESP8266 - //uint32_t val; - ip_addr addr; + struct IP4 { - /** empty ctor */ - explicit IP() { - addr.addr = 0; - } + //uint32_t val; + ip_addr_t addr; - /** ctor with IP-string */ - explicit IP(const char* ipStr) { - set(ipStr); - } + /** empty ctor */ + explicit IP() { + addr.addr = 0; + } - /** ctor with ip_addr_t */ - explicit IP(ip_addr addr) : addr(addr) { - ; - } + /** ctor with IP-string */ + explicit IP(const char* ipStr) { + set(ipStr); + } - /** set the IP by string: x.x.x.x */ - void set(const char* ipStr) { - addr.addr = ipaddr_addr(ipStr); - } + /** ctor with ip_addr_t */ + explicit IP(ip_addr addr) : addr(addr) { + ; + } - /** convert to ip_addr/ip_addr_t */ - const ip_addr* getPtr() const { - return &addr; - } + /** set the IP by string: x.x.x.x */ + void set(const char* ipStr) { + addr.addr = ipaddr_addr(ipStr); + } - /** convert to string */ - const char* toString() const { - //static char str[16]; - //ipaddr_aton(str, (ip_addr*)&addr); - //return str; - return ipaddr_ntoa(&addr); - } + /** convert to ip_addr/ip_addr_t */ + const ip_addr* getPtr() const { + return &addr; + } -}; + /** convert to string */ + const char* toString() const { + //static char str[16]; + //ipaddr_aton(str, (ip_addr*)&addr); + //return str; + return ipaddr_ntoa(&addr); + } + + }; + +#elif ESP32 + + #include + + struct IP4 { + + ip_addr_t addr; + + /** empty ctor */ + explicit IP4() { + addr.type = IPADDR_TYPE_V4; + addr.u_addr.ip4.addr = 0; + } + + /** ctor with IP-string */ + explicit IP4(const char* ipStr) { + addr.type = IPADDR_TYPE_V4; + set(ipStr); + } + + /** ctor with ip4_addr */ + explicit IP4(ip4_addr _addr) { + addr.type = IPADDR_TYPE_V4; + addr.u_addr.ip4 = _addr; + } + + /** ctor with ip_addr_t */ + explicit IP4(ip_addr_t _addr) { + addr = _addr; + } + + + /** set the IP by string: x.x.x.x */ + void set(const char* ipStr) { + //addr.u_addr.ip4 = ip4addr_aton(ipStr); + ip4addr_aton(ipStr, &addr.u_addr.ip4); + } + + /** convert to ip_addr/ip_addr_t */ + const ip_addr_t* getPtr() const { + return &addr; + } + + /** convert to string */ + const char* toString() const { + //static char str[16]; + //ipaddr_aton(str, (ip_addr*)&addr); + //return str; + return ipaddr_ntoa(&addr); + } + + }; + +#endif #endif // IP_H diff --git a/net/TCP.h b/net/TCP.h index e8eb58e..507f5f1 100644 --- a/net/TCP.h +++ b/net/TCP.h @@ -1,10 +1,8 @@ #ifndef TCP_H #define TCP_H -extern "C" { - #include "mem.h" - #include "espconn.h" -} + +#include "../Platforms.h" class TCP; @@ -15,156 +13,337 @@ typedef void (*TCPDisconnectCallback)(TCP* tcp); #include "../Debug.h" #include "IP.h" -class TCP { +#if ESP8266 -private: - - static constexpr const char* NAME = "TCP"; - - espconn* con; - esp_tcp tcp; - bool connected = false; - - TCPDataCallback onData = nullptr; - TCPConnectCallback onConnect = nullptr; - TCPDisconnectCallback onDisconnect = nullptr; - void* userData = nullptr; - -public: - - TCP() { - init(); + extern "C" { + #include "mem.h" + #include "espconn.h" } - /** dtor */ - ~TCP() { - cleanup(); - } + class TCP { + private: - /** send data to the other side */ - bool send(const uint8_t* data, const size_t dataLen) { - const int res = espconn_sent(con, (unsigned char*)data, dataLen); - return (res == 0); - } + static constexpr const char* NAME = "TCP"; - /** connect to the given IP and port */ - void connect(const IP ip, const Port port) { - disconnect(); - debugMod2(NAME, "connect(%s, %d)", ip.toString(), port); - con->proto.tcp->remote_port = port; - con->proto.tcp->local_port = espconn_port(); - os_memcpy(con->proto.tcp->remote_ip, ip.getPtr(), 4); - espconn_connect(con); - connected = true; - } + espconn* con; + esp_tcp tcp; + bool connected = false; + TCPDataCallback onData = nullptr; + TCPConnectCallback onConnect = nullptr; + TCPDisconnectCallback onDisconnect = nullptr; + void* userData = nullptr; - /** terminate connection */ - void disconnect() { - if (!connected) {return;} - espconn_disconnect(con); - connected = false; - } + public: - void hold() { - if (connected) { - espconn_recv_hold(con); + TCP() { + init(); } - } - void unhold() { - if (connected) { - espconn_recv_unhold(con); + /** dtor */ + ~TCP() { + cleanup(); } - } - /** set the callback to call whenever data is received */ - void setDataCallback(TCPDataCallback callback) { - this->onData = callback; - } - /** set the callback to call when connection is established */ - void setConnectCallback(TCPConnectCallback callback) { - this->onConnect = callback; - } + /** send data to the other side */ + bool send(const uint8_t* data, const size_t dataLen) { + const int res = espconn_sent(con, (unsigned char*)data, dataLen); + return (res == 0); + } - /** set the callback to call when connection is lost */ - void setDisconnectCallback(TCPDisconnectCallback callback) { - this->onDisconnect = callback; - } + /** connect to the given IP and port */ + void connect(const IP ip, const Port port) { + disconnect(); + debugMod2(NAME, "connect(%s, %d)", ip.toString(), port); + con->proto.tcp->remote_port = port; + con->proto.tcp->local_port = espconn_port(); + os_memcpy(con->proto.tcp->remote_ip, ip.getPtr(), 4); + espconn_connect(con); + connected = true; + } - /** attach user-data to this object. can bed used within callbacks */ - void setUserData(void* ptr) { - this->userData = ptr; - } - /** get previously attached user data */ - void* getUserData() const { - return this->userData; - } + /** terminate connection */ + void disconnect() { + if (!connected) {return;} + espconn_disconnect(con); + connected = false; + } -private: + void hold() { + if (connected) { + espconn_recv_hold(con); + } + } - /** called for incoming data */ - static void _onData(void* ptr, char* data, unsigned short len) { - TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; - if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)data, len);} - } + void unhold() { + if (connected) { + espconn_recv_unhold(con); + } + } - /** called when connection is established */ - static void _onConnect(void* ptr) { - TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; - if (tcp->onConnect) {tcp->onConnect(tcp);} - tcp->connected = true; - } + /** set the callback to call whenever data is received */ + void setDataCallback(TCPDataCallback callback) { + this->onData = callback; + } - /** called when connection is lost */ - static void _onDisconnect(void* ptr) { - TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; - if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} - tcp->connected = false; - } + /** set the callback to call when connection is established */ + void setConnectCallback(TCPConnectCallback callback) { + this->onConnect = callback; + } + + /** set the callback to call when connection is lost */ + void setDisconnectCallback(TCPDisconnectCallback callback) { + this->onDisconnect = callback; + } + + /** attach user-data to this object. can bed used within callbacks */ + void setUserData(void* ptr) { + this->userData = ptr; + } + + /** get previously attached user data */ + void* getUserData() const { + return this->userData; + } + + private: + + /** called for incoming data */ + static void _onData(void* ptr, char* data, unsigned short len) { + TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; + if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)data, len);} + } + + /** called when connection is established */ + static void _onConnect(void* ptr) { + TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; + if (tcp->onConnect) {tcp->onConnect(tcp);} + tcp->connected = true; + } + + /** called when connection is lost */ + static void _onDisconnect(void* ptr) { + TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; + if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} + tcp->connected = false; + } - /** initialize the UDP "connection" */ - void init() { + /** initialize the UDP "connection" */ + void init() { - debugMod(NAME, "init()"); + debugMod(NAME, "init()"); - // allocate connection-objects - con = (espconn*) os_zalloc(sizeof(espconn)); - ets_memset( con, 0, sizeof( espconn ) ); + // allocate connection-objects + con = (espconn*) os_zalloc(sizeof(espconn)); + ets_memset( con, 0, sizeof( espconn ) ); - // configure - con->type = ESPCONN_TCP; - con->state = ESPCONN_NONE; + // configure + con->type = ESPCONN_TCP; + con->state = ESPCONN_NONE; - //con->proto.tcp = (esp_tcp*) os_zalloc(sizeof(esp_tcp)); - con->proto.tcp = &tcp; + //con->proto.tcp = (esp_tcp*) os_zalloc(sizeof(esp_tcp)); + con->proto.tcp = &tcp; - // attach ourselves for the callback - con->reverse = (void*) this; // user-data to refer to this class - espconn_regist_recvcb(con, _onData); - espconn_regist_connectcb(con, _onConnect); - espconn_regist_disconcb(con, _onDisconnect); -// espconn_regist_reconcb(con, _onDisconnect); + // attach ourselves for the callback + con->reverse = (void*) this; // user-data to refer to this class + espconn_regist_recvcb(con, _onData); + espconn_regist_connectcb(con, _onConnect); + espconn_regist_disconcb(con, _onDisconnect); + // espconn_regist_reconcb(con, _onDisconnect); - } + } - /** cleanup everything */ - void cleanup() { + /** cleanup everything */ + void cleanup() { - debugMod(NAME, "cleanup()"); + debugMod(NAME, "cleanup()"); - espconn_delete(con); - //os_free(con->proto.tcp); - os_free(con); - con = nullptr; + espconn_delete(con); + //os_free(con->proto.tcp); + os_free(con); + con = nullptr; - } + } -}; + }; + +#elif ESP32 + + #include "lwip/err.h" + #include "lwip/tcp.h" + #include "lwip/ip_addr.h" + + class TCP { + + private: + + static constexpr const char* NAME = "TCP"; + + struct tcp_pcb* pcb; + + bool connected = false; + + TCPDataCallback onData = nullptr; + TCPConnectCallback onConnect = nullptr; + TCPDisconnectCallback onDisconnect = nullptr; + void* userData = nullptr; + + public: + + TCP() { + init(); + } + + /** dtor */ + ~TCP() { + cleanup(); + } + + + /** send data to the other side */ + bool send(const uint8_t* data, const size_t dataLen) { + err_t res = tcp_write(pcb, data, dataLen, TCP_WRITE_FLAG_COPY); + return (res == ERR_OK); + } + + /** connect to the given IP and port */ + void connect(const IP4 ip, const Port port) { + debugMod2(NAME, "connect(%s, %d)", ip.toString(), port); + disconnect(); + tcp_connect(pcb, ip.getPtr(), port, _onConnectCallback); + } + + + /** terminate connection */ + void disconnect() { + if (!connected) {return;} + tcp_close(pcb); + connected = false; + } + + void hold() { +// if (connected) { +// espconn_recv_hold(con); +// } + } + + void unhold() { +// if (connected) { +// espconn_recv_unhold(con); +// } + } + + /** set the callback to call whenever data is received */ + void setDataCallback(TCPDataCallback callback) { + this->onData = callback; + } + + /** set the callback to call when connection is established */ + void setConnectCallback(TCPConnectCallback callback) { + this->onConnect = callback; + } + + /** set the callback to call when connection is lost */ + void setDisconnectCallback(TCPDisconnectCallback callback) { + this->onDisconnect = callback; + } + + /** attach user-data to this object. can bed used within callbacks */ + void setUserData(void* ptr) { + this->userData = ptr; + } + + /** get previously attached user data */ + void* getUserData() const { + return this->userData; + } + + /** inform backend we consumed some data */ + void consumed(uint16_t len) { + if (len) { + tcp_recved(pcb, len); + } + } + + private: + + /** called for incoming data */ + static err_t _onData(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err) { + + TCP* tcp = (TCP*) arg; + + // if p == nullptr -> disconnect -> inform listener + if (!p) { + if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} + return ERR_OK; + } + + // process pBuf (might be a chain of buffers) + do { + + // pass data to listener + if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)p->payload, p->len);} + + // next pBuf in chain (if any) + pbuf* next = p->next; + + // free current buffer (we consumed it completely) + pbuf_free(p); + + // proceed? + p = next; + + } while (p); + + // everything fine + return ERR_OK; + + } + + /** called when connection failed */ + static err_t _onConnectCallback(void* arg, struct tcp_pcb* tpcb, err_t err) { + TCP* tcp = (TCP*) arg; + if (err == ERR_OK) { + if (tcp->onConnect) {tcp->onConnect(tcp);} + tcp->connected = true; + } else { + if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} + tcp->connected = false; + } + return ERR_OK; + } + + /** initialize the UDP "connection" */ + void init() { + + debugMod(NAME, "init()"); + + pcb = tcp_new(); + if (!pcb) {debugMod(NAME, "error creating TCP socket");} + + // user-pointer + pcb->callback_arg = (void*) this; + + tcp_recv(pcb, _onData); + + } + + /** cleanup everything */ + void cleanup() { + + debugMod(NAME, "cleanup()"); + + // TODO + + } + + }; + +#endif #endif // TCP_H