From 528a00b0e99b838e83b8e63aa3305eef420699d0 Mon Sep 17 00:00:00 2001 From: kazu Date: Sun, 8 Jul 2018 17:47:59 +0200 Subject: [PATCH] many changes :Py --- Debug.h | 2 +- Platforms.h | 12 +++ c++.h | 53 +++++++++--- data/ChunkBuffer.h | 193 ++++++++++++++++++++++++++++++++++++++++++ data/RingBuffer.h | 90 +++++++++++++++----- ext/audio/AudioData.h | 6 +- ext/audio/VS1003.h | 122 +++++++++++++++++++------- ext/led/WS2812B.h | 30 ++++++- io/IO.h | 6 +- io/SoftSPI.h | 57 +++++++++---- net/HTTP.h | 160 ++++++++++++++++++++++++++++++++++ net/IP.h | 31 +++++-- net/TCP.h | 170 +++++++++++++++++++++++++++++++++++++ net/UDP.h | 2 +- 14 files changed, 843 insertions(+), 91 deletions(-) create mode 100644 Platforms.h create mode 100644 data/ChunkBuffer.h create mode 100644 net/HTTP.h create mode 100644 net/TCP.h diff --git a/Debug.h b/Debug.h index d781631..7a27ac7 100644 --- a/Debug.h +++ b/Debug.h @@ -42,7 +42,7 @@ void hexdump(const uint8_t* buf, uint8_t len) { #else - #define debug(module, str) + #define debug(str) #define debugMod(module, str) #define debugMod1(module, str, val) #define debugMod2(module, str, v1, v2) diff --git a/Platforms.h b/Platforms.h new file mode 100644 index 0000000..d6af7d6 --- /dev/null +++ b/Platforms.h @@ -0,0 +1,12 @@ + + +#define WEMOS_D1_MINI 1 +#define NODE_MCU 2 + + +// https://blog.anto.io/wp-content/uploads/2016/12/esp32.png +#define NODE32S 32 + + + +#define PLATFORM NODE32S diff --git a/c++.h b/c++.h index cf8b428..50cec8d 100644 --- a/c++.h +++ b/c++.h @@ -4,7 +4,11 @@ #define FALSE false typedef void (*int_handler_t)(void*); -/* + +// ONLY NEEDED FOR OLD SDK <= 2.0.0 +//#define OLD_SDK +#ifdef OLD_SDK + void *pvPortMalloc(size_t xWantedSize, const char* file, int line) __attribute__((malloc, alloc_size(1))); void *pvPortRealloc(void* ptr, size_t xWantedSize, const char* file, int line) __attribute__((alloc_size(2))); void vPortFree(void *ptr, const char* file, int line); @@ -98,17 +102,14 @@ inline uint32_t ETS_INTR_PENDING(void) #define ETS_CCOMPARE0_DISABLE() \ ETS_INTR_DISABLE(ETS_CCOMPARE0_INUM) - -//#define ETS_FRC_TIMER1_INTR_ATTACH(func, arg) \ -// ets_isr_attach(ETS_FRC_TIMER1_INUM, (int_handler_t)(func), (void *)(arg)) +//#define ETS_FRC_TIMER1_INTR_ATTACH(func, arg) ets_isr_attach(ETS_FRC_TIMER1_INUM, (int_handler_t)(func), (void *)(arg)) #define ETS_FRC_TIMER1_NMI_INTR_ATTACH(func) \ NmiTimSetFunc(func) -//#define ETS_GPIO_INTR_ATTACH(func, arg) \ -// ets_isr_attach(ETS_GPIO_INUM, (int_handler_t)(func), (void *)(arg)) +//#define ETS_GPIO_INTR_ATTACH(func, arg) ets_isr_attach(ETS_GPIO_INUM, (int_handler_t)(func), (void *)(arg)) #define ETS_GPIO_INTR_ENABLE() \ @@ -118,8 +119,7 @@ inline uint32_t ETS_INTR_PENDING(void) ETS_INTR_DISABLE(ETS_GPIO_INUM) -//#define ETS_UART_INTR_ATTACH(func, arg) \ -// ets_isr_attach(ETS_UART_INUM, (int_handler_t)(func), (void *)(arg)) +//#define ETS_UART_INTR_ATTACH(func, arg) ets_isr_attach(ETS_UART_INUM, (int_handler_t)(func), (void *)(arg)) #define ETS_UART_INTR_ENABLE() \ @@ -135,8 +135,7 @@ inline uint32_t ETS_INTR_PENDING(void) ETS_INTR_DISABLE(ETS_FRC_TIMER1_INUM) -//#define ETS_SPI_INTR_ATTACH(func, arg) \ -// ets_isr_attach(ETS_SPI_INUM, (int_handler_t)(func), (void *)(arg)) +//#define ETS_SPI_INTR_ATTACH(func, arg) ets_isr_attach(ETS_SPI_INUM, (int_handler_t)(func), (void *)(arg)) #define ETS_SPI_INTR_ENABLE() \ @@ -156,8 +155,7 @@ inline uint32_t ETS_INTR_PENDING(void) ETS_INTR_DISABLE(ETS_SLC_INUM) -//#define ETS_SDIO_INTR_ATTACH(func, arg) \ -// ets_isr_attach(ETS_SDIO_INUM, (int_handler_t)(func), (void *)(arg)) +//#define ETS_SDIO_INTR_ATTACH(func, arg) ets_isr_attach(ETS_SDIO_INUM, (int_handler_t)(func), (void *)(arg)) #define ETS_SDIO_INTR_ENABLE() \ ETS_INTR_ENABLE(ETS_SDIO_INUM) @@ -183,7 +181,31 @@ extern void ets_delay_us(uint32_t us); extern int os_printf_plus(const char * format, ...) __attribute__ ((format (printf, 1, 2))); -*/ + +#endif + +void* malloc(size_t size) { + return os_malloc(size); +} +void free(void* ptr) { + os_free(ptr); +} + + +void operator delete(void* ptr) { + os_free(ptr); +} + +void* operator new(size_t size) { + return os_malloc(size); +} + +/** missing within headers?! */ +int ipaddr_aton(const char *cp, ip_addr_t *addr)ICACHE_FLASH_ATTR; +char *ipaddr_ntoa(const ip_addr_t *addr); + + +//void run_sdk_tasks(); /* bool wifi_station_get_config(struct station_config *config); @@ -281,6 +303,7 @@ bool wifi_set_phy_mode(phy_mode_t mode); // ensure constructors are called // See more at: http://www.esp8266.com/viewtopic.php?f=9&t=478&start=8#sthash.uVL8shrS.dpuf +// https://www.esp8266.com/viewtopic.php?f=9&t=478&sid=4375c81b073ef125fb57ab2fc3b38041&start=8 // SEE NOTES BELOW!!! extern void (*__init_array_start)(void); extern void (*__init_array_end)(void); @@ -295,3 +318,7 @@ static void do_global_ctors(void) { } +// memory stuff +// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map + + diff --git a/data/ChunkBuffer.h b/data/ChunkBuffer.h new file mode 100644 index 0000000..f021d59 --- /dev/null +++ b/data/ChunkBuffer.h @@ -0,0 +1,193 @@ +#ifndef CHUNK_BUFFER_H +#define CHUNK_BUFFER_H + +#include "../Debug.h" + +/** + * linked list of several chunks that are either managed or unmanaged + */ +class ChunkBuffer { + +public: + + /** data of certain length */ + struct Data { + const uint8_t* ptr; + const uint16_t size; + Data(const uint8_t* ptr, const uint16_t size) : ptr(ptr), size(size) {;} + }; + + /** one data chunk within the buffer */ + struct Chunk { + + bool managed; + uint8_t* base; // do not modify this one! + + uint8_t* ptr; + uint16_t len; + + Chunk* next = nullptr; + + ~Chunk() { + debugMod("ChunkBuffer::Chunk", "dtor"); + if (managed) { + free(base); + base = nullptr; + } + } + + /** allocate a new managed chunk */ + static Chunk* alloc(const uint16_t size) { + debugMod1("ChunkBuffer::Chunk", "alloc(%d)", size); + Chunk* chunk = new Chunk(); + chunk->base = (uint8_t*)malloc(size); + chunk->ptr = chunk->base; + chunk->len = size; + chunk->managed = true; + return chunk; + } + + /** allocate a new wrapped chunk */ + static Chunk* wrap(uint8_t* ptr, const uint16_t size) { + debugMod1("ChunkBuffer::Chunk", "alloc(%d)", size); + Chunk* chunk = new Chunk(); + chunk->base = ptr; + chunk->ptr = chunk->base; + chunk->len = size; + chunk->managed = false; + return chunk; + } + + /** consume max(!) size bytes from this chunk */ + Data consume(const uint16_t size) { + debugMod1("ChunkBuffer::Chunk", "consume(%d)", size); + const uint16_t useBytes = min(size, this->len); + Data res(ptr, useBytes); + ptr += useBytes; // adjust pointer for next consume() call + len -= useBytes; // adjuste len for next consume() call; + return res; + } + + /** consume one byte, if available */ + int consumeByte() { + if (len) { + uint8_t res = *ptr; + ptr += 1; + len -= 1; + return res; + } + return -1; + } + + bool empty() const { + return len == 0; + } + + private: + Chunk() {;} + + }; + + static uint16_t min(const uint16_t a, const uint16_t b) { + return (aconsume(size); + } + + } + + int getByte() { + + cleanup(); + + if (root && !root->empty()) { + return root->consumeByte(); + } + return -1; + } + + /** append the given chunk to the buffer */ + void append(Chunk* c) { + + debugMod("ChunkBuffer", "append()"); + + if (!root) { + + // new chunk is the root + root = c; + + } else { + + // append new chunk at the end + Chunk* x = root; + while (x->next) {x = x->next;} + x->next = c; + + } + + } + + /** allocate a new managed chunk of the given size */ + Chunk* alloc(const uint16_t size) { + debugMod1("ChunkBuffer", "alloc(%d)", size); + return Chunk::alloc(size); + } + + /** get a new unmanaged wrapper chunk for the given data */ + Chunk* wrap(const uint8_t* data, const uint16_t size) { + debugMod1("ChunkBuffer", "wrap(%d)", size); + return Chunk::wrap((uint8_t*) data, size); + } + + /** number of available bytes */ + uint32_t available() const { + uint32_t s = 0; + Chunk* c = root; + while(c) { + s += c->len; + c = c->next; + } + return s; + } + + /** nothing available? */ + bool empty() const { + return (!root || root->len == 0); + } + +private: + + void cleanup() { + + // current chunk empty? -> cleanup and proceed with next chunk + if (root && root->empty()) { + Chunk* old = root; + root = old->next; // next chunk is the chunk (if any) after the current (empty) one + delete old; // delete the empty chunk and free the data + } + + } + +}; + +#endif // CHUNK_BUFFER_H diff --git a/data/RingBuffer.h b/data/RingBuffer.h index 9cd5f95..a6b1c25 100644 --- a/data/RingBuffer.h +++ b/data/RingBuffer.h @@ -1,56 +1,97 @@ #ifndef RINGBUFFER_H #define RINGBUFFER_H +#include "../Debug.h" + +typedef void (*RingBufferFullCallback)(); + + template class RingBuffer { private: + static constexpr const char* NAME = "RingBuffer"; + Scalar data[size]; volatile size_t head; volatile size_t tail; - volatile size_t used; + volatile mutable size_t used; public: + RingBuffer() { + init(); + } + void init() { head = 0; tail = 0; used = 0; } - /** add one value to the buffer */ + void clear() { + 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 */ - void addIgnore(const Scalar value) { - + /** add one value to the buffer, if there is enough space */ + void addOrIgnore(const Scalar value) { if (used == size) {return;} - data[head] = value; head = (head + 1) % size; ++used; } - /** add multiple values to the buffer */ - void add(const Scalar* value, const size_t len) { + /** add one value to the buffer, or call the callback if there is no space available */ + void addOrCallback(const Scalar value, RingBufferFullCallback cb) { + while(used == size) {cb();} + data[head] = value; + head = (head + 1) % size; + ++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) { - add(value[i]); + addBlocking(value[i]); } + debugMod1(NAME, "used: %d", used); } - /** anything available? */ - bool hasNext() const { - return used != 0; + /** 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) { + debugMod1(NAME, "addOrCallback(%d)", len); + for (size_t i = 0; i < len; ++i) { + addOrCallback(value[i], cb); + } + debugMod1(NAME, "used: %d", used); + } + +// /** anything available? */ +// bool hasNext() const { +// return used != 0; +// } + + /** get the next value from the buffer */ Scalar get() { const Scalar res = data[tail]; tail = (tail + 1) % size; @@ -58,22 +99,33 @@ public: return res; } - const Scalar& getConst() { + const Scalar& getConst() const { const uint8_t idx = tail; tail = (tail + 1) % size; --used; return data[idx]; } -// /** true if the buffer is currently empty */ -// bool isEmpty() const { -// return head == tail; -// } + /** true if the buffer is currently empty */ + bool empty() const { + return used == 0; + } + /** true if the buffer is currently full */ + bool full() const { + return size == used; + } + + /** get the number of used elements within the buffer */ size_t getNumUsed() const { return used; } + /** get the number of free elements within the buffer */ + size_t getNumFree() const { + return size-used; + } + }; #endif // RINGBUFFER_H diff --git a/ext/audio/AudioData.h b/ext/audio/AudioData.h index 573fa4c..d9b9a0c 100644 --- a/ext/audio/AudioData.h +++ b/ext/audio/AudioData.h @@ -1,7 +1,7 @@ #ifndef AUDIODATA_H #define AUDIODATA_H -#define min(a,b) ( (a class VS1003 { private: - 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) + 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_CTRL_DESELECT() spi::chipDeselect(); + #define MP3_CTRL_SELECT() spi::chipSelect(); #define MP3_DATA_DESELECT() GPIO5_H #define MP3_DATA_SELECT() GPIO5_L @@ -92,9 +96,15 @@ public: public: + VS1003() { + init(); + } + /** initialize the hardware */ void init() { + debugMod(NAME, "init()"); + SPI_INIT(); GPIO5_OUTPUT_SET; // Pin D1 - XDCS @@ -103,6 +113,7 @@ public: this->state = PlayState::STOPPED; awaitDREQ(); // Wait for the VS1003 to finish boot + reset(); } @@ -112,6 +123,7 @@ public: * @param Audio volume to set on both channels. */ void setVolume(const uint8_t vol) const { + debugMod1(NAME, "setVolume(%d)", vol); const uint8_t aVol = 255 - vol; const uint16_t values = (aVol << 0) | (aVol << 8); // left and right writeRegister(SCI_VOL, values); @@ -122,10 +134,16 @@ public: return (255 - (values & 0xFF)); } - void play(AudioData ad) { - this->playable = ad; - this->state = PlayState::PLAYING; - } +// void play(AudioData ad) { +// this->playable = ad; +// this->state = PlayState::PLAYING; +// } + +// void appendUnmanaged(const uint8_t* data, const uint16_t size) { +// ChunkBuffer::Chunk* c = playBuf.wrap(data, size); +// playBuf.append(c); +// this->state = PlayState::PLAYING; +// } void ICACHE_FLASH_ATTR record(RecordSource source) { @@ -164,18 +182,22 @@ public: return this->state; } + void play() { + //if(this->state == PlayState::STOPPED) { reset(); } // makes things more stable + this->state = PlayState::PLAYING; + } void ICACHE_FLASH_ATTR run() { if(this->state == PlayState::PLAYING) { - if (!playable.isEOF()) { - const Data chunk = playable.read(32); - playChunk32(chunk.data, chunk.len); + if (playBuf.empty()) { + //uint8_t stopSequence[4] = {0}; + //playChunk32(stopSequence, sizeof(stopSequence)); // end of stream + //this->state = PlayState::STOPPED; } else { - this->state = PlayState::STOPPED; - uint8_t stopSequence[4] = {0}; - playChunk32(stopSequence, sizeof(stopSequence)); + //const ChunkBuffer::Data chunk = playBuf.get(32); + //playChunk32(chunk.ptr, chunk.size); + sendPlayBuffer(); } - } } @@ -217,6 +239,16 @@ public: } + PlayBuf& getPlayBuffer() { + return playBuf; + } + + void stop() { + this->state = PlayState::STOPPED; + playBuf.clear(); + //reset(); + } + private: uint32_t getNumAvailableRecordBytes() { @@ -224,15 +256,30 @@ private: } void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const { + debugMod1(NAME, "playChunk32() len:%d", len); MP3_CTRL_DESELECT(); MP3_DATA_SELECT(); - awaitDREQ(); + for(uint32_t i = 0; i < len; ++i) { + awaitDREQ(); SPI_WRITE_8(buffer[i]); } MP3_DATA_DESELECT(); } + void ICACHE_FLASH_ATTR sendPlayBuffer() { + MP3_CTRL_DESELECT(); + MP3_DATA_SELECT(); + int written = 0; + while(canConsume() && !playBuf.empty()) { + const uint8_t byte = playBuf.get(); + if (byte == -1) {break;} + SPI_WRITE_8(byte); + ++written; + } + if (written) {debugMod1(NAME, "sendPlayBuffer(): %d bytes", written);} + MP3_DATA_DESELECT(); + } /** * @brief Write the given register. @@ -273,7 +320,17 @@ private: * @brief Wait for the VS1003 to set DREQ to high (it wants data). */ inline void awaitDREQ() const { + debugMod(NAME, "awaitDREQ()"); while(!GET_DREQ()) {;} + //debugMod(NAME, "awaitDREQ() complete"); + } + + /** + * @brief VS1003 is able do consume data + * @return + */ + inline bool canConsume() const { + return GET_DREQ(); } /** @@ -281,6 +338,9 @@ private: * @param modeMask Additional mask to or into the mode that is written to the VS1003's register. */ void reset(Mode modeMask = Mode::NONE) const { + + debugMod(NAME, "reset()"); + writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS os_delay_us(1); awaitDREQ(); @@ -297,13 +357,17 @@ private: os_delay_us(100); MP3_CTRL_DESELECT(); + + debugMod(NAME, "reset() complete"); + } private: - //IPlayable* playable = nullptr; - AudioData playable; + PlayState state = PlayState::STOPPED; + PlayBuf playBuf; + }; #endif // VS1003_H diff --git a/ext/led/WS2812B.h b/ext/led/WS2812B.h index 9604cd5..bd67a5b 100644 --- a/ext/led/WS2812B.h +++ b/ext/led/WS2812B.h @@ -97,6 +97,10 @@ public: 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); + } + }; template class WS2812B { @@ -117,6 +121,10 @@ template class WS2812B { /** ctor */ WS2812B() { + init(); + } + + void init() { LED_SET_PIN_TO_OUTPUT; } @@ -125,11 +133,25 @@ template class WS2812B { 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]; @@ -140,11 +162,11 @@ template class WS2812B { } /** flush configured changes */ - void flush() { + void flush() { LED_SET_PIN_TO_OUTPUT; - // cli(); + ets_intr_lock(); // process each LED for (uint8_t i = 0; i < numLEDs; ++i) { @@ -163,9 +185,9 @@ template class WS2812B { } - reset(); + ets_intr_unlock(); - //sei(); + reset(); } diff --git a/io/IO.h b/io/IO.h index 8813ea4..4c12a60 100644 --- a/io/IO.h +++ b/io/IO.h @@ -1,6 +1,8 @@ #ifndef IO_H #define IO_H +#include "../Platforms.h" + // http://www.electrodragon.com/w/ESP8266_IoT_Firmware // https://esp8266.ru/esp8266-pin-register-strapping/ // http://www.limpkin.fr/index.php?post/2014/12/07/First-Steps-with-the-ESP8266-03-Development-Board @@ -70,8 +72,8 @@ public: GPIO2_OUTPUT_SET; if (on) {GPIO2_H;} else {GPIO2_L;} #elif PLATFORM == NODE_MCU - GPIO16_OUTPUT_SET; - if (on) {GPIO16_H;} else {GPIO16_L;} + //GPIO16_OUTPUT_SET; + //if (on) {GPIO16_H;} else {GPIO16_L;} GPIO2_OUTPUT_SET; if (on) {GPIO2_H;} else {GPIO2_L;} #else diff --git a/io/SoftSPI.h b/io/SoftSPI.h index acc68c2..da4726f 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -2,27 +2,54 @@ #define SOFTSPI_H //#include "IO.h" -#include "fastGPIO.h" +#include "../Platforms.h" -// wemos D1 mini -#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 -#define SPI_CS_LO GPIO15_L -#define SPI_CS_HI GPIO15_H +#if (PLATFORM == NODE_MCU) || (PLATFORM == WEMOS_D1_MINI) -#define SPI_CLK_LO GPIO14_L -#define SPI_CLK_HI GPIO14_H + #include "fastGPIO.h" -#define SPI_MOSI_LO GPIO13_L -#define SPI_MOSI_HI GPIO13_H + #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 -#define SPI_MISO_READ GPIO12_IN + #define SPI_CS_LO GPIO15_L + #define SPI_CS_HI GPIO15_H -#define SPI_FAST + #define SPI_CLK_LO GPIO14_L + #define SPI_CLK_HI GPIO14_H + + #define SPI_MOSI_LO GPIO13_L + #define SPI_MOSI_HI GPIO13_H + + #define SPI_MISO_READ GPIO12_IN + + #define SPI_FAST + +#elif (PLATFORM == NODE32S) + + #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 + + #define SPI_CS_LO GPIO15_L + #define SPI_CS_HI GPIO15_H + + #define SPI_CLK_LO GPIO14_L + #define SPI_CLK_HI GPIO14_H + + #define SPI_MOSI_LO GPIO13_L + #define SPI_MOSI_HI GPIO13_H + + #define SPI_MISO_READ GPIO12_IN + + #define SPI_FAST + +#endif namespace spi { diff --git a/net/HTTP.h b/net/HTTP.h new file mode 100644 index 0000000..3588cac --- /dev/null +++ b/net/HTTP.h @@ -0,0 +1,160 @@ +#ifndef HTTP_H +#define HTTP_H + +#include "TCP.h" +#include "../Debug.h" + +class HTTPClient; + +typedef void (*HTTPCBodyDataCallback)(HTTPClient* httpc, const uint8_t* data, uint16_t len); +typedef void (*HTTPCConnectCallback)(HTTPClient* httpc); +typedef void (*HTTPCDisconnectCallback)(HTTPClient* httpc); + + +class HTTPClient { + + static constexpr const char* NAME = "HTTPC"; + TCP tcp; + + espconn dns; + ip_addr_t ip; + char host[64]; + char query[128]; + bool gotHeader = false; + int numBreaks = 0; + + HTTPCConnectCallback onConnect = nullptr; + HTTPCDisconnectCallback onDisconnect = nullptr; + HTTPCBodyDataCallback onBodyData = nullptr; + +public: + + /** ctor */ + HTTPClient() { + debugMod(NAME, "init()"); + tcp.setUserData(this); + tcp.setDataCallback(_onData); + tcp.setConnectCallback(_onConnect); + tcp.setDisconnectCallback(_onDisconnect); + } + + void hold() { + tcp.hold(); + } + + void unhold() { + tcp.unhold(); + } + + void setBodyDataCallback(HTTPCBodyDataCallback c) { + this->onBodyData = c; + } + + void setConnectCallback(HTTPCConnectCallback c) { + this->onConnect = c; + } + + void setDisconnectCallback(HTTPCDisconnectCallback c) { + this->onDisconnect = c; + } + + /** connect to the given IP/Port */ + void connect(const char* url) { + //strcpy(this->url, url); + + const char* hostStart = &url[7]; // skip the "http://" + const char* hostEnd = strchr(hostStart, '/'); // find the terminating / + strncpy(this->host, hostStart, hostEnd-hostStart); // hostname only + this->host[hostEnd-hostStart] = 0; // 0 terminator!! + strcpy(this->query, hostEnd); // query only + + debugMod3(NAME, "connect(%s) -> host: '%s' query: '%s'", url, this->host, this->query); + + resolveHost(this->host); + + } + + void disconnect() { + tcp.disconnect(); + gotHeader = false; + numBreaks = 0; + } + +private: + + /** from TCP: connected */ + static void _onConnect(TCP* tcp) { + debugMod(NAME, "connected"); + HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); + if (httpc->onConnect) {httpc->onConnect(httpc);} + httpc->sendRequest(); + } + + /** from TCP: disconnected */ + static void _onDisconnect(TCP* tcp) { + debugMod(NAME, "disconnected"); + HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); + if (httpc->onDisconnect) {httpc->onDisconnect(httpc);} + } + + /** from TCP: got data */ + static void _onData(TCP* tcp, const uint8_t* data, const uint16_t dataLen) { + HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); + httpc->onData(data, dataLen); + } + + /** analyze incoming data */ + void onData(const uint8_t* data, uint16_t dataLen) { + + // header not yet received. scan incoming data. count number of consecutive linebreaks + if (!gotHeader) { + while(dataLen && !gotHeader) { + const char c = *data; + if (c == '\r' || c == '\n') {++numBreaks;} else {numBreaks = 0;} + if (numBreaks == 4) { + debugMod(NAME, "got header"); + gotHeader = true; + } + ++data; --dataLen; + } + } + + // maybe we got the header now + if (gotHeader && dataLen) { + if (onBodyData) { + onBodyData(this, data, dataLen); + } + } + + } + +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); + 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; + if (ipaddr) { + //debugMod1(NAME, "dns lookup returned: %s", tmp.toString()); + http->tcp.connect(IP(*ipaddr), 80); + } else { + debugMod1(NAME, "dns lookup failed: %s", name); + } + } + +}; + +#endif // HTTP_H diff --git a/net/IP.h b/net/IP.h index eca1b07..6e98b96 100644 --- a/net/IP.h +++ b/net/IP.h @@ -5,21 +5,40 @@ struct IP { - uint32_t val; + //uint32_t val; + ip_addr addr; /** empty ctor */ - IP() : val(0) { - ; + explicit IP() { + addr.addr = 0; } /** ctor with IP-string */ - IP(const char* ipStr) { + explicit IP(const char* ipStr) { set(ipStr); } - /** set the IP */ + /** ctor with ip_addr_t */ + explicit IP(ip_addr addr) : addr(addr) { + ; + } + + /** set the IP by string: x.x.x.x */ void set(const char* ipStr) { - val = ipaddr_addr(ipStr); + addr.addr = ipaddr_addr(ipStr); + } + + /** 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); } }; diff --git a/net/TCP.h b/net/TCP.h new file mode 100644 index 0000000..e8eb58e --- /dev/null +++ b/net/TCP.h @@ -0,0 +1,170 @@ +#ifndef TCP_H +#define TCP_H + +extern "C" { + #include "mem.h" + #include "espconn.h" +} + +class TCP; + +typedef void (*TCPDataCallback)(TCP* tcp, const uint8_t* data, uint16_t len); +typedef void (*TCPConnectCallback)(TCP* tcp); +typedef void (*TCPDisconnectCallback)(TCP* tcp); + +#include "../Debug.h" +#include "IP.h" + +class TCP { + +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(); + } + + /** dtor */ + ~TCP() { + cleanup(); + } + + + /** 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); + } + + /** 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; + } + + + /** terminate connection */ + void disconnect() { + if (!connected) {return;} + espconn_disconnect(con); + 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; + } + +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() { + + debugMod(NAME, "init()"); + + // allocate connection-objects + con = (espconn*) os_zalloc(sizeof(espconn)); + ets_memset( con, 0, sizeof( espconn ) ); + + // configure + con->type = ESPCONN_TCP; + con->state = ESPCONN_NONE; + + //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); + + } + + /** cleanup everything */ + void cleanup() { + + debugMod(NAME, "cleanup()"); + + espconn_delete(con); + //os_free(con->proto.tcp); + os_free(con); + con = nullptr; + + } + +}; + +#endif // TCP_H diff --git a/net/UDP.h b/net/UDP.h index f4b343f..3c4e08c 100644 --- a/net/UDP.h +++ b/net/UDP.h @@ -52,7 +52,7 @@ public: // set remote port and IP con->proto.udp->remote_port = port; - os_memcpy(con->proto.udp->remote_ip, &(ip.val), 4); + os_memcpy(con->proto.udp->remote_ip, ip.getPtr(), 4); //os_printf("send %d bytes\r\n", dataLen); //os_printf("IP: %d.%d.%d.%d\n\r", con->proto.udp->remote_ip[0], con->proto.udp->remote_ip[1], con->proto.udp->remote_ip[2], con->proto.udp->remote_ip[3]);