diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29bb2d..a3485ec8228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +set(CORE_SRCS + cores/esp32/dummySerial.cpp + cores/esp32/Print.cpp + cores/esp32/stdlib_noniso.c + cores/esp32/Stream.cpp + cores/esp32/WString.cpp + ) + +set(LIBRARY_SRCS + ) + +set(BLE_SRCS + ) + +set(includedirs + cores/esp32/ + ) + + set(requires + esp_timer + driver + ) + +set(srcs ${CORE_SRCS} ${LIBRARY_SRCS} ${BLE_SRCS}) +##--##set(priv_includes cores/esp32/libb64) +##--##set(requires spi_flash mbedtls mdns esp_adc_cal wifi_provisioning nghttp wpa_supplicant) +##--##set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support openssl bt esp_ipc esp_hid) + +idf_component_register(INCLUDE_DIRS ${includedirs} PRIV_INCLUDE_DIRS ${priv_includes} SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires}) + +if(NOT CONFIG_FREERTOS_HZ EQUAL 1000 AND NOT "$ENV{ARDUINO_SKIP_TICK_CHECK}") + # See delay() in cores/esp32/esp32-hal-misc.c. + message(FATAL_ERROR "esp32-arduino requires CONFIG_FREERTOS_HZ=1000 " + "(currently ${CONFIG_FREERTOS_HZ})") +endif() diff --git a/cores/esp32/Arduino.h b/cores/esp32/Arduino.h index e69de29bb2d..829579fe988 100644 --- a/cores/esp32/Arduino.h +++ b/cores/esp32/Arduino.h @@ -0,0 +1,247 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Arduino_h +#define Arduino_h + +#include +#include +#include +#include +#include +#include +#include +#include + +//--//#include "esp_arduino_version.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/gpio.h" +//--//#include "esp32-hal.h" +//--//#include "esp8266-compat.h" +//--//#include "soc/gpio_reg.h" + +#include "stdlib_noniso.h" +//--//#include "binary.h" + +#include "esp_log.h" + +#define log_w(...) ESP_LOGW("Arduino", ##__VA_ARGS__) +#define log_e(...) ESP_LOGE("Arduino", ##__VA_ARGS__) + +#include "esp_timer.h" + +#define millis() (esp_timer_get_time() / 1000ULL) +#define micros64() (esp_timer_get_time()) +#define micros() ((unsigned long)(esp_timer_get_time())) + +#define PI 3.1415926535897932384626433832795 +#define HALF_PI 1.5707963267948966192313216916398 +#define TWO_PI 6.283185307179586476925286766559 +#define DEG_TO_RAD 0.017453292519943295769236907684886 +#define RAD_TO_DEG 57.295779513082320876798154814105 +#define EULER 2.718281828459045235360287471352 + +//--//#define SERIAL 0x0 +//--//#define DISPLAY 0x1 + +//--//#define LSBFIRST 0 +//--//#define MSBFIRST 1 + +//Interrupt Modes +#define RISING 0x01 +#define FALLING 0x02 +#define CHANGE 0x03 +#define ONLOW 0x04 +#define ONHIGH 0x05 +#define ONLOW_WE 0x0C +#define ONHIGH_WE 0x0D + +//--//#define DEFAULT 1 +//--//#define EXTERNAL 0 + +#ifndef __STRINGIFY +//--//#define __STRINGIFY(a) #a +#endif + +// can't define max() / min() because of conflicts with C++ +//--//#define _min(a,b) ((a)<(b)?(a):(b)) +//--//#define _max(a,b) ((a)>(b)?(a):(b)) +//--//#define _abs(x) ((x)>0?(x):-(x)) // abs() comes from STL +//--//#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) +//--//#define _round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) // round() comes from STL +//--//#define radians(deg) ((deg)*DEG_TO_RAD) +//--//#define degrees(rad) ((rad)*RAD_TO_DEG) +//--//#define sq(x) ((x)*(x)) + +// ESP32xx runs FreeRTOS... disabling interrupts can lead to issues, such as Watchdog Timeout +//--//#define sei() portENABLE_INTERRUPTS() +//--//#define cli() portDISABLE_INTERRUPTS() +//--//#define interrupts() sei() +//--//#define noInterrupts() cli() + +//--//#define clockCyclesPerMicrosecond() ( (long int)getCpuFrequencyMhz() ) +//--//#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) +//--//#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) + +//--//#define lowByte(w) ((uint8_t) ((w) & 0xff)) +//--//#define highByte(w) ((uint8_t) ((w) >> 8)) + +//--//#define bitRead(value, bit) (((value) >> (bit)) & 0x01) +//--//#define bitSet(value, bit) ((value) |= (1UL << (bit))) +//--//#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) +//--//#define bitToggle(value, bit) ((value) ^= (1UL << (bit))) +//--//#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit)) + +// avr-libc defines _NOP() since 1.6.2 +#ifndef _NOP +//--//#define _NOP() do { __asm__ volatile ("nop"); } while (0) +#endif + +//--//#define bit(b) (1UL << (b)) +//--//#define _BV(b) (1UL << (b)) + +//--//#define digitalPinToTimer(pin) (0) +//--//#define analogInPinToBit(P) (P) +#if SOC_GPIO_PIN_COUNT <= 32 +//--//#define digitalPinToPort(pin) (0) +//--//#define digitalPinToBitMask(pin) (1UL << digitalPinToGPIONumber(pin)) +//--//#define portOutputRegister(port) ((volatile uint32_t*)GPIO_OUT_REG) +//--//#define portInputRegister(port) ((volatile uint32_t*)GPIO_IN_REG) +//--//#define portModeRegister(port) ((volatile uint32_t*)GPIO_ENABLE_REG) +#elif SOC_GPIO_PIN_COUNT <= 64 +//--//#define digitalPinToPort(pin) ((digitalPinToGPIONumber(pin)>31)?1:0) +//--//#define digitalPinToBitMask(pin) (1UL << (digitalPinToGPIONumber(pin)&31)) +//--//#define portOutputRegister(port) ((volatile uint32_t*)((port)?GPIO_OUT1_REG:GPIO_OUT_REG)) +//--//#define portInputRegister(port) ((volatile uint32_t*)((port)?GPIO_IN1_REG:GPIO_IN_REG)) +//--//#define portModeRegister(port) ((volatile uint32_t*)((port)?GPIO_ENABLE1_REG:GPIO_ENABLE_REG)) +#else +//--//#error SOC_GPIO_PIN_COUNT > 64 not implemented +#endif + +//--//#define NOT_A_PIN -1 +//--//#define NOT_A_PORT -1 +//--//#define NOT_AN_INTERRUPT -1 +//--//#define NOT_ON_TIMER 0 + +typedef bool boolean; +typedef uint8_t byte; +typedef unsigned int word; + +#ifdef __cplusplus +void setup(void); +void loop(void); + +class dummySerial{ +public: + virtual void println(const char *); +}; + +extern dummySerial dSerial; + +#define Serial dSerial + +// The default is using Real Hardware random number generator +// But when randomSeed() is called, it turns to Psedo random +// generator, exactly as done in Arduino mainstream +long random(long); +long random(long, long); +// Calling randomSeed() will make random() +// using pseudo random like in Arduino +void randomSeed(unsigned long); +// Allow the Application to decide if the random generator +// will use Real Hardware random generation (true - default) +// or Pseudo random generation (false) as in Arduino MainStream +void useRealRandomGenerator(bool useRandomHW); +#endif +long map(long, long, long, long, long); + +#ifdef __cplusplus +extern "C" { +#endif + +//--//void init(void); +//--//void initVariant(void); +//--//void initArduino(void); + +//--//unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); +//--//unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); + +//--//uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); +//--//void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); + +#ifdef __cplusplus +} + +#include +#include + +//--//#include "WCharacter.h" +#include "WString.h" +#include "Stream.h" +#include "StreamString.h" +#include "Printable.h" +#include "Print.h" +//--//#include "IPAddress.h" +//--//#include "Client.h" +//--//#include "Server.h" +//--//#include "Udp.h" +//--//#include "HardwareSerial.h" +//--//#include "Esp.h" +//--//#include "esp32/spiram.h" + +// Use float-compatible stl abs() and round(), we don't use Arduino macros to avoid issues with the C++ libraries +using std::abs; +using std::isinf; +using std::isnan; +using std::max; +using std::min; +using std::round; + +//--//uint16_t makeWord(uint16_t w); +//--//uint16_t makeWord(uint8_t h, uint8_t l); + +//--//#define word(...) makeWord(__VA_ARGS__) + +//--//size_t getArduinoLoopTaskStackSize(void); +//--//#define SET_LOOP_TASK_STACK_SIZE(sz) size_t getArduinoLoopTaskStackSize() { return sz;} + +// allows user to bypass esp_spiram_test() +//--//#define BYPASS_SPIRAM_TEST(bypass) bool testSPIRAM(void) { if (bypass) return true; else return esp_spiram_test(); } + +//--//unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); +//--//unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); + +//--//extern "C" bool getLocalTime(struct tm * info, uint32_t ms = 5000); +//--//extern "C" void configTime(long gmtOffset_sec, int daylightOffset_sec, +//--// const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); +//--//extern "C" void configTzTime(const char* tz, +//--// const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); + +//--//void setToneChannel(uint8_t channel = 0); +//--//void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); +//--//void noTone(uint8_t _pin); + +#endif /* __cplusplus */ + +//--//#include "pins_arduino.h" +//--//#include "io_pin_remap.h" + +#endif /* _ESP32_CORE_ARDUINO_H_ */ diff --git a/cores/esp32/Print.cpp b/cores/esp32/Print.cpp new file mode 100644 index 00000000000..269ad733386 --- /dev/null +++ b/cores/esp32/Print.cpp @@ -0,0 +1,362 @@ +/* + Print.cpp - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 23 November 2006 by David A. Mellis + Modified December 2014 by Ivan Grokhotkov + Modified May 2015 by Michael C. Miller - ESP31B progmem support + */ + +#include +#include +#include +#include +#include "Arduino.h" + +#include "Print.h" +extern "C" { + #include "time.h" +} + +// Public Methods ////////////////////////////////////////////////////////////// + +/* default implementation: may be overridden */ +size_t Print::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + while(size--) { + n += write(*buffer++); + } + return n; +} + +size_t Print::printf(const char *format, ...) +{ + char loc_buf[64]; + char * temp = loc_buf; + va_list arg; + va_list copy; + va_start(arg, format); + va_copy(copy, arg); + int len = vsnprintf(temp, sizeof(loc_buf), format, copy); + va_end(copy); + if(len < 0) { + va_end(arg); + return 0; + } + if(len >= (int)sizeof(loc_buf)){ // comparation of same sign type for the compiler + temp = (char*) malloc(len+1); + if(temp == NULL) { + va_end(arg); + return 0; + } + len = vsnprintf(temp, len+1, format, arg); + } + va_end(arg); + len = write((uint8_t*)temp, len); + if(temp != loc_buf){ + free(temp); + } + return len; +} + +size_t Print::print(const String &s) +{ + return write(s.c_str(), s.length()); +} + +size_t Print::print(const char str[]) +{ + return write(str); +} + +size_t Print::print(char c) +{ + return write(c); +} + +size_t Print::print(unsigned char b, int base) +{ + return print((unsigned long) b, base); +} + +size_t Print::print(int n, int base) +{ + return print((long) n, base); +} + +size_t Print::print(unsigned int n, int base) +{ + return print((unsigned long) n, base); +} + +size_t Print::print(long n, int base) +{ + int t = 0; + if (base == 10 && n < 0) { + t = print('-'); + n = -n; + } + return printNumber(static_cast(n), base) + t; +} + +size_t Print::print(unsigned long n, int base) +{ + if(base == 0) { + return write(n); + } else { + return printNumber(n, base); + } +} + +size_t Print::print(long long n, int base) +{ + int t = 0; + if (base == 10 && n < 0) { + t = print('-'); + n = -n; + } + return printNumber(static_cast(n), base) + t; +} + +size_t Print::print(unsigned long long n, int base) +{ + if (base == 0) { + return write(n); + } else { + return printNumber(n, base); + } +} + +size_t Print::print(double n, int digits) +{ + return printFloat(n, digits); +} + +size_t Print::print(const Printable& x) +{ + return x.printTo(*this); +} + +size_t Print::print(struct tm * timeinfo, const char * format) +{ + const char * f = format; + if(!f){ + f = "%c"; + } + char buf[64]; + size_t written = strftime(buf, 64, f, timeinfo); + if(written == 0){ + return written; + } + return print(buf); +} + +size_t Print::println(void) +{ + return print("\r\n"); +} + +size_t Print::println(const String &s) +{ + size_t n = print(s); + n += println(); + return n; +} + +size_t Print::println(const char c[]) +{ + size_t n = print(c); + n += println(); + return n; +} + +size_t Print::println(char c) +{ + size_t n = print(c); + n += println(); + return n; +} + +size_t Print::println(unsigned char b, int base) +{ + size_t n = print(b, base); + n += println(); + return n; +} + +size_t Print::println(int num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(unsigned int num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(unsigned long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(long long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(unsigned long long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(double num, int digits) +{ + size_t n = print(num, digits); + n += println(); + return n; +} + +size_t Print::println(const Printable& x) +{ + size_t n = print(x); + n += println(); + return n; +} + +size_t Print::println(struct tm * timeinfo, const char * format) +{ + size_t n = print(timeinfo, format); + n += println(); + return n; +} + +// Private Methods ///////////////////////////////////////////////////////////// + +size_t Print::printNumber(unsigned long n, uint8_t base) +{ + char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte. + char *str = &buf[sizeof(buf) - 1]; + + *str = '\0'; + + // prevent crash if called with base == 1 + if(base < 2) { + base = 10; + } + + do { + char c = n % base; + n /= base; + + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while (n); + + return write(str); +} + +size_t Print::printNumber(unsigned long long n, uint8_t base) +{ + char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte. + char* str = &buf[sizeof(buf) - 1]; + + *str = '\0'; + + // prevent crash if called with base == 1 + if (base < 2) { + base = 10; + } + + do { + auto m = n; + n /= base; + char c = m - base * n; + + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while (n); + + return write(str); +} + +size_t Print::printFloat(double number, uint8_t digits) +{ + size_t n = 0; + + if(isnan(number)) { + return print("nan"); + } + if(isinf(number)) { + return print("inf"); + } + if(number > 4294967040.0) { + return print("ovf"); // constant determined empirically + } + if(number < -4294967040.0) { + return print("ovf"); // constant determined empirically + } + + // Handle negative numbers + if(number < 0.0) { + n += print('-'); + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for(uint8_t i = 0; i < digits; ++i) { + rounding /= 10.0; + } + + number += rounding; + + // Extract the integer part of the number and print it + unsigned long int_part = (unsigned long) number; + double remainder = number - (double) int_part; + n += print(int_part); + + // Print the decimal point, but only if there are digits beyond + if(digits > 0) { + n += print("."); + } + + // Extract digits from the remainder one at a time + while(digits-- > 0) { + remainder *= 10.0; + int toPrint = int(remainder); + n += print(toPrint); + remainder -= toPrint; + } + + return n; +} diff --git a/cores/esp32/Print.h b/cores/esp32/Print.h new file mode 100644 index 00000000000..57284c49762 --- /dev/null +++ b/cores/esp32/Print.h @@ -0,0 +1,116 @@ +/* + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Print_h +#define Print_h + +#include +#include + +#include "WString.h" +#include "Printable.h" + +#define DEC 10 +#define HEX 16 +#define OCT 8 +#define BIN 2 + +class Print +{ +private: + int write_error; + size_t printNumber(unsigned long, uint8_t); + size_t printNumber(unsigned long long, uint8_t); + size_t printFloat(double, uint8_t); +protected: + void setWriteError(int err = 1) + { + write_error = err; + } +public: + Print() : + write_error(0) + { + } + virtual ~Print() {} + int getWriteError() + { + return write_error; + } + void clearWriteError() + { + setWriteError(0); + } + + virtual size_t write(uint8_t) = 0; + size_t write(const char *str) + { + if(str == NULL) { + return 0; + } + return write((const uint8_t *) str, strlen(str)); + } + virtual size_t write(const uint8_t *buffer, size_t size); + size_t write(const char *buffer, size_t size) + { + return write((const uint8_t *) buffer, size); + } + + size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3))); + + // add availableForWrite to make compatible with Arduino Print.h + // default to zero, meaning "a single write may block" + // should be overriden by subclasses with buffering + virtual int availableForWrite() { return 0; } + size_t print(const __FlashStringHelper *ifsh) { return print(reinterpret_cast(ifsh)); } + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(long long, int = DEC); + size_t print(unsigned long long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); + size_t print(struct tm * timeinfo, const char * format = NULL); + + size_t println(const __FlashStringHelper *ifsh) { return println(reinterpret_cast(ifsh)); } + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(long long, int = DEC); + size_t println(unsigned long long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(struct tm * timeinfo, const char * format = NULL); + size_t println(void); + + virtual void flush() { /* Empty implementation for backward compatibility */ } + +}; + +#endif diff --git a/cores/esp32/Printable.h b/cores/esp32/Printable.h new file mode 100644 index 00000000000..aa4e62f8f11 --- /dev/null +++ b/cores/esp32/Printable.h @@ -0,0 +1,41 @@ +/* + Printable.h - Interface class that allows printing of complex types + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Printable_h +#define Printable_h + +#include + +class Print; + +/** The Printable class provides a way for new classes to allow themselves to be printed. + By deriving from Printable and implementing the printTo method, it will then be possible + for users to print out instances of this class by passing them into the usual + Print::print and Print::println methods. + */ + +class Printable +{ +public: + virtual ~Printable() {} + virtual size_t printTo(Print& p) const = 0; +}; + +#endif + diff --git a/cores/esp32/Stream.cpp b/cores/esp32/Stream.cpp new file mode 100644 index 00000000000..d187debfae1 --- /dev/null +++ b/cores/esp32/Stream.cpp @@ -0,0 +1,337 @@ +/* + Stream.cpp - adds parsing methods to Stream class + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Created July 2011 + parsing functions based on TextFinder library by Michael Margolis + */ + +#include "Arduino.h" +#include "Stream.h" +//--//#include "esp32-hal.h" + +#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait +#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field + +// private method to read stream with timeout +int Stream::timedRead() +{ + int c; + _startMillis = millis(); + do { + c = read(); + if(c >= 0) { + return c; + } + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// private method to peek stream with timeout +int Stream::timedPeek() +{ + int c; + _startMillis = millis(); + do { + c = peek(); + if(c >= 0) { + return c; + } + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// returns peek of the next digit in the stream or -1 if timeout +// discards non-numeric characters +int Stream::peekNextDigit() +{ + int c; + while(1) { + c = timedPeek(); + if(c < 0) { + return c; // timeout + } + if(c == '-') { + return c; + } + if(c >= '0' && c <= '9') { + return c; + } + read(); // discard non-numeric + } +} + +// Public Methods +////////////////////////////////////////////////////////////// + +void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait +{ + _timeout = timeout; +} +unsigned long Stream::getTimeout(void) { + return _timeout; +} + +// find returns true if the target string is found +bool Stream::find(const char *target) +{ + return findUntil(target, strlen(target), NULL, 0); +} + +// reads data from the stream until the target string of given length is found +// returns true if target string is found, false if timed out +bool Stream::find(const char *target, size_t length) +{ + return findUntil(target, length, NULL, 0); +} + +// as find but search ends if the terminator string is found +bool Stream::findUntil(const char *target, const char *terminator) +{ + return findUntil(target, strlen(target), terminator, strlen(terminator)); +} + +// reads data from the stream until the target string of the given length is found +// search terminated if the terminator string is found +// returns true if target string is found, false if terminated or timed out +bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) +{ + if (terminator == NULL) { + MultiTarget t[1] = {{target, targetLen, 0}}; + return findMulti(t, 1) == 0 ? true : false; + } else { + MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}}; + return findMulti(t, 2) == 0 ? true : false; + } +} + +int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) { + // any zero length target string automatically matches and would make + // a mess of the rest of the algorithm. + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + if (t->len <= 0) + return t - targets; + } + + while (1) { + int c = timedRead(); + if (c < 0) + return -1; + + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + // the simple case is if we match, deal with that first. + if (c == t->str[t->index]) { + if (++t->index == t->len) + return t - targets; + else + continue; + } + + // if not we need to walk back and see if we could have matched further + // down the stream (ie '1112' doesn't match the first position in '11112' + // but it will match the second position so we can't just reset the current + // index to 0 when we find a mismatch. + if (t->index == 0) + continue; + + int origIndex = t->index; + do { + --t->index; + // first check if current char works against the new current index + if (c != t->str[t->index]) + continue; + + // if it's the only char then we're good, nothing more to check + if (t->index == 0) { + t->index++; + break; + } + + // otherwise we need to check the rest of the found string + int diff = origIndex - t->index; + size_t i; + for (i = 0; i < t->index; ++i) { + if (t->str[i] != t->str[i + diff]) + break; + } + + // if we successfully got through the previous loop then our current + // index is good. + if (i == t->index) { + t->index++; + break; + } + + // otherwise we just try the next index + } while (t->index); + } + } + // unreachable + return -1; +} + +// returns the first valid (long) integer value from the current position. +// initial characters that are not digits (or the minus sign) are skipped +// function is terminated by the first character that is not a digit. +long Stream::parseInt() +{ + return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) +} + +// as above but a given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +long Stream::parseInt(char skipChar) +{ + boolean isNegative = false; + long value = 0; + int c; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) { + return 0; // zero returned if timeout + } + + do { + if(c == skipChar) { + } // ignore this charactor + else if(c == '-') { + isNegative = true; + } else if(c >= '0' && c <= '9') { // is c a digit? + value = value * 10 + c - '0'; + } + read(); // consume the character we got with peek + c = timedPeek(); + } while((c >= '0' && c <= '9') || c == skipChar); + + if(isNegative) { + value = -value; + } + return value; +} + +// as parseInt but returns a floating point value +float Stream::parseFloat() +{ + return parseFloat(NO_SKIP_CHAR); +} + +// as above but the given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +float Stream::parseFloat(char skipChar) +{ + boolean isNegative = false; + boolean isFraction = false; + long value = 0; + int c; + float fraction = 1.0; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) { + return 0; // zero returned if timeout + } + + do { + if(c == skipChar) { + } // ignore + else if(c == '-') { + isNegative = true; + } else if(c == '.') { + isFraction = true; + } else if(c >= '0' && c <= '9') { // is c a digit? + value = value * 10 + c - '0'; + if(isFraction) { + fraction *= 0.1f; + } + } + read(); // consume the character we got with peek + c = timedPeek(); + } while((c >= '0' && c <= '9') || c == '.' || c == skipChar); + + if(isNegative) { + value = -value; + } + if(isFraction) { + return value * fraction; + } else { + return value; + } +} + +// read characters from stream into buffer +// terminates if length characters have been read, or timeout (see setTimeout) +// returns the number of characters placed in the buffer +// the buffer is NOT null terminated. +// +size_t Stream::readBytes(char *buffer, size_t length) +{ + size_t count = 0; + while(count < length) { + int c = timedRead(); + if(c < 0) { + break; + } + *buffer++ = (char) c; + count++; + } + return count; +} + +// as readBytes with terminator character +// terminates if length characters have been read, timeout, or if the terminator character detected +// returns the number of characters placed in the buffer (0 means no valid data found) + +size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) +{ + if(length < 1) { + return 0; + } + size_t index = 0; + while(index < length) { + int c = timedRead(); + if(c < 0 || c == terminator) { + break; + } + *buffer++ = (char) c; + index++; + } + return index; // return number of characters, not including null terminator +} + +String Stream::readString() +{ + String ret; + int c = timedRead(); + while(c >= 0) { + ret += (char) c; + c = timedRead(); + } + return ret; +} + +String Stream::readStringUntil(char terminator) +{ + String ret; + int c = timedRead(); + while(c >= 0 && c != terminator) { + ret += (char) c; + c = timedRead(); + } + return ret; +} + diff --git a/cores/esp32/Stream.h b/cores/esp32/Stream.h new file mode 100644 index 00000000000..8df8226d730 --- /dev/null +++ b/cores/esp32/Stream.h @@ -0,0 +1,139 @@ +/* + Stream.h - base class for character-based streams. + Copyright (c) 2010 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + parsing functions based on TextFinder library by Michael Margolis + */ + +#ifndef Stream_h +#define Stream_h + +#include +#include "Print.h" + +// compatability macros for testing +/* + #define getInt() parseInt() + #define getInt(skipChar) parseInt(skipchar) + #define getFloat() parseFloat() + #define getFloat(skipChar) parseFloat(skipChar) + #define getString( pre_string, post_string, buffer, length) + readBytesBetween( pre_string, terminator, buffer, length) + */ + +class Stream: public Print +{ +protected: + unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read + unsigned long _startMillis; // used for timeout measurement + int timedRead(); // private method to read stream with timeout + int timedPeek(); // private method to peek stream with timeout + int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + +public: + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + + Stream():_startMillis(0) + { + _timeout = 1000; + } + virtual ~Stream() {} + +// parsing methods + + void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second + unsigned long getTimeout(void); + + bool find(const char *target); // reads data from the stream until the target string is found + bool find(uint8_t *target) + { + return find((char *) target); + } + // returns true if target string is found, false if timed out (see setTimeout) + + bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found + bool find(const uint8_t *target, size_t length) + { + return find((char *) target, length); + } + // returns true if target string is found, false if timed out + + bool find(char target) + { + return find (&target, 1); + } + + bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found + bool findUntil(const uint8_t *target, const char *terminator) + { + return findUntil((char *) target, terminator); + } + + bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found + bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) + { + return findUntil((char *) target, targetLen, terminate, termLen); + } + + long parseInt(); // returns the first valid (long) integer value from the current position. + // initial characters that are not digits (or the minus sign) are skipped + // integer is terminated by the first character that is not a digit. + + float parseFloat(); // float version of parseInt + + virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer + virtual size_t readBytes(uint8_t *buffer, size_t length) + { + return readBytes((char *) buffer, length); + } + // terminates if length characters have been read or timeout (see setTimeout) + // returns the number of characters placed in the buffer (0 means no valid data found) + + size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character + size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) + { + return readBytesUntil(terminator, (char *) buffer, length); + } + // terminates if length characters have been read, timeout, or if the terminator character detected + // returns the number of characters placed in the buffer (0 means no valid data found) + + // Arduino String functions to be added here + virtual String readString(); + String readStringUntil(char terminator); + +protected: + long parseInt(char skipChar); // as above but the given skipChar is ignored + // as above but the given skipChar is ignored + // this allows format characters (typically commas) in values to be ignored + + float parseFloat(char skipChar); // as above but the given skipChar is ignored + + struct MultiTarget { + const char *str; // string you're searching for + size_t len; // length of string you're searching for + size_t index; // index used by the search routine. + }; + + // This allows you to search for an arbitrary number of strings. + // Returns index of the target that is found first or -1 if timeout occurs. + int findMulti(struct MultiTarget *targets, int tCount); + +}; + +#endif diff --git a/cores/esp32/StreamString.cpp b/cores/esp32/StreamString.cpp new file mode 100644 index 00000000000..f50b6825b55 --- /dev/null +++ b/cores/esp32/StreamString.cpp @@ -0,0 +1,67 @@ +/** + StreamString.cpp + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + */ + +#include +#include "StreamString.h" + +size_t StreamString::write(const uint8_t *data, size_t size) { + if(size && data) { + const unsigned int newlen = length() + size; + if(reserve(newlen + 1)) { + memcpy((void *) (wbuffer() + len()), (const void *) data, size); + setLen(newlen); + *(wbuffer() + newlen) = 0x00; // add null for string end + return size; + } + } + return 0; +} + +size_t StreamString::write(uint8_t data) { + return concat((char) data); +} + +int StreamString::available() { + return length(); +} + +int StreamString::read() { + if(length()) { + char c = charAt(0); + remove(0, 1); + return c; + + } + return -1; +} + +int StreamString::peek() { + if(length()) { + char c = charAt(0); + return c; + } + return -1; +} + +void StreamString::flush() { +} + diff --git a/cores/esp32/StreamString.h b/cores/esp32/StreamString.h new file mode 100644 index 00000000000..fa983786c70 --- /dev/null +++ b/cores/esp32/StreamString.h @@ -0,0 +1,40 @@ +/** + StreamString.h + + Copyright (c) 2015 Markus Sattler. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef STREAMSTRING_H_ +#define STREAMSTRING_H_ +#include "Stream.h" +#include "WString.h" + +class StreamString: public Stream, public String +{ +public: + size_t write(const uint8_t *buffer, size_t size) override; + size_t write(uint8_t data) override; + + int available() override; + int read() override; + int peek() override; + void flush() override; +}; + + +#endif /* STREAMSTRING_H_ */ diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp new file mode 100644 index 00000000000..5af05576920 --- /dev/null +++ b/cores/esp32/WString.cpp @@ -0,0 +1,861 @@ +/* + WString.cpp - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All rights reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + Modified by Ivan Grokhotkov, 2014 - esp8266 support + Modified by Michael C. Miller, 2015 - esp8266 progmem support + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Arduino.h" +#include "WString.h" +#include "stdlib_noniso.h" +//--//#include "esp32-hal-log.h" + +/*********************************************/ +/* Constructors */ +/*********************************************/ + +String::String(const char *cstr) { + init(); + if (cstr) + copy(cstr, strlen(cstr)); +} + +String::String(const char *cstr, unsigned int length) { + init(); + if (cstr) + copy(cstr, length); +} + +String::String(const String &value) { + init(); + *this = value; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +String::String(String &&rval) { + init(); + move(rval); +} + +String::String(StringSumHelper &&rval) { + init(); + move(rval); +} +#endif + +String::String(char c) { + init(); + char buf[] = { c, '\0' }; + *this = buf; +} + +String::String(unsigned char value, unsigned char base) { + init(); + char buf[1 + 8 * sizeof(unsigned char)]; + utoa(value, buf, base); + *this = buf; +} + +String::String(int value, unsigned char base) { + init(); + char buf[2 + 8 * sizeof(int)]; + itoa(value, buf, base); + *this = buf; +} + +String::String(unsigned int value, unsigned char base) { + init(); + char buf[1 + 8 * sizeof(unsigned int)]; + utoa(value, buf, base); + *this = buf; +} + +String::String(long value, unsigned char base) { + init(); + char buf[2 + 8 * sizeof(long)]; + ltoa(value, buf, base); + *this = buf; +} + +String::String(unsigned long value, unsigned char base) { + init(); + char buf[1 + 8 * sizeof(unsigned long)]; + ultoa(value, buf, base); + *this = buf; +} + +String::String(float value, unsigned int decimalPlaces) { + init(); + char *buf = (char*)malloc(decimalPlaces + 42); + if (buf) { + *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + free(buf); + } else { + *this = "nan"; + log_e("No enought memory for the operation."); + } +} + +String::String(double value, unsigned int decimalPlaces) { + init(); + char *buf = (char*)malloc(decimalPlaces + 312); + if (buf) { + *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + free(buf); + } else { + *this = "nan"; + log_e("No enought memory for the operation."); + } +} + +String::String(long long value, unsigned char base) { + init(); + char buf[2 + 8 * sizeof(long long)]; + lltoa(value, buf, base); + *this = buf; +} + +String::String(unsigned long long value, unsigned char base) { + init(); + char buf[1 + 8 * sizeof(unsigned long long)]; + ulltoa(value, buf, base); + *this = buf; +} + +String::~String() { + invalidate(); +} + +/*********************************************/ +/* Memory Management */ +/*********************************************/ + +inline void String::init(void) { + setSSO(false); + setBuffer(nullptr); + setCapacity(0); + setLen(0); +} + +void String::invalidate(void) { + if(!isSSO() && wbuffer()) + free(wbuffer()); + init(); +} + +bool String::reserve(unsigned int size) { + if(buffer() && capacity() >= size) + return true; + if(changeBuffer(size)) { + if(len() == 0) + wbuffer()[0] = 0; + return true; + } + return false; +} + +bool String::changeBuffer(unsigned int maxStrLen) { + // Can we use SSO here to avoid allocation? + if (maxStrLen < sizeof(sso.buff) - 1) { + if (isSSO() || !buffer()) { + // Already using SSO, nothing to do + uint16_t oldLen = len(); + setSSO(true); + setLen(oldLen); + } else { // if bufptr && !isSSO() + // Using bufptr, need to shrink into sso.buff + char temp[sizeof(sso.buff)]; + memcpy(temp, buffer(), maxStrLen); + free(wbuffer()); + uint16_t oldLen = len(); + setSSO(true); + memcpy(wbuffer(), temp, maxStrLen); + setLen(oldLen); + } + return true; + } + // Fallthrough to normal allocator + size_t newSize = (maxStrLen + 16) & (~0xf); + // Make sure we can fit newsize in the buffer + if (newSize > CAPACITY_MAX) { + return false; + } + uint16_t oldLen = len(); + char *newbuffer = (char *) realloc(isSSO() ? nullptr : wbuffer(), newSize); + if (newbuffer) { + size_t oldSize = capacity() + 1; // include NULL. + if (isSSO()) { + // Copy the SSO buffer into allocated space + memmove(newbuffer, sso.buff, sizeof(sso.buff)); + } + if (newSize > oldSize) { + memset(newbuffer + oldSize, 0, newSize - oldSize); + } + setSSO(false); + setCapacity(newSize - 1); + setBuffer(newbuffer); + setLen(oldLen); // Needed in case of SSO where len() never existed + return true; + } + return false; +} + +/*********************************************/ +/* Copy and Move */ +/*********************************************/ + +String & String::copy(const char *cstr, unsigned int length) { + if(!reserve(length)) { + invalidate(); + return *this; + } + memmove(wbuffer(), cstr, length + 1); + setLen(length); + return *this; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +void String::move(String &rhs) { + if(buffer()) { + if(capacity() >= rhs.len()) { + memmove(wbuffer(), rhs.buffer(), rhs.length() + 1); + setLen(rhs.len()); + rhs.invalidate(); + return; + } else { + if (!isSSO()) { + free(wbuffer()); + setBuffer(nullptr); + } + } + } + if (rhs.isSSO()) { + setSSO(true); + memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff)); + } else { + setSSO(false); + setBuffer(rhs.wbuffer()); + } + setCapacity(rhs.capacity()); + setLen(rhs.len()); + rhs.setSSO(false); + rhs.setCapacity(0); + rhs.setBuffer(nullptr); + rhs.setLen(0); +} +#endif + +String & String::operator =(const String &rhs) { + if(this == &rhs) + return *this; + if(rhs.buffer()) + copy(rhs.buffer(), rhs.len()); + else + invalidate(); + return *this; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +String & String::operator =(String &&rval) { + if(this != &rval) + move(rval); + return *this; +} + +String & String::operator =(StringSumHelper &&rval) { + if(this != &rval) + move(rval); + return *this; +} +#endif + +String & String::operator =(const char *cstr) { + if(cstr) + copy(cstr, strlen(cstr)); + else + invalidate(); + return *this; +} + +/*********************************************/ +/* concat */ +/*********************************************/ + +bool String::concat(const String &s) { + // Special case if we're concatting ourself (s += s;) since we may end up + // realloc'ing the buffer and moving s.buffer in the method called + if (&s == this) { + unsigned int newlen = 2 * len(); + if (!s.buffer()) + return false; + if (s.len() == 0) + return true; + if (!reserve(newlen)) + return false; + memmove(wbuffer() + len(), buffer(), len()); + setLen(newlen); + wbuffer()[len()] = 0; + return true; + } else { + return concat(s.buffer(), s.len()); + } +} + +bool String::concat(const char *cstr, unsigned int length) { + unsigned int newlen = len() + length; + if(!cstr) + return false; + if(length == 0) + return true; + if(!reserve(newlen)) + return false; + if (cstr >= wbuffer() && cstr < wbuffer() + len()) + // compatible with SSO in ram #6155 (case "x += x.c_str()") + memmove(wbuffer() + len(), cstr, length + 1); + else + // compatible with source in flash #6367 + memcpy_P(wbuffer() + len(), cstr, length + 1); + setLen(newlen); + return true; +} + +bool String::concat(const char *cstr) { + if(!cstr) + return false; + return concat(cstr, strlen(cstr)); +} + +bool String::concat(char c) { + char buf[] = { c, '\0' }; + return concat(buf, 1); +} + +bool String::concat(unsigned char num) { + char buf[1 + 3 * sizeof(unsigned char)]; + utoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(int num) { + char buf[2 + 3 * sizeof(int)]; + itoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(unsigned int num) { + char buf[1 + 3 * sizeof(unsigned int)]; + utoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(long num) { + char buf[2 + 3 * sizeof(long)]; + ltoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(unsigned long num) { + char buf[1 + 3 * sizeof(unsigned long)]; + ultoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(long long num) { + char buf[2 + 3 * sizeof(long long)]; + lltoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(unsigned long long num) { + char buf[1 + 3 * sizeof(unsigned long long)]; + ulltoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +bool String::concat(float num) { + char buf[20]; + char* string = dtostrf(num, 4, 2, buf); + return concat(string, strlen(string)); +} + +bool String::concat(double num) { + char buf[20]; + char* string = dtostrf(num, 4, 2, buf); + return concat(string, strlen(string)); +} + +/*********************************************/ +/* Concatenate */ +/*********************************************/ + +StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(rhs.buffer(), rhs.len())) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr) { + StringSumHelper &a = const_cast(lhs); + if(!cstr || !a.concat(cstr, strlen(cstr))) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, char c) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(c)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, int num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, long num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, float num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, double num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, long long num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long long num) { + StringSumHelper &a = const_cast(lhs); + if(!a.concat(num)) + a.invalidate(); + return a; +} + +/*********************************************/ +/* Comparison */ +/*********************************************/ + +int String::compareTo(const String &s) const { + if(!buffer() || !s.buffer()) { + if(s.buffer() && s.len() > 0) + return 0 - *(unsigned char *) s.buffer(); + if(buffer() && len() > 0) + return *(unsigned char *) buffer(); + return 0; + } + return strcmp(buffer(), s.buffer()); +} + +bool String::equals(const String &s2) const { + return (len() == s2.len() && compareTo(s2) == 0); +} + +bool String::equals(const char *cstr) const { + if(len() == 0) + return (cstr == NULL || *cstr == 0); + if(cstr == NULL) + return buffer()[0] == 0; + return strcmp(buffer(), cstr) == 0; +} + +bool String::operator<(const String &rhs) const { + return compareTo(rhs) < 0; +} + +bool String::operator>(const String &rhs) const { + return compareTo(rhs) > 0; +} + +bool String::operator<=(const String &rhs) const { + return compareTo(rhs) <= 0; +} + +bool String::operator>=(const String &rhs) const { + return compareTo(rhs) >= 0; +} + +bool String::equalsIgnoreCase(const String &s2) const { + if(this == &s2) + return true; + if(len() != s2.len()) + return false; + if(len() == 0) + return true; + const char *p1 = buffer(); + const char *p2 = s2.buffer(); + while(*p1) { + if(tolower(*p1++) != tolower(*p2++)) + return false; + } + return true; +} + +unsigned char String::equalsConstantTime(const String &s2) const { + // To avoid possible time-based attacks present function + // compares given strings in a constant time. + if(len() != s2.len()) + return 0; + //at this point lengths are the same + if(len() == 0) + return 1; + //at this point lengths are the same and non-zero + const char *p1 = buffer(); + const char *p2 = s2.buffer(); + unsigned int equalchars = 0; + unsigned int diffchars = 0; + while(*p1) { + if(*p1 == *p2) + ++equalchars; + else + ++diffchars; + ++p1; + ++p2; + } + //the following should force a constant time eval of the condition without a compiler "logical shortcut" + unsigned char equalcond = (equalchars == len()); + unsigned char diffcond = (diffchars == 0); + return (equalcond & diffcond); //bitwise AND +} + +bool String::startsWith(const String &s2) const { + if(len() < s2.len()) + return false; + return startsWith(s2, 0); +} + +bool String::startsWith(const String &s2, unsigned int offset) const { + if(offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) + return false; + return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0; +} + +bool String::endsWith(const String &s2) const { + if(len() < s2.len() || !buffer() || !s2.buffer()) + return false; + return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0; +} + +/*********************************************/ +/* Character Access */ +/*********************************************/ + +char String::charAt(unsigned int loc) const { + return operator[](loc); +} + +void String::setCharAt(unsigned int loc, char c) { + if(loc < len()) + wbuffer()[loc] = c; +} + +char & String::operator[](unsigned int index) { + static char dummy_writable_char; + if(index >= len() || !buffer()) { + dummy_writable_char = 0; + return dummy_writable_char; + } + return wbuffer()[index]; +} + +char String::operator[](unsigned int index) const { + if(index >= len() || !buffer()) + return 0; + return buffer()[index]; +} + +void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const { + if(!bufsize || !buf) + return; + if(index >= len()) { + buf[0] = 0; + return; + } + unsigned int n = bufsize - 1; + if(n > len() - index) + n = len() - index; + strncpy((char *) buf, buffer() + index, n); + buf[n] = 0; +} + +/*********************************************/ +/* Search */ +/*********************************************/ + +int String::indexOf(char c) const { + return indexOf(c, 0); +} + +int String::indexOf(char ch, unsigned int fromIndex) const { + if(fromIndex >= len()) + return -1; + const char *temp = strchr(buffer() + fromIndex, ch); + if(temp == NULL) + return -1; + return temp - buffer(); +} + +int String::indexOf(const String &s2) const { + return indexOf(s2, 0); +} + +int String::indexOf(const String &s2, unsigned int fromIndex) const { + if(fromIndex >= len()) + return -1; + const char *found = strstr(buffer() + fromIndex, s2.buffer()); + if(found == NULL) + return -1; + return found - buffer(); +} + +int String::lastIndexOf(char theChar) const { + return lastIndexOf(theChar, len() - 1); +} + +int String::lastIndexOf(char ch, unsigned int fromIndex) const { + if(fromIndex >= len()) + return -1; + char tempchar = buffer()[fromIndex + 1]; + wbuffer()[fromIndex + 1] = '\0'; + char* temp = strrchr(wbuffer(), ch); + wbuffer()[fromIndex + 1] = tempchar; + if(temp == NULL) + return -1; + return temp - buffer(); +} + +int String::lastIndexOf(const String &s2) const { + return lastIndexOf(s2, len() - s2.len()); +} + +int String::lastIndexOf(const String &s2, unsigned int fromIndex) const { + if(s2.len() == 0 || len() == 0 || s2.len() > len()) + return -1; + if(fromIndex >= len()) + fromIndex = len() - 1; + int found = -1; + for(char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) { + p = strstr(p, s2.buffer()); + if(!p) + break; + if((unsigned int) (p - wbuffer()) <= fromIndex) + found = p - buffer(); + } + return found; +} + +String String::substring(unsigned int left, unsigned int right) const { + if(left > right) { + unsigned int temp = right; + right = left; + left = temp; + } + String out; + if(left >= len()) + return out; + if(right > len()) + right = len(); + out.copy(buffer() + left, right - left); + return out; +} + +/*********************************************/ +/* Modification */ +/*********************************************/ + +void String::replace(char find, char replace) { + if(!buffer()) + return; + for(char *p = wbuffer(); *p; p++) { + if(*p == find) + *p = replace; + } +} + +void String::replace(const String &find, const String &replace) { + if(len() == 0 || find.len() == 0) + return; + int diff = replace.len() - find.len(); + char *readFrom = wbuffer(); + char *foundAt; + if(diff == 0) { + while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + memmove(foundAt, replace.buffer(), replace.len()); + readFrom = foundAt + replace.len(); + } + } else if(diff < 0) { + char *writeTo = wbuffer(); + unsigned int l = len(); + while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + unsigned int n = foundAt - readFrom; + memmove(writeTo, readFrom, n); + writeTo += n; + memmove(writeTo, replace.buffer(), replace.len()); + writeTo += replace.len(); + readFrom = foundAt + find.len(); + l += diff; + } + memmove(writeTo, readFrom, strlen(readFrom)+1); + setLen(l); + } else { + unsigned int size = len(); // compute size needed for result + while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + readFrom = foundAt + find.len(); + size += diff; + } + if(size == len()) + return; + if(size > capacity() && !changeBuffer(size)) { + log_w("String.Replace() Insufficient space to replace string"); + return; + } + int index = len() - 1; + while(index >= 0 && (index = lastIndexOf(find, index)) >= 0) { + readFrom = wbuffer() + index + find.len(); + memmove(readFrom + diff, readFrom, len() - (readFrom - buffer())); + int newLen = len() + diff; + memmove(wbuffer() + index, replace.buffer(), replace.len()); + setLen(newLen); + wbuffer()[newLen] = 0; + index--; + } + } +} + +void String::remove(unsigned int index) { + // Pass the biggest integer as the count. The remove method + // below will take care of truncating it at the end of the + // string. + remove(index, (unsigned int) -1); +} + +void String::remove(unsigned int index, unsigned int count) { + if(index >= len()) { + return; + } + if(count <= 0) { + return; + } + if(count > len() - index) { + count = len() - index; + } + char *writeTo = wbuffer() + index; + unsigned int newlen = len() - count; + memmove(writeTo, wbuffer() + index + count, newlen - index); + setLen(newlen); + wbuffer()[newlen] = 0; +} + +void String::toLowerCase(void) { + if(!buffer()) + return; + for(char *p = wbuffer(); *p; p++) { + *p = tolower(*p); + } +} + +void String::toUpperCase(void) { + if(!buffer()) + return; + for(char *p = wbuffer(); *p; p++) { + *p = toupper(*p); + } +} + +void String::trim(void) { + if(!buffer() || len() == 0) + return; + char *begin = wbuffer(); + while(isspace(*begin)) + begin++; + char *end = wbuffer() + len() - 1; + while(isspace(*end) && end >= begin) + end--; + unsigned int newlen = end + 1 - begin; + if(begin > buffer()) + memmove(wbuffer(), begin, newlen); + setLen(newlen); + wbuffer()[newlen] = 0; +} + +/*********************************************/ +/* Parsing / Conversion */ +/*********************************************/ + +long String::toInt(void) const { + if (buffer()) + return atol(buffer()); + return 0; +} + +float String::toFloat(void) const { + if (buffer()) + return atof(buffer()); + return 0; +} + +double String::toDouble(void) const { + if (buffer()) + return atof(buffer()); + return 0.0; +} + +// global empty string to allow returning const String& with nothing + +const String emptyString; diff --git a/cores/esp32/WString.h b/cores/esp32/WString.h new file mode 100644 index 00000000000..6517109f29b --- /dev/null +++ b/cores/esp32/WString.h @@ -0,0 +1,404 @@ +/* + WString.h - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All right reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef String_class_h +#define String_class_h +#ifdef __cplusplus + +#include + +#include +#include +#include +#include + + +// A pure abstract class forward used as a means to proide a unique pointer type +// but really is never defined. +class __FlashStringHelper; +#define FPSTR(str_pointer) (reinterpret_cast(str_pointer)) +#define F(string_literal) (FPSTR(PSTR(string_literal))) + +// An inherited class for holding the result of a concatenation. These +// result objects are assumed to be writable by subsequent concatenations. +class StringSumHelper; + +// The string class +class String { + // use a function pointer to allow for "if (s)" without the + // complications of an operator bool(). for more information, see: + // http://www.artima.com/cppsource/safebool.html + typedef void (String::*StringIfHelperType)() const; + void StringIfHelper() const { + } + + public: + // constructors + // creates a copy of the initial value. + // if the initial value is null or invalid, or if memory allocation + // fails, the string will be marked as invalid (i.e. "if (s)" will + // be false). + String(const char *cstr = ""); + String(const char *cstr, unsigned int length); +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + String(const uint8_t *cstr, unsigned int length) : String(reinterpret_cast(cstr), length) {} +#endif + String(const String &str); + String(const __FlashStringHelper *str) : String(reinterpret_cast(str)) {} +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + String(String &&rval); + String(StringSumHelper &&rval); +#endif + explicit String(char c); + explicit String(unsigned char, unsigned char base = 10); + explicit String(int, unsigned char base = 10); + explicit String(unsigned int, unsigned char base = 10); + explicit String(long, unsigned char base = 10); + explicit String(unsigned long, unsigned char base = 10); + explicit String(float, unsigned int decimalPlaces = 2); + explicit String(double, unsigned int decimalPlaces = 2); + explicit String(long long, unsigned char base = 10); + explicit String(unsigned long long, unsigned char base = 10); + ~String(void); + + // memory management + // return true on success, false on failure (in which case, the string + // is left unchanged). reserve(0), if successful, will validate an + // invalid string (i.e., "if (s)" will be true afterwards) + bool reserve(unsigned int size); + inline unsigned int length(void) const { + if(buffer()) { + return len(); + } else { + return 0; + } + } + inline void clear(void) { + setLen(0); + } + inline bool isEmpty(void) const { + return length() == 0; + } + + // creates a copy of the assigned value. if the value is null or + // invalid, or if the memory allocation fails, the string will be + // marked as invalid ("if (s)" will be false). + String & operator =(const String &rhs); + String & operator =(const char *cstr); + String & operator = (const __FlashStringHelper *str) {return *this = reinterpret_cast(str);} +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + String & operator =(String &&rval); + String & operator =(StringSumHelper &&rval); +#endif + + // concatenate (works w/ built-in types, same as assignment) + + // returns true on success, false on failure (in which case, the string + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsuccessful. + bool concat(const String &str); + bool concat(const char *cstr); + bool concat(const char *cstr, unsigned int length); + bool concat(const uint8_t *cstr, unsigned int length) {return concat(reinterpret_cast(cstr), length);} + bool concat(char c); + bool concat(unsigned char c); + bool concat(int num); + bool concat(unsigned int num); + bool concat(long num); + bool concat(unsigned long num); + bool concat(float num); + bool concat(double num); + bool concat(long long num); + bool concat(unsigned long long num); + bool concat(const __FlashStringHelper * str) {return concat(reinterpret_cast(str));} + + // if there's not enough memory for the concatenated value, the string + // will be left unchanged (but this isn't signalled in any way) + String & operator +=(const String &rhs) { + concat(rhs); + return (*this); + } + String & operator +=(const char *cstr) { + concat(cstr); + return (*this); + } + String & operator +=(char c) { + concat(c); + return (*this); + } + String & operator +=(unsigned char num) { + concat(num); + return (*this); + } + String & operator +=(int num) { + concat(num); + return (*this); + } + String & operator +=(unsigned int num) { + concat(num); + return (*this); + } + String & operator +=(long num) { + concat(num); + return (*this); + } + String & operator +=(unsigned long num) { + concat(num); + return (*this); + } + String & operator +=(float num) { + concat(num); + return (*this); + } + String & operator +=(double num) { + concat(num); + return (*this); + } + String & operator +=(long long num) { + concat(num); + return (*this); + } + String & operator +=(unsigned long long num) { + concat(num); + return (*this); + } + String & operator += (const __FlashStringHelper *str) {return *this += reinterpret_cast(str);} + + friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); + friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); + friend StringSumHelper & operator +(const StringSumHelper &lhs, char c); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, int num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, long num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, long long num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long long num); + + // comparison (only works w/ Strings and "strings") + operator StringIfHelperType() const { + return buffer() ? &String::StringIfHelper : 0; + } + int compareTo(const String &s) const; + bool equals(const String &s) const; + bool equals(const char *cstr) const; + bool operator ==(const String &rhs) const { + return equals(rhs); + } + bool operator ==(const char *cstr) const { + return equals(cstr); + } + bool operator !=(const String &rhs) const { + return !equals(rhs); + } + bool operator !=(const char *cstr) const { + return !equals(cstr); + } + bool operator <(const String &rhs) const; + bool operator >(const String &rhs) const; + bool operator <=(const String &rhs) const; + bool operator >=(const String &rhs) const; + bool equalsIgnoreCase(const String &s) const; + unsigned char equalsConstantTime(const String &s) const; + bool startsWith(const String &prefix) const; + bool startsWith(const char *prefix) const { + return this->startsWith(String(prefix)); + } + bool startsWith(const __FlashStringHelper *prefix) const { + return this->startsWith(reinterpret_cast(prefix)); + } + bool startsWith(const String &prefix, unsigned int offset) const; + bool endsWith(const String &suffix) const; + bool endsWith(const char *suffix) const { + return this->endsWith(String(suffix)); + } + bool endsWith(const __FlashStringHelper * suffix) const { + return this->endsWith(reinterpret_cast(suffix)); + } + + // character access + char charAt(unsigned int index) const; + void setCharAt(unsigned int index, char c); + char operator [](unsigned int index) const; + char& operator [](unsigned int index); + void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + getBytes((unsigned char *) buf, bufsize, index); + } + const char* c_str() const { return buffer(); } + char* begin() { return wbuffer(); } + char* end() { return wbuffer() + length(); } + const char* begin() const { return c_str(); } + const char* end() const { return c_str() + length(); } + + // search + int indexOf(char ch) const; + int indexOf(char ch, unsigned int fromIndex) const; + int indexOf(const String &str) const; + int indexOf(const String &str, unsigned int fromIndex) const; + int lastIndexOf(char ch) const; + int lastIndexOf(char ch, unsigned int fromIndex) const; + int lastIndexOf(const String &str) const; + int lastIndexOf(const String &str, unsigned int fromIndex) const; + String substring(unsigned int beginIndex) const { + return substring(beginIndex, len()); + } + String substring(unsigned int beginIndex, unsigned int endIndex) const; + + // modification + void replace(char find, char replace); + void replace(const String &find, const String &replace); + void replace(const char *find, const String &replace) { + this->replace(String(find), replace); + } + void replace(const __FlashStringHelper *find, const String &replace) { + this->replace(reinterpret_cast(find), replace); + } + void replace(const char *find, const char *replace) { + this->replace(String(find), String(replace)); + } + void replace(const __FlashStringHelper *find, const char *replace) { + this->replace(reinterpret_cast(find), String(replace)); + } + void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) { + this->replace(reinterpret_cast(find), reinterpret_cast(replace)); + } + void remove(unsigned int index); + void remove(unsigned int index, unsigned int count); + void toLowerCase(void); + void toUpperCase(void); + void trim(void); + + // parsing/conversion + long toInt(void) const; + float toFloat(void) const; + double toDouble(void) const; + + protected: + // Contains the string info when we're not in SSO mode + struct _ptr { + char * buff; + uint32_t cap; + uint32_t len; + }; + // This allows strings up up to 11 (10 + \0 termination) without any extra space. + enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 }; // Characters to allocate space for SSO, must be 12 or more + struct _sso { + char buff[SSOSIZE]; + unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields + unsigned char isSSO : 1; + } __attribute__((packed)); // Ensure that GCC doesn't expand the flag byte to a 32-bit word for alignment issues +#ifdef BOARD_HAS_PSRAM + enum { CAPACITY_MAX = 3145728 }; +#else + enum { CAPACITY_MAX = 65535 }; +#endif + union { + struct _ptr ptr; + struct _sso sso; + }; + // Accessor functions + inline bool isSSO() const { return sso.isSSO; } + inline unsigned int len() const { return isSSO() ? sso.len : ptr.len; } + inline unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; } // Size of max string not including terminal NUL + inline void setSSO(bool set) { sso.isSSO = set; } + inline void setLen(int len) { + if (isSSO()) { + sso.len = len; + sso.buff[len] = 0; + } else { + ptr.len = len; + if (ptr.buff) { + ptr.buff[len] = 0; + } + } + } + inline void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; } + inline void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; } + // Buffer accessor functions + inline const char *buffer() const { return reinterpret_cast(isSSO() ? sso.buff : ptr.buff); } + inline char *wbuffer() const { return isSSO() ? const_cast(sso.buff) : ptr.buff; } // Writable version of buffer + + protected: + void init(void); + void invalidate(void); + bool changeBuffer(unsigned int maxStrLen); + + // copy and move + String & copy(const char *cstr, unsigned int length); + String & copy(const __FlashStringHelper *pstr, unsigned int length) { + return copy(reinterpret_cast(pstr), length); + } +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + void move(String &rhs); +#endif +}; + +class StringSumHelper: public String { + public: + StringSumHelper(const String &s) : + String(s) { + } + StringSumHelper(const char *p) : + String(p) { + } + StringSumHelper(char c) : + String(c) { + } + StringSumHelper(unsigned char num) : + String(num) { + } + StringSumHelper(int num) : + String(num) { + } + StringSumHelper(unsigned int num) : + String(num) { + } + StringSumHelper(long num) : + String(num) { + } + StringSumHelper(unsigned long num) : + String(num) { + } + StringSumHelper(float num) : + String(num) { + } + StringSumHelper(double num) : + String(num) { + } + StringSumHelper(long long num) : + String(num) { + } + StringSumHelper(unsigned long long num) : + String(num) { + } +}; + +inline StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) { + return lhs + reinterpret_cast(rhs); +} + +extern const String emptyString; + +#endif // __cplusplus +#endif // String_class_h diff --git a/cores/esp32/dummySerial.cpp b/cores/esp32/dummySerial.cpp new file mode 100644 index 00000000000..bc835711550 --- /dev/null +++ b/cores/esp32/dummySerial.cpp @@ -0,0 +1,9 @@ +// Dummy class to disable Arnduino Serial.println +// Add other Serial. members if needed + + +#include + +void dummySerial::println(const char * c) {} + +dummySerial dSerial; diff --git a/cores/esp32/pgmspace.h b/cores/esp32/pgmspace.h new file mode 100644 index 00000000000..75f7e801b95 --- /dev/null +++ b/cores/esp32/pgmspace.h @@ -0,0 +1,89 @@ +/* + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the RaspberryPi core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef PGMSPACE_INCLUDE +#define PGMSPACE_INCLUDE + +typedef void prog_void; +typedef char prog_char; +typedef unsigned char prog_uchar; +typedef char prog_int8_t; +typedef unsigned char prog_uint8_t; +typedef short prog_int16_t; +typedef unsigned short prog_uint16_t; +typedef long prog_int32_t; +typedef unsigned long prog_uint32_t; + +#define PROGMEM +#define PGM_P const char * +#define PGM_VOID_P const void * +#define PSTR(s) (s) +#define _SFR_BYTE(n) (n) + +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#define pgm_read_word(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned short *)(_addr); \ +}) +#define pgm_read_dword(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned long *)(_addr); \ +}) +#define pgm_read_float(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(const float *)(_addr); \ +}) +#define pgm_read_ptr(addr) ({ \ + typeof(addr) _addr = (addr); \ + *(void * const *)(_addr); \ +}) + +#define pgm_get_far_address(x) ((uint32_t)(&(x))) + +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#define pgm_read_word_near(addr) pgm_read_word(addr) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#define pgm_read_float_near(addr) pgm_read_float(addr) +#define pgm_read_ptr_near(addr) pgm_read_ptr(addr) +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#define pgm_read_word_far(addr) pgm_read_word(addr) +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#define pgm_read_float_far(addr) pgm_read_float(addr) +#define pgm_read_ptr_far(addr) pgm_read_ptr(addr) + +#define memcmp_P memcmp +#define memccpy_P memccpy +#define memmem_P memmem +#define memcpy_P memcpy +#define strcpy_P strcpy +#define strncpy_P strncpy +#define strcat_P strcat +#define strncat_P strncat +#define strcmp_P strcmp +#define strncmp_P strncmp +#define strcasecmp_P strcasecmp +#define strncasecmp_P strncasecmp +#define strlen_P strlen +#define strnlen_P strnlen +#define strstr_P strstr +#define printf_P printf +#define sprintf_P sprintf +#define snprintf_P snprintf +#define vsnprintf_P vsnprintf + +#endif diff --git a/cores/esp32/stdlib_noniso.c b/cores/esp32/stdlib_noniso.c new file mode 100644 index 00000000000..e1e9eed6d41 --- /dev/null +++ b/cores/esp32/stdlib_noniso.c @@ -0,0 +1,210 @@ +/* + core_esp8266_noniso.c - nonstandard (but usefull) conversion functions + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 03 April 2015 by Markus Sattler + + */ + +#include +#include +#include +#include +#include +#include "stdlib_noniso.h" +#include "esp_system.h" + +static void reverse(char* begin, char* end) { + char *is = begin; + char *ie = end - 1; + while(is < ie) { + char tmp = *ie; + *ie = *is; + *is = tmp; + ++is; + --ie; + } +} + +char* ltoa(long value, char* result, int base) { + if(base < 2 || base > 16) { + *result = 0; + return result; + } + + char* out = result; + long quotient = abs(value); + + do { + const long tmp = quotient / base; + *out = "0123456789abcdef"[quotient - (tmp * base)]; + ++out; + quotient = tmp; + } while(quotient); + + // Apply negative sign + if(value < 0) + *out++ = '-'; + + reverse(result, out); + *out = 0; + return result; +} + +char* lltoa (long long val, char* result, int base) { + if(base < 2 || base > 16) { + *result = 0; + return result; + } + + char* out = result; + long long quotient = val > 0 ? val : -val; + + do { + const long long tmp = quotient / base; + *out = "0123456789abcdef"[quotient - (tmp * base)]; + ++out; + quotient = tmp; + } while(quotient); + + // Apply negative sign + if(val < 0) + *out++ = '-'; + + reverse(result, out); + *out = 0; + return result; +} + +char* ultoa(unsigned long value, char* result, int base) { + if(base < 2 || base > 16) { + *result = 0; + return result; + } + + char* out = result; + unsigned long quotient = value; + + do { + const unsigned long tmp = quotient / base; + *out = "0123456789abcdef"[quotient - (tmp * base)]; + ++out; + quotient = tmp; + } while(quotient); + + reverse(result, out); + *out = 0; + return result; +} + +char* ulltoa (unsigned long long val, char* result, int base) { + if(base < 2 || base > 16) { + *result = 0; + return result; + } + + char* out = result; + unsigned long long quotient = val; + + do { + const unsigned long long tmp = quotient / base; + *out = "0123456789abcdef"[quotient - (tmp * base)]; + ++out; + quotient = tmp; + } while(quotient); + + reverse(result, out); + *out = 0; + return result; +} + +char * dtostrf(double number, signed int width, unsigned int prec, char *s) { + bool negative = false; + + if (isnan(number)) { + strcpy(s, "nan"); + return s; + } + if (isinf(number)) { + strcpy(s, "inf"); + return s; + } + + char* out = s; + + int fillme = width; // how many cells to fill for the integer part + if (prec > 0) { + fillme -= (prec+1); + } + + // Handle negative numbers + if (number < 0.0) { + negative = true; + fillme--; + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + // I optimized out most of the divisions + double rounding = 2.0; + for (unsigned int i = 0; i < prec; ++i) + rounding *= 10.0; + rounding = 1.0 / rounding; + + number += rounding; + + // Figure out how big our number really is + double tenpow = 1.0; + unsigned int digitcount = 1; + while (number >= 10.0 * tenpow) { + tenpow *= 10.0; + digitcount++; + } + + number /= tenpow; + fillme -= digitcount; + + // Pad unused cells with spaces + while (fillme-- > 0) { + *out++ = ' '; + } + + // Handle negative sign + if (negative) *out++ = '-'; + + // Print the digits, and if necessary, the decimal point + digitcount += prec; + int8_t digit = 0; + while (digitcount-- > 0) { + digit = (int8_t)number; + if (digit > 9) digit = 9; // insurance + *out++ = (char)('0' | digit); + if ((digitcount == prec) && (prec > 0)) { + *out++ = '.'; + } + number -= digit; + number *= 10.0; + } + + // make sure the string is terminated + *out = 0; + return s; +} + + diff --git a/cores/esp32/stdlib_noniso.h b/cores/esp32/stdlib_noniso.h new file mode 100644 index 00000000000..8356c1199aa --- /dev/null +++ b/cores/esp32/stdlib_noniso.h @@ -0,0 +1,53 @@ +/* + stdlib_noniso.h - nonstandard (but usefull) conversion functions + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef STDLIB_NONISO_H +#define STDLIB_NONISO_H + +#ifdef __cplusplus +extern "C" { +#endif + +int atoi(const char *s); + +long atol(const char* s); + +double atof(const char* s); + +char* itoa (int val, char *s, int radix); + +char* ltoa (long val, char *s, int radix); + +char* lltoa (long long val, char* s, int radix); + +char* utoa (unsigned int val, char *s, int radix); + +char* ultoa (unsigned long val, char *s, int radix); + +char* ulltoa (unsigned long long val, char* s, int radix); + +char* dtostrf (double val, signed int width, unsigned int prec, char *s); + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif