many changes :Py

This commit is contained in:
kazu
2018-07-08 17:47:59 +02:00
parent 11ed5a9159
commit 528a00b0e9
14 changed files with 843 additions and 91 deletions

View File

@@ -42,7 +42,7 @@ void hexdump(const uint8_t* buf, uint8_t len) {
#else #else
#define debug(module, str) #define debug(str)
#define debugMod(module, str) #define debugMod(module, str)
#define debugMod1(module, str, val) #define debugMod1(module, str, val)
#define debugMod2(module, str, v1, v2) #define debugMod2(module, str, v1, v2)

12
Platforms.h Normal file
View File

@@ -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

53
c++.h
View File

@@ -4,7 +4,11 @@
#define FALSE false #define FALSE false
typedef void (*int_handler_t)(void*); 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 *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 *pvPortRealloc(void* ptr, size_t xWantedSize, const char* file, int line) __attribute__((alloc_size(2)));
void vPortFree(void *ptr, const char* file, int line); void vPortFree(void *ptr, const char* file, int line);
@@ -98,17 +102,14 @@ inline uint32_t ETS_INTR_PENDING(void)
#define ETS_CCOMPARE0_DISABLE() \ #define ETS_CCOMPARE0_DISABLE() \
ETS_INTR_DISABLE(ETS_CCOMPARE0_INUM) 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) \ #define ETS_FRC_TIMER1_NMI_INTR_ATTACH(func) \
NmiTimSetFunc(func) NmiTimSetFunc(func)
//#define ETS_GPIO_INTR_ATTACH(func, arg) \ //#define ETS_GPIO_INTR_ATTACH(func, arg) ets_isr_attach(ETS_GPIO_INUM, (int_handler_t)(func), (void *)(arg))
// ets_isr_attach(ETS_GPIO_INUM, (int_handler_t)(func), (void *)(arg))
#define ETS_GPIO_INTR_ENABLE() \ #define ETS_GPIO_INTR_ENABLE() \
@@ -118,8 +119,7 @@ inline uint32_t ETS_INTR_PENDING(void)
ETS_INTR_DISABLE(ETS_GPIO_INUM) ETS_INTR_DISABLE(ETS_GPIO_INUM)
//#define ETS_UART_INTR_ATTACH(func, arg) \ //#define ETS_UART_INTR_ATTACH(func, arg) ets_isr_attach(ETS_UART_INUM, (int_handler_t)(func), (void *)(arg))
// ets_isr_attach(ETS_UART_INUM, (int_handler_t)(func), (void *)(arg))
#define ETS_UART_INTR_ENABLE() \ #define ETS_UART_INTR_ENABLE() \
@@ -135,8 +135,7 @@ inline uint32_t ETS_INTR_PENDING(void)
ETS_INTR_DISABLE(ETS_FRC_TIMER1_INUM) ETS_INTR_DISABLE(ETS_FRC_TIMER1_INUM)
//#define ETS_SPI_INTR_ATTACH(func, arg) \ //#define ETS_SPI_INTR_ATTACH(func, arg) ets_isr_attach(ETS_SPI_INUM, (int_handler_t)(func), (void *)(arg))
// ets_isr_attach(ETS_SPI_INUM, (int_handler_t)(func), (void *)(arg))
#define ETS_SPI_INTR_ENABLE() \ #define ETS_SPI_INTR_ENABLE() \
@@ -156,8 +155,7 @@ inline uint32_t ETS_INTR_PENDING(void)
ETS_INTR_DISABLE(ETS_SLC_INUM) ETS_INTR_DISABLE(ETS_SLC_INUM)
//#define ETS_SDIO_INTR_ATTACH(func, arg) \ //#define ETS_SDIO_INTR_ATTACH(func, arg) ets_isr_attach(ETS_SDIO_INUM, (int_handler_t)(func), (void *)(arg))
// ets_isr_attach(ETS_SDIO_INUM, (int_handler_t)(func), (void *)(arg))
#define ETS_SDIO_INTR_ENABLE() \ #define ETS_SDIO_INTR_ENABLE() \
ETS_INTR_ENABLE(ETS_SDIO_INUM) 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))); 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); 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 // ensure constructors are called
// See more at: http://www.esp8266.com/viewtopic.php?f=9&t=478&start=8#sthash.uVL8shrS.dpuf // 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!!! // SEE NOTES BELOW!!!
extern void (*__init_array_start)(void); extern void (*__init_array_start)(void);
extern void (*__init_array_end)(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

193
data/ChunkBuffer.h Normal file
View File

@@ -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 (a<b) ? (a) : (b);
}
private:
Chunk* root;
public:
ChunkBuffer() : root(nullptr) {
debugMod("ChunkBuffer", "ctor()");
}
/** get a block of max(!) size bytes from the buffer. might return less */
Data get(const uint16_t size) {
cleanup();
// something available?
if (!root) {
return Data(0,0);
} else {
return root->consume(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

View File

@@ -1,56 +1,97 @@
#ifndef RINGBUFFER_H #ifndef RINGBUFFER_H
#define RINGBUFFER_H #define RINGBUFFER_H
#include "../Debug.h"
typedef void (*RingBufferFullCallback)();
template <typename Scalar, int size> class RingBuffer { template <typename Scalar, int size> class RingBuffer {
private: private:
static constexpr const char* NAME = "RingBuffer";
Scalar data[size]; Scalar data[size];
volatile size_t head; volatile size_t head;
volatile size_t tail; volatile size_t tail;
volatile size_t used; volatile mutable size_t used;
public: public:
RingBuffer() {
init();
}
void init() { void init() {
head = 0; head = 0;
tail = 0; tail = 0;
used = 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) { void addBlocking(const Scalar value) {
while (used == size) {os_delay_us(1000);} while (used == size) {os_delay_us(1000);}
data[head] = value; data[head] = value;
head = (head + 1) % size; head = (head + 1) % size;
++used; ++used;
} }
/** add one value to the buffer */ /** add one value to the buffer, if there is enough space */
void addIgnore(const Scalar value) { void addOrIgnore(const Scalar value) {
if (used == size) {return;} if (used == size) {return;}
data[head] = value; data[head] = value;
head = (head + 1) % size; head = (head + 1) % size;
++used; ++used;
} }
/** add multiple values to the buffer */ /** add one value to the buffer, or call the callback if there is no space available */
void add(const Scalar* value, const size_t len) { 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) { for (size_t i = 0; i < len; ++i) {
add(value[i]); addBlocking(value[i]);
} }
debugMod1(NAME, "used: %d", used);
} }
/** anything available? */ /** add multiple values to the buffer, if there is enough space */
bool hasNext() const { void addOrIgnore(const Scalar* value, const size_t len) {
return used != 0; 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() { Scalar get() {
const Scalar res = data[tail]; const Scalar res = data[tail];
tail = (tail + 1) % size; tail = (tail + 1) % size;
@@ -58,22 +99,33 @@ public:
return res; return res;
} }
const Scalar& getConst() { const Scalar& getConst() const {
const uint8_t idx = tail; const uint8_t idx = tail;
tail = (tail + 1) % size; tail = (tail + 1) % size;
--used; --used;
return data[idx]; return data[idx];
} }
// /** true if the buffer is currently empty */ /** true if the buffer is currently empty */
// bool isEmpty() const { bool empty() const {
// return head == tail; 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 { size_t getNumUsed() const {
return used; return used;
} }
/** get the number of free elements within the buffer */
size_t getNumFree() const {
return size-used;
}
}; };
#endif // RINGBUFFER_H #endif // RINGBUFFER_H

View File

@@ -1,7 +1,7 @@
#ifndef AUDIODATA_H #ifndef AUDIODATA_H
#define AUDIODATA_H #define AUDIODATA_H
#define min(a,b) ( (a<b) ? (a) : (b) )
struct Data { struct Data {
const uint8_t* data; const uint8_t* data;
@@ -14,6 +14,10 @@ struct AudioData {
const uint8_t* data; const uint8_t* data;
uint32_t available; uint32_t available;
static uint32_t min(uint32_t a, uint32_t b) {
return (a<b) ? (a) : (b);
}
AudioData() : data(nullptr), available(0) {;} AudioData() : data(nullptr), available(0) {;}
AudioData(const uint8_t* data, const uint32_t len) : data(data), available(len) { AudioData(const uint8_t* data, const uint32_t len) : data(data), available(len) {

View File

@@ -1,34 +1,38 @@
#ifndef VS1003_H #ifndef VS1003_H
#define VS1003_H #define VS1003_H
#include "io/fastGPIO.h" #include "../../io/fastGPIO.h"
#include "../io/SoftSPI.h" #include "../../io/SoftSPI.h"
#include "../../Debug.h"
//#include "IPlayable.h" //#include "IPlayable.h"
#include "AudioData.h" //#include "AudioData.h"
#include "../../data/ChunkBuffer.h"
/** /**
* control a VS1003 audio chip attached to the SPI port * control a VS1003 audio chip attached to the SPI port
*/ */
class VS1003 { template <typename PlayBuf> class VS1003 {
private: private:
SoftSPI spi; static constexpr const char* NAME = "VS1003";
#define SPI_INIT() spi.init();
#define SPI_WRITE_8(byte) spi.writeByte(byte) //SoftSPI spi;
#define SPI_WRITE_16(word) spi.writeWord(word) #define SPI_INIT() spi::init();
#define SPI_READ_8(byte) spi.readByte(byte) #define SPI_WRITE_8(byte) spi::writeByte(byte)
#define SPI_READ_16(word) spi.readWord(word) #define SPI_WRITE_16(word) spi::writeWord(word)
#define SPI_READ_WRITE_8(byte) spi.readWriteByte(byte) #define SPI_READ_8(byte) spi::readByte(byte)
#define SPI_READ_WRITE_16(word) spi.readWriteWord(word) #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 SPI_DELAY() //os_delay_us(10) #define SPI_DELAY() //os_delay_us(10)
#define GET_DREQ() GPIO16_IN // PIN D0 #define GET_DREQ() GPIO16_IN // PIN D0
#define MP3_CTRL_DESELECT() spi::chipDeselect();
#define MP3_CTRL_DESELECT() spi.chipDeselect(); #define MP3_CTRL_SELECT() spi::chipSelect();
#define MP3_CTRL_SELECT() spi.chipSelect();
#define MP3_DATA_DESELECT() GPIO5_H #define MP3_DATA_DESELECT() GPIO5_H
#define MP3_DATA_SELECT() GPIO5_L #define MP3_DATA_SELECT() GPIO5_L
@@ -92,9 +96,15 @@ public:
public: public:
VS1003() {
init();
}
/** initialize the hardware */ /** initialize the hardware */
void init() { void init() {
debugMod(NAME, "init()");
SPI_INIT(); SPI_INIT();
GPIO5_OUTPUT_SET; // Pin D1 - XDCS GPIO5_OUTPUT_SET; // Pin D1 - XDCS
@@ -103,6 +113,7 @@ public:
this->state = PlayState::STOPPED; this->state = PlayState::STOPPED;
awaitDREQ(); // Wait for the VS1003 to finish boot awaitDREQ(); // Wait for the VS1003 to finish boot
reset(); reset();
} }
@@ -112,6 +123,7 @@ public:
* @param Audio volume to set on both channels. * @param Audio volume to set on both channels.
*/ */
void setVolume(const uint8_t vol) const { void setVolume(const uint8_t vol) const {
debugMod1(NAME, "setVolume(%d)", vol);
const uint8_t aVol = 255 - vol; const uint8_t aVol = 255 - vol;
const uint16_t values = (aVol << 0) | (aVol << 8); // left and right const uint16_t values = (aVol << 0) | (aVol << 8); // left and right
writeRegister(SCI_VOL, values); writeRegister(SCI_VOL, values);
@@ -122,10 +134,16 @@ public:
return (255 - (values & 0xFF)); return (255 - (values & 0xFF));
} }
void play(AudioData ad) { // void play(AudioData ad) {
this->playable = ad; // this->playable = ad;
this->state = PlayState::PLAYING; // 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) { void ICACHE_FLASH_ATTR record(RecordSource source) {
@@ -164,18 +182,22 @@ public:
return this->state; return this->state;
} }
void play() {
//if(this->state == PlayState::STOPPED) { reset(); } // makes things more stable
this->state = PlayState::PLAYING;
}
void ICACHE_FLASH_ATTR run() { void ICACHE_FLASH_ATTR run() {
if(this->state == PlayState::PLAYING) { if(this->state == PlayState::PLAYING) {
if (!playable.isEOF()) { if (playBuf.empty()) {
const Data chunk = playable.read(32); //uint8_t stopSequence[4] = {0};
playChunk32(chunk.data, chunk.len); //playChunk32(stopSequence, sizeof(stopSequence)); // end of stream
//this->state = PlayState::STOPPED;
} else { } else {
this->state = PlayState::STOPPED; //const ChunkBuffer::Data chunk = playBuf.get(32);
uint8_t stopSequence[4] = {0}; //playChunk32(chunk.ptr, chunk.size);
playChunk32(stopSequence, sizeof(stopSequence)); sendPlayBuffer();
} }
} }
} }
@@ -217,6 +239,16 @@ public:
} }
PlayBuf& getPlayBuffer() {
return playBuf;
}
void stop() {
this->state = PlayState::STOPPED;
playBuf.clear();
//reset();
}
private: private:
uint32_t getNumAvailableRecordBytes() { uint32_t getNumAvailableRecordBytes() {
@@ -224,15 +256,30 @@ private:
} }
void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const { void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const {
debugMod1(NAME, "playChunk32() len:%d", len);
MP3_CTRL_DESELECT(); MP3_CTRL_DESELECT();
MP3_DATA_SELECT(); MP3_DATA_SELECT();
awaitDREQ();
for(uint32_t i = 0; i < len; ++i) { for(uint32_t i = 0; i < len; ++i) {
awaitDREQ();
SPI_WRITE_8(buffer[i]); SPI_WRITE_8(buffer[i]);
} }
MP3_DATA_DESELECT(); 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. * @brief Write the given register.
@@ -273,7 +320,17 @@ private:
* @brief Wait for the VS1003 to set DREQ to high (it wants data). * @brief Wait for the VS1003 to set DREQ to high (it wants data).
*/ */
inline void awaitDREQ() const { inline void awaitDREQ() const {
debugMod(NAME, "awaitDREQ()");
while(!GET_DREQ()) {;} 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. * @param modeMask Additional mask to or into the mode that is written to the VS1003's register.
*/ */
void reset(Mode modeMask = Mode::NONE) const { void reset(Mode modeMask = Mode::NONE) const {
debugMod(NAME, "reset()");
writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS
os_delay_us(1); os_delay_us(1);
awaitDREQ(); awaitDREQ();
@@ -297,13 +357,17 @@ private:
os_delay_us(100); os_delay_us(100);
MP3_CTRL_DESELECT(); MP3_CTRL_DESELECT();
debugMod(NAME, "reset() complete");
} }
private: private:
//IPlayable* playable = nullptr;
AudioData playable;
PlayState state = PlayState::STOPPED; PlayState state = PlayState::STOPPED;
PlayBuf playBuf;
}; };
#endif // VS1003_H #endif // VS1003_H

View File

@@ -97,6 +97,10 @@ public:
return Color(r+o.r, g+o.g, b+o.g); 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 <int numLEDs> class WS2812B { template <int numLEDs> class WS2812B {
@@ -117,6 +121,10 @@ template <int numLEDs> class WS2812B {
/** ctor */ /** ctor */
WS2812B() { WS2812B() {
init();
}
void init() {
LED_SET_PIN_TO_OUTPUT; LED_SET_PIN_TO_OUTPUT;
} }
@@ -125,11 +133,25 @@ template <int numLEDs> class WS2812B {
colors[idx] = 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 */ /** enable/disable the given LED */
void setEnabled(const uint8_t idx, const bool en) { void setEnabled(const uint8_t idx, const bool en) {
enabled[idx] = 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? */ /** is the given LED enabled? */
bool isEnabled(const uint8_t idx) const { bool isEnabled(const uint8_t idx) const {
return enabled[idx]; return enabled[idx];
@@ -144,7 +166,7 @@ template <int numLEDs> class WS2812B {
LED_SET_PIN_TO_OUTPUT; LED_SET_PIN_TO_OUTPUT;
// cli(); ets_intr_lock();
// process each LED // process each LED
for (uint8_t i = 0; i < numLEDs; ++i) { for (uint8_t i = 0; i < numLEDs; ++i) {
@@ -163,9 +185,9 @@ template <int numLEDs> class WS2812B {
} }
reset(); ets_intr_unlock();
//sei(); reset();
} }

View File

@@ -1,6 +1,8 @@
#ifndef IO_H #ifndef IO_H
#define IO_H #define IO_H
#include "../Platforms.h"
// http://www.electrodragon.com/w/ESP8266_IoT_Firmware // http://www.electrodragon.com/w/ESP8266_IoT_Firmware
// https://esp8266.ru/esp8266-pin-register-strapping/ // 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 // 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; GPIO2_OUTPUT_SET;
if (on) {GPIO2_H;} else {GPIO2_L;} if (on) {GPIO2_H;} else {GPIO2_L;}
#elif PLATFORM == NODE_MCU #elif PLATFORM == NODE_MCU
GPIO16_OUTPUT_SET; //GPIO16_OUTPUT_SET;
if (on) {GPIO16_H;} else {GPIO16_L;} //if (on) {GPIO16_H;} else {GPIO16_L;}
GPIO2_OUTPUT_SET; GPIO2_OUTPUT_SET;
if (on) {GPIO2_H;} else {GPIO2_L;} if (on) {GPIO2_H;} else {GPIO2_L;}
#else #else

View File

@@ -2,9 +2,13 @@
#define SOFTSPI_H #define SOFTSPI_H
//#include "IO.h" //#include "IO.h"
#include "../Platforms.h"
#if (PLATFORM == NODE_MCU) || (PLATFORM == WEMOS_D1_MINI)
#include "fastGPIO.h" #include "fastGPIO.h"
// wemos D1 mini
#define SPI_CS_OUTPUT GPIO15_OUTPUT_SET #define SPI_CS_OUTPUT GPIO15_OUTPUT_SET
#define SPI_MOSI_OUTPUT GPIO13_OUTPUT_SET #define SPI_MOSI_OUTPUT GPIO13_OUTPUT_SET
#define SPI_MISO_INPUT GPIO12_INPUT_SET #define SPI_MISO_INPUT GPIO12_INPUT_SET
@@ -24,6 +28,29 @@
#define SPI_FAST #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 { namespace spi {
// MTDI GPIO12 MISO (DIN) D6 // MTDI GPIO12 MISO (DIN) D6

160
net/HTTP.h Normal file
View File

@@ -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

View File

@@ -5,21 +5,40 @@
struct IP { struct IP {
uint32_t val; //uint32_t val;
ip_addr addr;
/** empty ctor */ /** empty ctor */
IP() : val(0) { explicit IP() {
; addr.addr = 0;
} }
/** ctor with IP-string */ /** ctor with IP-string */
IP(const char* ipStr) { explicit IP(const char* ipStr) {
set(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) { 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);
} }
}; };

170
net/TCP.h Normal file
View File

@@ -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

View File

@@ -52,7 +52,7 @@ public:
// set remote port and IP // set remote port and IP
con->proto.udp->remote_port = port; 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("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]); //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]);