diff --git a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp index af989002..c47a3bc9 100644 --- a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp +++ b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp @@ -345,6 +345,121 @@ void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, } } +/**************************************************************************/ +/*! + @brief Draw an ellipse outline + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param rw Horizontal radius of ellipse + @param rh Vertical radius of ellipse + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh, + uint16_t color) { +#if defined(ESP8266) + yield(); +#endif + // Bresenham's ellipse algorithm + int16_t x = 0, y = rh; + int32_t rw2 = rw * rw, rh2 = rh * rh; + int32_t twoRw2 = 2 * rw2, twoRh2 = 2 * rh2; + + int32_t decision = rh2 - (rw2 * rh) + (rw2 / 4); + + startWrite(); + + // region 1 + while ((twoRh2 * x) < (twoRw2 * y)) { + writePixel(x0 + x, y0 + y, color); + writePixel(x0 - x, y0 + y, color); + writePixel(x0 + x, y0 - y, color); + writePixel(x0 - x, y0 - y, color); + x++; + if (decision < 0) { + decision += rh2 + (twoRh2 * x); + } else { + decision += rh2 + (twoRh2 * x) - (twoRw2 * y); + y--; + } + } + + // region 2 + decision = ((rh2 * (2 * x + 1) * (2 * x + 1)) >> 2) + + (rw2 * (y - 1) * (y - 1)) - (rw2 * rh2); + while (y >= 0) { + writePixel(x0 + x, y0 + y, color); + writePixel(x0 - x, y0 + y, color); + writePixel(x0 + x, y0 - y, color); + writePixel(x0 - x, y0 - y, color); + y--; + if (decision > 0) { + decision += rw2 - (twoRw2 * y); + } else { + decision += rw2 + (twoRh2 * x) - (twoRw2 * y); + x++; + } + } + + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw an ellipse with filled colour + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param rw Horizontal radius of ellipse + @param rh Vertical radius of ellipse + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh, + uint16_t color) { +#if defined(ESP8266) + yield(); +#endif + // Bresenham's ellipse algorithm + int16_t x = 0, y = rh; + int32_t rw2 = rw * rw, rh2 = rh * rh; + int32_t twoRw2 = 2 * rw2, twoRh2 = 2 * rh2; + + int32_t decision = rh2 - (rw2 * rh) + (rw2 / 4); + + startWrite(); + + // region 1 + while ((twoRh2 * x) < (twoRw2 * y)) { + x++; + if (decision < 0) { + decision += rh2 + (twoRh2 * x); + } else { + decision += rh2 + (twoRh2 * x) - (twoRw2 * y); + drawFastHLine(x0 - (x - 1), y0 + y, 2 * (x - 1) + 1, color); + drawFastHLine(x0 - (x - 1), y0 - y, 2 * (x - 1) + 1, color); + y--; + } + } + + // region 2 + decision = ((rh2 * (2 * x + 1) * (2 * x + 1)) >> 2) + + (rw2 * (y - 1) * (y - 1)) - (rw2 * rh2); + while (y >= 0) { + drawFastHLine(x0 - x, y0 + y, 2 * x + 1, color); + drawFastHLine(x0 - x, y0 - y, 2 * x + 1, color); + + y--; + if (decision > 0) { + decision += rw2 - (twoRw2 * y); + } else { + decision += rw2 + (twoRh2 * x) - (twoRw2 * y); + x++; + } + } + + endWrite(); +} + /**************************************************************************/ /*! @brief Draw a circle outline @@ -399,8 +514,8 @@ void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, @param x0 Center-point x coordinate @param y0 Center-point y coordinate @param r Radius of circle - @param cornername Mask bit #1 or bit #2 to indicate which quarters of - the circle we're doing + @param cornername Mask bit #1, #2, #4, and #8 to indicate which quarters + of the circle we're doing @param color 16-bit 5-6-5 Color to draw with */ /**************************************************************************/ @@ -459,11 +574,12 @@ void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, /**************************************************************************/ /*! - @brief Quarter-circle drawer with fill, used for circles and roundrects + @brief Half-circle drawer with fill, used for circles and roundrects @param x0 Center-point x coordinate @param y0 Center-point y coordinate @param r Radius of circle - @param corners Mask bits indicating which quarters we're doing + @param corners Mask bits indicating which sides of the circle we are + doing, left (1) and/or right (2) @param delta Offset from center-point, used for round-rects @param color 16-bit 5-6-5 Color to fill with */ @@ -1758,12 +1874,21 @@ const uint8_t PROGMEM GFXcanvas1::GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF, @brief Instatiate a GFX 1-bit canvas context for graphics @param w Display width, in pixels @param h Display height, in pixels + @param allocate_buffer If true, a buffer is allocated with malloc. If + false, the subclass must initialize the buffer before any drawing operation, + and free it in the destructor. If false (the default), the buffer is + allocated and freed by the library. */ /**************************************************************************/ -GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint32_t bytes = ((w + 7) / 8) * h; - if ((buffer = (uint8_t *)malloc(bytes))) { - memset(buffer, 0, bytes); +GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h, bool allocate_buffer) + : Adafruit_GFX(w, h), buffer_owned(allocate_buffer) { + if (allocate_buffer) { + uint32_t bytes = ((w + 7) / 8) * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } + } else { + buffer = nullptr; } } @@ -1773,7 +1898,7 @@ GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { */ /**************************************************************************/ GFXcanvas1::~GFXcanvas1(void) { - if (buffer) + if (buffer && buffer_owned) free(buffer); } @@ -2111,13 +2236,21 @@ void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w, @brief Instatiate a GFX 8-bit canvas context for graphics @param w Display width, in pixels @param h Display height, in pixels + @param allocate_buffer If true, a buffer is allocated with malloc. If + false, the subclass must initialize the buffer before any drawing operation, + and free it in the destructor. If false (the default), the buffer is + allocated and freed by the library. */ /**************************************************************************/ -GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint32_t bytes = w * h; - if ((buffer = (uint8_t *)malloc(bytes))) { - memset(buffer, 0, bytes); - } +GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h, bool allocate_buffer) + : Adafruit_GFX(w, h), buffer_owned(allocate_buffer) { + if (allocate_buffer) { + uint32_t bytes = w * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } + } else + buffer = nullptr; } /**************************************************************************/ @@ -2126,7 +2259,7 @@ GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { */ /**************************************************************************/ GFXcanvas8::~GFXcanvas8(void) { - if (buffer) + if (buffer && buffer_owned) free(buffer); } @@ -2379,12 +2512,21 @@ void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w, @brief Instatiate a GFX 16-bit canvas context for graphics @param w Display width, in pixels @param h Display height, in pixels + @param allocate_buffer If true, a buffer is allocated with malloc. If + false, the subclass must initialize the buffer before any drawing operation, + and free it in the destructor. If false (the default), the buffer is + allocated and freed by the library. */ /**************************************************************************/ -GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint32_t bytes = w * h * 2; - if ((buffer = (uint16_t *)malloc(bytes))) { - memset(buffer, 0, bytes); +GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h, bool allocate_buffer) + : Adafruit_GFX(w, h), buffer_owned(allocate_buffer) { + if (allocate_buffer) { + uint32_t bytes = w * h * 2; + if ((buffer = (uint16_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } + } else { + buffer = nullptr; } } @@ -2394,7 +2536,7 @@ GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { */ /**************************************************************************/ GFXcanvas16::~GFXcanvas16(void) { - if (buffer) + if (buffer && buffer_owned) free(buffer); } diff --git a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h index 63c6ab68..032d8ea4 100644 --- a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h +++ b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h @@ -73,6 +73,10 @@ public: void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color); + void drawEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh, + uint16_t color); + void fillEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh, + uint16_t color); void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, @@ -309,7 +313,7 @@ private: /// A GFX 1-bit canvas context for graphics class GFXcanvas1 : public Adafruit_GFX { public: - GFXcanvas1(uint16_t w, uint16_t h); + GFXcanvas1(uint16_t w, uint16_t h, bool allocate_buffer = true); ~GFXcanvas1(void); void drawPixel(int16_t x, int16_t y, uint16_t color); void fillScreen(uint16_t color); @@ -328,7 +332,9 @@ protected: bool getRawPixel(int16_t x, int16_t y) const; void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - uint8_t *buffer; ///< Raster data: no longer private, allow subclass access + uint8_t *buffer; ///< Raster data: no longer private, allow subclass access + bool buffer_owned; ///< If true, destructor will free buffer, else it will do + ///< nothing private: #ifdef __AVR__ @@ -340,7 +346,7 @@ private: /// A GFX 8-bit canvas context for graphics class GFXcanvas8 : public Adafruit_GFX { public: - GFXcanvas8(uint16_t w, uint16_t h); + GFXcanvas8(uint16_t w, uint16_t h, bool allocate_buffer = true); ~GFXcanvas8(void); void drawPixel(int16_t x, int16_t y, uint16_t color); void fillScreen(uint16_t color); @@ -359,13 +365,15 @@ protected: uint8_t getRawPixel(int16_t x, int16_t y) const; void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - uint8_t *buffer; ///< Raster data: no longer private, allow subclass access + uint8_t *buffer; ///< Raster data: no longer private, allow subclass access + bool buffer_owned; ///< If true, destructor will free buffer, else it will do + ///< nothing }; /// A GFX 16-bit canvas context for graphics class GFXcanvas16 : public Adafruit_GFX { public: - GFXcanvas16(uint16_t w, uint16_t h); + GFXcanvas16(uint16_t w, uint16_t h, bool allocate_buffer = true); ~GFXcanvas16(void); void drawPixel(int16_t x, int16_t y, uint16_t color); void fillScreen(uint16_t color); @@ -385,7 +393,9 @@ protected: uint16_t getRawPixel(int16_t x, int16_t y) const; void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - uint16_t *buffer; ///< Raster data: no longer private, allow subclass access + uint16_t *buffer; ///< Raster data: no longer private, allow subclass access + bool buffer_owned; ///< If true, destructor will free buffer, else it will do + ///< nothing }; #endif // _ADAFRUIT_GFX_H diff --git a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp index eeffce78..870979b3 100644 --- a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp +++ b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp @@ -17,9 +17,10 @@ * @section dependencies Dependencies * - * This library depends on - * Adafruit_GFX being present on your system. Please make sure you have - * installed the latest version before using this library. + * This library depends on + * Adafruit_GFX + * being present on your system. Please make sure you have installed the latest + * version before using this library. * * @section author Author * @@ -31,7 +32,8 @@ * BSD license, all text here must be included in any redistribution. */ -#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all +// Not for ATtiny, at all +#if !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny84__) #include "Adafruit_SPITFT.h" @@ -39,6 +41,13 @@ #if defined(__AVR_XMEGA__) // only tested with __AVR_ATmega4809__ #define AVR_WRITESPI(x) \ for (SPI0_DATA = (x); (!(SPI0_INTFLAGS & _BV(SPI_IF_bp)));) +#elif defined(__LGT8F__) +#define AVR_WRITESPI(x) \ + SPDR = (x); \ + asm volatile("nop"); \ + while ((SPFR & _BV(RDEMPT))) \ + ; \ + SPFR = _BV(RDEMPT) | _BV(WREMPT) #else #define AVR_WRITESPI(x) for (SPDR = (x); (!(SPSR & _BV(SPIF)));) #endif @@ -870,7 +879,7 @@ void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) { DMA_ADDRESS_INCREMENT_STEP_SIZE_1; descriptor[d].DSTADDR.reg = (uint32_t)tft8.writePort; } -#endif // __SAMD51 +#endif // __SAMD51 } // end parallel-specific DMA setup lastFillColor = 0x0000; @@ -878,13 +887,13 @@ void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) { dma.setCallback(dma_callback); return; // Success! // else clean up any partial allocation... - } // end descriptor memalign() + } // end descriptor memalign() free(pixelBuf[0]); pixelBuf[0] = pixelBuf[1] = NULL; - } // end pixelBuf malloc() - // Don't currently have a descriptor delete function in - // ZeroDMA lib, but if we did, it would be called here. - } // end addDescriptor() + } // end pixelBuf malloc() + // Don't currently have a descriptor delete function in + // ZeroDMA lib, but if we did, it would be called here. + } // end addDescriptor() dma.free(); // Deallocate DMA channel } #endif // end USE_SPI_DMA @@ -1023,7 +1032,7 @@ void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len, bool block, return; #elif defined(ARDUINO_ARCH_RP2040) - spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1; + spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE; if (!bigEndian) { // switch to 16-bit writes @@ -1037,6 +1046,15 @@ void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len, bool block, spi_write_blocking(pi_spi, (uint8_t *)colors, len * 2); } return; +#elif defined(ARDUINO_ARCH_RTTHREAD) + if (!bigEndian) { + swapBytes(colors, len); // convert little-to-big endian for display + } + hwspi._spi->transfer(colors, 2 * len); + if (!bigEndian) { + swapBytes(colors, len); // big-to-little endian to restore pixel buffer + } + return; #elif defined(USE_SPI_DMA) && \ (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) if ((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) { @@ -1244,7 +1262,44 @@ void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { rtos_free(pixbuf); return; } -#else // !ESP32 +#elif defined(ARDUINO_ARCH_RTTHREAD) + uint16_t pixbufcount; + uint16_t *pixbuf; + int16_t lines = height() / 4; +#define QUICKPATH_MAX_LEN 16 + uint16_t quickpath_buffer[QUICKPATH_MAX_LEN]; + + do { + pixbufcount = min(len, (lines * width())); + if (pixbufcount > QUICKPATH_MAX_LEN) { + pixbuf = (uint16_t *)rt_malloc(2 * pixbufcount); + } else { + pixbuf = quickpath_buffer; + } + lines -= 2; + } while (!pixbuf && lines > 0); + + if (pixbuf) { + uint16_t const swap_color = __builtin_bswap16(color); + + while (len) { + uint16_t count = min(len, pixbufcount); + // fill buffer with color + for (uint16_t i = 0; i < count; i++) { + pixbuf[i] = swap_color; + } + // Don't need to swap color inside the function + // It has been done outside this function + writePixels(pixbuf, count, true, true); + len -= count; + } + if (pixbufcount > QUICKPATH_MAX_LEN) { + rt_free(pixbuf); + } +#undef QUICKPATH_MAX_LEN + return; + } +#else // !ESP32 #if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) if (((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) && (len >= 16)) { // Don't bother with DMA on short pixel runs @@ -1318,11 +1373,11 @@ void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { dma.trigger(); while (dma_busy) ; // Wait for completion - // Unfortunately blocking is necessary. An earlier version returned - // immediately and checked dma_busy on startWrite() instead, but it - // turns out to be MUCH slower on many graphics operations (as when - // drawing lines, pixel-by-pixel), perhaps because it's a volatile - // type and doesn't cache. Working on this. + // Unfortunately blocking is necessary. An earlier version returned + // immediately and checked dma_busy on startWrite() instead, but it + // turns out to be MUCH slower on many graphics operations (as when + // drawing lines, pixel-by-pixel), perhaps because it's a volatile + // type and doesn't cache. Working on this. #if defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO) if (connection == TFT_HARD_SPI) { // SAMD51: SPI DMA seems to leave the SPI peripheral in a freaky @@ -1355,7 +1410,7 @@ void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { } } while (len); #elif defined(ARDUINO_ARCH_RP2040) - spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1; + spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE; color = __builtin_bswap16(color); while (len--) @@ -2034,9 +2089,9 @@ uint16_t Adafruit_SPITFT::readcommand16(uint16_t addr) { result = *(volatile uint16_t *)tft8.readPort; // 16-bit read *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state #else // !HAS_PORT_SET_CLR - *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state - result = *(volatile uint16_t *)tft8.readPort; // 16-bit read - *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + result = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state #endif // end !HAS_PORT_SET_CLR TFT_RD_HIGH(); // Read line HIGH endWrite(); @@ -2114,7 +2169,7 @@ void Adafruit_SPITFT::spiWrite(uint8_t b) { #elif defined(ESP8266) || defined(ESP32) hwspi._spi->write(b); #elif defined(ARDUINO_ARCH_RP2040) - spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1; + spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE; spi_write_blocking(pi_spi, &b, 1); #else hwspi._spi->transfer(b); @@ -2191,17 +2246,17 @@ uint8_t Adafruit_SPITFT::spiRead(void) { w = *tft8.readPort; // Read value from port *tft8.portDir = 0xFF; // Restore port to output #else // !__AVR__ - if (!tft8.wide) { // 8-bit TFT connection + if (!tft8.wide) { // 8-bit TFT connection #if defined(HAS_PORT_SET_CLR) - *tft8.dirClr = 0xFF; // Set port to input state - w = *tft8.readPort; // Read value from port - *tft8.dirSet = 0xFF; // Restore port to output + *tft8.dirClr = 0xFF; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.dirSet = 0xFF; // Restore port to output #else // !HAS_PORT_SET_CLR - *tft8.portDir = 0x00; // Set port to input state - w = *tft8.readPort; // Read value from port - *tft8.portDir = 0xFF; // Restore port to output + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output #endif // end HAS_PORT_SET_CLR - } else { // 16-bit TFT connection + } else { // 16-bit TFT connection #if defined(HAS_PORT_SET_CLR) *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state w = *(volatile uint16_t *)tft8.readPort; // 16-bit read @@ -2212,7 +2267,7 @@ uint8_t Adafruit_SPITFT::spiRead(void) { *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state #endif // end !HAS_PORT_SET_CLR } - TFT_RD_HIGH(); // Read line HIGH + TFT_RD_HIGH(); // Read line HIGH #endif // end !__AVR__ #else // !USE_FAST_PINIO w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO @@ -2399,9 +2454,11 @@ void Adafruit_SPITFT::SPI_WRITE16(uint16_t w) { #elif defined(ESP8266) || defined(ESP32) hwspi._spi->write16(w); #elif defined(ARDUINO_ARCH_RP2040) - spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1; + spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE; w = __builtin_bswap16(w); spi_write_blocking(pi_spi, (uint8_t *)&w, 2); +#elif defined(ARDUINO_ARCH_RTTHREAD) + hwspi._spi->transfer16(w); #else // MSB, LSB because TFTs are generally big-endian hwspi._spi->transfer(w >> 8); @@ -2455,9 +2512,12 @@ void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) { #elif defined(ESP8266) || defined(ESP32) hwspi._spi->write32(l); #elif defined(ARDUINO_ARCH_RP2040) - spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1; + spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE; l = __builtin_bswap32(l); spi_write_blocking(pi_spi, (uint8_t *)&l, 4); +#elif defined(ARDUINO_ARCH_RTTHREAD) + hwspi._spi->transfer16(l >> 16); + hwspi._spi->transfer16(l); #else hwspi._spi->transfer(l >> 24); hwspi._spi->transfer(l >> 16); @@ -2558,4 +2618,4 @@ inline void Adafruit_SPITFT::TFT_RD_LOW(void) { #endif // end !USE_FAST_PINIO } -#endif // end __AVR_ATtiny85__ +#endif // end __AVR_ATtiny85__ __AVR_ATtiny84__ diff --git a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h index 8064a742..7d0843e6 100644 --- a/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h +++ b/trunk/Arduino/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h @@ -20,7 +20,8 @@ #ifndef _ADAFRUIT_SPITFT_H_ #define _ADAFRUIT_SPITFT_H_ -#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all +// Not for ATtiny, at all +#if !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny84__) #include "Adafruit_GFX.h" #include @@ -31,8 +32,8 @@ typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit #define USE_FAST_PINIO ///< Use direct PORT register access #elif defined(ARDUINO_STM32_FEATHER) // WICED -typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED -typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit #elif defined(__arm__) #if defined(ARDUINO_ARCH_SAMD) // Adafruit M0, M4 @@ -65,7 +66,7 @@ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit #endif // end !ARM typedef volatile ADAGFX_PORT_t *PORTreg_t; ///< PORT register type -#if defined(__AVR__) +#if defined(__AVR__) && !defined(__LGT8F__) #define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed #else #define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed @@ -78,8 +79,8 @@ typedef volatile ADAGFX_PORT_t *PORTreg_t; ///< PORT register type defined(ADAFRUIT_CIRCUITPLAYGROUND_M0) #define USE_SPI_DMA ///< Auto DMA #else - //#define USE_SPI_DMA ///< If set, - // use DMA if available + // #define USE_SPI_DMA ///< If set, + // use DMA if available #endif // Another "oops" name -- this now also handles parallel DMA. // If DMA is enabled, Arduino sketch MUST #include @@ -102,6 +103,16 @@ typedef volatile ADAGFX_PORT_t *PORTreg_t; ///< PORT register type /*! For first arg to parallel constructor */ enum tftBusWidth { tft8bitbus, tft16bitbus }; +// SPI defaults for RP2040 +#if defined(ARDUINO_ARCH_RP2040) +#ifndef __SPI0_DEVICE +#define __SPI0_DEVICE spi0 +#endif +#ifndef __SPI1_DEVICE +#define __SPI1_DEVICE spi1 +#endif +#endif + // CLASS DEFINITION -------------------------------------------------------- /*! @@ -396,8 +407,8 @@ protected: PORTreg_t dcPortSet; ///< PORT register for data/command SET PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR #else // !HAS_PORT_SET_CLR - PORTreg_t csPort; ///< PORT register for chip select - PORTreg_t dcPort; ///< PORT register for data/command + PORTreg_t csPort; ///< PORT register for chip select + PORTreg_t dcPort; ///< PORT register for data/command #endif // end HAS_PORT_SET_CLR #endif // end USE_FAST_PINIO #if defined(__cplusplus) && (__cplusplus >= 201100) @@ -447,8 +458,8 @@ protected: volatile uint32_t *writePort; ///< PORT register for DATA WRITE volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ #else - volatile uint8_t *writePort; ///< PORT register for DATA WRITE - volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ + volatile uint8_t *writePort; ///< PORT register for DATA WRITE + volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ #endif #if defined(HAS_PORT_SET_CLR) // Port direction register pointers are always 8-bit regardless of @@ -507,10 +518,10 @@ protected: ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command #endif // end !KINETISK #else // !HAS_PORT_SET_CLR - ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR) - ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND) - ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR) - ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND) + ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR) + ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND) + ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR) + ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND) #endif // end HAS_PORT_SET_CLR #endif // end USE_FAST_PINIO uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc. @@ -526,5 +537,5 @@ protected: uint32_t _freq = 0; ///< Dummy var to keep subclasses happy }; -#endif // end __AVR_ATtiny85__ +#endif // end __AVR_ATtiny85__ __AVR_ATtiny84__ #endif // end _ADAFRUIT_SPITFT_H_ diff --git a/trunk/Arduino/libraries/Adafruit_GFX_Library/library.properties b/trunk/Arduino/libraries/Adafruit_GFX_Library/library.properties index 7d8849e5..8da8dba6 100644 --- a/trunk/Arduino/libraries/Adafruit_GFX_Library/library.properties +++ b/trunk/Arduino/libraries/Adafruit_GFX_Library/library.properties @@ -1,5 +1,5 @@ name=Adafruit GFX Library -version=1.11.9 +version=1.12.4 author=Adafruit maintainer=Adafruit sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from. diff --git a/trunk/Arduino/libraries/Adafruit_Unified_Sensor/Adafruit_Sensor.h b/trunk/Arduino/libraries/Adafruit_Unified_Sensor/Adafruit_Sensor.h index 8c6a07df..ad865da6 100644 --- a/trunk/Arduino/libraries/Adafruit_Unified_Sensor/Adafruit_Sensor.h +++ b/trunk/Arduino/libraries/Adafruit_Unified_Sensor/Adafruit_Sensor.h @@ -151,7 +151,7 @@ typedef struct { float tvoc; /**< Total Volatile Organic Compounds, in ppb */ float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is normal (unitless) */ - float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal + float nox_index; /**< NOx (Nitrogen Oxides) index where 1 is normal (unitless) */ float CO2; /**< Measured CO2 in parts per million (ppm) */ float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm diff --git a/trunk/Arduino/libraries/Adafruit_Unified_Sensor/library.properties b/trunk/Arduino/libraries/Adafruit_Unified_Sensor/library.properties index ed5a21bb..65ffd6ea 100644 --- a/trunk/Arduino/libraries/Adafruit_Unified_Sensor/library.properties +++ b/trunk/Arduino/libraries/Adafruit_Unified_Sensor/library.properties @@ -1,5 +1,5 @@ name=Adafruit Unified Sensor -version=1.1.14 +version=1.1.15 author=Adafruit maintainer=Adafruit sentence=Required for all Adafruit Unified Sensor based libraries. diff --git a/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.cpp b/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.cpp index a76bc51b..fd15d85e 100644 --- a/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.cpp +++ b/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.cpp @@ -1,1033 +1,809 @@ -// 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. - -#include "DallasTemperature.h" - -#if ARDUINO >= 100 -#include "Arduino.h" -#else -extern "C" { -#include "WConstants.h" -} -#endif - -// OneWire commands -#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad -#define COPYSCRATCH 0x48 // Copy scratchpad to EEPROM -#define READSCRATCH 0xBE // Read from scratchpad -#define WRITESCRATCH 0x4E // Write to scratchpad -#define RECALLSCRATCH 0xB8 // Recall from EEPROM to scratchpad -#define READPOWERSUPPLY 0xB4 // Determine if device needs parasite power -#define ALARMSEARCH 0xEC // Query bus for devices with an alarm condition - -// Scratchpad locations -#define TEMP_LSB 0 -#define TEMP_MSB 1 -#define HIGH_ALARM_TEMP 2 -#define LOW_ALARM_TEMP 3 -#define CONFIGURATION 4 -#define INTERNAL_BYTE 5 -#define COUNT_REMAIN 6 -#define COUNT_PER_C 7 -#define SCRATCHPAD_CRC 8 - -// DSROM FIELDS -#define DSROM_FAMILY 0 -#define DSROM_CRC 7 - -// Device resolution -#define TEMP_9_BIT 0x1F // 9 bit -#define TEMP_10_BIT 0x3F // 10 bit -#define TEMP_11_BIT 0x5F // 11 bit -#define TEMP_12_BIT 0x7F // 12 bit - -#define MAX_CONVERSION_TIMEOUT 750 - -// Alarm handler -#define NO_ALARM_HANDLER ((AlarmHandler *)0) - - -DallasTemperature::DallasTemperature() { -#if REQUIRESALARMS - setAlarmHandler(NO_ALARM_HANDLER); -#endif - useExternalPullup = false; -} - -DallasTemperature::DallasTemperature(OneWire* _oneWire) : DallasTemperature() { - setOneWire(_oneWire); -} - -bool DallasTemperature::validFamily(const uint8_t* deviceAddress) { - switch (deviceAddress[DSROM_FAMILY]) { - case DS18S20MODEL: - case DS18B20MODEL: - case DS1822MODEL: - case DS1825MODEL: - case DS28EA00MODEL: - return true; - default: - return false; - } -} - -/* - * Constructs DallasTemperature with strong pull-up turned on. Strong pull-up is mandated in DS18B20 datasheet for parasitic - * power (2 wires) setup. (https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf, p. 7, section 'Powering the DS18B20'). - */ -DallasTemperature::DallasTemperature(OneWire* _oneWire, uint8_t _pullupPin) : DallasTemperature(_oneWire) { - setPullupPin(_pullupPin); -} - -void DallasTemperature::setPullupPin(uint8_t _pullupPin) { - useExternalPullup = true; - pullupPin = _pullupPin; - pinMode(pullupPin, OUTPUT); - deactivateExternalPullup(); -} - -void DallasTemperature::setOneWire(OneWire* _oneWire) { - - _wire = _oneWire; - devices = 0; - ds18Count = 0; - parasite = false; - bitResolution = 9; - waitForConversion = true; - checkForConversion = true; - autoSaveScratchPad = true; - -} - -// initialise the bus -void DallasTemperature::begin(void) { - - DeviceAddress deviceAddress; - - _wire->reset_search(); - devices = 0; // Reset the number of devices when we enumerate wire devices - ds18Count = 0; // Reset number of DS18xxx Family devices - - while (_wire->search(deviceAddress)) { - - if (validAddress(deviceAddress)) { - devices++; - - if (validFamily(deviceAddress)) { - ds18Count++; - - if (!parasite && readPowerSupply(deviceAddress)) - parasite = true; - - uint8_t b = getResolution(deviceAddress); - if (b > bitResolution) bitResolution = b; - } - } - } -} - -// returns the number of devices found on the bus -uint8_t DallasTemperature::getDeviceCount(void) { - return devices; -} - -uint8_t DallasTemperature::getDS18Count(void) { - return ds18Count; -} - -// returns true if address is valid -bool DallasTemperature::validAddress(const uint8_t* deviceAddress) { - return (_wire->crc8(deviceAddress, 7) == deviceAddress[DSROM_CRC]); -} - -// finds an address at a given index on the bus -// returns true if the device was found -bool DallasTemperature::getAddress(uint8_t* deviceAddress, uint8_t index) { - - uint8_t depth = 0; - - _wire->reset_search(); - - while (depth <= index && _wire->search(deviceAddress)) { - if (depth == index && validAddress(deviceAddress)) - return true; - depth++; - } - - return false; - -} - -// attempt to determine if the device at the given address is connected to the bus -bool DallasTemperature::isConnected(const uint8_t* deviceAddress) { - - ScratchPad scratchPad; - return isConnected(deviceAddress, scratchPad); - -} - -// attempt to determine if the device at the given address is connected to the bus -// also allows for updating the read scratchpad -bool DallasTemperature::isConnected(const uint8_t* deviceAddress, - uint8_t* scratchPad) { - bool b = readScratchPad(deviceAddress, scratchPad); - return b && !isAllZeros(scratchPad) && (_wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]); -} - -bool DallasTemperature::readScratchPad(const uint8_t* deviceAddress, - uint8_t* scratchPad) { - - // send the reset command and fail fast - int b = _wire->reset(); - if (b == 0) - return false; - - _wire->select(deviceAddress); - _wire->write(READSCRATCH); - - // Read all registers in a simple loop - // byte 0: temperature LSB - // byte 1: temperature MSB - // byte 2: high alarm temp - // byte 3: low alarm temp - // byte 4: DS18S20: store for crc - // DS18B20 & DS1822: configuration register - // byte 5: internal use & crc - // byte 6: DS18S20: COUNT_REMAIN - // DS18B20 & DS1822: store for crc - // byte 7: DS18S20: COUNT_PER_C - // DS18B20 & DS1822: store for crc - // byte 8: SCRATCHPAD_CRC - for (uint8_t i = 0; i < 9; i++) { - scratchPad[i] = _wire->read(); - } - - b = _wire->reset(); - return (b == 1); -} - -void DallasTemperature::writeScratchPad(const uint8_t* deviceAddress, - const uint8_t* scratchPad) { - - _wire->reset(); - _wire->select(deviceAddress); - _wire->write(WRITESCRATCH); - _wire->write(scratchPad[HIGH_ALARM_TEMP]); // high alarm temp - _wire->write(scratchPad[LOW_ALARM_TEMP]); // low alarm temp - - // DS1820 and DS18S20 have no configuration register - if (deviceAddress[DSROM_FAMILY] != DS18S20MODEL) - _wire->write(scratchPad[CONFIGURATION]); - - if (autoSaveScratchPad) - saveScratchPad(deviceAddress); - else - _wire->reset(); -} - -// returns true if parasite mode is used (2 wire) -// returns false if normal mode is used (3 wire) -// if no address is given (or nullptr) it checks if any device on the bus -// uses parasite mode. -// See issue #145 -bool DallasTemperature::readPowerSupply(const uint8_t* deviceAddress) -{ - bool parasiteMode = false; - _wire->reset(); - if (deviceAddress == nullptr) - _wire->skip(); - else - _wire->select(deviceAddress); - - _wire->write(READPOWERSUPPLY); - if (_wire->read_bit() == 0) - parasiteMode = true; - _wire->reset(); - return parasiteMode; -} - -// set resolution of all devices to 9, 10, 11, or 12 bits -// if new resolution is out of range, it is constrained. -void DallasTemperature::setResolution(uint8_t newResolution) { - - bitResolution = constrain(newResolution, 9, 12); - DeviceAddress deviceAddress; - for (uint8_t i = 0; i < devices; i++) { - getAddress(deviceAddress, i); - setResolution(deviceAddress, bitResolution, true); - } -} - -/* PROPOSAL */ - -// set resolution of a device to 9, 10, 11, or 12 bits -// if new resolution is out of range, 9 bits is used. -bool DallasTemperature::setResolution(const uint8_t* deviceAddress, - uint8_t newResolution, bool skipGlobalBitResolutionCalculation) { - - bool success = false; - - // DS1820 and DS18S20 have no resolution configuration register - if (deviceAddress[DSROM_FAMILY] == DS18S20MODEL) - { - success = true; - } - else - { - - // handle the sensors with configuration register - newResolution = constrain(newResolution, 9, 12); - uint8_t newValue = 0; - ScratchPad scratchPad; - - // we can only update the sensor if it is connected - if (isConnected(deviceAddress, scratchPad)) - { - switch (newResolution) { - case 12: - newValue = TEMP_12_BIT; - break; - case 11: - newValue = TEMP_11_BIT; - break; - case 10: - newValue = TEMP_10_BIT; - break; - case 9: - default: - newValue = TEMP_9_BIT; - break; - } - - // if it needs to be updated we write the new value - if (scratchPad[CONFIGURATION] != newValue) - { - scratchPad[CONFIGURATION] = newValue; - writeScratchPad(deviceAddress, scratchPad); - } - // done - success = true; - } - } - - // do we need to update the max resolution used? - if (skipGlobalBitResolutionCalculation == false) - { - bitResolution = newResolution; - if (devices > 1) - { - for (uint8_t i = 0; i < devices; i++) - { - if (bitResolution == 12) break; - DeviceAddress deviceAddr; - getAddress(deviceAddr, i); - uint8_t b = getResolution(deviceAddr); - if (b > bitResolution) bitResolution = b; - } - } - } - - return success; -} - - -// returns the global resolution -uint8_t DallasTemperature::getResolution() { - return bitResolution; -} - -// returns the current resolution of the device, 9-12 -// returns 0 if device not found -uint8_t DallasTemperature::getResolution(const uint8_t* deviceAddress) { - - // DS1820 and DS18S20 have no resolution configuration register - if (deviceAddress[DSROM_FAMILY] == DS18S20MODEL) - return 12; - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - switch (scratchPad[CONFIGURATION]) { - case TEMP_12_BIT: - return 12; - - case TEMP_11_BIT: - return 11; - - case TEMP_10_BIT: - return 10; - - case TEMP_9_BIT: - return 9; - } - } - return 0; - -} - - -// sets the value of the waitForConversion flag -// TRUE : function requestTemperature() etc returns when conversion is ready -// FALSE: function requestTemperature() etc returns immediately (USE WITH CARE!!) -// (1) programmer has to check if the needed delay has passed -// (2) but the application can do meaningful things in that time -void DallasTemperature::setWaitForConversion(bool flag) { - waitForConversion = flag; -} - -// gets the value of the waitForConversion flag -bool DallasTemperature::getWaitForConversion() { - return waitForConversion; -} - -// sets the value of the checkForConversion flag -// TRUE : function requestTemperature() etc will 'listen' to an IC to determine whether a conversion is complete -// FALSE: function requestTemperature() etc will wait a set time (worst case scenario) for a conversion to complete -void DallasTemperature::setCheckForConversion(bool flag) { - checkForConversion = flag; -} - -// gets the value of the waitForConversion flag -bool DallasTemperature::getCheckForConversion() { - return checkForConversion; -} - -bool DallasTemperature::isConversionComplete() { - uint8_t b = _wire->read_bit(); - return (b == 1); -} - -// sends command for all devices on the bus to perform a temperature conversion -void DallasTemperature::requestTemperatures() { - - _wire->reset(); - _wire->skip(); - _wire->write(STARTCONVO, parasite); - - // ASYNC mode? - if (!waitForConversion) - return; - blockTillConversionComplete(bitResolution); - -} - -// sends command for one device to perform a temperature by address -// returns FALSE if device is disconnected -// returns TRUE otherwise -bool DallasTemperature::requestTemperaturesByAddress( - const uint8_t* deviceAddress) { - - uint8_t bitResolution = getResolution(deviceAddress); - if (bitResolution == 0) { - return false; //Device disconnected - } - - _wire->reset(); - _wire->select(deviceAddress); - _wire->write(STARTCONVO, parasite); - - // ASYNC mode? - if (!waitForConversion) - return true; - - blockTillConversionComplete(bitResolution); - - return true; - -} - -// Continue to check if the IC has responded with a temperature -void DallasTemperature::blockTillConversionComplete(uint8_t bitResolution) { - - if (checkForConversion && !parasite) { - unsigned long start = millis(); - while (!isConversionComplete() && (millis() - start < MAX_CONVERSION_TIMEOUT )) - yield(); - } else { - unsigned long delms = millisToWaitForConversion(bitResolution); - activateExternalPullup(); - delay(delms); - deactivateExternalPullup(); - } - -} - -// returns number of milliseconds to wait till conversion is complete (based on IC datasheet) -int16_t DallasTemperature::millisToWaitForConversion(uint8_t bitResolution) { - - switch (bitResolution) { - case 9: - return 94; - case 10: - return 188; - case 11: - return 375; - default: - return 750; - } - -} - -// Sends command to one device to save values from scratchpad to EEPROM by index -// Returns true if no errors were encountered, false indicates failure -bool DallasTemperature::saveScratchPadByIndex(uint8_t deviceIndex) { - - DeviceAddress deviceAddress; - if (!getAddress(deviceAddress, deviceIndex)) return false; - - return saveScratchPad(deviceAddress); - -} - -// Sends command to one or more devices to save values from scratchpad to EEPROM -// If optional argument deviceAddress is omitted the command is send to all devices -// Returns true if no errors were encountered, false indicates failure -bool DallasTemperature::saveScratchPad(const uint8_t* deviceAddress) { - - if (_wire->reset() == 0) - return false; - - if (deviceAddress == nullptr) - _wire->skip(); - else - _wire->select(deviceAddress); - - _wire->write(COPYSCRATCH,parasite); - - // Specification: NV Write Cycle Time is typically 2ms, max 10ms - // Waiting 20ms to allow for sensors that take longer in practice - if (!parasite) { - delay(20); - } else { - activateExternalPullup(); - delay(20); - deactivateExternalPullup(); - } - - return _wire->reset() == 1; - -} - -// Sends command to one device to recall values from EEPROM to scratchpad by index -// Returns true if no errors were encountered, false indicates failure -bool DallasTemperature::recallScratchPadByIndex(uint8_t deviceIndex) { - - DeviceAddress deviceAddress; - if (!getAddress(deviceAddress, deviceIndex)) return false; - - return recallScratchPad(deviceAddress); - -} - -// Sends command to one or more devices to recall values from EEPROM to scratchpad -// If optional argument deviceAddress is omitted the command is send to all devices -// Returns true if no errors were encountered, false indicates failure -bool DallasTemperature::recallScratchPad(const uint8_t* deviceAddress) { - - if (_wire->reset() == 0) - return false; - - if (deviceAddress == nullptr) - _wire->skip(); - else - _wire->select(deviceAddress); - - _wire->write(RECALLSCRATCH,parasite); - - // Specification: Strong pullup only needed when writing to EEPROM (and temp conversion) - unsigned long start = millis(); - while (_wire->read_bit() == 0) { - // Datasheet doesn't specify typical/max duration, testing reveals typically within 1ms - if (millis() - start > 20) return false; - yield(); - } - - return _wire->reset() == 1; - -} - -// Sets the autoSaveScratchPad flag -void DallasTemperature::setAutoSaveScratchPad(bool flag) { - autoSaveScratchPad = flag; -} - -// Gets the autoSaveScratchPad flag -bool DallasTemperature::getAutoSaveScratchPad() { - return autoSaveScratchPad; -} - -void DallasTemperature::activateExternalPullup() { - if(useExternalPullup) - digitalWrite(pullupPin, LOW); -} - -void DallasTemperature::deactivateExternalPullup() { - if(useExternalPullup) - digitalWrite(pullupPin, HIGH); -} - -// sends command for one device to perform a temp conversion by index -bool DallasTemperature::requestTemperaturesByIndex(uint8_t deviceIndex) { - - DeviceAddress deviceAddress; - getAddress(deviceAddress, deviceIndex); - - return requestTemperaturesByAddress(deviceAddress); - -} - -// Fetch temperature for device index -float DallasTemperature::getTempCByIndex(uint8_t deviceIndex) { - - DeviceAddress deviceAddress; - if (!getAddress(deviceAddress, deviceIndex)) { - return DEVICE_DISCONNECTED_C; - } - return getTempC((uint8_t*) deviceAddress); -} - -// Fetch temperature for device index -float DallasTemperature::getTempFByIndex(uint8_t deviceIndex) { - - DeviceAddress deviceAddress; - - if (!getAddress(deviceAddress, deviceIndex)) { - return DEVICE_DISCONNECTED_F; - } - - return getTempF((uint8_t*) deviceAddress); - -} - -// reads scratchpad and returns fixed-point temperature, scaling factor 2^-7 -int16_t DallasTemperature::calculateTemperature(const uint8_t* deviceAddress, - uint8_t* scratchPad) { - - int16_t fpTemperature = (((int16_t) scratchPad[TEMP_MSB]) << 11) - | (((int16_t) scratchPad[TEMP_LSB]) << 3); - - /* - DS1820 and DS18S20 have a 9-bit temperature register. - - Resolutions greater than 9-bit can be calculated using the data from - the temperature, and COUNT REMAIN and COUNT PER °C registers in the - scratchpad. The resolution of the calculation depends on the model. - - While the COUNT PER °C register is hard-wired to 16 (10h) in a - DS18S20, it changes with temperature in DS1820. - - After reading the scratchpad, the TEMP_READ value is obtained by - truncating the 0.5°C bit (bit 0) from the temperature data. The - extended resolution temperature can then be calculated using the - following equation: - - COUNT_PER_C - COUNT_REMAIN - TEMPERATURE = TEMP_READ - 0.25 + -------------------------- - COUNT_PER_C - - Hagai Shatz simplified this to integer arithmetic for a 12 bits - value for a DS18S20, and James Cameron added legacy DS1820 support. - - See - http://myarduinotoy.blogspot.co.uk/2013/02/12bit-result-from-ds18s20.html - */ - - if ((deviceAddress[DSROM_FAMILY] == DS18S20MODEL) && (scratchPad[COUNT_PER_C] != 0)) { - fpTemperature = ((fpTemperature & 0xfff0) << 3) - 32 - + (((scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) << 7) - / scratchPad[COUNT_PER_C]); - } - - return fpTemperature; -} - -// returns temperature in 1/128 degrees C or DEVICE_DISCONNECTED_RAW if the -// device's scratch pad cannot be read successfully. -// the numeric value of DEVICE_DISCONNECTED_RAW is defined in -// DallasTemperature.h. It is a large negative number outside the -// operating range of the device -int16_t DallasTemperature::getTemp(const uint8_t* deviceAddress) { - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) - return calculateTemperature(deviceAddress, scratchPad); - return DEVICE_DISCONNECTED_RAW; - -} - -// returns temperature in degrees C or DEVICE_DISCONNECTED_C if the -// device's scratch pad cannot be read successfully. -// the numeric value of DEVICE_DISCONNECTED_C is defined in -// DallasTemperature.h. It is a large negative number outside the -// operating range of the device -float DallasTemperature::getTempC(const uint8_t* deviceAddress) { - return rawToCelsius(getTemp(deviceAddress)); -} - -// returns temperature in degrees F or DEVICE_DISCONNECTED_F if the -// device's scratch pad cannot be read successfully. -// the numeric value of DEVICE_DISCONNECTED_F is defined in -// DallasTemperature.h. It is a large negative number outside the -// operating range of the device -float DallasTemperature::getTempF(const uint8_t* deviceAddress) { - return rawToFahrenheit(getTemp(deviceAddress)); -} - -// returns true if the bus requires parasite power -bool DallasTemperature::isParasitePowerMode(void) { - return parasite; -} - -// IF alarm is not used one can store a 16 bit int of userdata in the alarm -// registers. E.g. an ID of the sensor. -// See github issue #29 - -// note if device is not connected it will fail writing the data. -void DallasTemperature::setUserData(const uint8_t* deviceAddress, - int16_t data) { - // return when stored value == new value - if (getUserData(deviceAddress) == data) - return; - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - scratchPad[HIGH_ALARM_TEMP] = data >> 8; - scratchPad[LOW_ALARM_TEMP] = data & 255; - writeScratchPad(deviceAddress, scratchPad); - } -} - -int16_t DallasTemperature::getUserData(const uint8_t* deviceAddress) { - int16_t data = 0; - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - data = scratchPad[HIGH_ALARM_TEMP] << 8; - data += scratchPad[LOW_ALARM_TEMP]; - } - return data; -} - -// note If address cannot be found no error will be reported. -int16_t DallasTemperature::getUserDataByIndex(uint8_t deviceIndex) { - DeviceAddress deviceAddress; - getAddress(deviceAddress, deviceIndex); - return getUserData((uint8_t*) deviceAddress); -} - -void DallasTemperature::setUserDataByIndex(uint8_t deviceIndex, int16_t data) { - DeviceAddress deviceAddress; - getAddress(deviceAddress, deviceIndex); - setUserData((uint8_t*) deviceAddress, data); -} - -// Convert float Celsius to Fahrenheit -float DallasTemperature::toFahrenheit(float celsius) { - return (celsius * 1.8f) + 32.0f; -} - -// Convert float Fahrenheit to Celsius -float DallasTemperature::toCelsius(float fahrenheit) { - return (fahrenheit - 32.0f) * 0.555555556f; -} - -// convert from raw to Celsius -float DallasTemperature::rawToCelsius(int16_t raw) { - - if (raw <= DEVICE_DISCONNECTED_RAW) - return DEVICE_DISCONNECTED_C; - // C = RAW/128 - return (float) raw * 0.0078125f; - -} - -// convert from raw to Fahrenheit -float DallasTemperature::rawToFahrenheit(int16_t raw) { - - if (raw <= DEVICE_DISCONNECTED_RAW) - return DEVICE_DISCONNECTED_F; - // C = RAW/128 - // F = (C*1.8)+32 = (RAW/128*1.8)+32 = (RAW*0.0140625)+32 - return ((float) raw * 0.0140625f) + 32.0f; - -} - -// Returns true if all bytes of scratchPad are '\0' -bool DallasTemperature::isAllZeros(const uint8_t * const scratchPad, const size_t length) { - for (size_t i = 0; i < length; i++) { - if (scratchPad[i] != 0) { - return false; - } - } - - return true; -} - -#if REQUIRESALARMS - -/* - - ALARMS: - - TH and TL Register Format - - BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 - S 2^6 2^5 2^4 2^3 2^2 2^1 2^0 - - Only bits 11 through 4 of the temperature register are used - in the TH and TL comparison since TH and TL are 8-bit - registers. If the measured temperature is lower than or equal - to TL or higher than or equal to TH, an alarm condition exists - and an alarm flag is set inside the DS18B20. This flag is - updated after every temperature measurement; therefore, if the - alarm condition goes away, the flag will be turned off after - the next temperature conversion. - - */ - -// sets the high alarm temperature for a device in degrees Celsius -// accepts a float, but the alarm resolution will ignore anything -// after a decimal point. valid range is -55C - 125C -void DallasTemperature::setHighAlarmTemp(const uint8_t* deviceAddress, - int8_t celsius) { - - // return when stored value == new value - if (getHighAlarmTemp(deviceAddress) == celsius) - return; - - // make sure the alarm temperature is within the device's range - if (celsius > 125) - celsius = 125; - else if (celsius < -55) - celsius = -55; - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - scratchPad[HIGH_ALARM_TEMP] = (uint8_t) celsius; - writeScratchPad(deviceAddress, scratchPad); - } - -} - -// sets the low alarm temperature for a device in degrees Celsius -// accepts a float, but the alarm resolution will ignore anything -// after a decimal point. valid range is -55C - 125C -void DallasTemperature::setLowAlarmTemp(const uint8_t* deviceAddress, - int8_t celsius) { - - // return when stored value == new value - if (getLowAlarmTemp(deviceAddress) == celsius) - return; - - // make sure the alarm temperature is within the device's range - if (celsius > 125) - celsius = 125; - else if (celsius < -55) - celsius = -55; - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - scratchPad[LOW_ALARM_TEMP] = (uint8_t) celsius; - writeScratchPad(deviceAddress, scratchPad); - } - -} - -// returns a int8_t with the current high alarm temperature or -// DEVICE_DISCONNECTED for an address -int8_t DallasTemperature::getHighAlarmTemp(const uint8_t* deviceAddress) { - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) - return (int8_t) scratchPad[HIGH_ALARM_TEMP]; - return DEVICE_DISCONNECTED_C; - -} - -// returns a int8_t with the current low alarm temperature or -// DEVICE_DISCONNECTED for an address -int8_t DallasTemperature::getLowAlarmTemp(const uint8_t* deviceAddress) { - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) - return (int8_t) scratchPad[LOW_ALARM_TEMP]; - return DEVICE_DISCONNECTED_C; - -} - -// resets internal variables used for the alarm search -void DallasTemperature::resetAlarmSearch() { - - alarmSearchJunction = -1; - alarmSearchExhausted = 0; - for (uint8_t i = 0; i < 7; i++) { - alarmSearchAddress[i] = 0; - } - -} - -// This is a modified version of the OneWire::search method. -// -// Also added the OneWire search fix documented here: -// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295 -// -// Perform an alarm search. If this function returns a '1' then it has -// enumerated the next device and you may retrieve the ROM from the -// OneWire::address variable. If there are no devices, no further -// devices, or something horrible happens in the middle of the -// enumeration then a 0 is returned. If a new device is found then -// its address is copied to newAddr. Use -// DallasTemperature::resetAlarmSearch() to start over. -bool DallasTemperature::alarmSearch(uint8_t* newAddr) { - - uint8_t i; - int8_t lastJunction = -1; - uint8_t done = 1; - - if (alarmSearchExhausted) - return false; - if (!_wire->reset()) - return false; - - // send the alarm search command - _wire->write(0xEC, 0); - - for (i = 0; i < 64; i++) { - - uint8_t a = _wire->read_bit(); - uint8_t nota = _wire->read_bit(); - uint8_t ibyte = i / 8; - uint8_t ibit = 1 << (i & 7); - - // I don't think this should happen, this means nothing responded, but maybe if - // something vanishes during the search it will come up. - if (a && nota) - return false; - - if (!a && !nota) { - if (i == alarmSearchJunction) { - // this is our time to decide differently, we went zero last time, go one. - a = 1; - alarmSearchJunction = lastJunction; - } else if (i < alarmSearchJunction) { - - // take whatever we took last time, look in address - if (alarmSearchAddress[ibyte] & ibit) { - a = 1; - } else { - // Only 0s count as pending junctions, we've already exhausted the 0 side of 1s - a = 0; - done = 0; - lastJunction = i; - } - } else { - // we are blazing new tree, take the 0 - a = 0; - alarmSearchJunction = i; - done = 0; - } - // OneWire search fix - // See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295 - } - - if (a) - alarmSearchAddress[ibyte] |= ibit; - else - alarmSearchAddress[ibyte] &= ~ibit; - - _wire->write_bit(a); - } - - if (done) - alarmSearchExhausted = 1; - for (i = 0; i < 8; i++) - newAddr[i] = alarmSearchAddress[i]; - return true; - -} - -// returns true if device address might have an alarm condition -// (only an alarm search can verify this) -bool DallasTemperature::hasAlarm(const uint8_t* deviceAddress) { - - ScratchPad scratchPad; - if (isConnected(deviceAddress, scratchPad)) { - - int8_t temp = calculateTemperature(deviceAddress, scratchPad) >> 7; - - // check low alarm - if (temp <= (int8_t) scratchPad[LOW_ALARM_TEMP]) - return true; - - // check high alarm - if (temp >= (int8_t) scratchPad[HIGH_ALARM_TEMP]) - return true; - } - - // no alarm - return false; - -} - -// returns true if any device is reporting an alarm condition on the bus -bool DallasTemperature::hasAlarm(void) { - - DeviceAddress deviceAddress; - resetAlarmSearch(); - return alarmSearch(deviceAddress); -} - -// runs the alarm handler for all devices returned by alarmSearch() -// unless there no _AlarmHandler exist. -void DallasTemperature::processAlarms(void) { - -if (!hasAlarmHandler()) -{ - return; -} - - resetAlarmSearch(); - DeviceAddress alarmAddr; - - while (alarmSearch(alarmAddr)) { - if (validAddress(alarmAddr)) { - _AlarmHandler(alarmAddr); - } - } -} - -// sets the alarm handler -void DallasTemperature::setAlarmHandler(const AlarmHandler *handler) { - _AlarmHandler = handler; -} - -// checks if AlarmHandler has been set. -bool DallasTemperature::hasAlarmHandler() -{ - return _AlarmHandler != NO_ALARM_HANDLER; -} - -#endif - -#if REQUIRESNEW - -// MnetCS - Allocates memory for DallasTemperature. Allows us to instance a new object -void* DallasTemperature::operator new(unsigned int size) { // Implicit NSS obj size - - void * p;// void pointer - p = malloc(size);// Allocate memory - memset((DallasTemperature*)p,0,size);// Initialise memory - - //!!! CANT EXPLICITLY CALL CONSTRUCTOR - workaround by using an init() methodR - workaround by using an init() method - return (DallasTemperature*) p;// Cast blank region to NSS pointer -} - -// MnetCS 2009 - Free the memory used by this instance -void DallasTemperature::operator delete(void* p) { - - DallasTemperature* pNss = (DallasTemperature*) p; // Cast to NSS pointer - pNss->~DallasTemperature();// Destruct the object - - free(p);// Free the memory -} - -#endif +#include "DallasTemperature.h" + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +extern "C" { +#include "WConstants.h" +} +#endif + +// OneWire commands +#define STARTCONVO 0x44 // Tells device to take a temperature reading +#define COPYSCRATCH 0x48 // Copy scratchpad to EEPROM +#define READSCRATCH 0xBE // Read from scratchpad +#define WRITESCRATCH 0x4E // Write to scratchpad +#define RECALLSCRATCH 0xB8 // Recall from EEPROM to scratchpad +#define READPOWERSUPPLY 0xB4 // Determine if device needs parasite power +#define ALARMSEARCH 0xEC // Query bus for devices with an alarm condition + +// Scratchpad locations +#define TEMP_LSB 0 +#define TEMP_MSB 1 +#define HIGH_ALARM_TEMP 2 +#define LOW_ALARM_TEMP 3 +#define CONFIGURATION 4 +#define INTERNAL_BYTE 5 +#define COUNT_REMAIN 6 +#define COUNT_PER_C 7 +#define SCRATCHPAD_CRC 8 + +// Device resolution +#define TEMP_9_BIT 0x1F +#define TEMP_10_BIT 0x3F +#define TEMP_11_BIT 0x5F +#define TEMP_12_BIT 0x7F + +#define NO_ALARM_HANDLER ((AlarmHandler *)0) + +// DSROM FIELDS +#define DSROM_FAMILY 0 +#define DSROM_CRC 7 + +DallasTemperature::DallasTemperature() { + _wire = nullptr; + devices = 0; + ds18Count = 0; + parasite = false; + bitResolution = 9; + waitForConversion = true; + checkForConversion = true; + autoSaveScratchPad = true; + useExternalPullup = false; +#if REQUIRESALARMS + setAlarmHandler(NO_ALARM_HANDLER); + alarmSearchJunction = -1; + alarmSearchExhausted = 0; +#endif +} + +DallasTemperature::DallasTemperature(OneWire* _oneWire) : DallasTemperature() { + setOneWire(_oneWire); +} + +DallasTemperature::DallasTemperature(OneWire* _oneWire, uint8_t _pullupPin) : DallasTemperature(_oneWire) { + setPullupPin(_pullupPin); +} + +void DallasTemperature::setOneWire(OneWire* _oneWire) { + _wire = _oneWire; + devices = 0; + ds18Count = 0; + parasite = false; + bitResolution = 9; + waitForConversion = true; + checkForConversion = true; + autoSaveScratchPad = true; +} + +void DallasTemperature::setPullupPin(uint8_t _pullupPin) { + useExternalPullup = true; + pullupPin = _pullupPin; + pinMode(pullupPin, OUTPUT); + deactivateExternalPullup(); +} + +void DallasTemperature::begin(void) { + DeviceAddress deviceAddress; + + for (uint8_t retry = 0; retry < MAX_INITIALIZATION_RETRIES; retry++) { + _wire->reset_search(); + devices = 0; + ds18Count = 0; + + delay(INITIALIZATION_DELAY_MS); + + while (_wire->search(deviceAddress)) { + if (validAddress(deviceAddress)) { + devices++; + + if (validFamily(deviceAddress)) { + ds18Count++; + + if (!parasite && readPowerSupply(deviceAddress)) { + parasite = true; + } + + uint8_t b = getResolution(deviceAddress); + if (b > bitResolution) { + bitResolution = b; + } + } + } + } + + if (devices > 0) break; + } +} + +void DallasTemperature::activateExternalPullup() { + if (useExternalPullup) digitalWrite(pullupPin, LOW); +} + +void DallasTemperature::deactivateExternalPullup() { + if (useExternalPullup) digitalWrite(pullupPin, HIGH); +} + +bool DallasTemperature::validFamily(const uint8_t* deviceAddress) { + switch (deviceAddress[0]) { + case DS18S20MODEL: + case DS18B20MODEL: + case DS1822MODEL: + case DS1825MODEL: + case DS28EA00MODEL: + return true; + default: + return false; + } +} + +bool DallasTemperature::validAddress(const uint8_t* deviceAddress) { + return (_wire->crc8(const_cast(deviceAddress), 7) == deviceAddress[7]); +} + +bool DallasTemperature::getAddress(uint8_t* deviceAddress, uint8_t index) { + if (index < devices) { + uint8_t depth = 0; + + _wire->reset_search(); + + while (depth <= index && _wire->search(deviceAddress)) { + if (depth == index && validAddress(deviceAddress)) { + return true; + } + depth++; + } + } + return false; +} + +uint8_t DallasTemperature::getDeviceCount(void) { + return devices; +} + +uint8_t DallasTemperature::getDS18Count(void) { + return ds18Count; +} + +bool DallasTemperature::isConnected(const uint8_t* deviceAddress) { + ScratchPad scratchPad; + return isConnected(deviceAddress, scratchPad); +} + +bool DallasTemperature::isConnected(const uint8_t* deviceAddress, uint8_t* scratchPad) { + bool b = readScratchPad(deviceAddress, scratchPad); + return b && !isAllZeros(scratchPad) && (_wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]); +} + +bool DallasTemperature::readPowerSupply(const uint8_t* deviceAddress) { + bool parasiteMode = false; + _wire->reset(); + if (deviceAddress == nullptr) { + _wire->skip(); + } else { + _wire->select(deviceAddress); + } + + _wire->write(READPOWERSUPPLY); + if (_wire->read_bit() == 0) { + parasiteMode = true; + } + _wire->reset(); + return parasiteMode; +} + +bool DallasTemperature::isParasitePowerMode(void) { + return parasite; +} + +bool DallasTemperature::isAllZeros(const uint8_t* const scratchPad, const size_t length) { + for (size_t i = 0; i < length; i++) { + if (scratchPad[i] != 0) return false; + } + return true; +} + +bool DallasTemperature::readScratchPad(const uint8_t* deviceAddress, uint8_t* scratchPad) { + int b = _wire->reset(); + if (b == 0) return false; + + _wire->select(deviceAddress); + _wire->write(READSCRATCH); + + for (uint8_t i = 0; i < 9; i++) { + scratchPad[i] = _wire->read(); + } + + b = _wire->reset(); + return (b == 1); +} + +void DallasTemperature::writeScratchPad(const uint8_t* deviceAddress, const uint8_t* scratchPad) { + _wire->reset(); + _wire->select(deviceAddress); + _wire->write(WRITESCRATCH); + _wire->write(scratchPad[HIGH_ALARM_TEMP]); // high alarm temp + _wire->write(scratchPad[LOW_ALARM_TEMP]); // low alarm temp + + // DS1820 and DS18S20 have no configuration register + if (deviceAddress[0] != DS18S20MODEL) { + _wire->write(scratchPad[CONFIGURATION]); + } + + if (autoSaveScratchPad) { + saveScratchPad(deviceAddress); + } else { + _wire->reset(); + } +} + +bool DallasTemperature::saveScratchPad(const uint8_t* deviceAddress) { + if (_wire->reset() == 0) return false; + + if (deviceAddress == nullptr) + _wire->skip(); + else + _wire->select(deviceAddress); + + _wire->write(COPYSCRATCH, parasite); + + // Specification: NV Write Cycle Time is typically 2ms, max 10ms + // Waiting 20ms to allow for sensors that take longer in practice + if (!parasite) { + delay(20); + } else { + activateExternalPullup(); + delay(20); + deactivateExternalPullup(); + } + + return (_wire->reset() == 1); +} + +bool DallasTemperature::recallScratchPad(const uint8_t* deviceAddress) { + if (_wire->reset() == 0) return false; + + if (deviceAddress == nullptr) + _wire->skip(); + else + _wire->select(deviceAddress); + + _wire->write(RECALLSCRATCH, parasite); + + // Specification: Strong pullup only needed when writing to EEPROM + unsigned long start = millis(); + while (_wire->read_bit() == 0) { + if (millis() - start > 20) return false; + yield(); + } + + return (_wire->reset() == 1); +} + +int32_t DallasTemperature::getTemp(const uint8_t* deviceAddress, byte retryCount) { + ScratchPad scratchPad; + byte retries = 0; + + while (retries++ <= retryCount) { + if (isConnected(deviceAddress, scratchPad)) { + return calculateTemperature(deviceAddress, scratchPad); + } + } + + return DEVICE_DISCONNECTED_RAW; +} + +float DallasTemperature::getTempC(const uint8_t* deviceAddress, byte retryCount) { + return rawToCelsius(getTemp(deviceAddress, retryCount)); +} + +float DallasTemperature::getTempF(const uint8_t* deviceAddress) { + return rawToFahrenheit(getTemp(deviceAddress)); +} + +float DallasTemperature::getTempCByIndex(uint8_t index) { + DeviceAddress deviceAddress; + if (!getAddress(deviceAddress, index)) { + return DEVICE_DISCONNECTED_C; + } + return getTempC((uint8_t*)deviceAddress); +} + +float DallasTemperature::getTempFByIndex(uint8_t index) { + DeviceAddress deviceAddress; + if (!getAddress(deviceAddress, index)) { + return DEVICE_DISCONNECTED_F; + } + return getTempF((uint8_t*)deviceAddress); +} + +void DallasTemperature::setResolution(uint8_t newResolution) { + bitResolution = constrain(newResolution, 9, 12); + DeviceAddress deviceAddress; + _wire->reset_search(); + for (uint8_t i = 0; i < devices; i++) { + if (_wire->search(deviceAddress) && validAddress(deviceAddress)) { + setResolution(deviceAddress, bitResolution, true); + } + } +} + +bool DallasTemperature::setResolution(const uint8_t* deviceAddress, uint8_t newResolution, bool skipGlobalBitResolutionCalculation) { + bool success = false; + + if (deviceAddress[0] == DS18S20MODEL) { + success = true; + } else { + newResolution = constrain(newResolution, 9, 12); + uint8_t newValue = 0; + ScratchPad scratchPad; + + if (isConnected(deviceAddress, scratchPad)) { + switch (newResolution) { + case 12: newValue = TEMP_12_BIT; break; + case 11: newValue = TEMP_11_BIT; break; + case 10: newValue = TEMP_10_BIT; break; + case 9: + default: newValue = TEMP_9_BIT; break; + } + + if (scratchPad[CONFIGURATION] != newValue) { + scratchPad[CONFIGURATION] = newValue; + writeScratchPad(deviceAddress, scratchPad); + } + success = true; + } + } + + if (!skipGlobalBitResolutionCalculation && success) { + bitResolution = newResolution; + if (devices > 1) { + DeviceAddress deviceAddr; + _wire->reset_search(); + for (uint8_t i = 0; i < devices; i++) { + if (bitResolution == 12) break; + if (_wire->search(deviceAddr) && validAddress(deviceAddr)) { + uint8_t b = getResolution(deviceAddr); + if (b > bitResolution) bitResolution = b; + } + } + } + } + return success; +} + +uint8_t DallasTemperature::getResolution() { + return bitResolution; +} + +uint8_t DallasTemperature::getResolution(const uint8_t* deviceAddress) { + if (deviceAddress[0] == DS18S20MODEL) return 12; + + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + if (deviceAddress[0] == DS1825MODEL && scratchPad[CONFIGURATION] & 0x80) { + return 12; + } + + switch (scratchPad[CONFIGURATION]) { + case TEMP_12_BIT: return 12; + case TEMP_11_BIT: return 11; + case TEMP_10_BIT: return 10; + case TEMP_9_BIT: return 9; + } + } + return 0; +} + +float DallasTemperature::toFahrenheit(float celsius) { + return (celsius * 1.8f) + 32.0f; +} + +float DallasTemperature::toCelsius(float fahrenheit) { + return (fahrenheit - 32.0f) * 0.555555556f; +} + +float DallasTemperature::rawToCelsius(int32_t raw) { + if (raw <= DEVICE_DISCONNECTED_RAW) + return DEVICE_DISCONNECTED_C; + return (float)raw * 0.0078125f; // 1/128 +} + +float DallasTemperature::rawToFahrenheit(int32_t raw) { + if (raw <= DEVICE_DISCONNECTED_RAW) + return DEVICE_DISCONNECTED_F; + return rawToCelsius(raw) * 1.8f + 32.0f; +} + +int16_t DallasTemperature::celsiusToRaw(float celsius) { + return static_cast(celsius * 128.0f); +} + +uint16_t DallasTemperature::millisToWaitForConversion(uint8_t bitResolution) { + switch (bitResolution) { + case 9: return 94; + case 10: return 188; + case 11: return 375; + default: return 750; + } +} + +uint16_t DallasTemperature::millisToWaitForConversion() { + return millisToWaitForConversion(bitResolution); +} + +void DallasTemperature::setWaitForConversion(bool flag) { + waitForConversion = flag; +} + +bool DallasTemperature::getWaitForConversion() { + return waitForConversion; +} + +void DallasTemperature::setCheckForConversion(bool flag) { + checkForConversion = flag; +} + +bool DallasTemperature::getCheckForConversion() { + return checkForConversion; +} + +bool DallasTemperature::isConversionComplete() { + uint8_t b = _wire->read_bit(); + return (b == 1); +} + +void DallasTemperature::setAutoSaveScratchPad(bool flag) { + autoSaveScratchPad = flag; +} + +bool DallasTemperature::getAutoSaveScratchPad() { + return autoSaveScratchPad; +} + +DallasTemperature::request_t DallasTemperature::requestTemperatures() { + request_t req = {}; + req.result = true; + + _wire->reset(); + _wire->skip(); + _wire->write(STARTCONVO, parasite); + + req.timestamp = millis(); + if (!waitForConversion) return req; + + blockTillConversionComplete(bitResolution, req.timestamp); + return req; +} + +DallasTemperature::request_t DallasTemperature::requestTemperaturesByAddress(const uint8_t* deviceAddress) { + request_t req = {}; + uint8_t deviceBitResolution = getResolution(deviceAddress); + if (deviceBitResolution == 0) { + req.result = false; + return req; + } + + _wire->reset(); + _wire->select(deviceAddress); + _wire->write(STARTCONVO, parasite); + + req.timestamp = millis(); + req.result = true; + + if (!waitForConversion) return req; + + blockTillConversionComplete(deviceBitResolution, req.timestamp); + return req; +} + +DallasTemperature::request_t DallasTemperature::requestTemperaturesByIndex(uint8_t index) { + DeviceAddress deviceAddress; + getAddress(deviceAddress, index); + return requestTemperaturesByAddress(deviceAddress); +} + +void DallasTemperature::blockTillConversionComplete(uint8_t bitResolution) { + unsigned long start = millis(); + blockTillConversionComplete(bitResolution, start); +} + +void DallasTemperature::blockTillConversionComplete(uint8_t bitResolution, unsigned long start) { + if (checkForConversion && !parasite) { + while (!isConversionComplete() && ((unsigned long)(millis() - start) < (unsigned long)MAX_CONVERSION_TIMEOUT)) { + yield(); + } + } else { + unsigned long delayInMillis = millisToWaitForConversion(bitResolution); + activateExternalPullup(); + delay(delayInMillis); + deactivateExternalPullup(); + } +} + +void DallasTemperature::blockTillConversionComplete(uint8_t bitResolution, request_t req) { + if (req.result) { + blockTillConversionComplete(bitResolution, req.timestamp); + } +} + +int32_t DallasTemperature::calculateTemperature(const uint8_t* deviceAddress, uint8_t* scratchPad) { + int32_t fpTemperature = 0; + + // looking thru the spec sheets of all supported devices, bit 15 is always the signing bit + int32_t neg = 0x0; + if (scratchPad[TEMP_MSB] & 0x80) + neg = 0xFFF80000; + + // detect MAX31850 + if (deviceAddress[0] == DS1825MODEL && scratchPad[CONFIGURATION] & 0x80) { + if (scratchPad[TEMP_LSB] & 1) { // Fault Detected + if (scratchPad[HIGH_ALARM_TEMP] & 1) { + return DEVICE_FAULT_OPEN_RAW; + } else if (scratchPad[HIGH_ALARM_TEMP] >> 1 & 1) { + return DEVICE_FAULT_SHORTGND_RAW; + } else if (scratchPad[HIGH_ALARM_TEMP] >> 2 & 1) { + return DEVICE_FAULT_SHORTVDD_RAW; + } else { + return DEVICE_DISCONNECTED_RAW; + } + } + // We must mask out bit 1 (reserved) and 0 (fault) on TEMP_LSB + fpTemperature = (((int32_t)scratchPad[TEMP_MSB]) << 11) + | (((int32_t)scratchPad[TEMP_LSB] & 0xFC) << 3) + | neg; + } else { + fpTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 11) + | (((int16_t)scratchPad[TEMP_LSB]) << 3) + | neg; + } + + /* + DS1820 and DS18S20 have a 9-bit temperature register. + + Resolutions greater than 9-bit can be calculated using the data from + the temperature, and COUNT REMAIN and COUNT PER °C registers in the + scratchpad. The resolution of the calculation depends on the model. + + While the COUNT PER °C register is hard-wired to 16 (10h) in a + DS18S20, it changes with temperature in DS1820. + + After reading the scratchpad, the TEMP_READ value is obtained by + truncating the 0.5°C bit (bit 0) from the temperature data. The + extended resolution temperature can then be calculated using the + following equation: + + COUNT_PER_C - COUNT_REMAIN + TEMPERATURE = TEMP_READ - 0.25 + -------------------------- + COUNT_PER_C + + Hagai Shatz simplified this to integer arithmetic for a 12 bits + value for a DS18S20, and James Cameron added legacy DS1820 support. + + See - http://myarduinotoy.blogspot.co.uk/2013/02/12bit-result-from-ds18s20.html + */ + + if ((deviceAddress[DSROM_FAMILY] == DS18S20MODEL) && (scratchPad[COUNT_PER_C] != 0)) { + fpTemperature = (((fpTemperature & 0xfff0) << 3) - 32 + + (((scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) << 7) + / scratchPad[COUNT_PER_C])) | neg; + } + + return fpTemperature; +} + +#if REQUIRESALARMS + +void DallasTemperature::setAlarmHandler(const AlarmHandler* handler) { + _AlarmHandler = handler; +} + +void DallasTemperature::setHighAlarmTemp(const uint8_t* deviceAddress, int8_t celsius) { + // make sure the alarm temperature is within the device's range + if (celsius > 125) celsius = 125; + else if (celsius < -55) celsius = -55; + + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + scratchPad[HIGH_ALARM_TEMP] = (uint8_t)celsius; + writeScratchPad(deviceAddress, scratchPad); + } +} + +void DallasTemperature::setLowAlarmTemp(const uint8_t* deviceAddress, int8_t celsius) { + // make sure the alarm temperature is within the device's range + if (celsius > 125) celsius = 125; + else if (celsius < -55) celsius = -55; + + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + scratchPad[LOW_ALARM_TEMP] = (uint8_t)celsius; + writeScratchPad(deviceAddress, scratchPad); + } +} + +int8_t DallasTemperature::getHighAlarmTemp(const uint8_t* deviceAddress) { + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) + return (int8_t)scratchPad[HIGH_ALARM_TEMP]; + return DEVICE_DISCONNECTED_C; +} + +int8_t DallasTemperature::getLowAlarmTemp(const uint8_t* deviceAddress) { + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) + return (int8_t)scratchPad[LOW_ALARM_TEMP]; + return DEVICE_DISCONNECTED_C; +} + +void DallasTemperature::resetAlarmSearch() { + alarmSearchJunction = -1; + alarmSearchExhausted = 0; + for (uint8_t i = 0; i < 7; i++) { + alarmSearchAddress[i] = 0; + } +} + +bool DallasTemperature::alarmSearch(uint8_t* newAddr) { + uint8_t i; + int8_t lastJunction = -1; + uint8_t done = 1; + + if (alarmSearchExhausted) + return false; + + if (!_wire->reset()) + return false; + + _wire->write(ALARMSEARCH); + + for (i = 0; i < 64; i++) { + uint8_t a = _wire->read_bit(); + uint8_t nota = _wire->read_bit(); + uint8_t ibyte = i / 8; + uint8_t ibit = 1 << (i & 7); + + if (a && nota) + return false; + + if (!a && !nota) { + if (i == alarmSearchJunction) { + a = 1; + alarmSearchJunction = lastJunction; + } else if (i < alarmSearchJunction) { + if (alarmSearchAddress[ibyte] & ibit) { + a = 1; + } else { + a = 0; + done = 0; + lastJunction = i; + } + } else { + a = 0; + alarmSearchJunction = i; + done = 0; + } + } + + if (a) + alarmSearchAddress[ibyte] |= ibit; + else + alarmSearchAddress[ibyte] &= ~ibit; + + _wire->write_bit(a); + } + + if (done) + alarmSearchExhausted = 1; + for (i = 0; i < 8; i++) + newAddr[i] = alarmSearchAddress[i]; + return true; +} + +bool DallasTemperature::hasAlarm(const uint8_t* deviceAddress) { + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + int8_t temp = calculateTemperature(deviceAddress, scratchPad) >> 7; + return (temp <= (int8_t)scratchPad[LOW_ALARM_TEMP] || + temp >= (int8_t)scratchPad[HIGH_ALARM_TEMP]); + } + return false; +} + +bool DallasTemperature::hasAlarm(void) { + DeviceAddress deviceAddress; + resetAlarmSearch(); + return alarmSearch(deviceAddress); +} + +void DallasTemperature::processAlarms(void) { + if (!hasAlarmHandler()) + return; + + resetAlarmSearch(); + DeviceAddress alarmAddr; + + while (alarmSearch(alarmAddr)) { + if (validAddress(alarmAddr)) { + _AlarmHandler(alarmAddr); + } + } +} + +bool DallasTemperature::hasAlarmHandler() { + return (_AlarmHandler != NO_ALARM_HANDLER); +} + +#endif + +#if REQUIRESNEW + +void* DallasTemperature::operator new(unsigned int size) { + void* p = malloc(size); + memset(p, 0, size); + return p; +} + +void DallasTemperature::operator delete(void* p) { + free(p); +} + +#endif + +bool DallasTemperature::verifyDeviceCount(void) { + uint8_t actualCount = 0; + float temp; + + requestTemperatures(); + + do { + temp = getTempCByIndex(actualCount); + if (temp > DEVICE_DISCONNECTED_C) { + actualCount++; + } + } while (temp > DEVICE_DISCONNECTED_C && actualCount < 255); + + if (actualCount > devices) { + devices = actualCount; + begin(); + return true; + } + + return false; +} + +void DallasTemperature::setUserData(const uint8_t* deviceAddress, int16_t data) { + // return when stored value == new value + if (getUserData(deviceAddress) == data) + return; + + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + scratchPad[HIGH_ALARM_TEMP] = data >> 8; + scratchPad[LOW_ALARM_TEMP] = data & 255; + writeScratchPad(deviceAddress, scratchPad); + } +} + +void DallasTemperature::setUserDataByIndex(uint8_t deviceIndex, int16_t data) { + DeviceAddress deviceAddress; + if (getAddress(deviceAddress, deviceIndex)) { + setUserData((uint8_t*)deviceAddress, data); + } +} + +int16_t DallasTemperature::getUserData(const uint8_t* deviceAddress) { + int16_t data = 0; + ScratchPad scratchPad; + if (isConnected(deviceAddress, scratchPad)) { + data = scratchPad[HIGH_ALARM_TEMP] << 8; + data += scratchPad[LOW_ALARM_TEMP]; + } + return data; +} + +int16_t DallasTemperature::getUserDataByIndex(uint8_t deviceIndex) { + DeviceAddress deviceAddress; + getAddress(deviceAddress, deviceIndex); + return getUserData((uint8_t*)deviceAddress); +} \ No newline at end of file diff --git a/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.h b/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.h index cb9e0274..8bc1d449 100644 --- a/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.h +++ b/trunk/Arduino/libraries/DallasTemperature/DallasTemperature.h @@ -1,317 +1,192 @@ -#ifndef DallasTemperature_h -#define DallasTemperature_h - -#define DALLASTEMPLIBVERSION "3.8.1" // To be deprecated -> TODO remove in 4.0.0 - -// 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. - -// set to true to include code for new and delete operators -#ifndef REQUIRESNEW -#define REQUIRESNEW false -#endif - -// set to true to include code implementing alarm search functions -#ifndef REQUIRESALARMS -#define REQUIRESALARMS true -#endif - -#include -#ifdef __STM32F1__ -#include -#else -#include -#endif - -// Model IDs -#define DS18S20MODEL 0x10 // also DS1820 -#define DS18B20MODEL 0x28 // also MAX31820 -#define DS1822MODEL 0x22 -#define DS1825MODEL 0x3B -#define DS28EA00MODEL 0x42 - -// Error Codes -#define DEVICE_DISCONNECTED_C -127 -#define DEVICE_DISCONNECTED_F -196.6 -#define DEVICE_DISCONNECTED_RAW -7040 - -// For readPowerSupply on oneWire bus -// definition of nullptr for C++ < 11, using official workaround: -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf -#if __cplusplus < 201103L -const class -{ -public: - template - operator T *() const - { - return 0; - } - template - operator T C::*() const - { - return 0; - } - -private: - void operator&() const; -} nullptr = {}; -#endif - -typedef uint8_t DeviceAddress[8]; - -class DallasTemperature { -public: - - DallasTemperature(); - DallasTemperature(OneWire*); - DallasTemperature(OneWire*, uint8_t); - - void setOneWire(OneWire*); - - void setPullupPin(uint8_t); - - // initialise bus - void begin(void); - - // returns the number of devices found on the bus - uint8_t getDeviceCount(void); - - // returns the number of DS18xxx Family devices on bus - uint8_t getDS18Count(void); - - // returns true if address is valid - bool validAddress(const uint8_t*); - - // returns true if address is of the family of sensors the lib supports. - bool validFamily(const uint8_t* deviceAddress); - - // finds an address at a given index on the bus - bool getAddress(uint8_t*, uint8_t); - - // attempt to determine if the device at the given address is connected to the bus - bool isConnected(const uint8_t*); - - // attempt to determine if the device at the given address is connected to the bus - // also allows for updating the read scratchpad - bool isConnected(const uint8_t*, uint8_t*); - - // read device's scratchpad - bool readScratchPad(const uint8_t*, uint8_t*); - - // write device's scratchpad - void writeScratchPad(const uint8_t*, const uint8_t*); - - // read device's power requirements - bool readPowerSupply(const uint8_t* deviceAddress = nullptr); - - // get global resolution - uint8_t getResolution(); - - // set global resolution to 9, 10, 11, or 12 bits - void setResolution(uint8_t); - - // returns the device resolution: 9, 10, 11, or 12 bits - uint8_t getResolution(const uint8_t*); - - // set resolution of a device to 9, 10, 11, or 12 bits - bool setResolution(const uint8_t*, uint8_t, - bool skipGlobalBitResolutionCalculation = false); - - // sets/gets the waitForConversion flag - void setWaitForConversion(bool); - bool getWaitForConversion(void); - - // sets/gets the checkForConversion flag - void setCheckForConversion(bool); - bool getCheckForConversion(void); - - // sends command for all devices on the bus to perform a temperature conversion - void requestTemperatures(void); - - // sends command for one device to perform a temperature conversion by address - bool requestTemperaturesByAddress(const uint8_t*); - - // sends command for one device to perform a temperature conversion by index - bool requestTemperaturesByIndex(uint8_t); - - // returns temperature raw value (12 bit integer of 1/128 degrees C) - int16_t getTemp(const uint8_t*); - - // returns temperature in degrees C - float getTempC(const uint8_t*); - - // returns temperature in degrees F - float getTempF(const uint8_t*); - - // Get temperature for device index (slow) - float getTempCByIndex(uint8_t); - - // Get temperature for device index (slow) - float getTempFByIndex(uint8_t); - - // returns true if the bus requires parasite power - bool isParasitePowerMode(void); - - // Is a conversion complete on the wire? Only applies to the first sensor on the wire. - bool isConversionComplete(void); - - int16_t millisToWaitForConversion(uint8_t); - - // Sends command to one device to save values from scratchpad to EEPROM by index - // Returns true if no errors were encountered, false indicates failure - bool saveScratchPadByIndex(uint8_t); - - // Sends command to one or more devices to save values from scratchpad to EEPROM - // Returns true if no errors were encountered, false indicates failure - bool saveScratchPad(const uint8_t* = nullptr); - - // Sends command to one device to recall values from EEPROM to scratchpad by index - // Returns true if no errors were encountered, false indicates failure - bool recallScratchPadByIndex(uint8_t); - - // Sends command to one or more devices to recall values from EEPROM to scratchpad - // Returns true if no errors were encountered, false indicates failure - bool recallScratchPad(const uint8_t* = nullptr); - - // Sets the autoSaveScratchPad flag - void setAutoSaveScratchPad(bool); - - // Gets the autoSaveScratchPad flag - bool getAutoSaveScratchPad(void); - -#if REQUIRESALARMS - - typedef void AlarmHandler(const uint8_t*); - - // sets the high alarm temperature for a device - // accepts a int8_t. valid range is -55C - 125C - void setHighAlarmTemp(const uint8_t*, int8_t); - - // sets the low alarm temperature for a device - // accepts a int8_t. valid range is -55C - 125C - void setLowAlarmTemp(const uint8_t*, int8_t); - - // returns a int8_t with the current high alarm temperature for a device - // in the range -55C - 125C - int8_t getHighAlarmTemp(const uint8_t*); - - // returns a int8_t with the current low alarm temperature for a device - // in the range -55C - 125C - int8_t getLowAlarmTemp(const uint8_t*); - - // resets internal variables used for the alarm search - void resetAlarmSearch(void); - - // search the wire for devices with active alarms - bool alarmSearch(uint8_t*); - - // returns true if ia specific device has an alarm - bool hasAlarm(const uint8_t*); - - // returns true if any device is reporting an alarm on the bus - bool hasAlarm(void); - - // runs the alarm handler for all devices returned by alarmSearch() - void processAlarms(void); - - // sets the alarm handler - void setAlarmHandler(const AlarmHandler *); - - // returns true if an AlarmHandler has been set - bool hasAlarmHandler(); - -#endif - - // if no alarm handler is used the two bytes can be used as user data - // example of such usage is an ID. - // note if device is not connected it will fail writing the data. - // note if address cannot be found no error will be reported. - // in short use carefully - void setUserData(const uint8_t*, int16_t); - void setUserDataByIndex(uint8_t, int16_t); - int16_t getUserData(const uint8_t*); - int16_t getUserDataByIndex(uint8_t); - - // convert from Celsius to Fahrenheit - static float toFahrenheit(float); - - // convert from Fahrenheit to Celsius - static float toCelsius(float); - - // convert from raw to Celsius - static float rawToCelsius(int16_t); - - // convert from raw to Fahrenheit - static float rawToFahrenheit(int16_t); - -#if REQUIRESNEW - - // initialize memory area - void* operator new (unsigned int); - - // delete memory reference - void operator delete(void*); - -#endif - -private: - typedef uint8_t ScratchPad[9]; - - // parasite power on or off - bool parasite; - - // external pullup - bool useExternalPullup; - uint8_t pullupPin; - - // used to determine the delay amount needed to allow for the - // temperature conversion to take place - uint8_t bitResolution; - - // used to requestTemperature with or without delay - bool waitForConversion; - - // used to requestTemperature to dynamically check if a conversion is complete - bool checkForConversion; - - // used to determine if values will be saved from scratchpad to EEPROM on every scratchpad write - bool autoSaveScratchPad; - - // count of devices on the bus - uint8_t devices; - - // count of DS18xxx Family devices on bus - uint8_t ds18Count; - - // Take a pointer to one wire instance - OneWire* _wire; - - // reads scratchpad and returns the raw temperature - int16_t calculateTemperature(const uint8_t*, uint8_t*); - - void blockTillConversionComplete(uint8_t); - - // Returns true if all bytes of scratchPad are '\0' - bool isAllZeros(const uint8_t* const scratchPad, const size_t length = 9); - - // External pullup control - void activateExternalPullup(void); - void deactivateExternalPullup(void); - -#if REQUIRESALARMS - - // required for alarmSearch - uint8_t alarmSearchAddress[8]; - int8_t alarmSearchJunction; - uint8_t alarmSearchExhausted; - - // the alarm handler function pointer - AlarmHandler *_AlarmHandler; - -#endif - -}; -#endif +#ifndef DallasTemperature_h +#define DallasTemperature_h + +#define DALLASTEMPLIBVERSION "4.0.5" + +// Configuration +#ifndef REQUIRESNEW +#define REQUIRESNEW false +#endif + +#ifndef REQUIRESALARMS +#define REQUIRESALARMS true +#endif + +// Includes +#include +#include + +#ifdef __STM32F1__ +#include +#else +#include +#endif + +// Constants for device models +#define DS18S20MODEL 0x10 // also DS1820 +#define DS18B20MODEL 0x28 // also MAX31820 +#define DS1822MODEL 0x22 +#define DS1825MODEL 0x3B // also MAX31850 +#define DS28EA00MODEL 0x42 + +// Error Codes +#define DEVICE_DISCONNECTED_C -127 +#define DEVICE_DISCONNECTED_F -196.6 +#define DEVICE_DISCONNECTED_RAW -7040 + +#define DEVICE_FAULT_OPEN_C -254 +#define DEVICE_FAULT_OPEN_F -425.199982 +#define DEVICE_FAULT_OPEN_RAW -32512 + +#define DEVICE_FAULT_SHORTGND_C -253 +#define DEVICE_FAULT_SHORTGND_F -423.399994 +#define DEVICE_FAULT_SHORTGND_RAW -32384 + +#define DEVICE_FAULT_SHORTVDD_C -252 +#define DEVICE_FAULT_SHORTVDD_F -421.599976 +#define DEVICE_FAULT_SHORTVDD_RAW -32256 + +// Configuration Constants +#define MAX_CONVERSION_TIMEOUT 750 +#define MAX_INITIALIZATION_RETRIES 3 +#define INITIALIZATION_DELAY_MS 50 + +typedef uint8_t DeviceAddress[8]; + +class DallasTemperature { +public: + struct request_t { + bool result; + unsigned long timestamp; + operator bool() { return result; } + }; + + // Constructors + DallasTemperature(); + DallasTemperature(OneWire*); + DallasTemperature(OneWire*, uint8_t); + + // Setup & Configuration + void setOneWire(OneWire*); + void setPullupPin(uint8_t); + void begin(void); + bool verifyDeviceCount(void); + + // Device Information + uint8_t getDeviceCount(void); + uint8_t getDS18Count(void); + bool validAddress(const uint8_t*); + bool validFamily(const uint8_t* deviceAddress); + bool getAddress(uint8_t*, uint8_t); + bool isConnected(const uint8_t*); + bool isConnected(const uint8_t*, uint8_t*); + + // Scratchpad Operations + bool readScratchPad(const uint8_t*, uint8_t*); + void writeScratchPad(const uint8_t*, const uint8_t*); + bool readPowerSupply(const uint8_t* deviceAddress = nullptr); + + // Resolution Control + uint8_t getResolution(); + void setResolution(uint8_t); + uint8_t getResolution(const uint8_t*); + bool setResolution(const uint8_t*, uint8_t, bool skipGlobalBitResolutionCalculation = false); + + // Conversion Configuration + void setWaitForConversion(bool); + bool getWaitForConversion(void); + void setCheckForConversion(bool); + bool getCheckForConversion(void); + + // Temperature Operations + request_t requestTemperatures(void); + request_t requestTemperaturesByAddress(const uint8_t*); + request_t requestTemperaturesByIndex(uint8_t); + int32_t getTemp(const uint8_t*, byte retryCount = 0); + float getTempC(const uint8_t*, byte retryCount = 0); + float getTempF(const uint8_t*); + float getTempCByIndex(uint8_t); + float getTempFByIndex(uint8_t); + + // Conversion Status + bool isParasitePowerMode(void); + bool isConversionComplete(void); + static uint16_t millisToWaitForConversion(uint8_t); + uint16_t millisToWaitForConversion(); + + // EEPROM Operations + bool saveScratchPadByIndex(uint8_t); + bool saveScratchPad(const uint8_t* = nullptr); + bool recallScratchPadByIndex(uint8_t); + bool recallScratchPad(const uint8_t* = nullptr); + void setAutoSaveScratchPad(bool); + bool getAutoSaveScratchPad(void); + +#if REQUIRESALARMS + typedef void AlarmHandler(const uint8_t*); + void setHighAlarmTemp(const uint8_t*, int8_t); + void setLowAlarmTemp(const uint8_t*, int8_t); + int8_t getHighAlarmTemp(const uint8_t*); + int8_t getLowAlarmTemp(const uint8_t*); + void resetAlarmSearch(void); + bool alarmSearch(uint8_t*); + bool hasAlarm(const uint8_t*); + bool hasAlarm(void); + void processAlarms(void); + void setAlarmHandler(const AlarmHandler*); + bool hasAlarmHandler(); +#endif + + // User Data Operations + void setUserData(const uint8_t*, int16_t); + void setUserDataByIndex(uint8_t, int16_t); + int16_t getUserData(const uint8_t*); + int16_t getUserDataByIndex(uint8_t); + + // Temperature Conversion Utilities + static float toFahrenheit(float); + static float toCelsius(float); + static float rawToCelsius(int32_t); + static int16_t celsiusToRaw(float); + static float rawToFahrenheit(int32_t); + +#if REQUIRESNEW + void* operator new(unsigned int); + void operator delete(void*); +#endif + + // Conversion Completion Methods + void blockTillConversionComplete(uint8_t); + void blockTillConversionComplete(uint8_t, unsigned long); + void blockTillConversionComplete(uint8_t, request_t); + +private: + typedef uint8_t ScratchPad[9]; + + // Internal State + bool parasite; + bool useExternalPullup; + uint8_t pullupPin; + uint8_t bitResolution; + bool waitForConversion; + bool checkForConversion; + bool autoSaveScratchPad; + uint8_t devices; + uint8_t ds18Count; + OneWire* _wire; + + // Internal Methods + int32_t calculateTemperature(const uint8_t*, uint8_t*); + bool isAllZeros(const uint8_t* const scratchPad, const size_t length = 9); + void activateExternalPullup(void); + void deactivateExternalPullup(void); + +#if REQUIRESALARMS + uint8_t alarmSearchAddress[8]; + int8_t alarmSearchJunction; + uint8_t alarmSearchExhausted; + AlarmHandler* _AlarmHandler; +#endif +}; + +#endif // DallasTemperature_h \ No newline at end of file diff --git a/trunk/Arduino/libraries/DallasTemperature/README.md b/trunk/Arduino/libraries/DallasTemperature/README.md index 728b2d5e..835f6394 100644 --- a/trunk/Arduino/libraries/DallasTemperature/README.md +++ b/trunk/Arduino/libraries/DallasTemperature/README.md @@ -1,72 +1,127 @@ -# Arduino Library for Maxim Temperature Integrated Circuits -## Usage +# 🌡️ Arduino Temperature Control Library -This library supports the following devices : +[![Arduino CI](https://github.com/milesburton/Arduino-Temperature-Control-Library/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/jsoncheck.yml) +[![GitHub issues](https://img.shields.io/github/issues/milesburton/Arduino-Temperature-Control-Library.svg)](https://github.com/milesburton/Arduino-Temperature-Control-Library/issues) + +[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/milesburton/Arduino-Temperature-Control-Library/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/milesburton/Arduino-Temperature-Control-Library.svg?maxAge=3600)](https://github.com/milesburton/Arduino-Temperature-Control-Library/releases) +[![Commits since latest](https://img.shields.io/github/commits-since/milesburton/Arduino-Temperature-Control-Library/latest)](https://github.com/milesburton/Arduino-Temperature-Control-Library/commits/master) + +A robust and feature-complete Arduino library for Maxim Temperature Integrated Circuits. -* DS18B20 -* DS18S20 - Please note there appears to be an issue with this series. -* DS1822 -* DS1820 -* MAX31820 +## 📌 Supported Devices +- DS18B20 +- DS18S20 (⚠️ Known issues with this series) +- DS1822 +- DS1820 +- MAX31820 +- MAX31850 -You will need a pull-up resistor of about 5 KOhm between the 1-Wire data line -and your 5V power. If you are using the DS18B20, ground pins 1 and 3. The -centre pin is the data line '1-wire'. +## 🚀 Installation -In case of temperature conversion problems (result is `-85`), strong pull-up setup may be necessary. See section -_Powering the DS18B20_ in -[DS18B20 datasheet](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) (page 7) -and use `DallasTemperature(OneWire*, uint8_t)` constructor. +### Using Arduino IDE Library Manager (Recommended) +1. Open Arduino IDE +2. Go to Tools > Manage Libraries... +3. Search for "DallasTemperature" +4. Click Install +5. Also install the required "OneWire" library by Paul Stoffregen using the same method -We have included a "REQUIRESNEW" and "REQUIRESALARMS" definition. If you -want to slim down the code feel free to use either of these by including +### Manual Installation +1. Download the latest release from [GitHub releases](https://github.com/milesburton/Arduino-Temperature-Control-Library/releases) +2. In Arduino IDE, go to Sketch > Include Library > Add .ZIP Library... +3. Select the downloaded ZIP file +4. Repeat steps 1-3 for the required "OneWire" library +## 📝 Basic Usage +1. **Hardware Setup** + - Connect a 4k7 Ω pull-up resistor between the 1-Wire data line and 5V power. Note this applies to the Arduino platform, for ESP32 and 8266 you'll need to adjust the resistor value accordingly. + - For DS18B20: Ground pins 1 and 3 (the centre pin is the data line) + - For reliable readings, see pull-up requirements in the [DS18B20 datasheet](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) (page 7) - #define REQUIRESNEW +2. **Code Example** + ```cpp + #include + #include -or + // Data wire is connected to GPIO 4 + #define ONE_WIRE_BUS 4 - #define REQUIRESALARMS + OneWire oneWire(ONE_WIRE_BUS); + DallasTemperature sensors(&oneWire); + void setup(void) { + Serial.begin(9600); + sensors.begin(); + } -at the top of DallasTemperature.h + void loop(void) { + sensors.requestTemperatures(); + + delay(750); + + float tempC = sensors.getTempCByIndex(0); + Serial.print("Temperature: "); + Serial.print(tempC); + Serial.println("°C"); + delay(1000); + } + ``` -Finally, please include OneWire from Paul Stoffregen in the library manager before you begin. +## 🛠️ Advanced Features -## Credits +- Multiple sensors on the same bus +- Temperature conversion by address (`getTempC(address)` and `getTempF(address)`) +- Asynchronous mode (added in v3.7.0) +- Configurable resolution -The OneWire code has been derived from -http://www.arduino.cc/playground/Learning/OneWire. -Miles Burton originally developed this library. -Tim Newsome added support for multiple sensors on -the same bus. -Guil Barros [gfbarros@bappos.com] added getTempByAddress (v3.5) - Note: these are implemented as getTempC(address) and getTempF(address) -Rob Tillaart [rob.tillaart@gmail.com] added async modus (v3.7.0) +### Configuration Options +You can slim down the code by defining the following at the top of DallasTemperature.h: -## Website +```cpp +#define REQUIRESNEW // Use if you want to minimise code size +#define REQUIRESALARMS // Use if you need alarm functionality +``` +## 📚 Additional Documentation -You can find the latest version of the library at -https://www.milesburton.com/Dallas_Temperature_Control_Library +Visit our [Wiki](https://www.milesburton.com/w/index.php/Dallas_Temperature_Control_Library) for detailed documentation. -# License +## 🔧 Library Development -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. +If you want to contribute to the library development: -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. +### Using Dev Container +The project includes a development container configuration for VS Code that provides a consistent development environment. -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 +1. **Prerequisites** + - Visual Studio Code + - Docker + - VS Code Remote - Containers extension + +2. **Development Commands** + Within the dev container, use: + - `arduino-build` - Compile the library and examples + - `arduino-test` - Run the test suite + - `arduino-build-test` - Complete build and test process + + > Note: Currently compiling against arduino:avr:uno environment + +## ✨ Credits + +- Original development by Miles Burton +- Multiple sensor support by Tim Newsome +- Address-based temperature reading by Guil Barros gfbarros@bappos.com +- Async mode by Rob Tillaart rob.tillaart@gmail.com + +## 📄 License + +MIT License | Copyright (c) 2025 Miles Burton + +Full license text available in [LICENSE](LICENSE) file. diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/Alarm/Alarm.ino b/trunk/Arduino/libraries/DallasTemperature/examples/Alarm/Alarm.ino index 2aab5f5b..d94a0b80 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/Alarm/Alarm.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/Alarm/Alarm.ino @@ -7,7 +7,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device addresses @@ -21,15 +21,15 @@ void setup(void) // Start up the library sensors.begin(); - + // locate devices on the bus Serial.print("Found "); Serial.print(sensors.getDeviceCount(), DEC); Serial.println(" devices."); // search for devices on the bus and assign based on an index. - if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); - if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); + if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); + if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); // show the addresses we found on the bus Serial.print("Device 0 Address: "); @@ -39,7 +39,7 @@ void setup(void) Serial.print("Device 0 Alarms: "); printAlarms(insideThermometer); Serial.println(); - + Serial.print("Device 1 Address: "); printAddress(outsideThermometer); Serial.println(); @@ -47,25 +47,25 @@ void setup(void) Serial.print("Device 1 Alarms: "); printAlarms(outsideThermometer); Serial.println(); - + Serial.println("Setting alarm temps..."); // alarm when temp is higher than 30C sensors.setHighAlarmTemp(insideThermometer, 30); - + // alarm when temp is lower than -10C sensors.setLowAlarmTemp(insideThermometer, -10); - + // alarm when temp is higher than 31C sensors.setHighAlarmTemp(outsideThermometer, 31); - + // alarn when temp is lower than 27C sensors.setLowAlarmTemp(outsideThermometer, 27); - + Serial.print("New Device 0 Alarms: "); printAlarms(insideThermometer); Serial.println(); - + Serial.print("New Device 1 Alarms: "); printAlarms(outsideThermometer); Serial.println(); @@ -127,8 +127,8 @@ void checkAlarm(DeviceAddress deviceAddress) } void loop(void) -{ - // call sensors.requestTemperatures() to issue a global temperature +{ + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); @@ -141,15 +141,15 @@ void loop(void) /* // Alternate method: // Search the bus and iterate through addresses of devices with alarms - + // space for the alarm device's address DeviceAddress alarmAddr; Serial.println("Searching for alarms..."); - + // resetAlarmSearch() must be called before calling alarmSearch() sensors.resetAlarmSearch(); - + // alarmSearch() returns 0 when there are no devices with alarms while (sensors.alarmSearch(alarmAddr)) { @@ -159,4 +159,3 @@ void loop(void) */ } - diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino b/trunk/Arduino/libraries/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino index a6a1b2e7..11a1bd9e 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino @@ -7,7 +7,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device addresses @@ -16,7 +16,7 @@ DeviceAddress insideThermometer, outsideThermometer; // function that will be called when an alarm condition exists during DallasTemperatures::processAlarms(); void newAlarmHandler(const uint8_t* deviceAddress) { - Serial.println("Alarm Handler Start"); + Serial.println("Alarm Handler Start"); printAlarmInfo(deviceAddress); printTemp(deviceAddress); Serial.println(); @@ -76,35 +76,35 @@ void setup(void) // Start up the library sensors.begin(); - + // locate devices on the bus Serial.print("Found "); Serial.print(sensors.getDeviceCount(), DEC); Serial.println(" devices."); // search for devices on the bus and assign based on an index - if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); - if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); + if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); + if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); Serial.print("Device insideThermometer "); printAlarmInfo(insideThermometer); Serial.println(); - + Serial.print("Device outsideThermometer "); printAlarmInfo(outsideThermometer); Serial.println(); - + // set alarm ranges Serial.println("Setting alarm temps..."); sensors.setHighAlarmTemp(insideThermometer, 26); sensors.setLowAlarmTemp(insideThermometer, 22); sensors.setHighAlarmTemp(outsideThermometer, 25); sensors.setLowAlarmTemp(outsideThermometer, 21); - + Serial.print("New insideThermometer "); printAlarmInfo(insideThermometer); Serial.println(); - + Serial.print("New outsideThermometer "); printAlarmInfo(outsideThermometer); Serial.println(); @@ -115,12 +115,12 @@ void setup(void) } void loop(void) -{ +{ // ask the devices to measure the temperature sensors.requestTemperatures(); - - // if an alarm condition exists as a result of the most recent - // requestTemperatures() request, it exists until the next time + + // if an alarm condition exists as a result of the most recent + // requestTemperatures() request, it exists until the next time // requestTemperatures() is called AND there isn't an alarm condition // on the device if (sensors.hasAlarm()) @@ -138,7 +138,6 @@ void loop(void) printCurrentTemp(insideThermometer); printCurrentTemp(outsideThermometer); } - + delay(1000); } - diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/ExternalPullup/ExternalPullup.ino b/trunk/Arduino/libraries/DallasTemperature/examples/ExternalPullup/ExternalPullup.ino index b5a2b02c..1d95d011 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/ExternalPullup/ExternalPullup.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/ExternalPullup/ExternalPullup.ino @@ -8,7 +8,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire, ONE_WIRE_PULLUP); void setup(void) @@ -22,14 +22,14 @@ void setup(void) } void loop(void) -{ - // call sensors.requestTemperatures() to issue a global temperature +{ + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); // Send the command to get temperatures Serial.println("DONE"); - - for(int i=0;i OneWire ds18x20[] = { 3, 7 }; -const int oneWireCount = sizeof(ds18x20)/sizeof(OneWire); +const int oneWireCount = sizeof(ds18x20) / sizeof(OneWire); DallasTemperature sensor[oneWireCount]; void setup(void) { @@ -12,10 +12,10 @@ void setup(void) { Serial.print("============Ready with "); Serial.print(oneWireCount); Serial.println(" Sensors================"); - + // Start up the library on all defined bus-wires DeviceAddress deviceAddress; - for (int i = 0; i < oneWireCount; i++) {; + for (int i = 0; i < oneWireCount; i++) { sensor[i].setOneWire(&ds18x20[i]); sensor[i].begin(); if (sensor[i].getAddress(deviceAddress, 0)) sensor[i].setResolution(deviceAddress, 12); @@ -23,14 +23,14 @@ void setup(void) { } void loop(void) { - // call sensors.requestTemperatures() to issue a global temperature + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); for (int i = 0; i < oneWireCount; i++) { sensor[i].requestTemperatures(); } Serial.println("DONE"); - + delay(1000); for (int i = 0; i < oneWireCount; i++) { float temperature = sensor[i].getTempCByIndex(0); @@ -40,4 +40,4 @@ void loop(void) { Serial.println(temperature); } Serial.println(); -} \ No newline at end of file +} diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/Multiple/Multiple.ino b/trunk/Arduino/libraries/DallasTemperature/examples/Multiple/Multiple.ino index eb04877e..0a20cc21 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/Multiple/Multiple.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/Multiple/Multiple.ino @@ -102,7 +102,7 @@ void printAddress(DeviceAddress deviceAddress) void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); - if(tempC == DEVICE_DISCONNECTED_C) + if (tempC == DEVICE_DISCONNECTED_C) { Serial.println("Error: Could not read temperature data"); return; diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/SaveRecallScratchPad/SaveRecallScratchPad.ino b/trunk/Arduino/libraries/DallasTemperature/examples/SaveRecallScratchPad/SaveRecallScratchPad.ino index c07f42a5..7a7bb2f8 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/SaveRecallScratchPad/SaveRecallScratchPad.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/SaveRecallScratchPad/SaveRecallScratchPad.ino @@ -23,23 +23,23 @@ void setup() Serial.begin(9600); Serial.println(__FILE__); Serial.println("Dallas Temperature Demo"); - + sensors.begin(); - + // Get ID of first sensor (at index 0) - sensors.getAddress(deviceAddress,0); + sensors.getAddress(deviceAddress, 0); // By default configuration and alarm/userdata registers are also saved to EEPROM // when they're changed. Sensors recall these values automatically when powered up. - + // Turn OFF automatic saving of configuration and alarm/userdata registers to EEPROM sensors.setAutoSaveScratchPad(false); - + // Change configuration and alarm/userdata registers on the scratchpad int8_t resolution = 12; - sensors.setResolution(deviceAddress,resolution); + sensors.setResolution(deviceAddress, resolution); int16_t userdata = 24680; - sensors.setUserData(deviceAddress,userdata); + sensors.setUserData(deviceAddress, userdata); // Save configuration and alarm/userdata registers to EEPROM sensors.saveScratchPad(deviceAddress); @@ -53,28 +53,28 @@ void setup() // EEPROM by index: // // sensors.saveScratchPadByIndex(0); - + // Print current values on the scratchpad (resolution = 12, userdata = 24680) printValues(); - + } -void loop(){ - +void loop() { + // Change configuration and alarm/userdata registers on the scratchpad int8_t resolution = 10; - sensors.setResolution(deviceAddress,resolution); + sensors.setResolution(deviceAddress, resolution); int16_t userdata = 12345; - sensors.setUserData(deviceAddress,userdata); - + sensors.setUserData(deviceAddress, userdata); + // Print current values on the scratchpad (resolution = 10, userdata = 12345) printValues(); - + delay(2000); - + // Recall configuration and alarm/userdata registers from EEPROM sensors.recallScratchPad(deviceAddress); - + // recallScratchPad can also be used without a parameter to recall the configuration // and alarm/userdata registers of ALL connected sensors from EEPROM: // @@ -84,23 +84,23 @@ void loop(){ // from EEPROM by index: // // sensors.recallScratchPadByIndex(0); - + // Print current values on the scratchpad (resolution = 12, userdata = 24680) printValues(); - + delay(2000); - + } void printValues() { - + Serial.println(); Serial.println("Current values on the scratchpad:"); - + Serial.print("Resolution:\t"); Serial.println(sensors.getResolution(deviceAddress)); - + Serial.print("User data:\t"); Serial.println(sensors.getUserData(deviceAddress)); - + } diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/SetUserData/SetUserData.ino b/trunk/Arduino/libraries/DallasTemperature/examples/SetUserData/SetUserData.ino index 44bd7976..52d3f7fc 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/SetUserData/SetUserData.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/SetUserData/SetUserData.ino @@ -2,7 +2,7 @@ // This sketch does not use the ALARM registers and uses those 2 bytes as a counter // these 2 bytes can be used for other purposes as well e.g. last temperature or // a specific ID. -// +// #include #include @@ -13,7 +13,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); int count = 0; @@ -26,20 +26,20 @@ void setup(void) // Start up the library sensors.begin(); - + } void loop(void) -{ - // call sensors.requestTemperatures() to issue a global temperature +{ + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); // Send the command to get temperatures Serial.println("DONE"); - + Serial.print("Temperature for the device 1 (index 0) is: "); - Serial.println(sensors.getTempCByIndex(0)); - + Serial.println(sensors.getTempCByIndex(0)); + count++; sensors.setUserDataByIndex(0, count); int x = sensors.getUserDataByIndex(0); diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/Simple/Simple.ino b/trunk/Arduino/libraries/DallasTemperature/examples/Simple/Simple.ino index f5181f3d..b8abfc30 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/Simple/Simple.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/Simple/Simple.ino @@ -8,7 +8,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); /* @@ -28,22 +28,23 @@ void setup(void) * Main function, get and show the temperature */ void loop(void) -{ - // call sensors.requestTemperatures() to issue a global temperature +{ + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); // Send the command to get temperatures Serial.println("DONE"); + delay(1500); // After we got the temperatures, we can print them here. // We use the function ByIndex, and as an example get the temperature from the first sensor only. float tempC = sensors.getTempCByIndex(0); // Check if reading was successful - if(tempC != DEVICE_DISCONNECTED_C) + if (tempC != DEVICE_DISCONNECTED_C) { Serial.print("Temperature for the device 1 (index 0) is: "); Serial.println(tempC); - } + } else { Serial.println("Error: Could not read temperature data"); diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/Single/Single.ino b/trunk/Arduino/libraries/DallasTemperature/examples/Single/Single.ino index 554b9191..278f27ee 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/Single/Single.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/Single/Single.ino @@ -8,7 +8,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device address @@ -31,11 +31,11 @@ void setup(void) Serial.println(" devices."); // report parasite power requirements - Serial.print("Parasite power is: "); + Serial.print("Parasite power is: "); if (sensors.isParasitePowerMode()) Serial.println("ON"); else Serial.println("OFF"); - - // Assign address manually. The addresses below will beed to be changed + + // Assign address manually. The addresses below will need to be changed // to valid device addresses on your bus. Device address can be retrieved // by using either oneWire.search(deviceAddress) or individually via // sensors.getAddress(deviceAddress, index) @@ -44,16 +44,16 @@ void setup(void) // Method 1: // Search for devices on the bus and assign based on an index. Ideally, - // you would do this to initially discover addresses on the bus and then - // use those addresses and manually assign them (see above) once you know + // you would do this to initially discover addresses on the bus and then + // use those addresses and manually assign them (see above) once you know // the devices on your bus (and assuming they don't change). - if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); - + if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); + // method 2: search() // search() looks for the next device. Returns 1 if a new address has been - // returned. A zero might mean that the bus is shorted, there are no devices, - // or you have already retrieved all of them. It might be a good idea to - // check the CRC to make sure you didn't get garbage. The order is + // returned. A zero might mean that the bus is shorted, there are no devices, + // or you have already retrieved all of them. It might be a good idea to + // check the CRC to make sure you didn't get garbage. The order is // deterministic. You will always get the same devices in the same order // // Must be called before search() @@ -68,9 +68,9 @@ void setup(void) // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions) sensors.setResolution(insideThermometer, 9); - + Serial.print("Device 0 Resolution: "); - Serial.print(sensors.getResolution(insideThermometer), DEC); + Serial.print(sensors.getResolution(insideThermometer), DEC); Serial.println(); } @@ -85,7 +85,7 @@ void printTemperature(DeviceAddress deviceAddress) // method 2 - faster float tempC = sensors.getTempC(deviceAddress); - if(tempC == DEVICE_DISCONNECTED_C) + if (tempC == DEVICE_DISCONNECTED_C) { Serial.println("Error: Could not read temperature data"); return; @@ -99,13 +99,14 @@ void printTemperature(DeviceAddress deviceAddress) * Main function. It will request the tempC from the sensors and display on Serial. */ void loop(void) -{ - // call sensors.requestTemperatures() to issue a global temperature +{ + // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); // Send the command to get temperatures Serial.println("DONE"); - + delay(1500); + // It responds almost immediately. Let's print out the data printTemperature(insideThermometer); // Use a simple function to print out the data } diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/Tester/Tester.ino b/trunk/Arduino/libraries/DallasTemperature/examples/Tester/Tester.ino index 52decb34..35d2618b 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/Tester/Tester.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/Tester/Tester.ino @@ -8,7 +8,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); int numberOfDevices; // Number of temperature devices found @@ -23,48 +23,48 @@ void setup(void) // Start up the library sensors.begin(); - + // Grab a count of devices on the wire numberOfDevices = sensors.getDeviceCount(); - + // locate devices on the bus Serial.print("Locating devices..."); - + Serial.print("Found "); Serial.print(numberOfDevices, DEC); Serial.println(" devices."); // report parasite power requirements - Serial.print("Parasite power is: "); + Serial.print("Parasite power is: "); if (sensors.isParasitePowerMode()) Serial.println("ON"); else Serial.println("OFF"); - + // Loop through each device, print out address - for(int i=0;i #include @@ -10,7 +10,7 @@ // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); DeviceAddress tempDeviceAddress; @@ -34,46 +34,46 @@ void setup(void) sensors.begin(); sensors.getAddress(tempDeviceAddress, 0); sensors.setResolution(tempDeviceAddress, resolution); - + sensors.setWaitForConversion(false); sensors.requestTemperatures(); - delayInMillis = 750 / (1 << (12 - resolution)); - lastTempRequest = millis(); - - pinMode(13, OUTPUT); + delayInMillis = 750 / (1 << (12 - resolution)); + lastTempRequest = millis(); + + pinMode(13, OUTPUT); } void loop(void) -{ - +{ + if (millis() - lastTempRequest >= delayInMillis) // waited long enough?? { digitalWrite(13, LOW); Serial.print(" Temperature: "); temperature = sensors.getTempCByIndex(0); - Serial.println(temperature, resolution - 8); + Serial.println(temperature, resolution - 8); Serial.print(" Resolution: "); - Serial.println(resolution); + Serial.println(resolution); Serial.print("Idle counter: "); - Serial.println(idle); - Serial.println(); - - idle = 0; - - // immediately after fetching the temperature we request a new sample - // in the async modus + Serial.println(idle); + Serial.println(); + + idle = 0; + + // immediately after fetching the temperature we request a new sample + // in the async modus // for the demo we let the resolution change to show differences resolution++; if (resolution > 12) resolution = 9; - + sensors.setResolution(tempDeviceAddress, resolution); - sensors.requestTemperatures(); + sensors.requestTemperatures(); delayInMillis = 750 / (1 << (12 - resolution)); - lastTempRequest = millis(); + lastTempRequest = millis(); } - + digitalWrite(13, HIGH); - // we can do usefull things here + // we can do usefull things here // for the demo we just count the idle time in millis delay(1); idle++; diff --git a/trunk/Arduino/libraries/DallasTemperature/examples/readPowerSupply/readPowerSupply.ino b/trunk/Arduino/libraries/DallasTemperature/examples/readPowerSupply/readPowerSupply.ino index 308edebf..39a3aeb3 100644 --- a/trunk/Arduino/libraries/DallasTemperature/examples/readPowerSupply/readPowerSupply.ino +++ b/trunk/Arduino/libraries/DallasTemperature/examples/readPowerSupply/readPowerSupply.ino @@ -18,7 +18,7 @@ // Setup a oneWire instance to communicate with any OneWire devices OneWire oneWire(ONE_WIRE_BUS); -// Pass our oneWire reference to Dallas Temperature. +// Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device addresses diff --git a/trunk/Arduino/libraries/DallasTemperature/keywords.txt b/trunk/Arduino/libraries/DallasTemperature/keywords.txt index 2d17db8d..a471c3ee 100644 --- a/trunk/Arduino/libraries/DallasTemperature/keywords.txt +++ b/trunk/Arduino/libraries/DallasTemperature/keywords.txt @@ -76,3 +76,12 @@ calculateTemperature KEYWORD2 DEVICE_DISCONNECTED_C LITERAL1 DEVICE_DISCONNECTED_F LITERAL1 DEVICE_DISCONNECTED_RAW LITERAL1 +DEVICE_FAULT_OPEN_C LITERAL1 +DEVICE_FAULT_OPEN_F LITERAL1 +DEVICE_FAULT_OPEN_RAW LITERAL1 +DEVICE_FAULT_SHORTGND_C LITERAL1 +DEVICE_FAULT_SHORTGND_F LITERAL1 +DEVICE_FAULT_SHORTGND_RAW LITERAL1 +DEVICE_FAULT_SHORTVDD_C LITERAL1 +DEVICE_FAULT_SHORTVDD_F LITERAL1 +DEVICE_FAULT_SHORTVDD_RAW LITERAL1 diff --git a/trunk/Arduino/libraries/DallasTemperature/library.json b/trunk/Arduino/libraries/DallasTemperature/library.json index eef9d6fd..ed6671b3 100644 --- a/trunk/Arduino/libraries/DallasTemperature/library.json +++ b/trunk/Arduino/libraries/DallasTemperature/library.json @@ -1,7 +1,7 @@ { "name": "DallasTemperature", - "keywords": "onewire, 1-wire, bus, sensor, temperature", - "description": "Arduino Library for Dallas Temperature ICs (DS18B20, DS18S20, DS1822, DS1820)", + "keywords": "onewire, 1-wire, bus, sensor, temperature, DS18B20, DS18S20, DS1822, DS1820, MAX31850", + "description": "Arduino Library for Dallas Temperature ICs (DS18B20, DS18S20, DS1822, DS1820, MAX31850)", "repository": { "type": "git", @@ -11,7 +11,7 @@ [ { "name": "Miles Burton", - "email": "miles@mnetcs.com", + "email": "mail@milesburton.com", "url": "http://www.milesburton.com", "maintainer": true }, @@ -30,11 +30,11 @@ ], "dependencies": { - "name": "OneWire", - "authors": "Paul Stoffregen", - "frameworks": "arduino" + "paulstoffregen/OneWire": "^2.3.5" }, - "version": "3.9.0", + "version": "4.0.5", + "license": "MIT", "frameworks": "arduino", - "platforms": "*" + "platforms": "*", + "headers": "DallasTemperature.h" } diff --git a/trunk/Arduino/libraries/DallasTemperature/library.properties b/trunk/Arduino/libraries/DallasTemperature/library.properties index acdda8f0..09f0b688 100644 --- a/trunk/Arduino/libraries/DallasTemperature/library.properties +++ b/trunk/Arduino/libraries/DallasTemperature/library.properties @@ -1,10 +1,9 @@ name=DallasTemperature -version=3.9.0 -author=Miles Burton , Tim Newsome , Guil Barros , Rob Tillaart -maintainer=Miles Burton -sentence=Arduino Library for Dallas Temperature ICs -paragraph=Supports DS18B20, DS18S20, DS1822, DS1820 +version=4.0.5 +author=Miles Burton , Tim Newsome , Guil Barros , Rob Tillaart +maintainer=Miles Burton +sentence=Arduino library for Dallas/Maxim temperature ICs +paragraph=Support for DS18B20 and other Dallas/Maxim 1-Wire temperature sensors category=Sensors url=https://github.com/milesburton/Arduino-Temperature-Control-Library architectures=* -depends=OneWire diff --git a/trunk/Arduino/libraries/IRremote/Contributing.md b/trunk/Arduino/libraries/IRremote/Contributing.md index 4424f809..7483472d 100644 --- a/trunk/Arduino/libraries/IRremote/Contributing.md +++ b/trunk/Arduino/libraries/IRremote/Contributing.md @@ -18,7 +18,7 @@ Refrain from using overly long or capitalized titles as they are usually annoyin We use the original [C Style by Kerninghan / Ritchie](https://en.wikipedia.org/wiki/Indentation_style#K&R_style) in [variant: 1TBS (OTBS)](https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)).
In short: 4 spaces indentation, no tabs, opening braces on the same line, braces are mandatory on all if/while/do, no hard line length limit.
To beautify your code, you may use the online formatter [here](https://www.freecodeformat.com/c-format.php). -#### Cover **all** occurences of the problem / addition you address with your PR +#### Cover **all** occurrences of the problem / addition you address with your PR Do not forget the documentation like it is done for existing code. Code changes without proper documentation will be rejected! ## Adding new protocols diff --git a/trunk/Arduino/libraries/IRremote/Contributors.md b/trunk/Arduino/libraries/IRremote/Contributors.md index 349ccec1..0d6ccd93 100644 --- a/trunk/Arduino/libraries/IRremote/Contributors.md +++ b/trunk/Arduino/libraries/IRremote/Contributors.md @@ -31,5 +31,9 @@ These are the active contributors of this project that you may contact if there - [slott](https://stackoverflow.com/users/11680056/sklott) Seeduino print(unsigned long long...) support. - [Joe Ostrander](https://github.com/joeostrander) Added support for attiny1614. - [Buzzerb](https://github.com/Buzzerb) Added Extended NEC protocol to TinyIR and making it more consistent. +- [akellai](https://github.com/akellai) Added ESP 3.0 support. +- [Hayden McAfee](https://github.com/haydenmc) Help with UnoR4 support. +- [Yonghwan SO](https://github.com/sio4) Fixed bug in ReceiveDemo.cpp if DEBUG_BUTTON_PIN is not defined. #1306. +- [A.R.Jung](https://github.com/arjung467) Marantz-RC5 extension support #1314. Note: Please let [ArminJo](https://github.com/ArminJo) know if you have been missed. diff --git a/trunk/Arduino/libraries/IRremote/Doxyfile b/trunk/Arduino/libraries/IRremote/Doxyfile index a879240e..9e4c94a0 100644 --- a/trunk/Arduino/libraries/IRremote/Doxyfile +++ b/trunk/Arduino/libraries/IRremote/Doxyfile @@ -2339,7 +2339,7 @@ INCLUDED_BY_GRAPH = YES # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALL_GRAPH = NO +CALL_GRAPH = YES # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. @@ -2351,7 +2351,7 @@ CALL_GRAPH = NO # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALLER_GRAPH = NO +CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. diff --git a/trunk/Arduino/libraries/IRremote/README.md b/trunk/Arduino/libraries/IRremote/README.md index 16b7eb64..1be77e02 100644 --- a/trunk/Arduino/libraries/IRremote/README.md +++ b/trunk/Arduino/libraries/IRremote/README.md @@ -32,39 +32,52 @@ Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libr # Table of content - [Supported IR Protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#supported-ir-protocols) -- [Features](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#features) - * [New features with version 4.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-with-version-4x) - * [New features with version 3.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-with-version-3x) -- [Converting your 2.x program to the 4.x version](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version) +- [Common problem with IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#common-problem-with-irremote) +- [Using the new library version for old examples](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#using-the-new-library-version-for-old-examples) + * [New features of version 4.5](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-of-version-45) + * [New features of version 4.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-of-version-4x) + * [New features of version 3.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-of-version-3x) + * [Converting your 2.x program to the 4.x version](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version) * [How to convert old MSB first 32 bit IR data codes to new LSB first 32 bit IR data codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes) -- [Errors with using the 3.x versions for old tutorials](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#errors-with-using-the-3x-versions-for-old-tutorials) + * [Errors when using the 3.x versions for old tutorials](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#errors-when-using-the-3x-versions-for-old-tutorials) * [Staying on 2.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#staying-on-2x) - [Why *.hpp instead of *.cpp](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#why-hpp-instead-of-cpp) - [Using the new *.hpp files](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#using-the-new-hpp-files) +- [Tutorials](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tutorials) +- [3 ways to specify an IR code](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#3-ways-to-specify-an-ir-code) - [IRReceiver pinouts](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#irreceiver-pinouts) - [Receiving IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-ir-codes) * [decodedIRData structure](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#decodedirdata-structure) * [Ambiguous protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ambiguous-protocols) - * [Unknown protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#unknown-protocol) + * [RAM usage of different protocolsl](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ram-usage-of-different-protocols) + * [Handling unknown Protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#handling-unknown-protocols) + * [Disclaimer](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#disclaimer) + * [Other libraries, which may cover these protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#other-libraries-which-may-cover-these-protocols) + * [Protocol=PULSE_DISTANCE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolpulse_distance) + * [Protocol=UNKNOWN](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolunknown) + * [How to deal with protocols not supported by IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-deal-with-protocols-not-supported-by-irremote) - [Sending IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-ir-codes) + * [Sending UNKNOWN protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-unknown-protocol) + * [Sending IRDB IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-irdb-ir-codes) * [Send pin](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#send-pin) + * [Polarity of send pin](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#polarity-of-send-pin) + [List of public IR code databases](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#list-of-public-ir-code-databases) - [Tiny NEC receiver and sender](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender) - [The FAST protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#the-fast-protocol) - [FAQ and hints](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#faq-and-hints) + * [Receiving stops after analogWrite() or tone() or after running a motor](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-stops-after-analogwrite-or-tone-or-after-running-a-motor) + * [Receiving sets overflow flag](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-sets-overflow-flag) * [Problems with Neopixels, FastLed etc.](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#problems-with-neopixels-fastled-etc) * [Does not work/compile with another library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#does-not-workcompile-with-another-library) - * [Multiple IR receiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#multiple-ir-receiver) + * [Multiple IR receivers](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#multiple-ir-receivers) + * [Multiple IR sender instances](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#multiple-ir-sender-instances) * [Increase strength of sent output signal](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#increase-strength-of-sent-output-signal) + * [Simulate an IR receiver module](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#simulate-an-ir-receiver-module) * [Minimal CPU clock frequency](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#minimal-cpu-clock-frequency) * [Bang & Olufsen protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#bang--olufsen-protocol) -- [Handling unknown Protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#handling-unknown-protocols) - * [Disclaimer](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#disclaimer) - * [Protocol=PULSE_DISTANCE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolpulse_distance) - * [Protocol=UNKNOWN](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolunknown) - * [How to deal with protocols not supported by IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-deal-with-protocols-not-supported-by-irremote) - [Examples for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library) - [WOKWI online examples](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#wokwi-online-examples) +- [IR control of a robot car](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ir-control-of-a-robot-car) - [Issues and discussions](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#issues-and-discussions) - [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library) + [Changing include (*.h) files with Arduino IDE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#changing-include-h-files-with-arduino-ide) @@ -77,6 +90,7 @@ Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libr - [How we decode signals](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-we-decode-signals) - [NEC encoding diagrams](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#nec-encoding-diagrams) - [Quick comparison of 5 Arduino IR receiving libraries](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#quick-comparison-of-5-arduino-ir-receiving-libraries) +- [History](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/changelog.md) - [Useful links](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#useful-links) - [Contributors](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/Contributors.md) - [License](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#license) @@ -87,11 +101,13 @@ Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libr # Supported IR Protocols ` NEC / Onkyo / Apple `     ` Denon / Sharp `     ` Panasonic / Kaseikyo ` -` JVC `     ` LG `     ` RC5 `     ` RC6 `     ` Samsung `     ` Sony ` +` JVC `     ` LG `     ` RC5 `     ` RC6 `     ` Samsung `     ` Sony `     ` Marantz ` -` Universal Pulse Distance `     ` Universal Pulse Width `     ` Hash `     ` Pronto ` +` Universal Pulse Distance `     ` Universal Pulse Width `     ` Universal Pulse Distance Width` -` BoseWave `     ` Bang & Olufsen `     ` Lego `     ` FAST `     ` Whynter `     ` MagiQuest ` +` Hash `     ` Pronto ` + +` BoseWave `     ` Bang & Olufsen `     ` Lego `     ` FAST `     ` Whynter `     ` MagiQuest `     ` Velux ` Protocols can be switched off and on by defining macros before the line `#include ` like [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino#L33): @@ -102,33 +118,45 @@ Protocols can be switched off and on by defining macros before the line `#includ ```
-# Features -- Lots of tutorials and examples. -- Actively maintained. -- Allows receiving and sending of **raw timing data**. +# Common problem with IRremote +Or *"I build a gadged with 2 motors controlled by IR and the [IR stops after the first motor command](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-stops-after-analogwrite-or-tone-or-after-running-a-motor)"*.
+This is due to the fact, that the motor control by AnalogWrite() uses the same timer as IR receiving.
+See [this table](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage) for the list of timers and pins. -## New features with version 4.x -- **Since 4.3 `IrSender.begin(DISABLE_LED_FEEDBACK)` will no longer work**, use `IrSender.begin(DISABLE_LED_FEEDBACK, 0)` instead. -- New universal **Pulse Distance / Pulse Width decoder** added, which covers many previous unknown protocols. +# Using the new library version for old examples +This library has been refactored, breaking backward compatibility with the old version, on which many examples on the Internet are based. + +## New features of version 4.5 +- Support for **multiple receiver instances**.
+`irparams_struct irparams` is now member of `IRrecv`. Thus `rawDataPtr` (pointer to irparams) was removed from `IrReceiver.decodedIRData`.
+Old `IrReceiver.decodedIRData.rawDataPtr->rawbuf` is now `IrReceiver.irparams.rawbuf`, the same holds for `IrReceiver.irparams.rawlen`. +- LED feedback is always enabled for sending. It can only be disabled by using `#define NO_LED_SEND_FEEDBACK_CODE` or `#define NO_LED_FEEDBACK_CODE`. +- The second parameter of the function `IrSender.begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback)` has changed to `uint_fast8_t aFeedbackLEDPin`. Therefore this function call no longer works as expected because it uses the old boolean value of e.g. `ENABLE_LED_FEEDBACK` which evaluates to true or 1 as pin number of the LED feedback pin number. +- New experimental Velux send function. +- Send function for Marantz version of the RC5x protocol. This protocol has an additional 6 bit command extension and adds a short pause after the address is sent to identify this variant of the protocol. + +## New features of version 4.x +- **Since 4.3 `IrSender.begin(DISABLE_LED_FEEDBACK)` will no longer work**, use `#define NO_LED_SEND_FEEDBACK_CODE` instead. +- New universal **Pulse Distance / Pulse Width / Pulse Distance Width decoder** added, which covers many previous unknown protocols. - Printout of code how to send received command by `IrReceiver.printIRSendUsage(&Serial)`. - RawData type is now 64 bit for 32 bit platforms and therefore `decodedIRData.decodedRawData` can contain complete frame information for more protocols than with 32 bit as before. - **Callback** after receiving a command - It calls your code as soon as a message was received. - Improved handling of `PULSE_DISTANCE` + `PULSE_WIDTH` protocols. - New FAST protocol. -- Automatic printout of the **corresponding send function** with printIRSendUsage(). +- Automatic printout of the **corresponding send function** with `printIRSendUsage()`. -#### Converting your 3.x program to the 4.x version +### Converting your 3.x program to the 4.x version - You must replace `#define DECODE_DISTANCE` by `#define DECODE_DISTANCE_WIDTH` (only if you explicitly enabled this decoder). - The parameter `bool hasStopBit` is not longer required and removed e.g. for function `sendPulseDistanceWidth()`. -## New features with version 3.x +## New features of version 3.x - **Any pin** can be used for receiving and if `SEND_PWM_BY_TIMER` is not defined also for sending. - Feedback LED can be activated for sending / receiving. - An 8/16 bit ****command** value as well as an 16 bit **address** and a protocol number is provided for decoding (instead of the old 32 bit value). - Protocol values comply to **protocol standards**.
NEC, Panasonic, Sony, Samsung and JVC decode & send LSB first. - Supports **Universal Distance protocol**, which covers a lot of previous unknown protocols. -- Compatible with **tone()** library. See the [ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/21b5747a58e9d47c9e3f1beb056d58c875a92b47/examples/ReceiveDemo/ReceiveDemo.ino#L159-L169) example. +- Compatible with **tone()** library. See the [ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298) example. - Simultaneous sending and receiving. See the [SendAndReceive](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendAndReceive/SendAndReceive.ino#L167-L170) example. - Supports **more platforms**. - Allows for the generation of non PWM signal to just **simulate an active low receiver signal** for direct connect to existent receiving devices without using IR. @@ -140,25 +168,26 @@ Protocols can be switched off and on by defining macros before the line `#includ
-# Converting your 2.x program to the 4.x version +## Converting your 2.x program to the 4.x version Starting with the 3.1 version, **the generation of PWM for sending is done by software**, thus saving the hardware timer and **enabling arbitrary output pins for sending**.
-If you use an (old) Arduino core that does not use the `-flto` flag for compile, you can activate the line `#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN` in IRRemote.h, if you get false error messages regarding begin() during compilation. +If you use an (old) Arduino core that does not use the `-flto` flag for compile, you can activate the line `#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN` in IRRemote.h, +if you get false error messages regarding begin() during compilation. - **IRreceiver** and **IRsender** object have been added and can be used without defining them, like the well known Arduino **Serial** object. -- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program, and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver` and replace all `IRsend` or `irsend` with `IrSender`. -- Since the decoded values are now in `IrReceiver.decodedIRData` and not in `results` any more, remove the line `decode_results results` or similar. +- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver` and replace all `IRsend` or `irsend` with `IrSender`. - Like for the Serial object, call [`IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK)`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L106) or `IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK)` instead of the `IrReceiver.enableIRIn()` or `irrecv.enableIRIn()` in setup().
-For sending, call `IrSender.begin();` in setup().
-If IR_SEND_PIN is not defined (before the line `#include `) you must use e.g. `IrSender.begin(3, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN);` +If IR_SEND_PIN is not defined (before the line `#include `) you must use e.g. `IrSender.begin(3);` - Old `decode(decode_results *aResults)` function is replaced by simple `decode()`. So if you have a statement `if(irrecv.decode(&results))` replace it with `if (IrReceiver.decode())`. - The decoded result is now in in `IrReceiver.decodedIRData` and not in `results` any more, therefore replace any occurrences of `results.value` and `results.decode_type` (and similar) to `IrReceiver.decodedIRData.decodedRawData` and `IrReceiver.decodedIRData.protocol`. - Overflow, Repeat and other flags are now in [`IrReceiver.receivedIRData.flags`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L90-L101). -- Seldom used: `results.rawbuf` and `results.rawlen` must be replaced by `IrReceiver.decodedIRData.rawDataPtr->rawbuf` and `IrReceiver.decodedIRData.rawDataPtr->rawlen`. +- Seldom used: `results.rawbuf` and `results.rawlen` must be replaced by `IrReceiver.irparams.rawbuf` and `IrReceiver.decodedIRData.rawlen`. -- The 5 protocols **NEC, Panasonic, Sony, Samsung and JVC** have been converted to LSB first. Send functions for sending old MSB data for **NEC** and **JVC** were renamed to `sendNECMSB`, and `sendJVCMSB()`. The old `sendSAMSUNG()` and `sendSony()` MSB functions are still available. The old MSB version of `sendPanasonic()` function was deleted, since it had bugs nobody recognized.
-For converting MSB codes to LSB see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes). +- The 5 protocols **NEC, Panasonic, Sony, Samsung and JVC** have been converted to LSB first. Send functions for sending old MSB data were renamed to `sendNECMSB`, `sendSamsungMSB()`, `sendSonyMSB()` and `sendJVCMSB()`. +The old `sendSAMSUNG()` and `sendSony()` MSB functions are still available. +The old MSB version of `sendPanasonic()` function was deleted, since it had bugs nobody recognized and therefore was assumed to be never used.
+For converting MSB codes to LSB see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes) or use the new functions `bitreverseOneByte()` or `bitreverse32Bit()` for reversing. ### Example #### Old 2.x program: @@ -173,6 +202,7 @@ decode_results results; void setup() { ... + Serial.begin(115200); // Establish serial communication irrecv.enableIRIn(); // Start the receiver } @@ -195,6 +225,7 @@ void loop() { void setup() { ... + Serial.begin(115200); // // Establish serial communication IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); // Start the receiver } @@ -210,30 +241,52 @@ void loop() { } ``` +#### Sample output +For more, see the [UnitTest log](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/UnitTest/UnitTest.log). + +``` +Protocol=NEC Address=0xF1 Command=0x76 Raw-Data=0x89760EF1 32 bits LSB first +Send with: IrSender.sendNEC(0xF1, 0x76, ); + +Protocol=Kaseikyo_Denon Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0xFF1, 0x76, ); +``` + ## How to convert old MSB first 32 bit IR data codes to new LSB first 32 bit IR data codes For the new decoders for **NEC, Panasonic, Sony, Samsung and JVC**, the result `IrReceiver.decodedIRData.decodedRawData` is now **LSB-first**, as the definition of these protocols suggests!

-To convert one into the other, you must reverse the byte/nibble positions and then reverse all bit positions of each byte/nibble or write it as one binary string and reverse/mirror it.

-Example: -`0xCB 34 01 02`
+To convert one into the other, you must reverse the nibble (4 bit / Hex numbers) positions and then reverse all bit positions of each nibble or write it as one binary string and reverse/mirror it.
+This can be done by using the new functions `bitreverseOneByte()` or `bitreverse32Bit()`. + +### Nibble reverse +`0xCB 34 01 02` MSB value
`0x20 10 43 BC` after nibble reverse
-`0x40 80 2C D3` after bit reverse of each nibble

-### Nibble reverse map: +`0x40 80 2C D3` LSB value after bit reverse of each nibble + +### Nibble reverse map ``` 0->0 1->8 2->4 3->C 4->2 5->A 6->6 7->E 8->1 9->9 A->5 B->D C->3 D->B E->7 F->F ``` + +### Binary string reverse +If you **read the first binary sequence backwards** (right to left), you get the second sequence.
`0xCB340102` is binary `1100 1011 0011 0100 0000 0001 0000 0010`.
-`0x40802CD3` is binary `0100 0000 1000 0000 0010 1100 1101 0011`.
-If you **read the first binary sequence backwards** (right to left), you get the second sequence. -You may use `bitreverseOneByte()` or `bitreverse32Bit()` for this. +`0x40802CD3` is binary `0100 0000 1000 0000 0010 1100 1101 0011`. + +### Online tool which reverses every byte, but not the order of the bytes +Use this [tool provided by analysir](https://www.analysir.com/hex2nec.php). + +### Send MSB directly +Sending old MSB codes without conversion can be done by using `sendNECMSB()`, `sendSonyMSB()`, `sendSamsungMSB()`, `sendJVCMSB()`.
-# Errors with using the 4.x versions for old tutorials -If you suffer from errors with old tutorial code including `IRremote.h` instead of `IRremote.hpp`, just try to rollback to [Version 2.4.0](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0).
+## Errors when using the 4.x versions for old tutorials +If you suffer from errors with old tutorial code including `IRremote.h` instead of `IRremote.hpp`, +just try to rollback to [Version 2.4.0](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0).
Most likely your code will run and you will not miss the new features.
@@ -242,16 +295,14 @@ Most likely your code will run and you will not miss the new features. Consider using the [original 2.4 release form 2017](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0) or the last backwards compatible [2.8 version](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/2.8.0) for you project.
It may be sufficient and deals flawlessly with 32 bit IR codes.
-If this doesn't fit your case, be assured that 3.x is at least trying to be backwards compatible, so your old examples should still work fine. +If this doesn't work for you, you can be sure that 4.x is trying to be compatible with earlier versions, so your old examples should work just fine. -### Drawbacks +### Drawbacks of using 2.x - Only the following decoders are available:
` NEC `     ` Denon `     ` Panasonic `     ` JVC `     ` LG `
` RC5 `     ` RC6 `     ` Samsung `     ` Sony ` - The call of `irrecv.decode(&results)` uses the old MSB first decoders like in 2.x and sets the 32 bit codes in `results.value`. -- The old functions `sendNEC()` and `sendJVC()` are renamed to `sendNECMSB()` and `sendJVCMSB()`.
- Use them to send your **old MSB-first 32 bit IR data codes**. -- No decoding by a (constant) 8/16 bit address and an 8 bit command. +- No decoding to a more meaningful (constant) 8/16 bit address and 8 bit command.
@@ -259,9 +310,10 @@ If this doesn't fit your case, be assured that 3.x is at least trying to be back **Every \*.cpp file is compiled separately** by a call of the compiler exclusively for this cpp file. These calls are managed by the IDE / make system. In the Arduino IDE the calls are executed when you click on *Verify* or *Upload*. -And now our problem with Arduino is:
+And now **our problem with Arduino** is:
**How to set [compile options](#compile-options--macros-for-this-library) for all *.cpp files, especially for libraries used?**
-IDE's like [Sloeber](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-sloeber-ide) or [PlatformIO](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-platformio) support this by allowing to specify a set of options per project. +IDE's like [Sloeber](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#modifying-compile-options--macros-with-sloeber-ide) or +[PlatformIO](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#modifying-compile-options--macros-with-platformio) support this by allowing to specify a set of options per project. They add these options at each compiler call e.g. `-DTRACE`. But Arduino lacks this feature. @@ -269,10 +321,10 @@ So the **workaround** is not to compile all sources separately, but to concatena This is done by e.g. `#include "IRremote.hpp"`. But why not `#include "IRremote.cpp"`?
-Try it and you will see tons of errors, because each function of the *.cpp file is now compiled twice, -first by compiling the huge file and second by compiling the *.cpp file separately, like described above.
-So using the extension *cpp* is not longer possible, and one solution is to use *hpp* as extension, to show that it is an included *.cpp file.
-Every other extension e.g. *cinclude* would do, but *hpp* seems to be common sense. +If you try it, you will see lots of errors, because each function of the *.cpp file is now compiled twice:
+first when the huge file is compiled, and second when the .cpp file is compiled separately, as described above.
+Therefore, using the **.cpp** extension is no longer possible. One solution is to use the **.hpp** extension to indicate that it is an included .cpp file.
+Any other extension would work, e.g. *cinclude*, but *hpp* seems to be common sense. # Using the new *.hpp files In order to support [compile options](#compile-options--macros-for-this-library) more easily, @@ -293,11 +345,62 @@ The following macros will definitely be overridden with default values otherwise
+# Tutorials +- A very elaborated introduction to IR remotes and IRremote library from [DroneBot Workshop ](https://dronebotworkshop.com/ir-remotes/). + + +# 3 ways to specify an IR code +There are 3 different ways of specifying a particular IR code. + +## 1. Timing +The timing of each mark/pulse and space/distance_between_pulses is specified in a list or array. +This enables specifying **all IR codes**, but requires a lot of memory and is **not readable at all**. +One formal definition of such a timing array, including **specification of frequency and repeats** is the [**Pronto** format](http://www.harctoolbox.org/Glossary.html#ProntoSemantics).
+Memory can be saved by using a lower time resolution. +For IRremote you can use a 50 µs resolution which halves the memory requirement by using byte values instead of int16 values. +For receiving purposes you can use the **hash of the timing** provided by the `decodeHash()` decoder. + +## 2. Encoding schemes +There are 3 main encoding schemes which encodes a binary bitstream / hex value: +1. `PULSE_DISTANCE`. The distance between pulses determines the bit value. This requires always a stop bit! +Examples are NEC and KASEIKYO protocols. The pulse width is constant for most protocols. +2. `PULSE_WIDTH`. The width of a pulse determines the bit value, pulse distance is constant. This requires no stop bit! +The only known example is the SONY protocol. +3. [Phase / Manchester encoding](https://en.wikipedia.org/wiki/Manchester_code). +The time of the pulse/pause transition (phase) relative to the clock determines the bit value. Examples are RC5 and RC6 protocols. + +Phase encoding has a **constant bit length**, `PULSE_DISTANCE` with constant pulse width and `PULSE_WIDTH` have **no constant bit length**! + +A well known example for `PULSE_DISTANCE` with non constant pulse width encoding is the **RS232 serial encoding**. +Here the non constant pulse width is used to enable a **constant bit length**. + +Most IR signals have a **special header** to help in setting the automatic gain of the receiver circuit. +This header is not part of the encoding, but is often significant for a special protocol and therefore must be reproducible. + +Be aware that there are codes using a `PULSE_DISTANCE` encoding where more than a binary 0/1 is put into a pulse/pause combination. +This requires more than 2 different pulse or pause length combinations. +The [HobToHood protocol](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino) uses such an encoding. + +Using encoding schemes reduces the specification of an IR code to a bitstream / hex value, which is LSB by default and pulse / pause timings of header, 0, and 1. +The hex value is **quite readable**. +These schemes can not put any semantics like address, command or checksum on this bitstream. + +## 3. Protocols +There are a few common protocols that are implemented directly in IRremote. +They specify the frequency, the timings of header, 0, and 1 as well as other values like checksum, repeat distance, repeat coding, bit toggling etc. +The semantics of the hex value is also specified, allowing the usage of only 2 parameters **address** and **command** to specify an IR code. +This saves memory and is **highly readable**. +Often the address is also constant, which further reduces memory requirements. + + # IRReceiver pinouts ![IRReceiver Pinout](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IRReceiverPinout.jpg) +[Adafruit IR Sensor tutorial](https://learn.adafruit.com/ir-sensor) + + # Receiving IR codes -Check for a **completly received IR frame** with:
+In your program you check for a **completely received IR frame** with:
`if (IrReceiver.decode()) {}`
This also decodes the received data.
After successful decoding, the IR data is contained in the IRData structure, available as `IrReceiver.decodedIRData`. @@ -309,15 +412,19 @@ struct IRData { uint16_t address; // Decoded address uint16_t command; // Decoded command uint16_t extra; // Used for Kaseikyo unknown vendor ID. Ticks used for decoding Distance protocol. + IRRawDataType decodedRawData; // Up to 32 (64 bit for 32 bit CPU architectures) bit decoded raw data, used for sendRaw functions. +#if defined(DECODE_DISTANCE_WIDTH) + DistanceWidthTimingInfoStruct DistanceWidthTimingInfo; // 12 bytes + IRRawDataType decodedRawDataArray[DECODED_RAW_DATA_ARRAY_SIZE]; // 32/64 bit decoded raw data, to be used for sendPulseDistanceWidthFromArray functions. +#endif uint16_t numberOfBits; // Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible. uint8_t flags; // IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions - IRRawDataType decodedRawData; // Up to 32 (64 bit for 32 bit CPU architectures) bit decoded raw data, used for sendRaw functions. - uint32_t decodedRawDataArray[RAW_DATA_ARRAY_SIZE]; // 32 bit decoded raw data, to be used for send function. - irparams_struct *rawDataPtr; // Pointer of the raw timing data to be decoded. Mainly the data buffer filled by receiving ISR. + IRRawlenType rawlen; // Counter of entries in rawbuf of last received frame. + uint16_t initialGapTicks; // Contains the initial gap of the last received frame. }; ``` #### Flags -This is the [list of flags](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L88) contained in the flags field.
+This is the [list of flags](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L143) contained in the flags field.
Check it with e.g. `if(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)`. | Flag name | Description | @@ -327,7 +434,7 @@ Check it with e.g. `if(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)` | IRDATA_FLAGS_PARITY_FAILED | The current (autorepeat) frame violated parity check. | | IRDATA_FLAGS_TOGGLE_BIT | Is set if RC5 or RC6 toggle bit is set. | | IRDATA_FLAGS_EXTRA_INFO | There is extra info not contained in address and data (e.g. Kaseikyo unknown vendor ID, or in decodedRawDataArray). | -| IRDATA_FLAGS_WAS_OVERFLOW | irparams.rawlen is set to 0 in this case to avoid endless OverflowFlag. | +| IRDATA_FLAGS_WAS_OVERFLOW | Too many marks and spaces for the specified `RAW_BUFFER_LENGTH`. To avoid endless flagging of overflow, irparams.rawlen is set to 0 in this case. | | IRDATA_FLAGS_IS_MSB_FIRST | This value is mainly determined by the (known) protocol. | #### To access the **RAW data**, use: @@ -346,7 +453,8 @@ IrReceiver.printIRResultShort(&Serial); ```c++ IrReceiver.printIRResultRawFormatted(&Serial, true);` ``` -The raw data depends on the internal state of the Arduino timer in relation to the received signal and might therefore be slightly different each time. (resolution problem). The decoded values are the interpreted ones which are tolerant to such slight differences! +The raw data depends on the internal state of the Arduino timer in relation to the received signal and might therefore be slightly different each time. (resolution problem). +The decoded values are the interpreted ones which are tolerant to such slight differences! #### Print how to send the received data: ```c++ @@ -380,13 +488,84 @@ But be careful, the NEC2 protocol can only be detected by the NEC library decode ### Samsung, SamsungLG On a long press, the **SamsungLG protocol** does not repeat its frame, it sends a special short repeat frame. -## Unknown protocol -If your protocol seems not to be supported by this library, you may try the [IRMP library](https://github.com/IRMP-org/IRMP). +## RAM usage of different protocols +The `RAW_BUFFER_LENGTH` determines the length of the **byte buffer** where the received IR timing data is stored before decoding.
+**100** is sufficient for standard protocols **up to 48 bits**, with 1 bit consisting of one mark and space. +We always require additional 4 bytes, 1 byte for initial gap, 2 bytes for header and 1 byte for stop bit. +- **48** bit protocols are PANASONIC, KASEIKYO, SAMSUNG48, RC6. +- **32** bit protocols like NEC, SAMSUNG, WHYNTER, SONY(20), LG(28) require a **buffer length of 68**. +- **16** bit protocols like BOSEWAVE, DENON, FAST, JVC, LEGO_PF, RC5, SONY(12 or 15) require a **buffer length of 36**. +- MAGIQUEST requires a buffer length of **112**. +- Air conditioners often send a longer protocol data stream **up to 750 bits**. + +If the record gap determined by `RECORD_GAP_MICROS` is changed from the default 8 ms to more than 20 ms, the buffer is no longer a byte but a uint16_t buffer, requiring twice as much RAM. +
+ +## Handling unknown Protocols +### Disclaimer +**This library was designed to fit inside MCUs with relatively low levels of resources and was intended to work as a library together with other applications which also require some resources of the MCU to operate.** + +Use the **ReceiveDemo example** to print out all informations about your IR protocol.
+The **ReceiveDump example** gives you more information but has bad repeat detection due to the time required for printing the information. + +### Other libraries, which may cover these protocols +#### IRMP +If your protocol seems not to be supported by this library, you may try the [IRMP library](https://github.com/IRMP-org/IRMP), which especially supports manchester protocols much better. + +#### IRremoteESP8266 +For **air conditioners** , you may try the [IRremoteESP8266 library](https://github.com/crankyoldgit/IRremoteESP8266), which supports an impressive set of protocols and a lot of air conditioners and works also on ESP32. + +#### rawirdecode and HeatpumpIR +[Raw-IR-decoder-for-Arduino](https://github.com/ToniA/Raw-IR-decoder-for-Arduino) is not a library, but an arduino example sketch, which provides many methods of decoding especially **air conditioner** protocols. Sending of these protocols can be done by the Arduino library [HeatpumpIR](https://github.com/ToniA/arduino-heatpumpir). + + +### Protocol=PULSE_DISTANCE +If you get something like this: +``` +PULSE_DISTANCE: HeaderMarkMicros=8900 HeaderSpaceMicros=4450 MarkMicros=550 OneSpaceMicros=1700 ZeroSpaceMicros=600 NumberOfBits=56 0x43D8613C 0x3BC3BC +``` +then you have a code consisting of **56 bits**, which is probably from an air conditioner remote.
+You can send it with `sendPulseDistanceWidth()`. +```c++ +uint32_t tRawData[] = { 0xB02002, 0xA010 }; +IrSender.sendPulseDistance(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, false, 0, 0); +``` +You can send it with calling `sendPulseDistanceWidthData()` twice, once for the first 32 bit and next for the remaining 24 bits.
+The `PULSE_DISTANCE` / `PULSE_WIDTH` decoder just decodes a timing stream to a bitstream stored as hex values. +These decoders can not put any semantics like address, command or checksum on this bitstream. +But the bitstream is way more readable, than a timing stream. This bitstream is read **LSB first by default**. +If LSB does not suit for further research, you can change it [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_DistanceProtocol.hpp#L78). + +**If RAM is not more than 2k, the decoder only accepts mark or space durations up to 2500 microseconds to save RAM space, otherwise it accepts durations up to 10 ms.** + +### Protocol=UNKNOWN +If you see something like `Protocol=UNKNOWN Hash=0x13BD886C 35 bits received` as output of e.g. the ReceiveDemo example, you either have a problem with decoding a protocol, or an unsupported protocol. + +- If you have an **odd number of bits** received, your receiver circuit probably has problems. Maybe because the IR signal is too weak. +- If you see timings like `+ 600,- 600 + 550,- 150 + 200,- 100 + 750,- 550` then one 450 µs space was split into two 150 and 100 µs spaces with a spike / error signal of 200 µs between. Maybe because of a defective receiver or a weak signal in conjunction with another light emitting source nearby. +- If you see timings like `+ 500,- 550 + 450,- 550 + 450,- 500 + 500,-1550`, then marks are generally shorter than spaces and therefore `MARK_EXCESS_MICROS` (specified in your ino file) should be **negative** to compensate for this at decoding. +- If you see `Protocol=UNKNOWN Hash=0x0 1 bits received` it may be that the space after the initial mark is longer than [`RECORD_GAP_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.h#L124). + This was observed for some LG air conditioner protocols. Try again with a line e.g. `#define RECORD_GAP_MICROS 12000` before the line `#include ` in your .ino file. +- To see more info supporting you to find the reason for your UNKNOWN protocol, you must enable the line `//#define DEBUG` in IRremoteInt.h. + +### How to deal with protocols not supported by IRremote +If you do not know which protocol your IR transmitter uses, you have several choices. +- Just use the hash value to decide which command was received. See the [SimpleReceiverForHashCodes example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino). +- Use the [IRreceiveDemo example](examples/ReceiveDemo) or [IRreceiveDump example](examples/ReceiveDump) to dump out the IR timing. + You can then reproduce/send this timing with the [SendRawDemo example](examples/SendRawDemo). +- The [IRMP AllProtocol example](https://github.com/IRMP-org/IRMP#allprotocol-example) prints the protocol and data for one of the **[40 supported protocols](https://github.com/IRMP-org/IRMP?tab=readme-ov-file#list-of-protocols)**. + The same library can be used to send this codes. +- If you have a bigger Arduino board at hand (> 100 kByte program memory) you can try the + [IRremoteDecode example](https://github.com/bengtmartensson/Arduino-DecodeIR/blob/master/examples/IRremoteDecode/IRremoteDecode.ino) of the Arduino library [DecodeIR](https://github.com/bengtmartensson/Arduino-DecodeIR). +- Use [IrScrutinizer](http://www.harctoolbox.org/IrScrutinizer.html). + It can automatically generate a send sketch for your protocol by exporting as "Arduino Raw". It supports IRremote, + the old [IRLib](https://github.com/cyborg5/IRLib) and [Infrared4Arduino](https://github.com/bengtmartensson/Infrared4Arduino).
# Sending IR codes -If you have a device at hand which can generate the IR codes you want to work with (aka IR remote), **it is recommended** to receive the codes with the [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino), which will tell you on the serial output how to send them. +If you have a device at hand which can generate the IR codes you want to work with (aka IR remote), +**it is recommended** to receive the codes with the [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino), which will tell you on the serial output how to send them. ``` Protocol=LG Address=0x2 Command=0x3434 Raw-Data=0x23434E 28 bits MSB first @@ -395,21 +574,49 @@ Send with: IrSender.sendLG(0x2, 0x3434, ); You will discover that **the address is a constant** and the commands sometimes are sensibly grouped.
If you are uncertain about the numbers of repeats to use for sending, **3** is a good starting point. If this works, you can check lower values afterwards. -If you have enabled `DECODE_DISTANCE_WIDTH`, the code printed by `printIRSendUsage()` **differs between 8 and 32 bit platforms**, so it is best to run the receiving program on the same platform as the sending program. - -The codes found in the [irdb database](https://github.com/probonopd/irdb/tree/master/codes) specify a **device**, a **subdevice** and a **function**. Most of the times, *device* and *subdevice* can be taken as upper and lower byte of the **address parameter** and *function* is the **command parameter** for the **new structured functions** with address, command and repeat-count parameters like e.g. `IrSender.sendNEC((device << 8) | subdevice, 0x19, 2)`.
-An **exact mapping** can be found in the [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols). "D" and "S" denotes device and subdevice and "F" denotes the function. +If you have enabled `DECODE_DISTANCE_WIDTH`, the code printed by `printIRSendUsage()` **differs between 8 and 32 bit platforms**, +so it is best to run the receiving program on the same platform as the sending program. **All sending functions support the sending of repeats** if sensible. Repeat frames are sent at a fixed period determined by the protocol. e.g. 110 ms from start to start for NEC.
Keep in mind, that **there is no delay after the last sent mark**. If you handle the sending of repeat frames by your own, you must insert sensible delays before the repeat frames to enable correct decoding. -The old send*Raw() functions for sending like e.g. `IrSender.sendNECRaw(0xE61957A8,2)` are kept for backward compatibility to **(old)** tutorials and unsupported as well as error prone. +Bear in mind, that **some devices only accept commands if they are repeated one or two times**. + +Sending **old MSB codes without conversion** can be done by using `sendNECMSB()`, `sendSonyMSB()`, `sendSamsungMSB()`, `sendJVCMSB()` +or by [converting them manually to LSB](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes). + +## Sending UNKNOWN protocol +If the protocol is unknown by IRremote, which often is the case for airconditioner codes, +you can store the timing sequence in an array and send it with IrSender.sendRaw() or IrSender.sendRaw_P() +like done in [SendDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendDemo/SendDemo.ino#L180). +Do not forget to send repeats. + +## Sending IRDB IR codes +The codes found in the [Flipper-IRDB database](https://github.com/Lucaslhm/Flipper-IRDB) are quite straightforward to convert, because the also use the address / command scheme.
+Protocol matching is NECext -> Onkyo, Samsung32 -> Samsung, SIRC20 -> Sony with 20 bits etc. + +The codes found in the [irdb database](https://github.com/probonopd/irdb/tree/master/codes) specify a **device**, a **subdevice** and a **function**. +Most of the times, *device* and *subdevice* can be taken as upper and lower byte of the **address parameter** and *function* +is the **command parameter** for the **new structured functions** with address, command and repeat-count parameters +like e.g. `IrSender.sendNEC((device << 8) | subdevice, 0x19, 2)`.
+An **exact mapping** can be found in the [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols). +"D" and "S" denotes device and subdevice and "F" denotes the function. ## Send pin -Any pin can be choosen as send pin, because the PWM signal is generated by default with software bit banging, since `SEND_PWM_BY_TIMER` is not active. -If `IR_SEND_PIN` is specified (as c macro), it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro. Then you can change send pin at any time before sending an IR frame. See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). +Any pin can be chosen as send pin as long as `IR_SEND_PIN` is **not** defined. +This is because the PWM signal is generated by default with software bit banging, since `SEND_PWM_BY_TIMER` is not active.
+On **ESP32** ledc channel 0 is used for generating the IR PWM.
+If `IR_SEND_PIN` is specified (as C macro), it reduces program size and improves send timing for AVR. +If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro e.g. with `#undef IR_SEND_PIN`. +Then you can change send pin at any time before sending an IR frame. +See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). + +## Polarity of send pin +By default the polarity is HIGH for active and LOW for inactive or pause. +This corresponds to the connection schema: Pin -> Resistor-> IR-LED -> Ground.
+If you want to use the connection schema: VCC -> IR-LED -> Resistor -> Pin, you must specify `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN`. ### List of public IR code databases http://www.harctoolbox.org/IR-resources.html @@ -428,9 +635,17 @@ http://www.harctoolbox.org/IR-resources.html # Tiny NEC receiver and sender -For applications only requiring NEC, NEC variants or FAST -see below- protocol, there is a special receiver / sender included,
+For applications only requiring NEC, NEC variants or FAST -see below- protocol, there is a special receiver / sender included, which has very **small code size of 500 bytes and does NOT require any timer**. +## Principle of operation +Instead of sampling the input every 50 µs as IRremote does, TinyReceiver receiver uses a **pin change interrupt** for on-the-fly decoding which limits the choice of protocols.
+On each level change, the level and the time since the last change are used to incrementally decode the protocol.
+With this operating principle, we **cannot wait for a timeout** and then decode the protocol as IRremote does.
+Instead, we need to know which is the last bit (level change) of a protocol to do the final decoding +and the call of the optional **user provided callback function** `handleReceivedTinyIRData()`.
+This means, **we need to know the number of bits in a protocol** and therefore the protocol (family). + Check out the [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tinyreceiver--tinysender) and [IRDispatcherDemo](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#irdispatcherdemo) examples.
Take care to include `TinyIRReceiver.hpp` or `TinyIRSender.hpp` instead of `IRremote.hpp`. @@ -445,10 +660,10 @@ void setup() { } void loop() { - if (TinyIRReceiverData.justWritten) { - TinyIRReceiverData.justWritten = false; + if (TinyReceiverDecode()) { printTinyReceiverResultMinimal(&Serial); } + // No resume() required :-) } ``` @@ -505,10 +720,23 @@ void loop() {} The FAST protocol can be received by IRremote and TinyIRReceiver. # FAQ and hints +## Receiving stops after analogWrite() or tone() or after running a motor. +The receiver sample interval of 50 µs is generated by a timer. On many boards this must be a [hardware timer](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). +On some boards where a software timer is available, the software timer is used.
+Be aware that the hardware timer used for receiving should not be used for `analogWrite()`.
+Especially **motor** control often uses the `analogWrite()` function and will therefore stop the receiving if used on the pins indicated [here](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage).
+On the Uno and other AVR boards the receiver timer ist the same as the tone timer. Thus receiving will stop after a `tone()` command. +See [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298) how to deal with it, i.e. how to use `IrReceiver.restartTimer()`. + +## Receiving sets overflow flag. +The flag `IRDATA_FLAGS_WAS_OVERFLOW` is set, if `RAW_BUFFER_LENGTH` is too small for all the marks and spaces of the protocol. +This can happen on long protocol frames like the ones from air conditioner. +It also can happen, if `RECORD_GAP_MICROS` is smaller than the real gap between a frame and the repetition frame, thus interpreting both as one consecutive frame. +Best is to dump the timing then, to see which reason holds. ## Problems with Neopixels, FastLed etc. IRremote will not work right when you use **Neopixels** (aka WS2811/WS2812/WS2812B) or other libraries blocking interrupts for a longer time (> 50 µs).
-Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs. +Whether you use the Adafruit Neopixel library, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs. In turn, this stops the IR interrupt handler from running when it needs to. See also this [video](https://www.youtube.com/watch?v=62-nEJtm070). One **workaround** is to wait for the IR receiver to be idle before you send the Neopixel data with `if (IrReceiver.isIdle()) { strip.show();}`.
@@ -521,11 +749,19 @@ There are some other solutions to this on more powerful processors, This is often due to **timer resource conflicts** with the other library. Please see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). ## Multiple IR receivers -IRreceiver consists of one timer triggered function reading the digital IR signal value from one pin every 50 µs.
-So **multiple IR receivers** can only be used by connecting the output pins of several IR receivers together. -The IR receivers use an NPN transistor as output device with just a 30k resistor to VCC. -This is almost "open collector" and allows connecting of several output pins to one Arduino input pin.
-But keep in mind, that any weak / disturbed signal from one of the receivers will in turn also disturb a good signal from another one. +**This library now supports multiple IR receiver instances (IRrecv) per CPU** see the [MultipleReceivers example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/MultipleReceivers/MultipleReceivers.ino). + +There is also another way to use multiple receivers. +You can connect the output pins of each IR receiver to one input pin. +Then you can use the standard receiver object (IrReceiver). +The IR receiver modules internally use an NPN transistor as output device with just a 30k resistor to VCC. +This is basically an "open collector" and allows multiple output pins to be connected to one Arduino input pin.
+However, keep in mind that any weak / disturbed signal from one of the receivers will also interfere with a good signal from another receiver. + +## Multiple IR sender instances +**This library only supports one IR sender object (IRsend) per CPU.**
+However since sending is a serial task, you can use `setSendPin()` to switch the pin to send, thus emulating multiple sender.
+ ## Increase strength of sent output signal **The best way to increase the IR power for free** is to use 2 or 3 IR diodes in series. One diode requires 1.2 volt at 20 mA or 1.5 volt at 100 mA so you can supply up to 3 diodes with a 5 volt output.
@@ -536,10 +772,15 @@ If you do not require more current than 20 mA, there is no need to use an extern On my Arduino Nanos, I always use a 100 Ω series resistor and one IR LED :grinning:. +## Simulate an IR receiver module +To simulate an IR receiver (such as the TSOP1738) which provides an active-low output, you only need to enable the `USE_NO_SEND_PWM` macro. +In the case you must simulate exotic IR receivers, which provides an active-high output, you also need to enable the `USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM` macro. + ## Minimal CPU clock frequency For receiving, the **minimal CPU clock frequency is 4 MHz**, since the 50 µs timer ISR (Interrupt Service Routine) takes around 12 µs on a 16 MHz ATmega.
-The TinyReceiver, which reqires no polling, runs with 1 MHz.
-For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`. +The TinyReceiver, which requires no polling, runs with 1 MHz.
+For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. +You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`. ## Bang & Olufsen protocol The Bang & Olufsen protocol decoder is not enabled by default, i.e if no protocol is enabled explicitly by #define `DECODE_`. It must always be enabled explicitly by `#define DECODE_BEO`. @@ -547,135 +788,97 @@ This is because it has an **IR transmit frequency of 455 kHz** and therefore req And because **generating a 455 kHz PWM signal is currently only implemented for `SEND_PWM_BY_TIMER`**, sending only works if `SEND_PWM_BY_TIMER` or `USE_NO_SEND_PWM` is defined.
For more info, see [ir_BangOlufsen.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_BangOlufsen.hpp#L44). -# Handling unknown Protocols -## Disclaimer -**This library was designed to fit inside MCUs with relatively low levels of resources and was intended to work as a library together with other applications which also require some resources of the MCU to operate.** - -For **air conditioners** [see this fork](https://github.com/crankyoldgit/IRremoteESP8266), which supports an impressive set of protocols and a lot of air conditioners. - -For **long signals** see the blog entry: ["Recording long Infrared Remote control signals with Arduino"](https://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino). - - -## Protocol=PULSE_DISTANCE -If you get something like this: -``` -PULSE_DISTANCE: HeaderMarkMicros=8900 HeaderSpaceMicros=4450 MarkMicros=550 OneSpaceMicros=1700 ZeroSpaceMicros=600 NumberOfBits=56 0x43D8613C 0x3BC3BC -``` -then you have a code consisting of **56 bits**, which is probably from an air conditioner remote.
-You can send it with sendPulseDistance(). -```c++ -uint32_t tRawData[] = { 0xB02002, 0xA010 }; -IrSender.sendPulseDistance(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, false, 0, 0); -``` -You can send it with calling sendPulseDistanceWidthData() twice, once for the first 32 bit and next for the remaining 24 bits.
-**The PulseDistance or PulseWidth decoders just decode a timing steam to a bit stream**. -They can not put any semantics like address, command or checksum on this bitstream, since it is no known protocol. -But the bitstream is way more readable, than a timing stream. This bitstream is read **LSB first by default**. -If this does not suit you for further research, you can change it [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_DistanceProtocol.hpp#L48). - -## Protocol=UNKNOWN -If you see something like `Protocol=UNKNOWN Hash=0x13BD886C 35 bits received` as output of e.g. the ReceiveDemo example, you either have a problem with decoding a protocol, or an unsupported protocol. - -- If you have an **odd number of bits** received, your receiver circuit probably has problems. Maybe because the IR signal is too weak. -- If you see timings like `+ 600,- 600 + 550,- 150 + 200,- 100 + 750,- 550` then one 450 µs space was split into two 150 and 100 µs spaces with a spike / error signal of 200 µs between. Maybe because of a defective receiver or a weak signal in conjunction with another light emitting source nearby. -- If you see timings like `+ 500,- 550 + 450,- 550 + 500,- 500 + 500,-1550`, then marks are generally shorter than spaces and therefore `MARK_EXCESS_MICROS` (specified in your ino file) should be **negative** to compensate for this at decoding. -- If you see `Protocol=UNKNOWN Hash=0x0 1 bits received` it may be that the space after the initial mark is longer than [`RECORD_GAP_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.h#L124). - This was observed for some LG air conditioner protocols. Try again with a line e.g. `#define RECORD_GAP_MICROS 12000` before the line `#include ` in your .ino file. -- To see more info supporting you to find the reason for your UNKNOWN protocol, you must enable the line `//#define DEBUG` in IRremoteInt.h. - -## How to deal with protocols not supported by IRremote -If you do not know which protocol your IR transmitter uses, you have several choices. -- Use the [IRreceiveDump example](examples/ReceiveDump) to dump out the IR timing. - You can then reproduce/send this timing with the [SendRawDemo example](examples/SendRawDemo). - For **long codes** with more than 48 bits like from air conditioners, you can **change the length of the input buffer** in [IRremote.h](src/IRremoteInt.h#L36). -- The [IRMP AllProtocol example](https://github.com/IRMP-org/IRMP#allprotocol-example) prints the protocol and data for one of the **40 supported protocols**. - The same library can be used to send this codes. -- If you have a bigger Arduino board at hand (> 100 kByte program memory) you can try the - [IRremoteDecode example](https://github.com/bengtmartensson/Arduino-DecodeIR/blob/master/examples/IRremoteDecode/IRremoteDecode.ino) of the Arduino library [DecodeIR](https://github.com/bengtmartensson/Arduino-DecodeIR). -- Use [IrScrutinizer](http://www.harctoolbox.org/IrScrutinizer.html). - It can automatically generate a send sketch for your protocol by exporting as "Arduino Raw". It supports IRremote, - the old [IRLib](https://github.com/cyborg5/IRLib) and [Infrared4Arduino](https://github.com/bengtmartensson/Infrared4Arduino). - -
- # Examples for this library The examples are available at File > Examples > Examples from Custom Libraries / IRremote.
- In order to fit the examples to the 8K flash of ATtiny85 and ATtiny88, the [Arduino library ATtinySerialOut](https://github.com/ArminJo/ATtinySerialOut) is required for this CPU's. +**In order to fit the examples to the 8K flash of ATtiny85 and ATtiny88, the [Arduino library ATtinySerialOut](https://github.com/ArminJo/ATtinySerialOut) is required for this CPU's.**
+See also [DroneBot Workshop SimpleReceiver](https://dronebotworkshop.com/ir-remotes/#SimpleReceiver_Example_Code) and [SimpleSender](https://dronebotworkshop.com/ir-remotes/#SimpleSender_Example_Code). -#### SimpleReceiver + SimpleSender -The **[SimpleReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino)** and **[SimpleSender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/SimpleSender.ino)** examples are a good starting point. +### [SimpleReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino) + [SimpleSender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/SimpleSender.ino) +The **SimpleReceiver** and **SimpleSender** examples are a good starting point. A simple example can be tested online with [WOKWI](https://wokwi.com/projects/338611596994544210). -#### TinyReceiver + TinySender +### [SimpleReceiverForHashCodes](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino) +The **SimpleReceiverForHashCodes** uses only the hash decoder. +It converts all IR frames longer than 6 to a 32 bit hash code, thus enabling receiving of unknown protocols.
+See: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html + +### [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinyReceiver/TinyReceiver.ino) + [TinySender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinySender/TinySender.ino) If **code size** or **timer usage** matters, look at these examples.
-The **[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinyReceiver/TinyReceiver.ino)** example uses the **TinyIRReceiver** library +The **TinyReceiver** example uses the **TinyIRReceiver** library which can **only receive NEC, Extended NEC, ONKYO and FAST protocols, but does not require any timer**. They use pin change interrupt for on the fly decoding, which is the reason for the restricted protocol choice.
TinyReceiver can be tested online with [WOKWI](https://wokwi.com/arduino/projects/339264565653013075). -The **[TinySender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinySender/TinySender.ino)** example uses the **TinyIRSender** library which can **only send NEC, ONKYO and FAST protocols**.
+The **TinySender** example uses the **TinyIRSender** library which can **only send NEC, ONKYO and FAST protocols**.
It sends NEC protocol codes in standard format with 8 bit address and 8 bit command as in SimpleSender example. It has options to send using Extended NEC, ONKYO and FAST protocols. Saves 780 bytes program memory and 26 bytes RAM compared to SimpleSender, which does the same, but uses the IRRemote library (and is therefore much more flexible). -#### SmallReceiver +### [SmallReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino) If the protocol is not NEC and code size matters, look at this [example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SmallReceiver/SmallReceiver.ino).
-#### ReceiveDemo + AllProtocolsOnLCD -[ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino) receives all protocols and **generates a beep with the Arduino tone() function** on each packet received.
+### [ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino) + [AllProtocolsOnLCD](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino) +ReceiveDemo receives all protocols and **generates a beep with the Arduino tone() function** on each packet received.
Long press of one IR button (receiving of multiple repeats for one command) is detected.
-[AllProtocolsOnLCD](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino) additionally **displays the short result on a 1602 LCD**. The LCD can be connected parallel or serial (I2C).
+AllProtocolsOnLCD additionally **displays the short result on a 1602 LCD**. +The LCD can be connected parallel or serial (I2C).
By connecting debug pin to ground, you can force printing of the raw values for each frame. The pin number of the debug pin is printed during setup, because it depends on board and LCD connection type.
This example also serves as an **example how to use IRremote and tone() together**. -#### ReceiveDump -Receives all protocols and dumps the received signal in different flavors including Pronto format. Since the printing takes much time, repeat signals may be skipped or interpreted as UNKNOWN. +#### [ReceiveDump](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDump/ReceiveDump.ino) +Receives all protocols and dumps the received signal in different flavors including **Pronto format**.
+Since the printing takes much time, repeat signals may be skipped or interpreted as UNKNOWN. -#### SendDemo +#### [SendDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendDemo/SendDemo.ino) Sends all available protocols at least once. -#### SendAndReceive +#### [MultipleSendPins](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/MultipleSendPins/MultipleSendPins.ino) +Demonstrates sending IR codes toggling between 2 **different send pins**. + +#### [SendAndReceive](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendAndReceive/SendAndReceive.ino) Demonstrates **receiving while sending**. -#### ReceiveAndSend -Record and **play back last received IR signal** at button press. IR frames of known protocols are sent by the approriate protocol encoder. `UNKNOWN` protocol frames are stored as raw data and sent with `sendRaw()`. +#### [ReceiveAndSend](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveAndSend/ReceiveAndSend.ino) +Record and **play back last received IR signal** at button press. IR frames of known protocols are sent by the appropriate protocol encoder. +`UNKNOWN` protocol frames are stored as raw data and sent with `sendRaw()`. -#### ReceiveAndSendDistanceWidth +#### [ReceiveAndSendDistanceWidth](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveAndSendDistanceWidth/ReceiveAndSendDistanceWidth.ino) Try to decode each IR frame with the *universal* **DistanceWidth decoder**, store the data and send it on button press with `sendPulseDistanceWidthFromArray()`.
+If RAM is not more than 2k, the decoder only accepts mark or space durations up to 2500 microseconds to save RAM space, otherwise it accepts durations up to 10 ms.
Storing data for distance width protocol requires 17 bytes. The ReceiveAndSend example requires 16 bytes for known protocol data and 37 bytes for raw data of e.g.NEC protocol. -#### ReceiveOneAndSendMultiple +#### [ReceiveOneAndSendMultiple](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveOneAndSendMultiple/ReceiveOneAndSendMultiple.ino) Serves as a IR **remote macro expander**. Receives Samsung32 protocol and on receiving a specified input frame, it sends multiple Samsung32 frames with appropriate delays in between. This serves as a **Netflix-key emulation** for my old Samsung H5273 TV. -#### IRDispatcherDemo +#### [IRDispatcherDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/IRDispatcherDemo/IRDispatcherDemo.ino) Framework for **calling different functions of your program** for different IR codes. -#### IRrelay +#### [ControlRelay](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ControlRelay/ControlRelay.ino) **Control a relay** (connected to an output pin) with your remote. -#### IRremoteExtensionTest -[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino) for a user defined class, which itself uses the IRrecv class from IRremote. +#### [IRremoteExtensionTest](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino) +Example for a user defined class, which itself uses the IRrecv class from IRremote. -#### SendLGAirConditionerDemo -[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino) for sending LG air conditioner IR codes controlled by Serial input.
+#### [SendLGAirConditionerDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino) +Example for sending LG air conditioner IR codes controlled by Serial input.
By just using the function `bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter)` you can control the air conditioner by any other command source.
The file *acLG.h* contains the command documentation of the LG air conditioner IR protocol. Based on reverse engineering of the LG AKB73315611 remote. ![LG AKB73315611 remote](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/LG_AKB73315611.jpg)
IReceiverTimingAnalysis can be tested online with [WOKWI](https://wokwi.com/projects/299033930562011656) Click on the receiver while simulation is running to specify individual IR codes. -#### ReceiveAndSendHob2Hood -[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendLGAirConditionerDemo/ReceiveAndSendHobToHood.ino) for receiving and sending AEG / Elektrolux Hob2Hood protocol.
+#### [ReceiveAndSendHob2Hood](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino) +Example for receiving and sending AEG / Elektrolux Hob2Hood protocol.
-#### ReceiverTimingAnalysis -This [example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino) analyzes the signal delivered by your IR receiver module. +#### [ReceiverTimingAnalysis](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino) +This example analyzes the signal delivered by your IR receiver module. Values can be used to determine the stability of the received signal as well as a hint for determining the protocol.
It also computes the `MARK_EXCESS_MICROS` value, which is the extension of the mark (pulse) duration introduced by the IR receiver module.
It can be tested online with [WOKWI](https://wokwi.com/arduino/projects/299033930562011656). Click on the receiver while simulation is running to specify individual NEC IR codes. -#### UnitTest +#### [UnitTest](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/UnitTest/UnitTest.ino) ReceiveDemo + SendDemo in one program. Demonstrates **receiving while sending**. Here you see the delay of the receiver output (blue) from the IR diode input (yellow). ![Delay](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_UnitTest_delay.bmp) @@ -687,12 +890,20 @@ Here you see the delay of the receiver output (blue) from the IR diode input (ye - [ReceiverTimingAnalysis](https://wokwi.com/projects/299033930562011656) - [Receiver with LCD output and switch statement](https://wokwi.com/projects/298934082074575369) +# IR control of a robot car +This [example](https://github.com/ArminJo/PWMMotorControl?tab=readme-ov-file#basicircontrol) of the **Arduino PWMMotorControl library** controls the basic functions of a robot car using the IRremote library.
+It controls 2 PWM motor channels, 2 motors at each channel.
+[Here](https://www.instructables.com/Arduino-4WD-Car-Assembly-and-Code-With-Optional-In/) you can find the instructable for car assembly and code.
+ +IR_RobotCar with TL1838 IR receiver plugged into expansion board.
+![IR_RobotCar](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_RobotCar.jpg) +
# Issues and discussions - Do not open an issue without first testing some of the examples! - If you have a problem, please post the MCVE (Minimal Complete Verifiable Example) showing this problem. My experience is, that most of the times you will find the problem while creating this MCVE :smile:. -- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); **it helps us help you when we can read your code!** +- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); **it helps us to help you when we can read your code!**
@@ -703,40 +914,48 @@ Modify them by enabling / disabling them, or change the values if applicable. | Name | Default value | Description | |-|-:|-| -| `RAW_BUFFER_LENGTH` | 100 | Buffer size of raw input buffer. Must be even! 100 is sufficient for *regular* protocols of up to 48 bits, but for most air conditioner protocols a value of up to 750 is required. Use the ReceiveDump example to find smallest value for your requirements. | -| `EXCLUDE_UNIVERSAL_PROTOCOLS` | disabled | Excludes the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) from `decode()`. Saves up to 1000 bytes program memory. | -| `DECODE_` | all | Selection of individual protocol(s) to be decoded. You can specify multiple protocols. See [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.hpp#L98-L121) | -| `DECODE_STRICT_CHECKS` | disabled | Check for additional required characteristics of protocol timing like length of mark for a constant mark protocol, where space length determines the bit value. Requires up to 194 additional bytes of program memory. | +| `RAW_BUFFER_LENGTH` | 200 | Buffer size of raw input uint16_t buffer. Must be even! If it is too small, overflow flag will be set. 100 is sufficient for *regular* protocols of up to 48 bits, but for most air conditioner protocols a value of up to 750 is required. Use the ReceiveDump example to find smallest value for your requirements. A value of 200 requires 200 bytes RAM. | +| `EXCLUDE_UNIVERSAL_PROTOCOLS` | disabled | Excludes the universal decoder for pulse distance width protocols and decodeHash (special decoder for all protocols) from `decode()`. Saves up to 1000 bytes program memory. | +| `EXCLUDE_EXOTIC_PROTOCOLS` | disabled | Excludes BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF from `decode()` and from sending with `IrSender.write()`. Saves up to 650 bytes program memory. | +| `DECODE_` | all | Selection of individual protocol(s) to be decoded. You can specify multiple protocols. See [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.hpp#L98-L121) | +| `USE_THRESHOLD_DECODER` | disabled | If enabled, may give slightly better results especially for jittering signals and protocols with short 1 pulses / pauses and forces value of MARK_EXCESS_MICROS to 0 to save program memory. Requires up to additional 120 bytes program memory. | +| `USE_STRICT_DECODER` | disabled | Check for all 4 one and zero protocol timings. Only sensible for development or very exotic requirements. Requires up to 300 additional bytes of program memory. | | `IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK` | disabled | Saves up to 60 bytes of program memory and 2 bytes RAM. | -| `MARK_EXCESS_MICROS` | 20 | MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules. | -| `RECORD_GAP_MICROS` | 5000 | Minimum gap between IR transmissions, to detect the end of a protocol.
Must be greater than any space of a protocol e.g. the NEC header space of 4500 µs.
Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 24 ms.
Keep in mind, that this is the delay between the end of the received command and the start of decoding. | -| `IR_INPUT_IS_ACTIVE_HIGH` | disabled | Enable it if you use a RF receiver, which has an active HIGH output signal. | -| `IR_SEND_PIN` | disabled | If specified, it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must not use / disable this macro in your source. | -| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the advantage of more exact PWM generation, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the disadvantage of using a hardware timer, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM gereration for each pin without using a shared resource (timer). | -| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | -| `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. | -| `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! | -| `DISABLE_CODE_FOR_RECEIVER` | disabled | Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. | -| `EXCLUDE_EXOTIC_PROTOCOLS` | disabled | Excludes BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF from `decode()` and from sending with `IrSender.write()`. Saves up to 650 bytes program memory. | -| `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. | -| `NO_LED_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send and receive. Saves around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR (Interrupt Service Routine) processing time. | -| `MICROS_PER_TICK` | 50 | Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 µs at 38 kHz. | -| `TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING` | 25 | Relative tolerance (in percent) for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding. | +| `MARK_EXCESS_MICROS` | 0 | MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules. Is set to 20 if `DO_NOT_USE_THRESHOLD_DECODER` is enabled. | +| `RECORD_GAP_MICROS` | 5000 | Minimum gap between IR transmissions, to detect the end of a protocol.
Must be greater than any space of a protocol e.g. the NEC header space of 4500 µs.
Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 24 ms.
Keep in mind, that this is the delay between the end of the received command and the start of decoding. | +| `DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE` | 50 if RAM <= 2k, else 200 | A value of 200 allows to decode mark or space durations up to 10 ms. | +| `IR_INPUT_IS_ACTIVE_HIGH` | disabled | Enable it if you use a RF receiver, which has an active HIGH output signal. | +| `IR_SEND_PIN` | disabled | If specified, it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must not use / disable this macro in your source. | +| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the **advantage of more exact PWM generation**, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the **disadvantage of using a hardware timer**, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM generation for each pin without using a shared resource (timer). | +| `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. | +| `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN` | disabled | Reverses the polarity of space level for PWM at the send pin, i.e. space is high. Can be used to connect IR LED between VCC and the send pin. It is like open drain but with electrical active high in its logical inactive state. | +| `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode for PWM at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! | +| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | +| `USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM` | disabled | Only evaluated if `USE_NO_SEND_PWM` is enabled. Simulate an **active high** receiver signal instead of an active low signal. | +| `DISABLE_CODE_FOR_RECEIVER` | disabled | Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. | +| `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. | +| `NO_LED_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send and receive. Saves around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR (Interrupt Service Routine) processing time. | +| `NO_LED_RECEIVE_FEEDBACK_CODE` | disabled | Disables the LED feedback code for receive. Saves around 100 bytes program memory for receiving and halving the receiver ISR (Interrupt Service Routine) processing time. | +| `NO_LED_SEND_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send. Saves around 322 bytes for sending. | +| `MICROS_PER_TICK` | 50 | Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 µs at 38 kHz. | +| `TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT` | 25 | Relative tolerance for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding. | | `DEBUG` | disabled | Enables lots of lovely debug output. | | `IR_USE_AVR_TIMER*` | | Selection of timer to be used for generating IR receiving sample interval. | These next macros for **TinyIRReceiver** must be defined in your program before the line `#include ` to take effect. | Name | Default value | Description | |-|-:|-| -| `IR_RECEIVE_PIN` | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. | +| `IR_RECEIVE_PIN` | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. Not used in IRremote. | | `IR_FEEDBACK_LED_PIN` | `LED_BUILTIN` | The pin number for TinyIRReceiver feedback LED, which gets compiled in. | -| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED function. Saves 14 bytes program memory. | -| `DISABLE_PARITY_CHECKS` | disabled | Disables the addres and command parity checks. Saves 48 bytes program memory. | +| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED code for send and receive. Saves 14 bytes program memory. | +| `NO_LED_RECEIVE_FEEDBACK_CODE` | disabled | Disables the LED feedback code for receive. | +| `NO_LED_SEND_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send. | +| `DISABLE_PARITY_CHECKS` | disabled | Disables the address and command parity checks. Saves 48 bytes program memory. | | `USE_EXTENDED_NEC_PROTOCOL` | disabled | Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | | `USE_ONKYO_PROTOCOL` | disabled | Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | | `USE_FAST_PROTOCOL` | disabled | Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC. | | `ENABLE_NEC2_REPEATS` | disabled | Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. | -| `USE_CALLBACK_FOR_TINY_RECEIVER` | disabled | Call the fixed function `void handleReceivedTinyIRData()` each time a frame or repeat is received. | +| `USE_CALLBACK_FOR_TINY_RECEIVER` | disabled | Call the user provided function `void handleReceivedTinyIRData()` each time a frame or repeat is received. | The next macro for **IRCommandDispatcher** must be defined in your program before the line `#include ` to take effect. | `USE_TINY_IR_RECEIVER` | disabled | Use [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tinyreceiver--tinysender) for receiving IR codes. | @@ -761,9 +980,11 @@ If you are using [Sloeber](https://eclipse.baeyens.it) as your IDE, you can easi # Supported Boards **Issues and discussions with the content "Is it possible to use this library with the ATTinyXYZ? / board XYZ" without any reasonable explanations will be immediately closed without further notice.**
+For **ESP8266/ESP32**, [the IRremoteESP8266 library](https://github.com/crankyoldgit/IRremoteESP8266) supports an [impressive set of protocols and a lot of air conditioners](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md)
+**ATtiny CPU's are tested with the [Arduino library ATtinySerialOut](https://github.com/ArminJo/ATtinySerialOut) library**.

Digispark boards are only tested with [ATTinyCore](https://github.com/SpenceKonde/ATTinyCore) using `New Style` pin mapping for the Digispark Pro board.
-ATtiny boards are only tested with [ATTinyCore](https://github.com/SpenceKonde/ATTinyCore#supported-devices) or [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore). +**ATtiny boards** are tested with **[ATTinyCore](https://github.com/SpenceKonde/ATTinyCore#supported-devices) or [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore) only**. - Arduino Uno / Mega / Leonardo / Duemilanove / Diecimila / LilyPad / Mini / Fio / Nano etc. - Arduino Uno R4, but not yet tested, because of lack of a R4 board. **Sending does not work** on the `arduino:renesas_uno:unor4wifi`. @@ -775,15 +996,15 @@ ATtiny boards are only tested with [ATTinyCore](https://github.com/SpenceKonde/A - ATmega4809 (Nano every) - ATtiny3217 (Tiny Core 32 Dev Board) - ATtiny84, 85, 167 (Digispark + Digispark Pro) -- SAMD21 (Zero, MKR*, **but not SAMD51 and not DUE, the latter is SAM architecture**) +- SAMD (Zero, MKR*, **but not DUE, the latter is SAM architecture**) - ESP8266 -- ESP32 (ESP32-C3 since board package 2.0.2 from Espressif) **not for ESP32 core version > 3.0.0** +- ESP32 (ESP32-C3 since board package 2.0.2 from Espressif). New CPUs (as of January 2025) are not guaranteed to work! - Sparkfun Pro Micro - Nano Every, Uno WiFi Rev2, nRF5 BBC MicroBit, Nano33_BLE - BluePill with STM32 - RP2040 based boards (Raspberry Pi Pico, Nano RP2040 Connect etc.) -For ESP8266/ESP32, [this library](https://github.com/crankyoldgit/IRremoteESP8266) supports an [impressive set of protocols and a lot of air conditioners](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md) + We are open to suggestions for adding support to new boards, however we highly recommend you contact your supplier first and ask them to provide support from their side.
If you can provide **examples of using a periodic timer for interrupts** for the new board, and the board name for selection in the Arduino IDE, then you have way better chances to get your board supported by IRremote. @@ -791,7 +1012,9 @@ If you can provide **examples of using a periodic timer for interrupts** for the
# Timer and pin usage -The **receiver sample interval of 50 µs is generated by a timer**. On many boards this must be a hardware timer. On some boards where a software timer is available, the software timer is used. +The **receiver sample interval of 50 µs is generated by a timer**. On many boards this must be a hardware timer. On some boards where a software timer is available, the software timer is used.
+On **ESP8266** `timer1` is used for receive interrupts, which makes it incompatible to the Servo and other libraries.
+On **ESP32** `hw_timer_t` is used for receive interrupts. Every pin can be used for receiving.
If software PWM is selected, which is default, every pin can also be used for sending. Sending with software PWM does not require a timer! @@ -820,9 +1043,10 @@ The code for the timer and the **timer selection** is located in [private/IRTime | ATmega168, **ATmega328** | 1, **2** | 9, **3** | 9 & 10, **3 & 11** | | ATmega1280, **ATmega2560** | 1, **2**, 3, 4, 5 | 5, 6, **9**, 11, 46 | 5, 6, **9**, 11, 46 | | ATmega4809 | **TCB0** | **A4** | | -| Leonardo (Atmega32u4) | 1, 3, **4_HS** | 5, **9**, 13 | 5, **9**, 13 | +| Leonardo (Atmega32u4) | 1, 3, **4_HS** | 5, **9**, 13 | 5, **9**, 13 | | Zero (SAMD) | **TC3** | \*, **9** | | -| [ESP32](http://esp32.net/) | **Ledc chan. 0** | All pins | | +| [ESP8266](http://esp8266.net/) | **timer1** | % | | +| [ESP32](http://esp32.net/) | **hw_timer_t**
**Ledc channel 0** | All pins | | | [Sparkfun Pro Micro](https://www.sparkfun.com/products/12640) | 1, **3** | **5**, 9 | | | [Teensy 1.0](https://www.pjrc.com/teensy/pinout.html) | **1** | **17** | 15, 18 | | [Teensy 2.0](https://www.pjrc.com/teensy/pinout.html) | 1, 3, **4_HS** | 9, **10**, 14 | 12 | @@ -832,7 +1056,7 @@ The code for the timer and the **timer selection** is located in [private/IRTime | [Teensy 4.0 - 4.1](https://www.pjrc.com/teensy/pinout.html) | **FlexPWM1.3** | **8** | 7, 25 | | [BluePill / STM32F103C8T6](https://github.com/stm32duino/Arduino_Core_STM32) | **3** | % | **PA6 & PA7 & PB0 & PB1** | | [BluePill / STM32F103C8T6](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill) | **TIM4** | % | **PB6 & PB7 & PB8 & PB9** | -| [RP2040 / Pi Pico](https://github.com/earlephilhower/arduino-pico) | [default alarm pool](https://raspberrypi.github.io/pico-sdk-doxygen/group__repeating__timer.html) | All pins | No pin | +| [RP2040 / Pi Pico](https://github.com/earlephilhower/arduino-pico) | [default alarm pool](https://www.raspberrypi.com/documentation/pico-sdk/high_level.html#group_alarm_1ga40b4a03bf9e967d4e7170d20c5c9fb15) | All pins | No pin | | [RP2040 / Mbed based](https://github.com/arduino/ArduinoCore-mbed) | Mbed Ticker | All pins | No pin | ### No timer required for sending @@ -849,8 +1073,11 @@ Since the Arduino `micros()` function has a resolution of 4 µs at 16 MHz, ## Incompatibilities to other libraries and Arduino commands like tone() and analogWrite() If you use a library which requires the same timer as IRremote, you have a problem, since **the timer resource cannot be shared simultaneously** by both libraries. +### Use NEC protocol and TinyReceiver +[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender) does not require a timer, it relies on interrupts, thus avoiding any timer resource problems. + ### Change timer -The best approach is to change the timer used for IRremote, which can be accomplished by specifying the timer before `#include `.
+The best approach is to **change the timer** used for IRremote, which can be accomplished by specifying the timer before `#include `.
The timer specifications available for your board can be found in [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp).
```c++ @@ -867,23 +1094,29 @@ The timer specifications available for your board can be found in [private/IRTim Here you see the Arduino Mega board and the available specifications are `IR_USE_AVR_TIMER[1,2,3,4,5]`.
You **just have to include a line** e.g. `#define IR_USE_AVR_TIMER3` before `#include ` to enable timer 3. -But be aware that the new timer in turn might be incompatible with other libraries or commands.
-For other boards/platforms you must look for the appropriate section guarded by e.g. `#elif defined(ESP32)`. +But be aware that the new timer in turn might be again incompatible with other libraries or Arduino functions.
+For non AVR boards/platforms you must look for the appropriate section guarded by e.g. `#elif defined(ESP32)`. ### Stop and start timer Another approach can be to share the timer **sequentially** if their functionality is used only for a short period of time like for the **Arduino tone() command**. -An example can be seen [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/21b5747a58e9d47c9e3f1beb056d58c875a92b47/examples/ReceiveDemo/ReceiveDemo.ino#L159-L169), where the timer settings for IR receive are restored after the tone has stopped. -For this we must call `IrReceiver.restartTimer()` or better `IrReceiver.restartTimer(microsecondsOfToneDuration)`.
-This only works since each call to` tone()` completely initializes the timer 2 used by the `tone()` command. +An example can be seen [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298), where the IR timer is restarted after the tone has stopped. + +```c++ +IrReceiver.stopTimer(); // Stop timer consistently before calling tone() or other functions using the timer resource. +tone(TONE_PIN, 2200, 8); +delay(8); +IrReceiver.restartTimer(); // Restart IR timer after timer resource is no longer blocked. +``` +This works on AVR boards like Uno because each call to` tone()` completely initializes the timer 2 used by the `tone()` command. ## Hardware-PWM signal generation for sending If you define `SEND_PWM_BY_TIMER`, the send PWM signal is forced to be generated by a hardware timer on most platforms.
By default, the same timer as for the receiver is used.
Since each hardware timer has its dedicated output pin(s), you must change timer or timer sub-specifications to change PWM output pin. See [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp)
-**Exeptions** are currently [ESP32, ARDUINO_ARCH_RP2040, PARTICLE and ARDUINO_ARCH_MBED](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/39bdf8d7bf5b90dc221f8ae9fb3efed9f0a8a1db/examples/SimpleSender/PinDefinitionsAndMore.h#L273), where **PWM generation does not require a timer**. +**Exceptions** are currently [ESP32, ARDUINO_ARCH_RP2040, PARTICLE and ARDUINO_ARCH_MBED](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/PinDefinitionsAndMore.h#L341), where **PWM generation does not require a timer**. ## Why do we use 30% duty cycle for sending -We [do it](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRSend.hpp#L1192) according to the statement in the [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf): +We [do it](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRSend.hpp#L1194) according to the statement in the [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf): - Carrier duty cycle 50 %, peak current of emitter IF = 200 mA, the resulting transmission distance is 25 m. - Carrier duty cycle 10 %, peak current of emitter IF = 800 mA, the resulting transmission distance is 29 m. - Factor 1.16 The reason is, that it is not the pure energy of the fundamental which is responsible for the receiver to detect a signal. @@ -894,12 +1127,14 @@ Due to automatic gain control and other bias effects, high intensity of the 38 k # How we decode signals The IR signal is sampled at a **50 µs interval**. For a constant 525 µs pulse or pause we therefore get 10 or 11 samples, each with 50% probability.
And believe me, if you send a 525 µs signal, your receiver will output something between around 400 and 700 µs!
-Therefore **we decode by default with a +/- 25% margin** using the formulas [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremoteInt.h#L376-L399).
-E.g. for the NEC protocol with its 560 µs unit length, we have TICKS_LOW = 8.358 and TICKS_HIGH = 15.0. This means, we accept any value between 8 ticks / 400 µs and 15 ticks / 750 µs (inclusive) as a mark or as a zero space. For a one space we have TICKS_LOW = 25.07 and TICKS_HIGH = 45.0.
-And since the receivers generated marks are longer or shorter than the spaces, +Therefore **we decode by default with a +/- 25% margin** using the formulas [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremoteInt.h#L469-L491).
+E.g. for the NEC protocol with its 560 µs unit length, we have TICKS_LOW = 8.358 and TICKS_HIGH = 15.0. +This means, we accept any value between 8 ticks / 400 µs and 15 ticks / 750 µs (inclusive) as a mark or as a zero space. +For a one space we have TICKS_LOW = 25.07 and TICKS_HIGH = 45.0.
+And since the receivers generated marks are longer or shorter than the spaces, we have introduced the [`MARK_EXCESS_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library) macro -to compensate for this receiver (and signal strength as well as ambient light dependent :disappointed: ) specific deviation.
-Welcome to the world of **real world signal processing**. +to compensate for this receiver and signal strength as well as ambient light dependent :disappointed: specific deviation.
+**Welcome to the world of real world signal processing**.
@@ -915,7 +1150,7 @@ Created with sigrok PulseView with IR_NEC decoder by DjordjeMandic.
# Quick comparison of 5 Arduino IR receiving libraries **This is a short comparison and may not be complete or correct.** -I created this comparison matrix for [myself](https://github.com/ArminJo) in order to choose a small IR lib for my project and to have a quick overview, when to choose which library.
+I created this comparison matrix for [myself](https://github.com/ArminJo) in order to choose a small IR library for my project and to have a quick overview, when to choose which library.
It is dated from **24.06.2022** and updated 10/2023. If you have complains about the data or request for extensions, please send a PM or open a discussion. [Here](https://github.com/crankyoldgit/IRremoteESP8266) you find an **ESP8266/ESP32** version of IRremote with an **[impressive list of supported protocols](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md)**. @@ -941,23 +1176,29 @@ It is dated from **24.06.2022** and updated 10/2023. If you have complains about
+# [History](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/changelog.md) + # Useful links +- [Online NEC to Pronto converting tool](https://www.yamaha.com/ypab/irhex_converter.asp) - [List of public IR code databases](http://www.harctoolbox.org/IR-resources.html) - [LIRC database](http://lirc-remotes.sourceforge.net/remotes-table.html) - [IRMP list of IR protocols](https://www.mikrocontroller.net/articles/IRMP_-_english#IR_Protocols) - [IRDB database for IR codes](https://github.com/probonopd/irdb/tree/master/codes) - [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols) +- [Good introduction to IR remotes by DroneBot Workshop](https://dronebotworkshop.com/ir-remotes/) - [IR Remote Control Theory and some protocols (upper right hamburger icon)](https://www.sbprojects.net/knowledge/ir/) - [Interpreting Decoded IR Signals (v2.45)](http://www.hifi-remote.com/johnsfine/DecodeIR.html) - ["Recording long Infrared Remote control signals with Arduino"](https://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino) - The original blog post of Ken Shirriff [A Multi-Protocol Infrared Remote Library for the Arduino](http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html) - [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf) +# [Contributors](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/Contributors.md) + # License Up to the version 2.7.0, the License is GPLv2. From the version 2.8.0, the license is the MIT license. # Copyright Initially coded 2009 Ken Shirriff http://www.righto.com
-Copyright (c) 2016-2017 Rafi Khan
-Copyright (c) 2020-2023 [Armin Joachimsmeyer](https://github.com/ArminJo) +Copyright (c) 2016-2017 Rafi Khan https://rafikhan.io
+Copyright (c) 2020-2025 [Armin Joachimsmeyer](https://github.com/ArminJo) diff --git a/trunk/Arduino/libraries/IRremote/changelog.md b/trunk/Arduino/libraries/IRremote/changelog.md index 74d08b45..0da4b706 100644 --- a/trunk/Arduino/libraries/IRremote/changelog.md +++ b/trunk/Arduino/libraries/IRremote/changelog.md @@ -2,6 +2,62 @@ The latest version may not be released! See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master +# 4.5.0 +- Added support for multiple receiver instances. +- irparams_struct irparams is now member of IRrecv. Thus removed rawDataPtr (pointer to irparams) from IrReceiver.decodedIRData. +- Removed return value for all decodePulseDistanceWidthData() decoding functions, which returned a constant true. +- Removed parameter aEnableLEDFeedback in function IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) and IRsend::begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin). +- LED feedback is always enabled for sending. It can only be disabled by using the macro NO_LED_SEND_FEEDBACK_CODE. +- Added output for UNKNOWN protocol to printIRSendUsage(). +- Added experimental sendVelux(). +- Added sendMaranz(). +- Fixed bug in ReceiveDemo.cpp if DEBUG_BUTTON_PIN is not defined. #1306. +- Fixed minor bugs in Denon decoder. +- Minor bug fixes for DEBUG. +- New handling of MARK_EXCESS_MICROS without strange rounding inconsistency. +- Added experimental threshold decoding. + +# 4.4.3 +- Added USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN to make the software aware of send LED connected between VCC and send pin. +- Fixed backward compatibility bug for printIRResultShort(3 params). +- Minor improvements. + +# 4.4.2 +- Support for SAMD51 timer3 if timer 5 is not available (Adafruit ItsyBitsy M4). +- attachInterrupt() on SAMD has a different semantic :-(. See: https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/. +- Fixed overflow handling. +- Improved repeat detection for DistanceWidthProtocol. +- Print of IR frame duration in printIRResultShort(); +- PulseDistanceWidthProtocolConstants now in PROGMEM, this saves 190 bytes RAM for unit test. +- Support for PROGMEM PulseDistanceWidthProtocol data. +- Support duplicated 8 bit address for sendSamsungLG(). + +# 4.4.1 +- Support for ESP core 3.x by akellai. +- restartTimer() now uses variable sMicrosAtLastStopTimer to keep track of uncounted ticks between stopTimer() and restartTimer(). +- Removed functions addTicksToInternalTickCounter() and addMicrosToInternalTickCounter(), which were added in 4.1.0. +- Version 2.2.0 of TinyIR with new TinyReceiverDecode() function to be used as drop in for IrReceiver.decode(). +- Support of RC6A. + +# 4.4.0 +- Using 8 bit raw timing buffer for all timings except frame gap (former rawbuf[0]). +- Renamed decodedIRData.initialGap to decodedIRData.initialGapTicks. +- sendNEC() and sendNEC2() now accepts 16 bit command to better map to NECext protocol found in IRDB databases. +- ir_DistanceWidthProtocol() now decodes up to 10 ms mark or spaces if RAM is bigger than 2 k. +- Improved sensitivity and decoding of PULSE_DISTANCE + PULSE_WIDTH protocols. +- Changed TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING to TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT. +- Improved examples AllProtocolsOnLCD, UnitTest and SimpleReceiver. +- New functions decodePulseDistanceWidthData() with 6 parameters and decodePulseDistanceWidthDataStrict() with 7 parameters. + +# 4.3.2 +- Added sendSonyMSB(unsigned long data, int nbits) as a clone of sendSony(unsigned long data, int nbits) to be more consistent. +- Added sendSamsungMSB(unsigned long data, int nbits) as a clone of sendSAMSUNG(unsigned long data, int nbits) to be more consistent. +- Added ESP32 core 3.x error message. + +# 4.3.1 + - Fixed overflow bug for rawlen > 254. + - Removed deprecated sendPulseDistance... functions with parameter aSendStopBit. + # 4.3.0 - Removed default value USE_DEFAULT_FEEDBACK_LED_PIN for last parameter of IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin). Therefore IrSender.begin(DISABLE_LED_FEEDBACK) will not longer work! @@ -31,14 +87,14 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Usage of ATTinyCore pin numbering scheme e.g. PIN_PB2. - Added ARDUINO_ARCH_NRF52 to support Seeed XIAO nRF52840 Sense. - First untested support of Uno R4. -- Extraced version macros to IRVersion.h. +- Extracted version macros to IRVersion.h. ## 4.1.2 - Workaround for ESP32 RTOS delay() timing bug influencing the mark() function. ## 4.1.1 - SAMD51 use timer3 if timer5 not available. -- Disabled #define LOCAL_DEBUG in IRReceive.hpp, which was accidently enabled at 4.1.0. +- Disabled #define LOCAL_DEBUG in IRReceive.hpp, which was accidentally enabled at 4.1.0. ## 4.1.0 - Fixed bug in printing durations > 64535 in printIRResultRawFormatted(). @@ -65,7 +121,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Introduced common structure PulseDistanceWidthProtocolConstants. - Where possible, changed all send and decode functions to use PulseDistanceWidthProtocolConstants. - Improved MSB/LSB handling -- New convenience fuctions bitreverse32Bit() and bitreverseOneByte(). +- New convenience functions bitreverse32Bit() and bitreverseOneByte(). - Improved Magiquest protocol. - Fix for #1028 - Prevent long delay caused by overflow when frame duration < repeat period - Thanks to Stephen Humphries! - Support for ATtiny816 - Thanks to elockman. @@ -109,7 +165,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Improved pin mapping for TinyReceiver. ## 3.7.1 -- SendRaw now supports bufferlenght > 255. +- SendRaw now supports buffer length > 255. - Improved DistanceProtocol decoder output. - Fixed ESP32 send bug for 2.x ESP32 cores. @@ -277,7 +333,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Corrected keywords.txt. - BoseWave protocol added PR #690. - Formatting comply to the new stylesheet. -- Renamed "boarddefs.h" [ISSUE #375](https://github.com/Arduino-IRremote/Arduino-IRremote/issues/375). +- Renamed "boarddefs.h". - Renamed `SEND_PIN` to `IR_SEND_PIN`. - Renamed state macros. - Enabled `DUTY_CYCLE` for send signal. @@ -344,7 +400,7 @@ Changes from #268 by adamlhumphreys - Fixed #110 Mess - Created Gitter Room - Added Gitter Badge -- Standardised Code Base +- Standardized Code Base - Clean Debug Output - Optimized Send Loops - Modularized Design @@ -372,4 +428,4 @@ Changes from #268 by adamlhumphreys - Broke Teensy 3 / 3.1 Support ### Not Working -- Teensy 3 / 3.1 Support is in Development +- Teensy 3 / 3.1 Support is in Development \ No newline at end of file diff --git a/trunk/Arduino/libraries/IRremote/examples/IRremoteInfo/IRremoteInfo.ino b/trunk/Arduino/libraries/IRremote/examples/IRremoteInfo/IRremoteInfo.ino index 22ae75fb..25d0f09d 100644 --- a/trunk/Arduino/libraries/IRremote/examples/IRremoteInfo/IRremoteInfo.ino +++ b/trunk/Arduino/libraries/IRremote/examples/IRremoteInfo/IRremoteInfo.ino @@ -34,9 +34,17 @@ void dumpFooter(); void setup() { Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/|| defined(SERIALUSB_PID) || defined(ARDUINO_attiny3217) - delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! #endif + // Just to know which program is running on my Arduino Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); @@ -193,7 +201,7 @@ void dumpPulseParams() { ; Serial.println(F(" uSecs")); Serial.print(F("Measurement tolerance: ")); - Serial.print(TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING); + Serial.print(TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT); Serial.println(F("%")); } diff --git a/trunk/Arduino/libraries/IRremote/examples/MicroGirs/MicroGirs.ino b/trunk/Arduino/libraries/IRremote/examples/MicroGirs/MicroGirs.ino index 52e6c3a8..194728d3 100644 --- a/trunk/Arduino/libraries/IRremote/examples/MicroGirs/MicroGirs.ino +++ b/trunk/Arduino/libraries/IRremote/examples/MicroGirs/MicroGirs.ino @@ -67,12 +67,11 @@ #include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. #if !defined(RAW_BUFFER_LENGTH) -# if RAMEND <= 0x4FF || RAMSIZE < 0x4FF -#define RAW_BUFFER_LENGTH 180 // 750 (600 if we have only 2k RAM) is the value for air condition remotes. Default is 112 if DECODE_MAGIQUEST is enabled, otherwise 100. -# elif RAMEND <= 0x8FF || RAMSIZE < 0x8FF -#define RAW_BUFFER_LENGTH 500 // 750 (600 if we have only 2k RAM) is the value for air condition remotes. Default is 112 if DECODE_MAGIQUEST is enabled, otherwise 100. +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 # else -#define RAW_BUFFER_LENGTH 750 // 750 (600 if we have only 2k RAM) is the value for air condition remotes. Default is 112 if DECODE_MAGIQUEST is enabled, otherwise 100. +#define RAW_BUFFER_LENGTH 750 # endif #endif @@ -83,6 +82,8 @@ #define BAUDRATE 115200 #define NO_DECODER +//#define NO_LED_FEEDBACK_CODE // Saves 346 bytes program memory + #include "IRremote.hpp" #include @@ -190,8 +191,7 @@ String Tokenizer::getRest() { } String Tokenizer::getLine() { - if (index == invalidIndex) - return String(""); + if (index == invalidIndex) return String(""); int i = payload.indexOf('\n', index); String s = (i > 0) ? payload.substring(index, i) : payload.substring(index); @@ -200,16 +200,13 @@ String Tokenizer::getLine() { } String Tokenizer::getToken() { - if (index < 0) - return String(""); + if (index < 0) return String(""); int i = payload.indexOf(' ', index); String s = (i > 0) ? payload.substring(index, i) : payload.substring(index); index = (i > 0) ? i : invalidIndex; - if (index != invalidIndex) - if (index != invalidIndex) - while (payload.charAt(index) == ' ') - index++; + if (index != invalidIndex) if (index != invalidIndex) while (payload.charAt(index) == ' ') + index++; return s; } @@ -253,26 +250,23 @@ static inline unsigned hz2khz(frequency_t hz) { */ static void sendRaw(const microseconds_t intro[], unsigned lengthIntro, const microseconds_t repeat[], unsigned lengthRepeat, const microseconds_t ending[], unsigned lengthEnding, frequency_t frequency, unsigned times) { - if (lengthIntro > 0U) - IrSender.sendRaw(intro, lengthIntro, hz2khz(frequency)); - if (lengthRepeat > 0U) - for (unsigned i = 0U; i < times - (lengthIntro > 0U); i++) - IrSender.sendRaw(repeat, lengthRepeat, hz2khz(frequency)); - if (lengthEnding > 0U) - IrSender.sendRaw(ending, lengthEnding, hz2khz(frequency)); + if (lengthIntro > 0U) IrSender.sendRaw(intro, lengthIntro, hz2khz(frequency)); + if (lengthRepeat > 0U) for (unsigned i = 0U; i < times - (lengthIntro > 0U); i++) + IrSender.sendRaw(repeat, lengthRepeat, hz2khz(frequency)); + if (lengthEnding > 0U) IrSender.sendRaw(ending, lengthEnding, hz2khz(frequency)); } #endif // TRANSMIT #if defined(RECEIVE) static void dump(Stream &stream) { - unsigned int count = IrReceiver.decodedIRData.rawDataPtr->rawlen; + unsigned int count = IrReceiver.irparams.rawlen; // If buffer gets full, count = RAW_BUFFER_LENGTH, which is odd, // and IrScrutinizer does not like that. count &= ~1; for (unsigned int i = 1; i < count; i++) { stream.write(i & 1 ? '+' : '-'); - stream.print(IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK, DEC); + stream.print(IrReceiver.irparams.rawbuf[i] * MICROS_PER_TICK, DEC); stream.print(" "); } stream.print('-'); @@ -300,8 +294,6 @@ static void receive(Stream &stream) { */ void setup() { Serial.begin(BAUDRATE); - while (!Serial) - ; // wait for serial port to connect. Serial.println(F(PROGNAME " " VERSION)); // Just to know which program is running on my Arduino @@ -317,9 +309,13 @@ void setup() { #endif #if defined(IR_SEND_PIN) - IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + /* + * No IR library setup required :-) + * Default is to use IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin + * and use feedback LED at default feedback LED pin if not disabled by #define NO_LED_SEND_FEEDBACK_CODE + */ #else - IrSender.begin(3, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN); // Specify send pin and enable feedback LED at default feedback LED pin + IrSender.begin(3); // Specify send pin and enable feedback LED at default feedback LED pin #endif } diff --git a/trunk/Arduino/libraries/IRremote/keywords.txt b/trunk/Arduino/libraries/IRremote/keywords.txt index 935f913e..03bdd289 100644 --- a/trunk/Arduino/libraries/IRremote/keywords.txt +++ b/trunk/Arduino/libraries/IRremote/keywords.txt @@ -16,45 +16,56 @@ decodedIRData KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### - -setFeedbackLED KEYWORD2 -enableLEDFeedback KEYWORD2 -enableLEDFeedbackForSend KEYWORD2 -disableLEDFeedback KEYWORD2 -disableLEDFeedbackForSend KEYWORD2 -printIRResultShort KEYWORD2 -begin KEYWORD2 -start KEYWORD2 +# IRReceive available KEYWORD2 +begin KEYWORD2 +decode KEYWORD2 +disableIRIn KEYWORD2 +enableIRIn KEYWORD2 +isIdle KEYWORD2 +printActiveIRProtocols KEYWORD2 +printIRResultMinimal KEYWORD2 +printIRResultRawFormatted KEYWORD2 +printIRResultShort KEYWORD2 +printIRSendUsage KEYWORD2 +registerReceiveCompleteCallback KEYWORD2 +restartAfterSend KEYWORD2 +restartTimer KEYWORD2 +restartTimerWithTicksToAdd KEYWORD2 +resume KEYWORD2 +setReceivePin KEYWORD2 +start KEYWORD2 read KEYWORD2 stop KEYWORD2 end KEYWORD2 -enableLEDFeedback KEYWORD2 -decode KEYWORD2 -resume KEYWORD2 -enableIRIn KEYWORD2 -disableIRIn KEYWORD2 -sendNEC KEYWORD2 + +# IRSend setSendPin KEYWORD2 write KEYWORD2 -enableIROut KEYWORD2 -IRLedOff KEYWORD2 -sendRaw KEYWORD2 +sendApple KEYWORD2 +sendFAST KEYWORD2 sendJVC KEYWORD2 +sendLegoPowerFunctions KEYWORD2 sendLG KEYWORD2 -sendLGRepeat KEYWORD2 +sendLG2 KEYWORD2 +sendLG2Repeat KEYWORD2 sendLGRaw KEYWORD2 +sendMaranz KEYWORD2 +sendMagiQuest KEYWORD2 sendNEC KEYWORD2 +sendNEC2 KEYWORD2 sendNECRepeat KEYWORD2 sendNECRaw KEYWORD2 sendOnkyo KEYWORD2 -sendApple KEYWORD2 sendPanasonic KEYWORD2 +sendPronto KEYWORD2 sendKaseikyo KEYWORD2 sendKaseikyo_Denon KEYWORD2 sendKaseikyo_Sharp KEYWORD2 sendKaseikyo_JVC KEYWORD2 sendKaseikyo_Mitsubishi KEYWORD2 +sendRaw KEYWORD2 +sendRaw_P KEYWORD2 sendRC5 KEYWORD2 sendRC6 KEYWORD2 sendSamsungRepeat KEYWORD2 @@ -62,23 +73,42 @@ sendSamsung KEYWORD2 sendSharp KEYWORD2 sendSony KEYWORD2 sendSharpRaw KEYWORD2 -sendLegoPowerFunctions KEYWORD2 -sendMagiQuest KEYWORD2 -sendPronto KEYWORD2 -sendMagiQuest KEYWORD2 +sendVelux KEYWORD2 + +# IRFeedbackLED +disableLEDFeedback KEYWORD2 +disableLEDFeedbackForSend KEYWORD2 +enableLEDFeedback KEYWORD2 +enableLEDFeedbackForSend KEYWORD2 +setFeedbackLED KEYWORD2 +setLEDFeedback KEYWORD2 + +# TinyIRReceiver +disablePCIInterruptForTinyReceiver KEYWORD2 +enablePCIInterruptForTinyReceiver KEYWORD2 +initPCIInterruptForTinyReceiver KEYWORD2 +isIRReceiverAttachedForTinyReceiver KEYWORD2 +printTinyReceiverResultMinimal KEYWORD2 +TinyReceiverDecode KEYWORD2 +# TinyIRSender +sendExtendedNEC KEYWORD2 sendFAST KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### +UNKNOWN LITERAL1 PULSE_DISTANCE LITERAL1 PULSE_WIDTH LITERAL1 +APPLE LITERAL1 DENON LITERAL1 -DISH LITERAL1 JVC LITERAL1 LG LITERAL1 LG2 LITERAL1 NEC LITERAL1 +NEC2 LITERAL1 +ONKYO LITERAL1 PANASONIC LITERAL1 KASEIKYO LITERAL1 KASEIKYO_JVC LITERAL1 @@ -87,18 +117,57 @@ KASEIKYO_SHARP LITERAL1 KASEIKYO_MITSUBISHI LITERAL1 RC5 LITERAL1 RC6 LITERAL1 +RC6A LITERAL1 SAMSUNG LITERAL1 +SAMSUNGLG LITERAL1 +SAMSUNG48 LITERAL1 SHARP LITERAL1 SONY LITERAL1 -ONKYO LITERAL1 -APPLE LITERAL1 BANG_OLUFSEN LITERAL1 BOSEWAVE LITERAL1 LEGO_PF LITERAL1 MAGIQUEST LITERAL1 WHYNTER LITERAL1 FAST LITERAL1 -UNKNOWN LITERAL1 -IR_RECEIVE_PIN LITERAL1 + +IRDATA_FLAGS_IS_REPEAT LITERAL1 +IRDATA_FLAGS_IS_AUTO_REPEAT LITERAL1 +IRDATA_FLAGS_PARITY_FAILED LITERAL1 +IRDATA_FLAGS_TOGGLE_BIT LITERAL1 +IRDATA_FLAGS_EXTRA_INFO LITERAL1 +IRDATA_FLAGS_WAS_OVERFLOW LITERAL1 +IRDATA_FLAGS_IS_MSB_FIRST LITERAL1 + + +RAW_BUFFER_LENGTH LITERAL1 +EXCLUDE_UNIVERSAL_PROTOCOLS LITERAL1 +EXCLUDE_EXOTIC_PROTOCOLS LITERAL1 +IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK LITERAL1 +MARK_EXCESS_MICROS LITERAL1 +RECORD_GAP_MICROS LITERAL1 +DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE LITERAL1 +IR_INPUT_IS_ACTIVE_HIGH LITERAL1 IR_SEND_PIN LITERAL1 -FEEDBACK_LED_IS_ACTIVE_LOW LITERAL1 +SEND_PWM_BY_TIMER LITERAL1 +IR_SEND_DUTY_CYCLE_PERCENT LITERAL1 +USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN LITERAL1 +USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN LITERAL1 +USE_NO_SEND_PWM LITERAL1 +USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM LITERAL1 +DISABLE_CODE_FOR_RECEIVER LITERAL1 +FEEDBACK_LED_IS_ACTIVE_LOW LITERAL1 +NO_LED_FEEDBACK_CODE LITERAL1 +NO_LED_RECEIVE_FEEDBACK_CODE LITERAL1 +NO_LED_SEND_FEEDBACK_CODE LITERAL1 +MICROS_PER_TICK LITERAL1 +TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT LITERAL1 + +# Constants for TinyIRReceiver +IR_RECEIVE_PIN LITERAL1 +IR_FEEDBACK_LED_PIN LITERAL1 +DISABLE_PARITY_CHECKS LITERAL1 +USE_EXTENDED_NEC_PROTOCOL LITERAL1 +USE_ONKYO_PROTOCOL LITERAL1 +USE_FAST_PROTOCOL LITERAL1 +ENABLE_NEC2_REPEATS LITERAL1 +USE_CALLBACK_FOR_TINY_RECEIVER LITERAL1 diff --git a/trunk/Arduino/libraries/IRremote/library.json b/trunk/Arduino/libraries/IRremote/library.json index 0bf21962..7aecaa48 100644 --- a/trunk/Arduino/libraries/IRremote/library.json +++ b/trunk/Arduino/libraries/IRremote/library.json @@ -1,15 +1,14 @@ { "name": "IRremote", - "keywords": "communication, infrared, ir, remote", + "version": "4.5.0", "description": "Send and receive infrared signals with multiple protocols", + "keywords": "communication, infrared, ir, remote", + "homepage": "https://github.com/Arduino-IRremote/Arduino-IRremote", "repository": { "type": "git", - "url": "https://github.com/z3t0/Arduino-IRremote.git" + "url": "https://github.com/Arduino-IRremote/Arduino-IRremote.git" }, - "version": "4.3.0", - "frameworks": "arduino", - "platforms": ["atmelavr", "atmelmegaavr", "atmelsam", "espressif8266", "espressif32", "ststm32"], "authors" : [ { @@ -26,5 +25,10 @@ "email":"ken.shirriff@gmail.com" } ], - "headers": "IRRemote.hpp" + "license": "MIT", + "frameworks": "arduino", + "platforms": ["atmelavr", "atmelmegaavr", "atmelsam", "espressif8266", "espressif32", "ststm32", "raspberrypi"], + "headers": "IRRemote.hpp", + "examples": "examples/*/*.ino", + "export": {"exclude": [".github", "pictures"]} } diff --git a/trunk/Arduino/libraries/IRremote/library.properties b/trunk/Arduino/libraries/IRremote/library.properties index cc6e5e95..ec368ba6 100644 --- a/trunk/Arduino/libraries/IRremote/library.properties +++ b/trunk/Arduino/libraries/IRremote/library.properties @@ -1,9 +1,9 @@ name=IRremote -version=4.3.0 +version=4.5.0 author=shirriff, z3t0, ArminJo maintainer=Armin Joachimsmeyer sentence=Send and receive infrared signals with multiple protocols -paragraph=Currently included protocols: Denon / Sharp, JVC, LG / LG2, NEC / Onkyo / Apple, Panasonic / Kaseikyo, RC5, RC6, Samsung, Sony, (Pronto), BangOlufsen, BoseWave, Lego, Whynter, FAST, MagiQuest, Universal Pulse Distance and Pulse Width. NEW: TinyRSender improvements, sendSamsung bug fixes, new fields rawlen and initialGap and new functions stop/startTimer...(). +paragraph=Currently included protocols: Denon / Sharp, JVC, LG / LG2, NEC / Onkyo / Apple, Panasonic / Kaseikyo, RC5, RC6, Samsung, Sony, (Pronto), BangOlufsen, BoseWave, Lego, Whynter, FAST, MagiQuest, Velux, Universal Pulse Distance and Pulse Width. category=Communication url=https://github.com/Arduino-IRremote/Arduino-IRremote architectures=avr,megaavr,samd,esp8266,esp32,stm32,STM32F1,mbed,mbed_nano,rp2040,mbed_rp2040,renesas_uno diff --git a/trunk/Arduino/libraries/IRremote/src/IRremote.h b/trunk/Arduino/libraries/IRremote/src/IRremote.h index 3af3f582..73cae038 100644 --- a/trunk/Arduino/libraries/IRremote/src/IRremote.h +++ b/trunk/Arduino/libraries/IRremote/src/IRremote.h @@ -9,14 +9,13 @@ #include "IRremote.hpp" -#warning Thank you for using the IRremote library! -#warning It seems, that you are using a old version 2.0 code / example. +#warning It seems, that you are using an old version 2.0 code / example. #warning This version is no longer supported! -#warning Please use one of the new code examples from the library available at "File > Examples > Examples from Custom Libraries / IRremote". -#warning Or downgrade your library to version 2.6.0. +#warning Upgrade instructions can be found here: "https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version" +#warning Please use one of the new code examples from the library, available at "File > Examples > Examples from Custom Libraries / IRremote". #warning Start with the SimpleReceiver or SimpleSender example. -#warning The examples are documented here: https://github.com/Arduino-IRremote/Arduino-IRremote#examples-for-this-library -#warning A guide how to convert your 2.0 program is here: https://github.com/Arduino-IRremote/Arduino-IRremote#converting-your-2x-program-to-the-4x-version +#warning The examples are documented here: "https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library" +#warning Or just downgrade your library to version 2.6.0. /********************************************************************************************************************** * The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials @@ -31,18 +30,19 @@ bool IRrecv::decode(decode_results *aResults) { if (!sMessageWasSent) { Serial.println(F("**************************************************************************************************")); Serial.println(F("Thank you for using the IRremote library!")); - Serial.println(F("It seems, that you are using a old version 2.0 code / example.")); + Serial.println(F("It seems, that you are using an old version 2.0 code / example.")); Serial.println(F("This version is no longer supported!")); + Serial.println(); + Serial.println(F("Upgrade instructions can be found here:")); + Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version")); + Serial.println(); Serial.println(F("Please use one of the new code examples from the library,")); Serial.println(F(" available at \"File > Examples > Examples from Custom Libraries / IRremote\".")); - Serial.println(F("Or downgrade your library to version 2.6.0.")); - Serial.println(); Serial.println(F("Start with the SimpleReceiver or SimpleSender example.")); - Serial.println(); Serial.println(F("The examples are documented here:")); - Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote#examples-for-this-library")); - Serial.println(F("A guide how to convert your 2.0 program is here:")); - Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote#converting-your-2x-program-to-the-4x-version")); + Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library")); + Serial.println(); + Serial.println(F("Or just downgrade your library to version 2.6.0.")); Serial.println(); Serial.println(F("Thanks")); Serial.println(F("**************************************************************************************************")); diff --git a/trunk/Arduino/libraries/Keyboard/library.properties b/trunk/Arduino/libraries/Keyboard/library.properties index b5c99d89..1ae91ec7 100644 --- a/trunk/Arduino/libraries/Keyboard/library.properties +++ b/trunk/Arduino/libraries/Keyboard/library.properties @@ -1,9 +1,9 @@ name=Keyboard -version=1.0.5 +version=1.0.6 author=Arduino maintainer=Arduino sentence=Allows an Arduino board with USB capabilities to act as a Keyboard. paragraph=This library plugs on the HID library. It can be used with or without other HID-based libraries (Mouse, Gamepad etc) category=Device Control url=https://www.arduino.cc/reference/en/language/functions/usb/keyboard/ -architectures=avr, samd, sam +architectures=avr, samd, sam, renesas_uno diff --git a/trunk/Arduino/libraries/OneWire/OneWire.cpp b/trunk/Arduino/libraries/OneWire/OneWire.cpp index 38bf4ee2..cb0d670f 100644 --- a/trunk/Arduino/libraries/OneWire/OneWire.cpp +++ b/trunk/Arduino/libraries/OneWire/OneWire.cpp @@ -32,6 +32,11 @@ private email about OneWire). OneWire is now very mature code. No changes other than adding definitions for newer hardware support are anticipated. + ESP32 mods authored by stickbreaker: + @stickbreaker 30APR2018 add IRAM_ATTR to read_bit() write_bit() to solve ICache miss timing failure. + thanks @everslick re: https://github.com/espressif/arduino-esp32/issues/1335 + Altered by garyd9 for clean merge with Paul Stoffregen's source + Version 2.3: Unknown chip fallback mode, Roger Clark Teensy-LC compatibility, Paul Stoffregen @@ -143,6 +148,16 @@ sample code bearing this copyright. #include "OneWire.h" #include "util/OneWire_direct_gpio.h" +#ifdef ARDUINO_ARCH_ESP32 +// due to the dual core esp32, a critical section works better than disabling interrupts +# define noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux) +# define interrupts() portEXIT_CRITICAL(&mux);} +// for info on this, search "IRAM_ATTR" at https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/general-notes.html +# define CRIT_TIMING IRAM_ATTR +#else +# define CRIT_TIMING +#endif + void OneWire::begin(uint8_t pin) { @@ -161,10 +176,10 @@ void OneWire::begin(uint8_t pin) // // Returns 1 if a device asserted a presence pulse, 0 otherwise. // -uint8_t OneWire::reset(void) +uint8_t CRIT_TIMING OneWire::reset(void) { IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; - volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; + __attribute__((unused)) volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; uint8_t r; uint8_t retries = 125; @@ -195,10 +210,10 @@ uint8_t OneWire::reset(void) // Write a bit. Port and bit is used to cut lookup time and provide // more certain timing. // -void OneWire::write_bit(uint8_t v) +void CRIT_TIMING OneWire::write_bit(uint8_t v) { IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; - volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; + __attribute__((unused)) volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; if (v & 1) { noInterrupts(); @@ -223,10 +238,10 @@ void OneWire::write_bit(uint8_t v) // Read a bit. Port and bit is used to cut lookup time and provide // more certain timing. // -uint8_t OneWire::read_bit(void) +uint8_t CRIT_TIMING OneWire::read_bit(void) { IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; - volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; + __attribute__((unused)) volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; uint8_t r; noInterrupts(); @@ -578,3 +593,11 @@ uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc) #endif #endif + +// undef defines for no particular reason +#ifdef ARDUINO_ARCH_ESP32 +# undef noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux) +# undef interrupts() portEXIT_CRITICAL(&mux);} +#endif +// for info on this, search "IRAM_ATTR" at https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/general-notes.html +#undef CRIT_TIMING diff --git a/trunk/Arduino/libraries/OneWire/library.json b/trunk/Arduino/libraries/OneWire/library.json index f370f06f..c540133b 100644 --- a/trunk/Arduino/libraries/OneWire/library.json +++ b/trunk/Arduino/libraries/OneWire/library.json @@ -52,7 +52,7 @@ "type": "git", "url": "https://github.com/PaulStoffregen/OneWire" }, - "version": "2.3.7", + "version": "2.3.8", "homepage": "https://www.pjrc.com/teensy/td_libs_OneWire.html", "frameworks": "Arduino", "examples": [ diff --git a/trunk/Arduino/libraries/OneWire/library.properties b/trunk/Arduino/libraries/OneWire/library.properties index 5256ce47..f4c596f9 100644 --- a/trunk/Arduino/libraries/OneWire/library.properties +++ b/trunk/Arduino/libraries/OneWire/library.properties @@ -1,5 +1,5 @@ name=OneWire -version=2.3.7 +version=2.3.8 author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom maintainer=Paul Stoffregen sentence=Access 1-wire temperature sensors, memory and other chips. diff --git a/trunk/Arduino/libraries/OneWire/util/OneWire_direct_gpio.h b/trunk/Arduino/libraries/OneWire/util/OneWire_direct_gpio.h index 435443a8..830241c3 100644 --- a/trunk/Arduino/libraries/OneWire/util/OneWire_direct_gpio.h +++ b/trunk/Arduino/libraries/OneWire/util/OneWire_direct_gpio.h @@ -106,18 +106,58 @@ // DO NOT CREATE GITHUB ISSUES for ESP support. All ESP questions must be asked // on ESP community forums. #define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO) -#define PIN_TO_BITMASK(pin) (1 << pin) +#define PIN_TO_BITMASK(pin) (1UL << (pin)) #define IO_REG_TYPE uint32_t #define IO_REG_BASE_ATTR #define IO_REG_MASK_ATTR -#define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS -#define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS -#define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS -#define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS -#define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS + +static inline __attribute__((always_inline)) +void directModeInput(IO_REG_TYPE mask) +{ + if(mask > 0x8000) + { + GP16FFS(GPFFS_GPIO(16)); + GPC16 = 0; + GP16E &= ~1; + } + else + { + GPE &= ~(mask); + } +} + +static inline __attribute__((always_inline)) +void directModeOutput(IO_REG_TYPE mask) +{ + if(mask > 0x8000) + { + GP16FFS(GPFFS_GPIO(16)); + GPC16 = 0; + GP16E |= 1; + } + else + { + GPE |= (mask); + } +} +static inline __attribute__((always_inline)) +bool directRead(IO_REG_TYPE mask) +{ + if(mask > 0x8000) + return GP16I & 0x01; + else + return ((GPI & (mask)) ? true : false); +} + +#define DIRECT_READ(base, mask) directRead(mask) +#define DIRECT_MODE_INPUT(base, mask) directModeInput(mask) +#define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask) +#define DIRECT_WRITE_LOW(base, mask) (mask > 0x8000) ? GP16O &= ~1 : (GPOC = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) (mask > 0x8000) ? GP16O |= 1 : (GPOS = (mask)) #elif defined(ARDUINO_ARCH_ESP32) #include +#include #define PIN_TO_BASEREG(pin) (0) #define PIN_TO_BITMASK(pin) (pin) #define IO_REG_TYPE uint32_t @@ -427,6 +467,40 @@ void directWriteHigh(IO_REG_TYPE mask) #define DIRECT_MODE_INPUT(base, mask) directModeInput(mask) #define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask) +#elif defined(__MBED__) + +#include "platform/mbed_critical.h" +#include "DigitalInOut.h" +#include +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (new mbed::DigitalInOut(digitalPinToPinName(pin))) +#define IO_REG_TYPE mbed::DigitalInOut* +#define IO_REG_BASE_ATTR +#define IO_REG_MASK_ATTR +#define DIRECT_READ(base, pin) (*pin) +#define DIRECT_WRITE_LOW(base, pin) (*pin = 0) +#define DIRECT_WRITE_HIGH(base, pin) (*pin = 1) +#define DIRECT_MODE_INPUT(base, pin) (pin->input()) +#define DIRECT_MODE_OUTPUT(base, pin) (pin->output()) +#undef interrupts +#undef noInterrupts +#define noInterrupts() osThreadSetPriority(osThreadGetId(), osPriorityRealtime) //core_util_critical_section_enter() +#define interrupts() osThreadSetPriority(osThreadGetId(), osPriorityNormal) //core_util_critical_section_exit() + +#elif defined(ARDUINO_ARCH_MBED_RP2040)|| defined(ARDUINO_ARCH_RP2040) +#define delayMicroseconds(time) busy_wait_us(time) +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (pin) +#define IO_REG_TYPE unsigned int +#define IO_REG_BASE_ATTR +#define IO_REG_MASK_ATTR +#define DIRECT_READ(base, pin) digitalRead(pin) +#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) +#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) +#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) +#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) +#warning "OneWire. RP2040 in Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite." + #else #define PIN_TO_BASEREG(pin) (0) #define PIN_TO_BITMASK(pin) (pin) diff --git a/trunk/Arduino/libraries/OneWire/util/OneWire_direct_regtype.h b/trunk/Arduino/libraries/OneWire/util/OneWire_direct_regtype.h index ca6aff7e..8e30813a 100644 --- a/trunk/Arduino/libraries/OneWire/util/OneWire_direct_regtype.h +++ b/trunk/Arduino/libraries/OneWire/util/OneWire_direct_regtype.h @@ -45,6 +45,10 @@ #elif defined(__arc__) /* Arduino101/Genuino101 specifics */ #define IO_REG_TYPE uint32_t +#elif defined(__MBED__) +#include "DigitalInOut.h" +#define IO_REG_TYPE mbed::DigitalInOut* + #elif defined(__riscv) #define IO_REG_TYPE uint32_t diff --git a/trunk/Arduino/libraries/SD/README.adoc b/trunk/Arduino/libraries/SD/README.adoc index fb3ed030..ab370cba 100644 --- a/trunk/Arduino/libraries/SD/README.adoc +++ b/trunk/Arduino/libraries/SD/README.adoc @@ -1,26 +1,13 @@ -= SD Library for Arduino = +:repository-owner: arduino-libraries +:repository-name: SD -image:https://travis-ci.org/arduino-libraries/SD.svg?branch=master[Build Status, link=https://travis-ci.org/arduino-libraries/SD] += {repository-name} Library for Arduino = + +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] The SD library allows for reading from and writing to SD cards. For more information about this library please visit us at -http://www.arduino.cc/en/Reference/SD - -== License == - - Copyright (C) 2009 by William Greiman -Copyright (c) 2010 SparkFun Electronics - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . +http://www.arduino.cc/en/Reference/{repository-name} diff --git a/trunk/Arduino/libraries/SD/examples/CardInfo/CardInfo.ino b/trunk/Arduino/libraries/SD/examples/CardInfo/CardInfo.ino index 97913efd..ceef52ac 100644 --- a/trunk/Arduino/libraries/SD/examples/CardInfo/CardInfo.ino +++ b/trunk/Arduino/libraries/SD/examples/CardInfo/CardInfo.ino @@ -1,22 +1,21 @@ /* SD card test - This example shows how use the utility libraries on which the' + This example shows how use the utility libraries on which the SD library is based in order to get info about your SD card. Very useful for testing a card when you're not sure whether its working or not. - + Pin numbers reflect the default SPI pins for Uno and Nano models. The circuit: SD card attached to SPI bus as follows: - ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila - ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila + ** SDO - pin 11 on Arduino Uno/Duemilanove/Diecimila + ** SDI - pin 12 on Arduino Uno/Duemilanove/Diecimila ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila ** CS - depends on your SD card shield or module. - Pin 4 used here for consistency with other Arduino examples - + Pin 10 used here for consistency with other Arduino examples created 28 Mar 2011 by Limor Fried - modified 9 Apr 2012 + modified 24 July 2020 by Tom Igoe */ // include the SD library: @@ -29,11 +28,12 @@ SdVolume volume; SdFile root; // change this to match your SD shield or module; +// Default SPI on Uno and Nano: pin 10 // Arduino Ethernet shield: pin 4 // Adafruit SD shields and modules: pin 10 // Sparkfun SD shield: pin 8 -// MKRZero SD: SDCARD_SS_PIN -const int chipSelect = 4; +// MKR Zero SD: SDCARD_SS_PIN +const int chipSelect = 10; void setup() { // Open serial communications and wait for port to open: @@ -52,6 +52,7 @@ void setup() { Serial.println("* is a card inserted?"); Serial.println("* is your wiring correct?"); Serial.println("* did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); while (1); } else { Serial.println("Wiring is correct and a card is present."); @@ -96,13 +97,13 @@ void setup() { volumesize = volume.blocksPerCluster(); // clusters are collections of blocks volumesize *= volume.clusterCount(); // we'll have a lot of clusters - volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB) - Serial.print("Volume size (Kb): "); + volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1 KB) + Serial.print("Volume size (KB): "); Serial.println(volumesize); - Serial.print("Volume size (Mb): "); + Serial.print("Volume size (MB): "); volumesize /= 1024; Serial.println(volumesize); - Serial.print("Volume size (Gb): "); + Serial.print("Volume size (GB): "); Serial.println((float)volumesize / 1024.0); Serial.println("\nFiles found on the card (name, date and size in bytes): "); @@ -110,6 +111,7 @@ void setup() { // list all files in the card with date and size root.ls(LS_R | LS_DATE | LS_SIZE); + root.close(); } void loop(void) { diff --git a/trunk/Arduino/libraries/SD/examples/Datalogger/Datalogger.ino b/trunk/Arduino/libraries/SD/examples/Datalogger/Datalogger.ino index c2631c0a..c5a509cc 100644 --- a/trunk/Arduino/libraries/SD/examples/Datalogger/Datalogger.ino +++ b/trunk/Arduino/libraries/SD/examples/Datalogger/Datalogger.ino @@ -2,18 +2,21 @@ SD card datalogger This example shows how to log data from three analog sensors - to an SD card using the SD library. + to an SD card using the SD library. Pin numbers reflect the default + SPI pins for Uno and Nano models The circuit: - analog sensors on analog ins 0, 1, and 2 + analog sensors on analog pins 0, 1, and 2 SD card attached to SPI bus as follows: - ** MOSI - pin 11 - ** MISO - pin 12 + ** SDO - pin 11 + ** SDI - pin 12 ** CLK - pin 13 - ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) + ** CS - depends on your SD card shield or module. + Pin 10 used here for consistency with other Arduino examples + (for MKR Zero SD: SDCARD_SS_PIN) created 24 Nov 2010 - modified 9 Apr 2012 + modified 24 July 2020 by Tom Igoe This example code is in the public domain. @@ -23,25 +26,26 @@ #include #include -const int chipSelect = 4; +const int chipSelect = 10; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only - } - + // wait for Serial Monitor to connect. Needed for native USB port boards only: + while (!Serial); Serial.print("Initializing SD card..."); - // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { - Serial.println("Card failed, or not present"); - // don't do anything more: - while (1); + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); + while (true); } - Serial.println("card initialized."); + + Serial.println("initialization done."); } void loop() { @@ -73,12 +77,3 @@ void loop() { Serial.println("error opening datalog.txt"); } } - - - - - - - - - diff --git a/trunk/Arduino/libraries/SD/examples/DumpFile/DumpFile.ino b/trunk/Arduino/libraries/SD/examples/DumpFile/DumpFile.ino index 74919991..b6e99444 100644 --- a/trunk/Arduino/libraries/SD/examples/DumpFile/DumpFile.ino +++ b/trunk/Arduino/libraries/SD/examples/DumpFile/DumpFile.ino @@ -3,13 +3,16 @@ This example shows how to read a file from the SD card using the SD library and send it over the serial port. + Pin numbers reflect the default SPI pins for Uno and Nano models. The circuit: SD card attached to SPI bus as follows: - ** MOSI - pin 11 - ** MISO - pin 12 + ** SDO - pin 11 + ** SDI - pin 12 ** CLK - pin 13 - ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) + ** CS - depends on your SD card shield or module. + Pin 10 used here for consistency with other Arduino examples + (for MKR Zero SD: SDCARD_SS_PIN) created 22 December 2010 by Limor Fried @@ -17,31 +20,29 @@ by Tom Igoe This example code is in the public domain. - */ - -#include #include -const int chipSelect = 4; +const int chipSelect = 10; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only - } - + // wait for Serial Monitor to connect. Needed for native USB port boards only: + while (!Serial); Serial.print("Initializing SD card..."); - // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { - Serial.println("Card failed, or not present"); - // don't do anything more: - while (1); + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); + while (true); } - Serial.println("card initialized."); + + Serial.println("initialization done."); // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. @@ -62,4 +63,3 @@ void setup() { void loop() { } - diff --git a/trunk/Arduino/libraries/SD/examples/Files/Files.ino b/trunk/Arduino/libraries/SD/examples/Files/Files.ino index cabebba5..2df02691 100644 --- a/trunk/Arduino/libraries/SD/examples/Files/Files.ino +++ b/trunk/Arduino/libraries/SD/examples/Files/Files.ino @@ -1,39 +1,43 @@ /* SD card basic file example - This example shows how to create and destroy an SD card file - The circuit: + This example shows how to create and destroy an SD card file. + The circuit. Pin numbers reflect the default + SPI pins for Uno and Nano models: SD card attached to SPI bus as follows: - ** MOSI - pin 11 - ** MISO - pin 12 + ** SDO - pin 11 + ** SDI - pin 12 ** CLK - pin 13 - ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) + ** CS - depends on your SD card shield or module. + Pin 10 used here for consistency with other Arduino examples + (for MKR Zero SD: SDCARD_SS_PIN) created Nov 2010 by David A. Mellis - modified 9 Apr 2012 + modified 24 July 2020 by Tom Igoe This example code is in the public domain. - */ -#include #include +const int chipSelect = 10; File myFile; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only - } - + // wait for Serial Monitor to connect. Needed for native USB port boards only: +while (!Serial); Serial.print("Initializing SD card..."); - if (!SD.begin(4)) { - Serial.println("initialization failed!"); + if (!SD.begin(chipSelect)) { + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this serial monitor after fixing your issue!"); while (1); } Serial.println("initialization done."); @@ -70,6 +74,3 @@ void setup() { void loop() { // nothing happens after setup finishes. } - - - diff --git a/trunk/Arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino b/trunk/Arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino index 29d8ec66..101bfd05 100644 --- a/trunk/Arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino +++ b/trunk/Arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino @@ -3,89 +3,116 @@ This example demonstrates how to perform non-blocking writes to a file on a SD card. The file will contain the current millis() - value every 10ms. If the SD card is busy, the data will be buffered + value every 10ms. If the SD card is busy, the data will be dataBuffered in order to not block the sketch. + If data is successfully written, the built in LED will flash. After a few + seconds, check the card for a file called datalog.txt + NOTE: myFile.availableForWrite() will automatically sync the file contents as needed. You may lose some unsynced data still if myFile.sync() or myFile.close() is not called. + Pin numbers reflect the default SPI pins for Uno and Nano models + Updated for clarity and uniformity with other examples + The circuit: - - Arduino MKR Zero board - - micro SD card attached + analog sensors on analog ins 0, 1, and 2 + SD card attached to SPI bus as follows: + ** SDO - pin 11 + ** SDI - pin 12 + ** CLK - pin 13 + ** CS - depends on your SD card shield or module. + Pin 10 used here for consistency with other Arduino examples + (for MKR Zero SD: SDCARD_SS_PIN) + + modified 24 July 2020 + by Tom Igoe This example code is in the public domain. */ - #include +const int chipSelect = 10; + // file name to use for writing -const char filename[] = "demo.txt"; +const char filename[] = "datalog.txt"; // File object to represent file -File txtFile; - +File myFile; // string to buffer output -String buffer; - +String dataBuffer; +// last time data was written to card: unsigned long lastMillis = 0; void setup() { + // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial); - - // reserve 1kB for String used as a buffer - buffer.reserve(1024); + // reserve 1 kB for String used as a dataBuffer + dataBuffer.reserve(1024); // set LED pin to output, used to blink when writing pinMode(LED_BUILTIN, OUTPUT); - // init the SD card - if (!SD.begin()) { - Serial.println("Card failed, or not present"); - // don't do anything more: - while (1); + // wait for Serial Monitor to connect. Needed for native USB port boards only: + while (!Serial); + + Serial.print("Initializing SD card..."); + + if (!SD.begin(chipSelect)) { + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); + while (true); } + Serial.println("initialization done."); + // If you want to start from an empty file, // uncomment the next line: - // SD.remove(filename); - + // SD.remove(filename); // try to open the file for writing - txtFile = SD.open(filename, FILE_WRITE); - if (!txtFile) { + + myFile = SD.open(filename, FILE_WRITE); + if (!myFile) { Serial.print("error opening "); Serial.println(filename); - while (1); + while (true); } // add some new lines to start - txtFile.println(); - txtFile.println("Hello World!"); + myFile.println(); + myFile.println("Hello World!"); + Serial.println("Starting to write to file..."); } void loop() { // check if it's been over 10 ms since the last line added unsigned long now = millis(); if ((now - lastMillis) >= 10) { - // add a new line to the buffer - buffer += "Hello "; - buffer += now; - buffer += "\r\n"; - + // add a new line to the dataBuffer + dataBuffer += "Hello "; + dataBuffer += now; + dataBuffer += "\r\n"; + // print the buffer length. This will change depending on when + // data is actually written to the SD card file: + Serial.print("Unsaved data buffer length (in bytes): "); + Serial.println(dataBuffer.length()); + // note the time that the last line was added to the string lastMillis = now; } // check if the SD card is available to write data without blocking - // and if the buffered data is enough for the full chunk size - unsigned int chunkSize = txtFile.availableForWrite(); - if (chunkSize && buffer.length() >= chunkSize) { + // and if the dataBuffered data is enough for the full chunk size + unsigned int chunkSize = myFile.availableForWrite(); + if (chunkSize && dataBuffer.length() >= chunkSize) { // write to file and blink LED digitalWrite(LED_BUILTIN, HIGH); - txtFile.write(buffer.c_str(), chunkSize); + myFile.write(dataBuffer.c_str(), chunkSize); digitalWrite(LED_BUILTIN, LOW); - - // remove written data from buffer - buffer.remove(0, chunkSize); + // remove written data from dataBuffer + dataBuffer.remove(0, chunkSize); } } diff --git a/trunk/Arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino b/trunk/Arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino index d964668c..b505a274 100644 --- a/trunk/Arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino +++ b/trunk/Arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino @@ -2,41 +2,44 @@ SD card read/write This example shows how to read and write data to and from an SD card file - The circuit: + The circuit. Pin numbers reflect the default + SPI pins for Uno and Nano models: SD card attached to SPI bus as follows: - ** MOSI - pin 11 - ** MISO - pin 12 + ** SDO - pin 11 + ** SDI - pin 12 ** CLK - pin 13 - ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) + ** CS - pin 4 (For For Uno, Nano: pin 10. For MKR Zero SD: SDCARD_SS_PIN) created Nov 2010 by David A. Mellis - modified 9 Apr 2012 + modified 24 July 2020 by Tom Igoe This example code is in the public domain. */ - -#include #include +const int chipSelect = 10; File myFile; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only - } - + // wait for Serial Monitor to connect. Needed for native USB port boards only: + while (!Serial); Serial.print("Initializing SD card..."); - if (!SD.begin(4)) { - Serial.println("initialization failed!"); - while (1); + if (!SD.begin(chipSelect)) { + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); + while (true); } + Serial.println("initialization done."); // open the file. note that only one file can be open at a time, @@ -75,5 +78,3 @@ void setup() { void loop() { // nothing happens after setup } - - diff --git a/trunk/Arduino/libraries/SD/examples/listfiles/listfiles.ino b/trunk/Arduino/libraries/SD/examples/listfiles/listfiles.ino index 48f84fb9..ded9b13f 100644 --- a/trunk/Arduino/libraries/SD/examples/listfiles/listfiles.ino +++ b/trunk/Arduino/libraries/SD/examples/listfiles/listfiles.ino @@ -1,15 +1,18 @@ /* Listfiles - This example shows how print out the files in a - directory on a SD card + This example shows how to print out the files in a + directory on a SD card. Pin numbers reflect the default + SPI pins for Uno and Nano models The circuit: SD card attached to SPI bus as follows: - ** MOSI - pin 11 - ** MISO - pin 12 + ** SDO - pin 11 + ** SDI - pin 12 ** CLK - pin 13 - ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) + ** CS - depends on your SD card shield or module. + Pin 10 used here for consistency with other Arduino examples + (for MKR Zero SD: SDCARD_SS_PIN) created Nov 2010 by David A. Mellis @@ -17,28 +20,34 @@ by Tom Igoe modified 2 Feb 2014 by Scott Fitzgerald - + modified 24 July 2020 + by Tom Igoe + This example code is in the public domain. */ -#include #include +const int chipSelect = 10; File root; void setup() { - // Open serial communications and wait for port to open: + // Open serial communications and wait for port to open: Serial.begin(9600); - while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only - } + // wait for Serial Monitor to connect. Needed for native USB port boards only: + while (!Serial); Serial.print("Initializing SD card..."); - if (!SD.begin(4)) { - Serial.println("initialization failed!"); - while (1); + if (!SD.begin(chipSelect)) { + Serial.println("initialization failed. Things to check:"); + Serial.println("1. is a card inserted?"); + Serial.println("2. is your wiring correct?"); + Serial.println("3. did you change the chipSelect pin to match your shield or module?"); + Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"); + while (true); } + Serial.println("initialization done."); root = SD.open("/"); @@ -75,6 +84,3 @@ void printDirectory(File dir, int numTabs) { entry.close(); } } - - - diff --git a/trunk/Arduino/libraries/SD/library.properties b/trunk/Arduino/libraries/SD/library.properties index 60e88592..9e6d791a 100644 --- a/trunk/Arduino/libraries/SD/library.properties +++ b/trunk/Arduino/libraries/SD/library.properties @@ -1,9 +1,9 @@ name=SD -version=1.2.4 +version=1.3.0 author=Arduino, SparkFun maintainer=Arduino sentence=Enables reading and writing on SD cards. -paragraph=Once an SD memory card is connected to the SPI interface of the Arduino or Genuino board you can create files and read/write on them. You can also move through directories on the SD card. +paragraph=Once an SD memory card is connected to the SPI interface of the Arduino board you can create files and read/write on them. You can also move through directories on the SD card. category=Data Storage url=http://www.arduino.cc/en/Reference/SD architectures=* diff --git a/trunk/Arduino/libraries/SD/src/File.cpp b/trunk/Arduino/libraries/SD/src/File.cpp index 5e37166b..3e27fb41 100644 --- a/trunk/Arduino/libraries/SD/src/File.cpp +++ b/trunk/Arduino/libraries/SD/src/File.cpp @@ -49,7 +49,7 @@ char *File::name(void) { } // a directory is a special type of file -boolean File::isDirectory(void) { +bool File::isDirectory(void) { return (_file && _file->isDir()); } @@ -123,7 +123,7 @@ void File::flush() { } } -boolean File::seek(uint32_t pos) { +bool File::seek(uint32_t pos) { if (! _file) { return false; } diff --git a/trunk/Arduino/libraries/SD/src/SD.cpp b/trunk/Arduino/libraries/SD/src/SD.cpp index 8c9a29e7..423db451 100644 --- a/trunk/Arduino/libraries/SD/src/SD.cpp +++ b/trunk/Arduino/libraries/SD/src/SD.cpp @@ -54,9 +54,11 @@ namespace SDLib { - // Used by `getNextPathComponent` -#define MAX_COMPONENT_LEN 12 // What is max length? -#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1 +// Used by `getNextPathComponent` +#define MAX_COMPONENT_LEN 12 +#define PATH_COMPONENT_BUFFER_LEN (MAX_COMPONENT_LEN + 1) +// BASENAME:char(8) + '.':char(1) + EXT:char(3) = 12 (a.k.a short 8.3 name) +// And an extra space for '\0' for path buffer bool getNextPathComponent(const char *path, unsigned int *p_offset, char *buffer) { @@ -117,12 +119,12 @@ namespace SDLib { - boolean walkPath(const char *filepath, SdFile& parentDir, - boolean(*callback)(SdFile& parentDir, - const char *filePathComponent, - boolean isLastComponent, - void *object), - void *object = NULL) { + bool walkPath(const char *filepath, SdFile& parentDir, + bool(*callback)(SdFile& parentDir, + const char *filePathComponent, + bool isLastComponent, + void *object), + void *object = NULL) { /* When given a file path (and parent directory--normally root), @@ -170,9 +172,9 @@ namespace SDLib { while (true) { - boolean moreComponents = getNextPathComponent(filepath, &offset, buffer); + bool moreComponents = getNextPathComponent(filepath, &offset, buffer); - boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + bool shouldContinue = callback((*p_parent), buffer, !moreComponents, object); if (!shouldContinue) { // TODO: Don't repeat this code? @@ -188,7 +190,7 @@ namespace SDLib { break; } - boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY); + bool exists = (*p_child).open(*p_parent, buffer, O_RDONLY); // If it's one we've created then we // don't need the parent handle anymore. @@ -232,8 +234,8 @@ namespace SDLib { */ - boolean callback_pathExists(SdFile& parentDir, const char *filePathComponent, - boolean /* isLastComponent */, void * /* object */) { + bool callback_pathExists(SdFile& parentDir, const char *filePathComponent, + bool /* isLastComponent */, void * /* object */) { /* Callback used to determine if a file/directory exists in parent @@ -244,7 +246,7 @@ namespace SDLib { */ SdFile child; - boolean exists = child.open(parentDir, filePathComponent, O_RDONLY); + bool exists = child.open(parentDir, filePathComponent, O_RDONLY); if (exists) { child.close(); @@ -255,8 +257,8 @@ namespace SDLib { - boolean callback_makeDirPath(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void *object) { + bool callback_makeDirPath(SdFile& parentDir, const char *filePathComponent, + bool isLastComponent, void *object) { /* Callback used to create a directory in the parent directory if @@ -265,7 +267,7 @@ namespace SDLib { Returns true if a directory was created or it already existed. */ - boolean result = false; + bool result = false; SdFile child; result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); @@ -279,8 +281,8 @@ namespace SDLib { /* - boolean callback_openPath(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { + bool callback_openPath(SdFile& parentDir, char *filePathComponent, + bool isLastComponent, void *object) { Callback used to open a file specified by a filepath that may specify one or more directories above it. @@ -310,16 +312,16 @@ namespace SDLib { - boolean callback_remove(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void * /* object */) { + bool callback_remove(SdFile& parentDir, const char *filePathComponent, + bool isLastComponent, void * /* object */) { if (isLastComponent) { return SdFile::remove(parentDir, filePathComponent); } return true; } - boolean callback_rmdir(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void * /* object */) { + bool callback_rmdir(SdFile& parentDir, const char *filePathComponent, + bool isLastComponent, void * /* object */) { if (isLastComponent) { SdFile f; if (!f.open(parentDir, filePathComponent, O_READ)) { @@ -336,7 +338,7 @@ namespace SDLib { - boolean SDClass::begin(uint8_t csPin) { + bool SDClass::begin(uint8_t csPin) { if (root.isOpen()) { root.close(); } @@ -353,7 +355,7 @@ namespace SDLib { root.openRoot(volume); } - boolean SDClass::begin(uint32_t clock, uint8_t csPin) { + bool SDClass::begin(uint32_t clock, uint8_t csPin) { if (root.isOpen()) { root.close(); } @@ -453,7 +455,7 @@ namespace SDLib { */ - int pathidx; + int pathidx = 0; // do the interactive search SdFile parentdir = getParentDir(filepath, &pathidx); @@ -523,7 +525,7 @@ namespace SDLib { */ - //boolean SDClass::close() { + //bool SDClass::close() { // /* // // Closes the file opened by the `open` method. @@ -533,7 +535,7 @@ namespace SDLib { //} - boolean SDClass::exists(const char *filepath) { + bool SDClass::exists(const char *filepath) { /* Returns true if the supplied file path exists. @@ -543,7 +545,7 @@ namespace SDLib { } - //boolean SDClass::exists(char *filepath, SdFile& parentDir) { + //bool SDClass::exists(char *filepath, SdFile& parentDir) { // /* // // Returns true if the supplied file path rooted at `parentDir` @@ -554,7 +556,7 @@ namespace SDLib { //} - boolean SDClass::mkdir(const char *filepath) { + bool SDClass::mkdir(const char *filepath) { /* Makes a single directory or a hierarchy of directories. @@ -565,7 +567,7 @@ namespace SDLib { return walkPath(filepath, root, callback_makeDirPath); } - boolean SDClass::rmdir(const char *filepath) { + bool SDClass::rmdir(const char *filepath) { /* Remove a single directory or a hierarchy of directories. @@ -576,7 +578,7 @@ namespace SDLib { return walkPath(filepath, root, callback_rmdir); } - boolean SDClass::remove(const char *filepath) { + bool SDClass::remove(const char *filepath) { return walkPath(filepath, root, callback_remove); } diff --git a/trunk/Arduino/libraries/SD/src/SD.h b/trunk/Arduino/libraries/SD/src/SD.h index fa34ccd5..c81a7d3c 100644 --- a/trunk/Arduino/libraries/SD/src/SD.h +++ b/trunk/Arduino/libraries/SD/src/SD.h @@ -41,14 +41,14 @@ namespace SDLib { virtual int available(); virtual void flush(); int read(void *buf, uint16_t nbyte); - boolean seek(uint32_t pos); + bool seek(uint32_t pos); uint32_t position(); uint32_t size(); void close(); operator bool(); char * name(); - boolean isDirectory(void); + bool isDirectory(void); File openNextFile(uint8_t mode = O_RDONLY); void rewindDirectory(void); @@ -68,8 +68,8 @@ namespace SDLib { public: // This needs to be called to set up the connection to the SD card // before other methods are used. - boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN); - boolean begin(uint32_t clock, uint8_t csPin); + bool begin(uint8_t csPin = SD_CHIP_SELECT_PIN); + bool begin(uint32_t clock, uint8_t csPin); //call this when a card is removed. It will allow you to insert and initialise a new card. void end(); @@ -83,26 +83,26 @@ namespace SDLib { } // Methods to determine if the requested file path exists. - boolean exists(const char *filepath); - boolean exists(const String &filepath) { + bool exists(const char *filepath); + bool exists(const String &filepath) { return exists(filepath.c_str()); } // Create the requested directory heirarchy--if intermediate directories // do not exist they will be created. - boolean mkdir(const char *filepath); - boolean mkdir(const String &filepath) { + bool mkdir(const char *filepath); + bool mkdir(const String &filepath) { return mkdir(filepath.c_str()); } // Delete the file. - boolean remove(const char *filepath); - boolean remove(const String &filepath) { + bool remove(const char *filepath); + bool remove(const String &filepath) { return remove(filepath.c_str()); } - boolean rmdir(const char *filepath); - boolean rmdir(const String &filepath) { + bool rmdir(const char *filepath); + bool rmdir(const String &filepath) { return rmdir(filepath.c_str()); } @@ -116,7 +116,7 @@ namespace SDLib { int fileOpenMode; friend class File; - friend boolean callback_openPath(SdFile&, const char *, boolean, void *); + friend bool callback_openPath(SdFile&, const char *, bool, void *); }; extern SDClass SD; diff --git a/trunk/Arduino/libraries/SD/src/utility/Sd2PinMap.h b/trunk/Arduino/libraries/SD/src/utility/Sd2PinMap.h index 0609ffe3..12d8926f 100644 --- a/trunk/Arduino/libraries/SD/src/utility/Sd2PinMap.h +++ b/trunk/Arduino/libraries/SD/src/utility/Sd2PinMap.h @@ -31,7 +31,10 @@ #endif // Sd2PinMap_h -#elif defined(__AVR_ATmega4809__) // Arduino UNO WiFI Rev2 follows +#elif defined(__AVR_ATmega4809__) || defined(__AVR_ATmega4808__) || \ +defined(__AVR_ATmega3209__) || defined(__AVR_ATmega3208__) || \ +defined(__AVR_ATmega1609__) || defined(__AVR_ATmega1608__) || \ +defined(__AVR_ATmega809__) || defined(__AVR_ATmega808__) #ifndef Sd2PinMap_h #define Sd2PinMap_h diff --git a/trunk/Arduino/libraries/SD/src/utility/SdFat.h b/trunk/Arduino/libraries/SD/src/utility/SdFat.h index e57974df..46b880bd 100644 --- a/trunk/Arduino/libraries/SD/src/utility/SdFat.h +++ b/trunk/Arduino/libraries/SD/src/utility/SdFat.h @@ -41,17 +41,17 @@ class SdVolume; // SdFile class #ifdef O_RDONLY //ARDUINO_ARCH_MBED -#undef O_READ -#undef O_RDONLY -#undef O_WRITE -#undef O_WRONLY -#undef O_RDWR -#undef O_ACCMODE -#undef O_APPEND -#undef O_SYNC -#undef O_CREAT -#undef O_EXCL -#undef O_TRUNC + #undef O_READ + #undef O_RDONLY + #undef O_WRITE + #undef O_WRONLY + #undef O_RDWR + #undef O_ACCMODE + #undef O_APPEND + #undef O_SYNC + #undef O_CREAT + #undef O_EXCL + #undef O_TRUNC #endif // flags for ls() diff --git a/trunk/Arduino/libraries/SdFat/README.md b/trunk/Arduino/libraries/SdFat/README.md index ec001ee1..e16f74e4 100644 --- a/trunk/Arduino/libraries/SdFat/README.md +++ b/trunk/Arduino/libraries/SdFat/README.md @@ -1,13 +1,70 @@ -### Warning: Major Reformat of Source in 2.2.2 +### Warning: This version has major internal changes. -There are a huge number of changes in 2.2.2 since I decided to use clang-format -to force Google style formatting. +SdFat version 2.3.0 has major changes to implement RP2040/RP2350 SDIO. -I did this to avoid warnings from the static analysis programs Cppcheck and -cpplint. +In addition there are number of bug fixes. -clang-format is aggressive so it may actually cause code to fail. For example -clang-format rearranges the order of includes according to the selected style. +Begin by running the Rp2040SdioSetup example to try RP2040/RP2350 SDIO. + +This example requires a SDIO Card socket with the following six lines. + +CLK - A clock signal sent to the card by the MCU. +CMD - A bidirectional line for for commands and responses. +DAT[0:3] - Four bidirectional lines for data transfer. + +CLK and CMD can be connected to any GPIO pins. DAT[0:3] can be connected +to any four consecutive GPIO pins in the order DAT0, DAT1, DAT2, DAT3. + +Here is an example of SDIO for Pico using an Adafruit socket, PiCowbell +Proto and PiCowbell Proto Doubler. + +![Alt text](images/SdioSpi.jpg) + +This Socket supports SDIO with: +``` +#define RP_CLK_GPIO 10 +#define RP_CMD_GPIO 11 +#define RP_DAT0_GPIO 12 // DAT1: GPIO13 DAT2: GPIO14, DAT3: GPIO15. +``` +It also can be used on SPI1 with: +``` +const uint8_t SD_CS_PIN = 15; +#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK, &SPI1) + + // In setup + SPI1.setSCK(10); + SPI1.setTX(11); + SPI1.setRX(12); +``` + +This setup gets the following result in the bench example using SDIO. + +
+FILE_SIZE_MB = 5
+BUF_SIZE = 512 bytes
+Starting write test, please wait.
+
+write speed and latency
+speed,max,min,avg
+KB/Sec,usec,usec,usec
+15014.05,1165,32,32
+15289.54,1249,32,32
+
+Starting read test, please wait.
+
+read speed and latency
+speed,max,min,avg
+KB/Sec,usec,usec,usec
+15624.00,58,32,32
+15624.00,51,32,32
+
+ + +File copy constructors and file assignment operators have been made private by +default in 2.2.3 to prevent call by value and multiple copies of file instances. + +SdFatConfig.h has options to make file constructors and assignment operators +public. UTF-8 encoded filenames are supported in v2.1.0 or later. diff --git a/trunk/Arduino/libraries/SdFat/examples/DirectoryFunctions/DirectoryFunctions.ino b/trunk/Arduino/libraries/SdFat/examples/DirectoryFunctions/DirectoryFunctions.ino index e102a438..9ab4db70 100644 --- a/trunk/Arduino/libraries/SdFat/examples/DirectoryFunctions/DirectoryFunctions.ino +++ b/trunk/Arduino/libraries/SdFat/examples/DirectoryFunctions/DirectoryFunctions.ino @@ -1,12 +1,13 @@ /* * Example use of chdir(), ls(), mkdir(), and rmdir(). */ +#define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" #include "sdios.h" // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. -#define SD_FAT_TYPE 0 +#define SD_FAT_TYPE 3 /* Change the value of SD_CS_PIN if you are using SPI and your hardware does not use the default value, SS. @@ -28,13 +29,16 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; #define SPI_CLOCK SD_SCK_MHZ(50) // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO //------------------------------------------------------------------------------ #if SD_FAT_TYPE == 0 diff --git a/trunk/Arduino/libraries/SdFat/examples/OpenNext/OpenNext.ino b/trunk/Arduino/libraries/SdFat/examples/OpenNext/OpenNext.ino index 51fd4911..a2ff1242 100644 --- a/trunk/Arduino/libraries/SdFat/examples/OpenNext/OpenNext.ino +++ b/trunk/Arduino/libraries/SdFat/examples/OpenNext/OpenNext.ino @@ -1,11 +1,12 @@ /* * Print size, modify date/time, and name for all files in root. */ +#define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. -#define SD_FAT_TYPE 0 +#define SD_FAT_TYPE 3 /* Change the value of SD_CS_PIN if you are using SPI and your hardware does not use the default value, SS. @@ -27,13 +28,16 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; #define SPI_CLOCK SD_SCK_MHZ(50) // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO #if SD_FAT_TYPE == 0 SdFat sd; diff --git a/trunk/Arduino/libraries/SdFat/examples/QuickStart/QuickStart.ino b/trunk/Arduino/libraries/SdFat/examples/QuickStart/QuickStart.ino index 83fe99ac..eb884ab7 100644 --- a/trunk/Arduino/libraries/SdFat/examples/QuickStart/QuickStart.ino +++ b/trunk/Arduino/libraries/SdFat/examples/QuickStart/QuickStart.ino @@ -143,7 +143,7 @@ void loop() { } cout << F("\nCard successfully initialized.\n"); if (sd.vol()->fatType() == 0) { - cout << F("Can't find a valid FAT16/FAT32 partition.\n"); + cout << F("Can't find a valid FAT16/FAT32/exFAT partition.\n"); reformatMsg(); return; } @@ -163,7 +163,11 @@ void loop() { cout << F("Card size: ") << sizeMB; cout << F(" MB (MB = 1,000,000 bytes)\n"); cout << endl; - cout << F("Volume is FAT") << int(sd.vol()->fatType()); + if (sd.fatType() <= 32) { + cout << F("\nVolume is FAT") << int(sd.fatType()); + } else { + cout << F("\nVolume is exFAT"); + } cout << F(", Cluster size (bytes): ") << sd.vol()->bytesPerCluster(); cout << endl << endl; diff --git a/trunk/Arduino/libraries/SdFat/examples/SdFormatter/SdFormatter.ino b/trunk/Arduino/libraries/SdFat/examples/SdFormatter/SdFormatter.ino index 6cfd37be..1bc5a605 100644 --- a/trunk/Arduino/libraries/SdFat/examples/SdFormatter/SdFormatter.ino +++ b/trunk/Arduino/libraries/SdFat/examples/SdFormatter/SdFormatter.ino @@ -10,6 +10,7 @@ * For very small cards this program uses FAT16 * and the above SDFormatter uses FAT12. */ +#define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" #include "sdios.h" @@ -40,13 +41,16 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; #define SPI_CLOCK SD_SCK_MHZ(50) // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO //============================================================================== // Serial output stream ArduinoOutStream cout(Serial); diff --git a/trunk/Arduino/libraries/SdFat/examples/SdInfo/SdInfo.ino b/trunk/Arduino/libraries/SdFat/examples/SdInfo/SdInfo.ino index 7ac7815d..5d04570d 100644 --- a/trunk/Arduino/libraries/SdFat/examples/SdInfo/SdInfo.ino +++ b/trunk/Arduino/libraries/SdFat/examples/SdInfo/SdInfo.ino @@ -1,6 +1,12 @@ /* * This program attempts to initialize an SD card and analyze its structure. + * The CID and CSD registers are also printed in HEX for use in online + * decoders like these. + * + * https://gurumeditation.org/1342/sd-memory-card-register-decoder/ + * https://archive.goughlui.com/static/multicid.htm */ +#define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" #include "sdios.h" /* @@ -25,13 +31,16 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; #endif // SDCARD_SS_PIN // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16)) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16)) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO //------------------------------------------------------------------------------ SdFs sd; @@ -55,7 +64,8 @@ void cidDmp() { cout << F("Serial number: ") << hex << cid.psn() << dec << endl; cout << F("Manufacturing date: "); cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl; - cout << endl; + cout << F("CID HEX: "); + hexDmp(&cid, sizeof(cid)); } //------------------------------------------------------------------------------ void clearSerialInput() { @@ -69,7 +79,7 @@ void clearSerialInput() { //------------------------------------------------------------------------------ void csdDmp() { eraseSize = csd.eraseSize(); - cout << F("cardSize: ") << 0.000512 * csd.capacity(); + cout << F("\ncardSize: ") << 0.000512 * csd.capacity(); cout << F(" MB (MB = 1,000,000 bytes)\n"); cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n"); @@ -85,6 +95,8 @@ void csdDmp() { } else { cout << F("zeros\n"); } + cout << F("CSD HEX: "); + hexDmp(&csd, sizeof(csd)); } //------------------------------------------------------------------------------ void errorPrint() { @@ -96,10 +108,19 @@ void errorPrint() { } } //------------------------------------------------------------------------------ +void hexDmp(void* reg, uint8_t size) { + uint8_t* u8 = reinterpret_cast(reg); + cout << hex << noshowbase; + for (size_t i = 0; i < size; i++) { + cout << setw(2) << setfill('0') << int(u8[i]); + } + cout << dec << endl; +} +//------------------------------------------------------------------------------ bool mbrDmp() { MbrSector_t mbr; bool valid = true; - if (!sd.card()->readSector(0, (uint8_t *)&mbr)) { + if (!sd.card()->readSector(0, (uint8_t*)&mbr)) { cout << F("\nread MBR failed.\n"); errorPrint(); return false; @@ -107,7 +128,7 @@ bool mbrDmp() { cout << F("\nSD Partition Table\n"); cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n"); for (uint8_t ip = 1; ip < 5; ip++) { - MbrPart_t *pt = &mbr.part[ip - 1]; + MbrPart_t* pt = &mbr.part[ip - 1]; if ((pt->boot != 0 && pt->boot != 0X80) || getLe32(pt->relativeSectors) > csd.capacity()) { valid = false; @@ -242,7 +263,7 @@ void loop() { printCardType(); cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl; cout << F("HighSpeedMode: "); - if (scr.sdSpecVer() && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) && + if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) && (2 & cmd6Data[13])) { cout << F("true\n"); } else { diff --git a/trunk/Arduino/libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino b/trunk/Arduino/libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino index 388bf9f2..1300cfa0 100644 --- a/trunk/Arduino/libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino +++ b/trunk/Arduino/libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino @@ -8,7 +8,7 @@ // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. -#define SD_FAT_TYPE 0 +#define SD_FAT_TYPE 3 // // Chip select may be constant or RAM variable. const uint8_t SD_CS_PIN = 10; diff --git a/trunk/Arduino/libraries/SdFat/examples/bench/bench.ino b/trunk/Arduino/libraries/SdFat/examples/bench/bench.ino index 4778f44d..1c646f67 100644 --- a/trunk/Arduino/libraries/SdFat/examples/bench/bench.ino +++ b/trunk/Arduino/libraries/SdFat/examples/bench/bench.ino @@ -1,13 +1,22 @@ /* * This program is a simple binary write/read benchmark. */ -#include "FreeStack.h" +#define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" +#include "FreeStack.h" #include "sdios.h" // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. -#define SD_FAT_TYPE 0 +#if defined __has_include +#if __has_include() +#define SD_FAT_TYPE 3 // Can't use SdFat/File +#endif // __has_include() +#endif // defined __has_include + +#ifndef SD_FAT_TYPE +#define SD_FAT_TYPE 0 // Use SdFat/File +#endif // SD_FAT_TYPE /* Change the value of SD_CS_PIN if you are using SPI and your hardware does not use the default value, SS. @@ -27,14 +36,24 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; // Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur. #define SPI_CLOCK SD_SCK_MHZ(50) +// Example SDIO definition for RP2040/RP2350. See the Rp2040SdioSetup example. +#if defined(ARDUINO_ADAFRUIT_METRO_RP2040) && !defined(RP_CLK_GPIO) +#define RP_CLK_GPIO 18 +#define RP_CMD_GPIO 19 +#define RP_DAT0_GPIO 20 // DAT1: GPIO21, DAT2: GPIO22, DAT3: GPIO23. +#endif // defined(ARDUINO_ADAFRUIT_METRO_RP2040) + // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO // Set PRE_ALLOCATE true to pre-allocate file clusters. const bool PRE_ALLOCATE = true; @@ -128,6 +147,11 @@ void setup() { "\nSet ENABLE_DEDICATED_SPI nonzero in\n" "SdFatConfig.h for best SPI performance.\n"); } + if (!SD_HAS_CUSTOM_SPI && !USE_SPI_ARRAY_TRANSFER && isSpi(SD_CONFIG)) { + cout << F( + "\nSetting USE_SPI_ARRAY_TRANSFER nonzero in\n" + "SdFatConfig.h may improve SPI performance.\n"); + } // use uppercase in hex and use 0X base prefix cout << uppercase << showbase << endl; } diff --git a/trunk/Arduino/libraries/SdFat/examples/rename/rename.ino b/trunk/Arduino/libraries/SdFat/examples/rename/rename.ino index 2a512340..aa9fedcc 100644 --- a/trunk/Arduino/libraries/SdFat/examples/rename/rename.ino +++ b/trunk/Arduino/libraries/SdFat/examples/rename/rename.ino @@ -1,12 +1,13 @@ /* * This program demonstrates use of rename(). */ + #define DISABLE_FS_H_WARNING // Disable warning for type File not defined. #include "SdFat.h" #include "sdios.h" // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. -#define SD_FAT_TYPE 0 +#define SD_FAT_TYPE 3 /* Change the value of SD_CS_PIN if you are using SPI and @@ -29,13 +30,16 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN; #define SPI_CLOCK SD_SCK_MHZ(50) // Try to select the best SD card configuration. -#if HAS_SDIO_CLASS +#if defined(HAS_TEENSY_SDIO) #define SD_CONFIG SdioConfig(FIFO_SDIO) +#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO) +// See the Rp2040SdioSetup example for RP2040/RP2350 boards. +#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO) #elif ENABLE_DEDICATED_SPI #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK) -#else // HAS_SDIO_CLASS +#else // HAS_TEENSY_SDIO #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK) -#endif // HAS_SDIO_CLASS +#endif // HAS_TEENSY_SDIO #if SD_FAT_TYPE == 0 SdFat sd; diff --git a/trunk/Arduino/libraries/SdFat/extras/cpplint.bat b/trunk/Arduino/libraries/SdFat/extras/cpplint.bat index f874f45f..3246259e 100644 --- a/trunk/Arduino/libraries/SdFat/extras/cpplint.bat +++ b/trunk/Arduino/libraries/SdFat/extras/cpplint.bat @@ -1,2 +1,2 @@ -sh cpplint.sh +bash cpplint.sh pause \ No newline at end of file diff --git a/trunk/Arduino/libraries/SdFat/extras/cpplint.py b/trunk/Arduino/libraries/SdFat/extras/cpplint.py index 704618f5..9d4ff5af 100644 --- a/trunk/Arduino/libraries/SdFat/extras/cpplint.py +++ b/trunk/Arduino/libraries/SdFat/extras/cpplint.py @@ -42,30 +42,43 @@ same line, but it is far from perfect (in either direction). """ import codecs +import collections import copy import getopt +import glob +import itertools import math # for log import os import re -import sre_compile import string import sys -import unicodedata import sysconfig +import unicodedata +import xml.etree.ElementTree -try: - xrange # Python 2 -except NameError: - xrange = range # Python 3 +# if empty, use defaults +_valid_extensions = set([]) +__VERSION__ = '1.7' _USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] +Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit|sed|gsed] + [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] + [--repository=path] [--linelength=digits] [--headers=x,y,...] + [--recursive] + [--exclude=path] + [--extensions=hpp,cpp,...] + [--includeorder=default|standardcfirst] + [--config=filename] [--quiet] + [--version] [file] ... + Style checker for C/C++ source files. + This is a fork of the Google style checker with minor extensions. + The style guidelines this tries to follow are those in https://google.github.io/styleguide/cppguide.html @@ -73,22 +86,37 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] certain of the problem, and 1 meaning it could be a legitimate construct. This will miss some errors, and is not a substitute for a code review. - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. + To suppress false-positive errors of certain categories, add a + 'NOLINT(category[, category...])' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. To suppress categories + on the next line use NOLINTNEXTLINE instead of NOLINT. To suppress errors in + a block of code 'NOLINTBEGIN(category[, category...])' comment to a line at + the start of the block and to end the block add a comment with 'NOLINTEND'. + NOLINT blocks are inclusive so any statements on the same line as a BEGIN + or END will have the error suppression applied. The files passed in will be linted; at least one file must be provided. - Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the - extensions with the --extensions flag. + Default linted extensions are %s. + Other file types will be ignored. + Change the extensions with the --extensions flag. Flags: - output=vs7 + output=emacs|eclipse|vs7|junit|sed|gsed By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. + compatible output (vs7) may also be used. Further support exists for + eclipse (eclipse), and JUnit (junit). XML parsers such as those used + in Jenkins and Bamboo may also be used. + The sed format outputs sed commands that should fix some of the errors. + Note that this requires gnu sed. If that is installed as gsed on your + system (common e.g. on macOS with homebrew) you can use the gsed output + format. Sed commands are written to stdout, not stderr, so you should be + able to pipe output straight to a shell to run the fixes. verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. + Errors with lower verbosity levels have lower confidence and are more + likely to be false positives. quiet Don't print anything if no errors are found. @@ -98,16 +126,24 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] error messages whose category names pass the filters will be printed. (Category names are printed with the message and look like "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". + "-FOO" means "do not print categories that start with FOO". "+FOO" means "do print categories that start with FOO". Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-whitespace,-runtime/printf,+runtime/printf_format --filter=-,+build/include_what_you_use To see a list of all the categories used in cpplint, pass no arg: --filter= + Filters can directly be limited to files and also line numbers. The + syntax is category:file:line , where line is optional. The filter limitation + works for both + and - and can be combined with ordinary filters: + + Examples: --filter=-whitespace:foo.h,+whitespace/braces:foo.h + --filter=-whitespace,-runtime/printf:foo.h:14,+runtime/printf_format:foo.h + --filter=-,+build/include_what_you_use:foo.h:321 + counting=total|toplevel|detailed The total number of errors found is always printed. If 'toplevel' is provided, then the count of errors in each of @@ -115,17 +151,41 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. + repository=path + The top level directory of the repository, used to derive the header + guard CPP variable. By default, this is determined by searching for a + path that contains .git, .hg, or .svn. When this flag is specified, the + given path is used instead. This option allows the header guard CPP + variable to remain consistent even if members of a team have different + repository root directories (such as when checking out a subdirectory + with SVN). In addition, users of non-mainstream version control systems + can use this flag to ensure readable header guard CPP variables. Examples: - Assuming that top/src/.git exists (and cwd=top/src), the header guard - CPP variables for top/src/chrome/browser/ui/browser.h are: + Assuming that Alice checks out ProjectName and Bob checks out + ProjectName/trunk and trunk contains src/chrome/ui/browser.h, then + with no --repository flag, the header guard CPP variable will be: + + Alice => TRUNK_SRC_CHROME_BROWSER_UI_BROWSER_H_ + Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_ + + If Alice uses the --repository=trunk flag and Bob omits the flag or + uses --repository=. then the header guard CPP variable will be: + + Alice => SRC_CHROME_BROWSER_UI_BROWSER_H_ + Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_ + + root=subdir + The root directory used for deriving header guard CPP variable. + This directory is relative to the top level directory of the repository + which by default is determined by searching for a directory that contains + .git, .hg, or .svn but can also be controlled with the --repository flag. + If the specified directory does not exist, this flag is ignored. + + Examples: + Assuming that src is the top level directory of the repository (and + cwd=top/src), the header guard CPP variables for + src/chrome/browser/ui/browser.h are: No flag => CHROME_BROWSER_UI_BROWSER_H_ --root=chrome => BROWSER_UI_BROWSER_H_ @@ -139,17 +199,48 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] Examples: --linelength=120 + recursive + Search for files to lint recursively. Each directory given in the list + of files to be linted is replaced by all files that descend from that + directory. Files with extensions not in the valid extensions list are + excluded. + + exclude=path + Exclude the given path from the list of files to be linted. Relative + paths are evaluated relative to the current directory and shell globbing + is performed. This flag can be provided multiple times to exclude + multiple files. + + Examples: + --exclude=one.cc + --exclude=src/*.cc + --exclude=src/*.cc --exclude=test/*.cc + extensions=extension,extension,... The allowed file extensions that cpplint will check Examples: - --extensions=hpp,cpp + --extensions=%s + + includeorder=default|standardcfirst + For the build/include_order rule, the default is to blindly assume angle + bracket includes with file extension are c-system-headers (default), + even knowing this will have false classifications. + The default is established at google. + standardcfirst means to instead use an allow-list of known c headers and + treat all others as separate group of "other system headers". The C headers + included are those of the C-standard lib and closely related ones. + + config=filename + Search for config files with the specified name instead of CPPLINT.cfg headers=x,y,... The header extensions that cpplint will treat as .h in checks. Values are automatically added to --extensions list. + (by default, only files with extensions %s will be assumed to be headers) Examples: + --headers=%s --headers=hpp,hxx --headers=hpp @@ -174,7 +265,7 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] "exclude_files" allows to specify a regular expression to be matched against a file name. If the expression matches, the file is skipped and not run - through liner. + through the linter. "linelength" allows to specify the allowed line length for the project. @@ -189,7 +280,7 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] Example file: filter=-build/include_order,+build/include_alpha - exclude_files=.*\.cc + exclude_files=.*\\.cc The above example disables build/include_order warning and enables build/include_alpha as well as excludes all .cc from being @@ -204,17 +295,19 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] _ERROR_CATEGORIES = [ 'build/class', 'build/c++11', - 'build/c++14', - 'build/c++tr1', + 'build/c++17', 'build/deprecated', 'build/endif_comment', 'build/explicit_make_pair', 'build/forward_decl', 'build/header_guard', 'build/include', + 'build/include_subdir', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', + 'build/namespaces_headers', + 'build/namespaces_literals', 'build/namespaces', 'build/printf_format', 'build/storage_class', @@ -242,7 +335,6 @@ _ERROR_CATEGORIES = [ 'runtime/invalid_increment', 'runtime/member_string_references', 'runtime/memset', - 'runtime/indentation_namespace', 'runtime/operator', 'runtime/printf', 'runtime/printf_format', @@ -261,6 +353,7 @@ _ERROR_CATEGORIES = [ 'whitespace/ending_newline', 'whitespace/forcolon', 'whitespace/indent', + 'whitespace/indent_namespace', 'whitespace/line_length', 'whitespace/newline', 'whitespace/operators', @@ -270,6 +363,13 @@ _ERROR_CATEGORIES = [ 'whitespace/todo', ] +# keywords to use with --outputs which generate stdout for machine processing +_MACHINE_OUTPUTS = [ + 'junit', + 'sed', + 'gsed' +] + # These error categories are no longer enforced by cpplint, but for backwards- # compatibility they may still appear in NOLINT comments. _LEGACY_ERROR_CATEGORIES = [ @@ -277,6 +377,36 @@ _LEGACY_ERROR_CATEGORIES = [ 'readability/function', ] +# These prefixes for categories should be ignored since they relate to other +# tools which also use the NOLINT syntax, e.g. clang-tidy. +_OTHER_NOLINT_CATEGORY_PREFIXES = [ + 'clang-analyzer-', + 'abseil-', + 'altera-', + 'android-', + 'boost-', + 'bugprone-', + 'cert-', + 'concurrency-', + 'cppcoreguidelines-', + 'darwin-', + 'fuchsia-', + 'google-', + 'hicpp-', + 'linuxkernel-', + 'llvm-', + 'llvmlibc-', + 'misc-', + 'modernize-', + 'mpi-', + 'objc-', + 'openmp-', + 'performance-', + 'portability-', + 'readability-', + 'zircon-', + ] + # The default state of the category filter. This is overridden by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). @@ -305,7 +435,7 @@ _CPP_HEADERS = frozenset([ 'alloc.h', 'builtinbuf.h', 'bvector.h', - 'complex.h', + # 'complex.h', collides with System C header "complex.h" since C11 'defalloc.h', 'deque.h', 'editbuf.h', @@ -351,7 +481,7 @@ _CPP_HEADERS = frozenset([ 'tree.h', 'type_traits.h', 'vector.h', - # 17.6.1.2 C++ library headers + # C++ library headers 'algorithm', 'array', 'atomic', @@ -405,7 +535,45 @@ _CPP_HEADERS = frozenset([ 'utility', 'valarray', 'vector', - # 17.6.1.2 C++ headers for C library facilities + # C++14 headers + 'shared_mutex', + # C++17 headers + 'any', + 'charconv', + 'codecvt', + 'execution', + 'filesystem', + 'memory_resource', + 'optional', + 'string_view', + 'variant', + # C++20 headers + 'barrier', + 'bit', + 'compare', + 'concepts', + 'coroutine', + 'format', + 'latch' + 'numbers', + 'ranges', + 'semaphore', + 'source_location', + 'span', + 'stop_token', + 'syncstream', + 'version', + # C++23 headers + 'expected', + 'flat_map', + 'flat_set', + 'generator', + 'mdspan', + 'print', + 'spanstream', + 'stacktrace', + 'stdfloat', + # C++ headers for C library facilities 'cassert', 'ccomplex', 'cctype', @@ -434,6 +602,189 @@ _CPP_HEADERS = frozenset([ 'cwctype', ]) +# C headers +_C_HEADERS = frozenset([ + # System C headers + 'assert.h', + 'complex.h', + 'ctype.h', + 'errno.h', + 'fenv.h', + 'float.h', + 'inttypes.h', + 'iso646.h', + 'limits.h', + 'locale.h', + 'math.h', + 'setjmp.h', + 'signal.h', + 'stdalign.h', + 'stdarg.h', + 'stdatomic.h', + 'stdbool.h', + 'stddef.h', + 'stdint.h', + 'stdio.h', + 'stdlib.h', + 'stdnoreturn.h', + 'string.h', + 'tgmath.h', + 'threads.h', + 'time.h', + 'uchar.h', + 'wchar.h', + 'wctype.h', + # C23 headers + 'stdbit.h', + 'stdckdint.h', + # additional POSIX C headers + 'aio.h', + 'arpa/inet.h', + 'cpio.h', + 'dirent.h', + 'dlfcn.h', + 'fcntl.h', + 'fmtmsg.h', + 'fnmatch.h', + 'ftw.h', + 'glob.h', + 'grp.h', + 'iconv.h', + 'langinfo.h', + 'libgen.h', + 'monetary.h', + 'mqueue.h', + 'ndbm.h', + 'net/if.h', + 'netdb.h', + 'netinet/in.h', + 'netinet/tcp.h', + 'nl_types.h', + 'poll.h', + 'pthread.h', + 'pwd.h', + 'regex.h', + 'sched.h', + 'search.h', + 'semaphore.h', + 'setjmp.h', + 'signal.h', + 'spawn.h', + 'strings.h', + 'stropts.h', + 'syslog.h', + 'tar.h', + 'termios.h', + 'trace.h', + 'ulimit.h', + 'unistd.h', + 'utime.h', + 'utmpx.h', + 'wordexp.h', + # additional GNUlib headers + 'a.out.h', + 'aliases.h', + 'alloca.h', + 'ar.h', + 'argp.h', + 'argz.h', + 'byteswap.h', + 'crypt.h', + 'endian.h', + 'envz.h', + 'err.h', + 'error.h', + 'execinfo.h', + 'fpu_control.h', + 'fstab.h', + 'fts.h', + 'getopt.h', + 'gshadow.h', + 'ieee754.h', + 'ifaddrs.h', + 'libintl.h', + 'mcheck.h', + 'mntent.h', + 'obstack.h', + 'paths.h', + 'printf.h', + 'pty.h', + 'resolv.h', + 'shadow.h', + 'sysexits.h', + 'ttyent.h', + # Additional linux glibc headers + 'dlfcn.h', + 'elf.h', + 'features.h', + 'gconv.h', + 'gnu-versions.h', + 'lastlog.h', + 'libio.h', + 'link.h', + 'malloc.h', + 'memory.h', + 'netash/ash.h', + 'netatalk/at.h', + 'netax25/ax25.h', + 'neteconet/ec.h', + 'netipx/ipx.h', + 'netiucv/iucv.h', + 'netpacket/packet.h', + 'netrom/netrom.h', + 'netrose/rose.h', + 'nfs/nfs.h', + 'nl_types.h', + 'nss.h', + 're_comp.h', + 'regexp.h', + 'sched.h', + 'sgtty.h', + 'stab.h', + 'stdc-predef.h', + 'stdio_ext.h', + 'syscall.h', + 'termio.h', + 'thread_db.h', + 'ucontext.h', + 'ustat.h', + 'utmp.h', + 'values.h', + 'wait.h', + 'xlocale.h', + # Hardware specific headers + 'arm_neon.h', + 'emmintrin.h', + 'xmmintin.h', + ]) + +# Folders of C libraries so commonly used in C++, +# that they have parity with standard C libraries. +C_STANDARD_HEADER_FOLDERS = frozenset([ + # standard C library + "sys", + # glibc for linux + "arpa", + "asm-generic", + "bits", + "gnu", + "net", + "netinet", + "protocols", + "rpc", + "rpcsvc", + "scsi", + # linux kernel header + "drm", + "linux", + "misc", + "mtd", + "rdma", + "sound", + "video", + "xen", + ]) + # Type names _TYPES = re.compile( r'^(?:' @@ -457,7 +808,8 @@ _THIRD_PARTY_HEADERS_PATTERN = re.compile( r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') # Pattern for matching FileInfo.BaseName() against test file name -_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' +_test_suffixes = ['_test', '_regtest', '_unittest'] +_TEST_FILE_SUFFIX = '(' + '|'.join(_test_suffixes) + r')$' # Pattern that matches only complete whitespace, possibly across multiple lines. _EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) @@ -471,21 +823,21 @@ _CHECK_MACROS = [ ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) +_CHECK_REPLACEMENT = dict([(macro_var, {}) for macro_var in _CHECK_MACROS]) for op, replacement in [('==', 'EQ'), ('!=', 'NE'), ('>=', 'GE'), ('>', 'GT'), ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['DCHECK'][op] = f'DCHECK_{replacement}' + _CHECK_REPLACEMENT['CHECK'][op] = f'CHECK_{replacement}' + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = f'EXPECT_{replacement}' + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = f'ASSERT_{replacement}' for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = f'EXPECT_{inv_replacement}' + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = f'ASSERT_{inv_replacement}' # Alternative tokens and their replacements. For full list, see section 2.5 # Alternative tokens [lex.digraph] in the C++ standard. @@ -512,16 +864,17 @@ _ALT_TOKEN_REPLACEMENT = { # False positives include C-style multi-line comments and multi-line strings # but those have always been troublesome for cpplint. _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + r'([ =()])(' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')([ (]|$)') # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). _C_SYS_HEADER = 1 _CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 +_OTHER_SYS_HEADER = 3 +_LIKELY_MY_HEADER = 4 +_POSSIBLE_MY_HEADER = 5 +_OTHER_HEADER = 6 # These constants define the current inline assembly state _NO_ASM = 0 # Outside of inline assembly block @@ -541,7 +894,21 @@ _SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' # Match string that indicates we're working on a Linux Kernel file. _SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') -_regexp_compile_cache = {} +# Commands for sed to fix the problem +_SED_FIXUPS = { + 'Remove spaces around =': r's/ = /=/', + 'Remove spaces around !=': r's/ != /!=/', + 'Remove space before ( in if (': r's/if (/if(/', + 'Remove space before ( in for (': r's/for (/for(/', + 'Remove space before ( in while (': r's/while (/while(/', + 'Remove space before ( in switch (': r's/switch (/switch(/', + 'Should have a space between // and comment': r's/\/\//\/\/ /', + 'Missing space before {': r's/\([^ ]\){/\1 {/', + 'Tab found, replace by spaces': r's/\t/ /g', + 'Line ends in whitespace. Consider deleting these extra spaces.': r's/\s*$//', + 'You don\'t need a ; after a }': r's/};/}/', + 'Missing space after ,': r's/,\([^ ]\)/, \1/g', +} # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. @@ -552,33 +919,142 @@ _error_suppressions = {} _root = None _root_debug = False +# The top level repository directory. If set, _root is calculated relative to +# this directory instead of the directory containing version control artifacts. +# This is set by the --repository flag. +_repository = None + +# Files to exclude from linting. This is set by the --exclude flag. +_excludes = None + +# Whether to suppress all PrintInfo messages, UNRELATED to --quiet flag +_quiet = False + # The allowed line length of files. # This is set by --linelength flag. _line_length = 80 -# The allowed extensions for file names -# This is set by --extensions flag. -_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) +# This allows to use different include order rule than default +_include_order = "default" + +# This allows different config files to be used +_config_filename = "CPPLINT.cfg" # Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc. # This is set by --headers flag. -_hpp_headers = set(['h']) +_hpp_headers = set([]) -# {str, bool}: a map from error categories to booleans which indicate if the -# category should be suppressed for every line. -_global_error_suppressions = {} +class ErrorSuppressions: + """Class to track all error suppressions for cpplint""" + + class LineRange: + """Class to represent a range of line numbers for which an error is suppressed""" + def __init__(self, begin, end): + self.begin = begin + self.end = end + + def __str__(self): + return f'[{self.begin}-{self.end}]' + + def __contains__(self, obj): + return self.begin <= obj <= self.end + + def ContainsRange(self, other): + return self.begin <= other.begin and self.end >= other.end + + def __init__(self): + self._suppressions = collections.defaultdict(list) + self._open_block_suppression = None + + def _AddSuppression(self, category, line_range): + suppressed = self._suppressions[category] + if not (suppressed and suppressed[-1].ContainsRange(line_range)): + suppressed.append(line_range) + + def GetOpenBlockStart(self): + """:return: The start of the current open block or `-1` if there is not an open block""" + return self._open_block_suppression.begin if self._open_block_suppression else -1 + + def AddGlobalSuppression(self, category): + """Add a suppression for `category` which is suppressed for the whole file""" + self._AddSuppression(category, self.LineRange(0, math.inf)) + + def AddLineSuppression(self, category, linenum): + """Add a suppression for `category` which is suppressed only on `linenum`""" + self._AddSuppression(category, self.LineRange(linenum, linenum)) + + def StartBlockSuppression(self, category, linenum): + """Start a suppression block for `category` on `linenum`. inclusive""" + if self._open_block_suppression is None: + self._open_block_suppression = self.LineRange(linenum, math.inf) + self._AddSuppression(category, self._open_block_suppression) + + def EndBlockSuppression(self, linenum): + """End the current block suppression on `linenum`. inclusive""" + if self._open_block_suppression: + self._open_block_suppression.end = linenum + self._open_block_suppression = None + + def IsSuppressed(self, category, linenum): + """:return: `True` if `category` is suppressed for `linenum`""" + suppressed = self._suppressions[category] + self._suppressions[None] + return any(linenum in lr for lr in suppressed) + + def HasOpenBlock(self): + """:return: `True` if a block suppression was started but not ended""" + return self._open_block_suppression is not None + + def Clear(self): + """Clear all current error suppressions""" + self._suppressions.clear() + self._open_block_suppression = None + +_error_suppressions = ErrorSuppressions() def ProcessHppHeadersOption(val): global _hpp_headers try: - _hpp_headers = set(val.split(',')) - # Automatically append to extensions list so it does not have to be set 2 times - _valid_extensions.update(_hpp_headers) + _hpp_headers = {ext.strip() for ext in val.split(',')} except ValueError: PrintUsage('Header extensions must be comma separated list.') +def ProcessIncludeOrderOption(val): + if val is None or val == "default": + pass + elif val == "standardcfirst": + global _include_order + _include_order = val + else: + PrintUsage('Invalid includeorder value %s. Expected default|standardcfirst') + def IsHeaderExtension(file_extension): - return file_extension in _hpp_headers + return file_extension in GetHeaderExtensions() + +def GetHeaderExtensions(): + if _hpp_headers: + return _hpp_headers + if _valid_extensions: + return {h for h in _valid_extensions if 'h' in h} + return set(['h', 'hh', 'hpp', 'hxx', 'h++', 'cuh']) + +# The allowed extensions for file names +# This is set by --extensions flag +def GetAllExtensions(): + return GetHeaderExtensions().union(_valid_extensions or set( + ['c', 'cc', 'cpp', 'cxx', 'c++', 'cu'])) + +def ProcessExtensionsOption(val): + global _valid_extensions + try: + extensions = [ext.strip() for ext in val.split(',')] + _valid_extensions = set(extensions) + except ValueError: + PrintUsage('Extensions should be a comma-separated list of values;' + 'for example: extensions=hpp,cpp\n' + f'This could not be parsed: "{val}"') + +def GetNonHeaderExtensions(): + return GetAllExtensions().difference(GetHeaderExtensions()) def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of line error-suppressions. @@ -593,26 +1069,50 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error): linenum: int, the number of the current line. error: function, an error handler. """ - matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) + matched = re.search(r'\bNOLINT(NEXTLINE|BEGIN|END)?\b(\([^)]+\))?', raw_line) if matched: - if matched.group(1): - suppressed_line = linenum + 1 + no_lint_type = matched.group(1) + if no_lint_type == 'NEXTLINE': + def ProcessCategory(category): + _error_suppressions.AddLineSuppression(category, linenum + 1) + elif no_lint_type == 'BEGIN': + if _error_suppressions.HasOpenBlock(): + error(filename, linenum, 'readability/nolint', 5, + f'NONLINT block already defined on line {_error_suppressions.GetOpenBlockStart()}') + + def ProcessCategory(category): + _error_suppressions.StartBlockSuppression(category, linenum) + elif no_lint_type == 'END': + if not _error_suppressions.HasOpenBlock(): + error(filename, linenum, 'readability/nolint', 5, 'Not in a NOLINT block') + + def ProcessCategory(category): + if category is not None: + error(filename, linenum, 'readability/nolint', 5, + f'NOLINT categories not supported in block END: {category}') + _error_suppressions.EndBlockSuppression(linenum) else: - suppressed_line = linenum - category = matched.group(2) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(suppressed_line) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] + def ProcessCategory(category): + _error_suppressions.AddLineSuppression(category, linenum) + categories = matched.group(2) + if categories in (None, '(*)'): # => "suppress all" + ProcessCategory(None) + elif categories.startswith('(') and categories.endswith(')'): + for category in set(map(lambda c: c.strip(), categories[1:-1].split(','))): if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(suppressed_line) + ProcessCategory(category) + elif any(c for c in _OTHER_NOLINT_CATEGORY_PREFIXES if category.startswith(c)): + # Ignore any categories from other tools. + pass elif category not in _LEGACY_ERROR_CATEGORIES: error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - + f'Unknown NOLINT error category: {category}') def ProcessGlobalSuppresions(lines): + """Deprecated; use ProcessGlobalSuppressions.""" + ProcessGlobalSuppressions(lines) + +def ProcessGlobalSuppressions(lines): """Updates the list of global error suppressions. Parses any lint directives in the file that have global effect. @@ -624,74 +1124,36 @@ def ProcessGlobalSuppresions(lines): for line in lines: if _SEARCH_C_FILE.search(line): for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True + _error_suppressions.AddGlobalSuppression(category) if _SEARCH_KERNEL_FILE.search(line): for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True + _error_suppressions.AddGlobalSuppression(category) def ResetNolintSuppressions(): """Resets the set of NOLINT suppressions to empty.""" - _error_suppressions.clear() - _global_error_suppressions.clear() + _error_suppressions.Clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by - ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. + ParseNolintSuppressions/ProcessGlobalSuppressions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: - bool, True iff the error should be suppressed due to a NOLINT comment or - global suppression. + bool, True iff the error should be suppressed due to a NOLINT comment, + block suppression or global suppression. """ - return (_global_error_suppressions.get(category, False) or - linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def ReplaceAll(pattern, rep, s): - """Replaces instances of pattern in a string with a replacement. - - The compiled regex is kept in a cache shared by Match and Search. - - Args: - pattern: regex pattern - rep: replacement text - s: search string - - Returns: - string with replacements made (or original string if no replacements) - """ - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].sub(rep, s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) + return _error_suppressions.IsSuppressed(category, linenum) def _IsSourceExtension(s): """File extension (excluding dot) matches a source file extension.""" - return s in ('c', 'cc', 'cpp', 'cxx') + return s in GetNonHeaderExtensions() class _IncludeState(object): @@ -712,11 +1174,13 @@ class _IncludeState(object): _MY_H_SECTION = 1 _C_SECTION = 2 _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 + _OTHER_SYS_SECTION = 4 + _OTHER_H_SECTION = 5 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', _CPP_SYS_HEADER: 'C++ system header', + _OTHER_SYS_HEADER: 'other system header', _LIKELY_MY_HEADER: 'header this file implements', _POSSIBLE_MY_HEADER: 'header this file may implement', _OTHER_HEADER: 'other header', @@ -726,11 +1190,14 @@ class _IncludeState(object): _MY_H_SECTION: 'a header this file implements', _C_SECTION: 'C system header', _CPP_SECTION: 'C++ system header', + _OTHER_SYS_SECTION: 'other system header', _OTHER_H_SECTION: 'other header', } def __init__(self): self.include_list = [[]] + self._section = None + self._last_header = None self.ResetSection('') def FindHeader(self, header): @@ -801,7 +1268,7 @@ class _IncludeState(object): # If previous line was a blank line, assume that the headers are # intentionally sorted the way they are. if (self._last_header > header_path and - Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): + re.match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): return False return True @@ -819,9 +1286,8 @@ class _IncludeState(object): error message describing what's wrong. """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) + error_message = (f'Found {self._TYPE_NAMES[header_type]}' + f' after {self._SECTION_NAMES[self._section]}') last_section = self._section @@ -837,6 +1303,12 @@ class _IncludeState(object): else: self._last_header = '' return error_message + elif header_type == _OTHER_SYS_HEADER: + if self._section <= self._OTHER_SYS_SECTION: + self._section = self._OTHER_SYS_SECTION + else: + self._last_header = '' + return error_message elif header_type == _LIKELY_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION @@ -875,9 +1347,18 @@ class _CppLintState(object): # output format: # "emacs" - format that emacs can parse (default) + # "eclipse" - format that eclipse can parse # "vs7" - format that Microsoft Visual Studio 7 can parse + # "junit" - format that Jenkins, Bamboo, etc can parse + # "sed" - returns a gnu sed command to fix the problem + # "gsed" - like sed, but names the command gsed, e.g. for macOS homebrew users self.output_format = 'emacs' + # For JUnit output, save errors and failures until the end so that they + # can be written into the XML + self._junit_errors = [] + self._junit_failures = [] + def SetOutputFormat(self, output_format): """Sets the output format for errors.""" self.output_format = output_format @@ -925,7 +1406,7 @@ class _CppLintState(object): for filt in self.filters: if not (filt.startswith('+') or filt.startswith('-')): raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) + f' ({filt} does not)') def BackupFilters(self): """ Saves the current filter list to backup storage.""" @@ -952,10 +1433,70 @@ class _CppLintState(object): def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stdout.write('Total errors found: %d\n' % self.error_count) + for category, count in sorted(dict.items(self.errors_by_category)): + self.PrintInfo(f'Category \'{category}\' errors found: {count}\n') + if self.error_count > 0: + self.PrintInfo(f'Total errors found: {self.error_count}\n') + + def PrintInfo(self, message): + # _quiet does not represent --quiet flag. + # Hide infos from stdout to keep stdout pure for machine consumption + if not _quiet and self.output_format not in _MACHINE_OUTPUTS: + sys.stdout.write(message) + + def PrintError(self, message): + if self.output_format == 'junit': + self._junit_errors.append(message) + else: + sys.stderr.write(message) + + def AddJUnitFailure(self, filename, linenum, message, category, confidence): + self._junit_failures.append((filename, linenum, message, category, + confidence)) + + def FormatJUnitXML(self): + num_errors = len(self._junit_errors) + num_failures = len(self._junit_failures) + + testsuite = xml.etree.ElementTree.Element('testsuite') + testsuite.attrib['errors'] = str(num_errors) + testsuite.attrib['failures'] = str(num_failures) + testsuite.attrib['name'] = 'cpplint' + + if num_errors == 0 and num_failures == 0: + testsuite.attrib['tests'] = str(1) + xml.etree.ElementTree.SubElement(testsuite, 'testcase', name='passed') + + else: + testsuite.attrib['tests'] = str(num_errors + num_failures) + if num_errors > 0: + testcase = xml.etree.ElementTree.SubElement(testsuite, 'testcase') + testcase.attrib['name'] = 'errors' + error = xml.etree.ElementTree.SubElement(testcase, 'error') + error.text = '\n'.join(self._junit_errors) + if num_failures > 0: + # Group failures by file + failed_file_order = [] + failures_by_file = {} + for failure in self._junit_failures: + failed_file = failure[0] + if failed_file not in failed_file_order: + failed_file_order.append(failed_file) + failures_by_file[failed_file] = [] + failures_by_file[failed_file].append(failure) + # Create a testcase for each file + for failed_file in failed_file_order: + failures = failures_by_file[failed_file] + testcase = xml.etree.ElementTree.SubElement(testsuite, 'testcase') + testcase.attrib['name'] = failed_file + failure = xml.etree.ElementTree.SubElement(testcase, 'failure') + template = '{0}: {1} [{2}] [{3}]' + texts = [template.format(f[1], f[2], f[3], f[4]) for f in failures] + failure.text = '\n'.join(texts) + + xml_decl = '\n' + return xml_decl + xml.etree.ElementTree.tostring(testsuite, 'utf-8').decode('utf-8') + _cpplint_state = _CppLintState() @@ -1067,7 +1608,7 @@ class _FunctionState(object): if not self.in_a_function: return - if Match(r'T(EST|est)', self.current_function): + if re.match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: base_trigger = self._NORMAL_TRIGGER @@ -1080,9 +1621,8 @@ class _FunctionState(object): error_level = 5 error(filename, linenum, 'readability/fn_size', error_level, 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) + f' {self.current_function} has {self.lines_in_function} non-comment lines' + f' (error triggered by exceeding {trigger} lines).') def End(self): """Stop analyzing function body.""" @@ -1109,12 +1649,12 @@ class FileInfo(object): return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): - """FullName after removing the local path to the repository. + r"""FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus + "C:\\Documents and Settings\\..." or "/home/username/..." in them and thus people on different computers who have checked the source out to different locations won't see bogus errors. """ @@ -1123,6 +1663,20 @@ class FileInfo(object): if os.path.exists(fullname): project_dir = os.path.dirname(fullname) + # If the user specified a repository path, it exists, and the file is + # contained in it, use the specified repository path + if _repository: + repo = FileInfo(_repository).FullName() + root_dir = project_dir + while os.path.exists(root_dir): + # allow case insensitive compare on Windows + if os.path.normcase(root_dir) == os.path.normcase(repo): + return os.path.relpath(fullname, root_dir).replace('\\', '/') + one_up_dir = os.path.dirname(root_dir) + if one_up_dir == root_dir: + break + root_dir = one_up_dir + if os.path.exists(os.path.join(project_dir, ".svn")): # If there's a .svn file in the current directory, we recursively look # up the directory tree for the top of the SVN checkout @@ -1143,6 +1697,7 @@ class FileInfo(object): os.path.exists(os.path.join(current_dir, ".hg")) or os.path.exists(os.path.join(current_dir, ".svn"))): root_dir = current_dir + break current_dir = os.path.dirname(current_dir) if (os.path.exists(os.path.join(root_dir, ".git")) or @@ -1173,7 +1728,7 @@ class FileInfo(object): return self.Split()[1] def Extension(self): - """File extension - text following the final period.""" + """File extension - text following the final period, includes that period.""" return self.Split()[2] def NoExtension(self): @@ -1185,7 +1740,7 @@ class FileInfo(object): return _IsSourceExtension(self.Extension()[1:]) -def _ShouldPrintError(category, confidence, linenum): +def _ShouldPrintError(category, confidence, filename, linenum): """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: @@ -1199,11 +1754,16 @@ def _ShouldPrintError(category, confidence, linenum): is_filtered = False for one_filter in _Filters(): + filter_cat, filter_file, filter_line = _ParseFilterSelector(one_filter[1:]) + category_match = category.startswith(filter_cat) + file_match = filter_file == "" or filter_file == filename + line_match = filter_line == linenum or filter_line == -1 + if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): + if category_match and file_match and line_match: is_filtered = True elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): + if category_match and file_match and line_match: is_filtered = False else: assert False # should have been checked for in SetFilter. @@ -1220,9 +1780,9 @@ def Error(filename, linenum, category, confidence, message): that is, how certain we are this is a legitimate style regression, and not a misidentification or a use that's sometimes justified. - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. + False positives can be suppressed by the use of "NOLINT(category)" + comments, NOLINTNEXTLINE or in blocks started by NOLINTBEGIN. These + are parsed into _error_suppressions. Args: filename: The name of the file containing the error. @@ -1235,17 +1795,28 @@ def Error(filename, linenum, category, confidence, message): and 1 meaning that it could be a legitimate construct. message: The error message. """ - if _ShouldPrintError(category, confidence, linenum): + if _ShouldPrintError(category, confidence, filename, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): error cpplint: [%s] %s [%d]\n' % ( - filename, linenum, category, message, confidence)) + _cpplint_state.PrintError(f'{filename}({linenum}): error cpplint:' + f' [{category}] {message} [{confidence}]\n') elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) + sys.stderr.write(f'{filename}:{linenum}: warning:' + f' {message} [{category}] [{confidence}]\n') + elif _cpplint_state.output_format == 'junit': + _cpplint_state.AddJUnitFailure(filename, linenum, message, category, confidence) + elif _cpplint_state.output_format in ['sed', 'gsed']: + if message in _SED_FIXUPS: + sys.stdout.write(f"{_cpplint_state.output_format} -i" + f" '{linenum}{_SED_FIXUPS[message]}' {filename}" + f" # {message} [{category}] [{confidence}]\n") + else: + sys.stderr.write(f'# {filename}:{linenum}: ' + f' "{message}" [{category}] [{confidence}]\n') else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) + final_message = (f'{filename}:{linenum}: ' + f' {message} [{category}] [{confidence}]\n') + sys.stderr.write(final_message) # Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. @@ -1315,7 +1886,7 @@ def CleanseRawStrings(raw_lines): # Found the end of the string, match leading space for this # line and resume copying the original lines, and also insert # a "" on the last line. - leading_space = Match(r'^(\s*)\S', line) + leading_space = re.match(r'^(\s*)\S', line) line = leading_space.group(1) + '""' + line[end + len(delimiter):] delimiter = None else: @@ -1336,9 +1907,9 @@ def CleanseRawStrings(raw_lines): # before removing raw strings. This is because there are some # cpplint checks that requires the comments to be preserved, but # we don't want to check comments that are inside raw strings. - matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) + matched = re.match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) if (matched and - not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', + not re.match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', matched.group(1))): delimiter = ')' + matched.group(2) + '"' @@ -1421,6 +1992,28 @@ def CleanseComments(line): return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) +def ReplaceAlternateTokens(line): + """Replace any alternate token by its original counterpart. + + In order to comply with the google rule stating that unary operators should + never be followed by a space, an exception is made for the 'not' and 'compl' + alternate tokens. For these, any trailing space is removed during the + conversion. + + Args: + line: The line being processed. + + Returns: + The line with alternate tokens replaced. + """ + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + token = _ALT_TOKEN_REPLACEMENT[match.group(2)] + tail = '' if match.group(2) in ['not', 'compl'] and match.group(3) == ' ' \ + else r'\3' + line = re.sub(match.re, rf'\1{token}{tail}', line, count=1) + return line + + class CleansedLines(object): """Holds 4 copies of all lines with different preprocessing applied to them. @@ -1433,15 +2026,17 @@ class CleansedLines(object): """ def __init__(self, lines): + if '-readability/alt_tokens' in _cpplint_state.filters: + for i, line in enumerate(lines): + lines[i] = ReplaceAlternateTokens(line) self.elided = [] self.lines = [] self.raw_lines = lines self.num_lines = len(lines) self.lines_without_raw_strings = CleanseRawStrings(lines) - for linenum in range(len(self.lines_without_raw_strings)): - self.lines.append(CleanseComments( - self.lines_without_raw_strings[linenum])) - elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) + for line in self.lines_without_raw_strings: + self.lines.append(CleanseComments(line)) + elided = self._CollapseStrings(line) self.elided.append(CleanseComments(elided)) def NumLines(self): @@ -1474,7 +2069,7 @@ class CleansedLines(object): collapsed = '' while True: # Find the first quote character - match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) + match = re.match(r'^([^\'"]*)([\'"])(.*)$', elided) if not match: collapsed += elided break @@ -1499,8 +2094,8 @@ class CleansedLines(object): # correctly as long as there are digits on both sides of the # separator. So we are fine as long as we don't see something # like "0.'3" (gcc 4.9.0 will not allow this literal). - if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): - match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) + if re.search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): + match_literal = re.match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) collapsed += head + match_literal.group(1).replace("'", '') elided = match_literal.group(2) else: @@ -1529,7 +2124,7 @@ def FindEndOfExpressionInLine(line, startpos, stack): On finding an unclosed expression: (-1, None) Otherwise: (-1, new stack at end of this line) """ - for i in xrange(startpos, len(line)): + for i in range(startpos, len(line)): char = line[i] if char in '([{': # Found start of parenthesized expression, push to expression stack @@ -1542,7 +2137,7 @@ def FindEndOfExpressionInLine(line, startpos, stack): stack.pop() if not stack: return (-1, None) - elif i > 0 and Search(r'\boperator\s*$', line[0:i]): + elif i > 0 and re.search(r'\boperator\s*$', line[0:i]): # operator<, don't add to stack continue else: @@ -1571,7 +2166,7 @@ def FindEndOfExpressionInLine(line, startpos, stack): # Ignore "->" and operator functions if (i > 0 and - (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): + (line[i - 1] == '-' or re.search(r'\boperator\s*$', line[0:i - 1]))): continue # Pop the stack if there is a matching '<'. Otherwise, ignore @@ -1618,7 +2213,7 @@ def CloseExpression(clean_lines, linenum, pos): """ line = clean_lines.elided[linenum] - if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): + if (line[pos] not in '({[<') or re.match(r'<[<=]', line[pos:]): return (line, clean_lines.NumLines(), -1) # Check first line @@ -1666,8 +2261,8 @@ def FindStartOfExpressionInLine(line, endpos, stack): # Ignore it if it's a "->" or ">=" or "operator>" if (i > 0 and (line[i - 1] == '-' or - Match(r'\s>=\s', line[i - 1:]) or - Search(r'\boperator\s*$', line[0:i]))): + re.match(r'\s>=\s', line[i - 1:]) or + re.search(r'\boperator\s*$', line[0:i]))): i -= 1 else: stack.append('>') @@ -1758,7 +2353,7 @@ def CheckForCopyright(filename, lines, error): # We'll say it should occur by line 10. Don't forget there's a # placeholder line at the front. - for line in xrange(1, min(len(lines), 11)): + for line in range(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, @@ -1775,7 +2370,7 @@ def GetIndentLevel(line): Returns: An integer count of leading spaces, possibly zero. """ - indent = Match(r'^( *)\S', line) + indent = re.match(r'^( *)\S', line) if indent: return len(indent.group(1)) else: @@ -1793,10 +2388,10 @@ def PathSplitToList(path): lst = [] while True: (head, tail) = os.path.split(path) - if head == path: # absolute paths end + if head == path: # absolute paths end lst.append(head) break - if tail == path: # relative paths end + if tail == path: # relative paths end lst.append(tail) break @@ -1830,8 +2425,8 @@ def GetHeaderGuardCPPVariable(filename): def FixupPathFromRoot(): if _root_debug: - sys.stderr.write("\n_root fixup, _root = '%s', repository name = '%s'\n" - %(_root, fileinfo.RepositoryName())) + sys.stderr.write(f"\n_root fixup, _root = '{_root}'," + f" repository name = '{fileinfo.RepositoryName()}'\n") # Process the file path with the --root flag if it was set. if not _root: @@ -1853,27 +2448,28 @@ def GetHeaderGuardCPPVariable(filename): if _root_debug: sys.stderr.write(("_root lstrip (maybe_path=%s, file_path_from_root=%s," + - " _root=%s)\n") %(maybe_path, file_path_from_root, _root)) + " _root=%s)\n") % (maybe_path, file_path_from_root, _root)) if maybe_path: return os.path.join(*maybe_path) # --root=.. , will prepend the outer directory to the header guard full_path = fileinfo.FullName() - root_abspath = os.path.abspath(_root) + # adapt slashes for windows + root_abspath = os.path.abspath(_root).replace('\\', '/') maybe_path = StripListPrefix(PathSplitToList(full_path), PathSplitToList(root_abspath)) if _root_debug: sys.stderr.write(("_root prepend (maybe_path=%s, full_path=%s, " + - "root_abspath=%s)\n") %(maybe_path, full_path, root_abspath)) + "root_abspath=%s)\n") % (maybe_path, full_path, root_abspath)) if maybe_path: return os.path.join(*maybe_path) if _root_debug: - sys.stderr.write("_root ignore, returning %s\n" %(file_path_from_root)) + sys.stderr.write(f"_root ignore, returning {file_path_from_root}\n") # --root=FAKE_DIR is ignored return file_path_from_root @@ -1902,7 +2498,12 @@ def CheckForHeaderGuard(filename, clean_lines, error): # and not the general NOLINT or NOLINT(*) syntax. raw_lines = clean_lines.lines_without_raw_strings for i in raw_lines: - if Search(r'//\s*NOLINT\(build/header_guard\)', i): + if re.search(r'//\s*NOLINT\(build/header_guard\)', i): + return + + # Allow pragma once instead of header guards + for i in raw_lines: + if re.search(r'^\s*#pragma\s+once', i): return cppvar = GetHeaderGuardCPPVariable(filename) @@ -1929,8 +2530,7 @@ def CheckForHeaderGuard(filename, clean_lines, error): if not ifndef or not define or ifndef != define: error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) + f'No #ifndef header guard found, suggested CPP variable is: {cppvar}') return # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ @@ -1943,66 +2543,75 @@ def CheckForHeaderGuard(filename, clean_lines, error): ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) + f'#ifndef header guard has wrong style, please use: {cppvar}') # Check for "//" comments on endif line. ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, error) - match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) + match = re.match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) if match: if match.group(1) == '_': # Issue low severity warning for deprecated double trailing underscore error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif // %s"' % cppvar) + f'#endif line should be "#endif // {cppvar}"') return # Didn't find the corresponding "//" comment. If this file does not # contain any "//" comments at all, it could be that the compiler # only wants "/**/" comments, look for those instead. no_single_line_comments = True - for i in xrange(1, len(raw_lines) - 1): + for i in range(1, len(raw_lines) - 1): line = raw_lines[i] - if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): + if re.match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): no_single_line_comments = False break if no_single_line_comments: - match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) + match = re.match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) if match: if match.group(1) == '_': # Low severity warning for double trailing underscore error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif /* %s */"' % cppvar) + f'#endif line should be "#endif /* {cppvar} */"') return # Didn't find anything error(filename, endif_linenum, 'build/header_guard', 5, - '#endif line should be "#endif // %s"' % cppvar) + f'#endif line should be "#endif // {cppvar}"') def CheckHeaderFileIncluded(filename, include_state, error): - """Logs an error if a .cc file does not include its header.""" + """Logs an error if a source file does not include its header.""" # Do not check test files fileinfo = FileInfo(filename) - if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): + if re.search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): return - headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' - if not os.path.exists(headerfile): - return - headername = FileInfo(headerfile).RepositoryName() - first_include = 0 - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] + first_include = message = None + basefilename = filename[0:len(filename) - len(fileinfo.Extension())] + for ext in GetHeaderExtensions(): + headerfile = basefilename + '.' + ext + if not os.path.exists(headerfile): + continue + headername = FileInfo(headerfile).RepositoryName() + include_uses_unix_dir_aliases = False + for section_list in include_state.include_list: + for f in section_list: + include_text = f[0] + if "./" in include_text: + include_uses_unix_dir_aliases = True + if headername in include_text or include_text in headername: + return + if not first_include: + first_include = f[1] - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) + message = f'{fileinfo.RepositoryName()} should include its header file {headername}' + if include_uses_unix_dir_aliases: + message += ". Relative paths like . and .. are not allowed." + + if message: + error(filename, first_include, 'build/include', 5, message) def CheckForBadCharacters(filename, lines, error): @@ -2023,7 +2632,7 @@ def CheckForBadCharacters(filename, lines, error): error: The function to call with any errors found. """ for linenum, line in enumerate(lines): - if u'\ufffd' in line: + if '\ufffd' in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') if '\0' in line: @@ -2135,7 +2744,7 @@ def CheckPosixThreading(filename, clean_lines, linenum, error): for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: # Additional pattern matching check to confirm that this is the # function we are looking for - if Search(pattern, line): + if re.search(pattern, line): error(filename, linenum, 'runtime/threadsafe_fn', 2, 'Consider using ' + multithread_safe_func + '...) instead of ' + single_thread_func + @@ -2155,7 +2764,7 @@ def CheckVlogArguments(filename, clean_lines, linenum, error): error: The function to call with any errors found. """ line = clean_lines.elided[linenum] - if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): + if re.search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): error(filename, linenum, 'runtime/vlog', 5, 'VLOG() should be used with numeric verbosity level. ' 'Use LOG() if you want symbolic severity levels.') @@ -2189,17 +2798,17 @@ def CheckInvalidIncrement(filename, clean_lines, linenum, error): def IsMacroDefinition(clean_lines, linenum): - if Search(r'^#define', clean_lines[linenum]): + if re.search(r'^#define', clean_lines[linenum]): return True - if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): + if linenum > 0 and re.search(r'\\$', clean_lines[linenum - 1]): return True return False def IsForwardClassDeclaration(clean_lines, linenum): - return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) + return re.match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) class _BlockInfo(object): @@ -2294,15 +2903,15 @@ class _ClassInfo(_BlockInfo): def CheckBegin(self, filename, clean_lines, linenum, error): # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + if re.search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): self.is_derived = True def CheckEnd(self, filename, clean_lines, linenum, error): # If there is a DISALLOW macro, it should appear near the end of # the class. seen_last_thing_in_class = False - for i in xrange(linenum - 1, self.starting_linenum, -1): - match = Search( + for i in range(linenum - 1, self.starting_linenum, -1): + match = re.search( r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + self.name + r'\)', clean_lines.elided[i]) @@ -2312,20 +2921,20 @@ class _ClassInfo(_BlockInfo): match.group(1) + ' should be the last thing in the class') break - if not Match(r'^\s*$', clean_lines.elided[i]): + if not re.match(r'^\s*$', clean_lines.elided[i]): seen_last_thing_in_class = True # Check that closing brace is aligned with beginning of the class. # Only do this if the closing brace is indented by only whitespaces. # This means we will not check single-line class definitions. - indent = Match(r'^( *)\}', clean_lines.elided[linenum]) + indent = re.match(r'^( *)\}', clean_lines.elided[linenum]) if indent and len(indent.group(1)) != self.class_indent: if self.is_struct: parent = 'struct ' + self.name else: parent = 'class ' + self.name error(filename, linenum, 'whitespace/indent', 3, - 'Closing brace should be aligned with beginning of %s' % parent) + f'Closing brace should be aligned with beginning of {parent}') class _NamespaceInfo(_BlockInfo): @@ -2352,7 +2961,7 @@ class _NamespaceInfo(_BlockInfo): # deciding what these nontrivial things are, so this check is # triggered by namespace size only, which works most of the time. if (linenum - self.starting_linenum < 10 - and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): + and not re.match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): return # Look for matching comment at end of namespace. @@ -2369,18 +2978,17 @@ class _NamespaceInfo(_BlockInfo): # expected namespace. if self.name: # Named namespace - if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + + if not re.match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + r'[\*/\.\\\s]*$'), line): error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) + f'Namespace should be terminated with "// namespace {self.name}"') else: # Anonymous namespace - if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): + if not re.match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): # If "// namespace anonymous" or "// anonymous namespace (more text)", # mention "// anonymous namespace" as an acceptable form - if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): + if re.match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): error(filename, linenum, 'readability/namespace', 5, 'Anonymous namespace should be terminated with "// namespace"' ' or "// anonymous namespace"') @@ -2483,7 +3091,7 @@ class NestingState(object): while linenum < clean_lines.NumLines(): # Find the earliest character that might indicate a template argument line = clean_lines.elided[linenum] - match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) + match = re.match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) if not match: linenum += 1 pos = 0 @@ -2543,11 +3151,11 @@ class NestingState(object): Args: line: current line to check. """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + if re.match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): # Beginning of #if block, save the nesting stack here. The saved # stack will allow us to restore the parsing state in the #else case. self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): + elif re.match(r'^\s*#\s*(else|elif)\b', line): # Beginning of #else block if self.pp_stack: if not self.pp_stack[-1].seen_else: @@ -2562,7 +3170,7 @@ class NestingState(object): else: # TODO(unknown): unexpected #else, issue warning? pass - elif Match(r'^\s*#\s*endif\b', line): + elif re.match(r'^\s*#\s*endif\b', line): # End of #if or #else blocks. if self.pp_stack: # If we saw an #else, we will need to restore the nesting @@ -2634,7 +3242,7 @@ class NestingState(object): # declarations even if it weren't followed by a whitespace, this # is so that we don't confuse our namespace checker. The # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + namespace_decl_match = re.match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) if not namespace_decl_match: break @@ -2651,9 +3259,9 @@ class NestingState(object): # such as in: # class LOCKABLE API Object { # }; - class_decl_match = Match( - r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' - r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' + class_decl_match = re.match( + r'^(\s*(?:template\s*<[\w\s<>,:=]*>\s*)?' + r'(class|struct)\s+(?:[a-zA-Z0-9_]+\s+)*(\w+(?:::\w+)*))' r'(.*)$', line) if (class_decl_match and (not self.stack or self.stack[-1].open_parentheses == 0)): @@ -2681,7 +3289,7 @@ class NestingState(object): # Update access control if we are inside a class/struct if self.stack and isinstance(self.stack[-1], _ClassInfo): classinfo = self.stack[-1] - access_match = Match( + access_match = re.match( r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' r':(?:[^:]|$)', line) @@ -2692,7 +3300,7 @@ class NestingState(object): # check if the keywords are not preceded by whitespaces. indent = access_match.group(1) if (len(indent) != classinfo.class_indent + 1 and - Match(r'^\s*$', indent)): + re.match(r'^\s*$', indent)): if classinfo.is_struct: parent = 'struct ' + classinfo.name else: @@ -2701,13 +3309,13 @@ class NestingState(object): if access_match.group(3): slots = access_match.group(3) error(filename, linenum, 'whitespace/indent', 3, - '%s%s: should be indented +1 space inside %s' % ( - access_match.group(2), slots, parent)) + f'{access_match.group(2)}{slots}:' + f' should be indented +1 space inside {parent}') # Consume braces or semicolons from what's left of the line while True: # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + matched = re.match(r'^[^{;)}]*([{;)}])(.*)$', line) if not matched: break @@ -2718,7 +3326,7 @@ class NestingState(object): # stack otherwise. if not self.SeenOpenBrace(): self.stack[-1].seen_open_brace = True - elif Match(r'^extern\s*"[^"]*"\s*\{', line): + elif re.match(r'^extern\s*"[^"]*"\s*\{', line): self.stack.append(_ExternCInfo(linenum)) else: self.stack.append(_BlockInfo(linenum, True)) @@ -2769,12 +3377,10 @@ class NestingState(object): for obj in self.stack: if isinstance(obj, _ClassInfo): error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) + f'Failed to find complete declaration of class {obj.name}') elif isinstance(obj, _NamespaceInfo): error(filename, obj.starting_linenum, 'build/namespaces', 5, - 'Failed to find complete declaration of namespace %s' % - obj.name) + f'Failed to find complete declaration of namespace {obj.name}') def CheckForNonStandardConstructs(filename, clean_lines, linenum, @@ -2809,25 +3415,25 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum, # Remove comments from the line, but leave in strings for now. line = clean_lines.lines[linenum] - if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): + if re.search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): error(filename, linenum, 'runtime/printf_format', 3, '%q in format strings is deprecated. Use %ll instead.') - if Search(r'printf\s*\(.*".*%\d+\$', line): + if re.search(r'printf\s*\(.*".*%\d+\$', line): error(filename, linenum, 'runtime/printf_format', 2, '%N$ formats are unconventional. Try rewriting to avoid them.') # Remove escaped backslashes before looking for undefined escapes. line = line.replace('\\\\', '') - if Search(r'("|\').*\\(%|\[|\(|{)', line): + if re.search(r'("|\').*\\(%|\[|\(|{)', line): error(filename, linenum, 'build/printf_format', 3, '%, [, (, and { are undefined character escapes. Unescape them.') # For the rest, work with both comments and strings removed. line = clean_lines.elided[linenum] - if Search(r'\b(const|volatile|void|char|short|int|long' + if re.search(r'\b(const|volatile|void|char|short|int|long' r'|float|double|signed|unsigned' r'|schar|u?int8|u?int16|u?int32|u?int64)' r'\s+(register|static|extern|typedef)\b', @@ -2836,20 +3442,20 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum, 'Storage-class specifier (static, extern, typedef, etc) should be ' 'at the beginning of the declaration.') - if Match(r'\s*#\s*endif\s*[^/\s]+', line): + if re.match(r'\s*#\s*endif\s*[^/\s]+', line): error(filename, linenum, 'build/endif_comment', 5, 'Uncommented text after #endif is non-standard. Use a comment.') - if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): + if re.match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): error(filename, linenum, 'build/forward_decl', 5, 'Inner-style forward declarations are invalid. Remove this line.') - if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + if re.search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line): error(filename, linenum, 'build/deprecated', 3, '>? and = 1 and not noarg_constructor and - len(defaulted_args) >= len(constructor_args) - 1)) + len(defaulted_args) >= len(constructor_args) - 1) or + # variadic arguments with zero or one argument + (len(constructor_args) <= 2 and + len(variadic_args) >= 1)) initializer_list_constructor = bool( onearg_constructor and - Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) + re.search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) copy_constructor = bool( onearg_constructor and - Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' - % re.escape(base_classname), constructor_args[0].strip())) + re.match(r'((const\s+(volatile\s+)?)?|(volatile\s+(const\s+)?))?' + rf'{re.escape(base_classname)}(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&', + constructor_args[0].strip()) + ) if (not is_marked_explicit and onearg_constructor and not initializer_list_constructor and not copy_constructor): - if defaulted_args: - error(filename, linenum, 'runtime/explicit', 5, + if defaulted_args or variadic_args: + error(filename, linenum, 'runtime/explicit', 4, 'Constructors callable with one argument ' 'should be marked explicit.') else: - error(filename, linenum, 'runtime/explicit', 5, + error(filename, linenum, 'runtime/explicit', 4, 'Single-parameter constructors should be marked explicit.') - elif is_marked_explicit and not onearg_constructor: - if noarg_constructor: - error(filename, linenum, 'runtime/explicit', 5, - 'Zero-parameter constructors should not be marked explicit.') def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): @@ -2957,7 +3563,7 @@ def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): r'\bfor\s*\((.*)\)\s*{', r'\bwhile\s*\((.*)\)\s*[{;]', r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) + match = re.search(pattern, line) if match: fncall = match.group(1) # look inside the parens for function calls break @@ -2976,26 +3582,26 @@ def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', + not re.search(r'\b(if|elif|for|while|switch|return|new|delete|catch|sizeof)\b', fncall) and # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + not re.search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + not re.search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if re.search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call error(filename, linenum, 'whitespace/parens', 4, 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + elif re.search(r'\(\s+(?!(\s*\\)|\()', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and - not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and - not Search(r'\bcase\s+\(', fncall)): + if (re.search(r'\w\s+\(', fncall) and + not re.search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and + not re.search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and + not re.search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and + not re.search(r'\bcase\s+\(', fncall)): # TODO(unknown): Space after an operator function seem to be a common # error, silence those for now by restricting them to highest verbosity. - if Search(r'\boperator_*\b', line): + if re.search(r'\boperator_*\b', line): error(filename, linenum, 'whitespace/parens', 0, 'Extra space before ( in function call') else: @@ -3003,10 +3609,10 @@ def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + if re.search(r'[^)]\s+\)\s*[^{\s]', fncall): # If the closing parenthesis is preceded by only whitespaces, # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): + if re.search(r'^\s+\)', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Closing ) should be moved to the previous line') else: @@ -3032,10 +3638,10 @@ def IsBlankLine(line): def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, error): is_namespace_indent_item = ( - len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and - nesting_state.previous_stack_top == nesting_state.stack[-2]) + len(nesting_state.stack) >= 1 and + (isinstance(nesting_state.stack[-1], _NamespaceInfo) or + (isinstance(nesting_state.previous_stack_top, _NamespaceInfo))) + ) if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, clean_lines.elided, line): @@ -3072,28 +3678,28 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, starting_func = False regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) + match_result = re.match(regexp, line) if match_result: # If the name is all caps and underscores, figure it's a macro and # ignore it, unless it's TEST or TEST_F. function_name = match_result.group(1).split()[-1] if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): + not re.match(r'[A-Z_]+$', function_name)): starting_func = True if starting_func: body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): + for start_linenum in range(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions + if re.search(r'(;|})', start_line): # Declarations and trivial functions body_found = True break # ... ignore - elif Search(r'{', start_line): + if re.search(r'{', start_line): body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) + function = re.search(r'((\w|:)*)\(', line).group(1) + if re.match(r'TEST', function): # Handle TEST... macros + parameter_regexp = re.search(r'(\(.*\))', joined_line) if parameter_regexp: # Ignore bad syntax function += parameter_regexp.group(1) else: @@ -3104,10 +3710,10 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, # No body for the function (or evidence of a non-function) was found. error(filename, linenum, 'readability/fn_size', 5, 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end + elif re.match(r'^\}\s*$', line): # function end function_state.Check(error, filename, linenum) function_state.End() - elif not Match(r'^\s*$', line): + elif not re.match(r'^\s*$', line): function_state.Count() # Count non-blank/non-comment lines. @@ -3129,7 +3735,7 @@ def CheckComment(line, filename, linenum, next_line_start, error): # Check if the // may be in quotes. If so, ignore it if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: # Allow one space for new scopes, two spaces otherwise: - if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and + if (not (re.match(r'^.*{ *//', line) and next_line_start == commentpos) and ((commentpos >= 1 and line[commentpos-1] not in string.whitespace) or (commentpos >= 2 and @@ -3154,7 +3760,8 @@ def CheckComment(line, filename, linenum, next_line_start, error): '"// TODO(my_username): Stuff."') middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison + # Comparisons made explicit for correctness + # -- pylint: disable=g-explicit-bool-comparison if middle_whitespace != ' ' and middle_whitespace != '': error(filename, linenum, 'whitespace/todo', 2, 'TODO(my_username) should be followed by a space') @@ -3162,8 +3769,8 @@ def CheckComment(line, filename, linenum, next_line_start, error): # If the comment contains an alphanumeric character, there # should be a space somewhere between it and the // unless # it's a /// or //! Doxygen comment. - if (Match(r'//[^ ]*\w', comment) and - not Match(r'(///|//\!)(\s+|$)', comment)): + if (re.match(r'//[^ ]*\w', comment) and + not re.match(r'(///|//\!)(\s+|$)', comment)): error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -3226,12 +3833,12 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False - if Match(r' {6}\w', prev_line): # Initializer list? + if re.match(r' {6}\w', prev_line): # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum-2 while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): + and re.match(r' {6}\w', elided[search_position])): search_position -= 1 exception = (search_position >= 0 and elided[search_position][:5] == ' :') @@ -3242,9 +3849,9 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # or colon (for initializer lists) we assume that it is the last line of # a function header. If we have a colon indented 4 spaces, it is an # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + exception = (re.match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', prev_line) - or Match(r' {4}:', prev_line)) + or re.match(r' {4}:', prev_line)) if not exception: error(filename, linenum, 'whitespace/blank_line', 2, @@ -3261,16 +3868,16 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] if (next_line - and Match(r'\s*}', next_line) + and re.match(r'\s*}', next_line) and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, 'Redundant blank line at the end of a code block ' 'should be deleted.') - matched = Match(r'\s*(public|protected|private):', prev_line) + matched = re.match(r'\s*(public|protected|private):', prev_line) if matched: error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) + f'Do not leave a blank line after "{matched.group(1)}:"') # Next, check comments next_line_start = 0 @@ -3282,16 +3889,17 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # get rid of comments and strings line = clean_lines.elided[linenum] - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []', 'return []() {};', or 'auto [abc, ...] = ...;'. - if Search(r'\w\s+\[', line) and not Search(r'(?:auto&?|delete|return)\s+\[', line): + # You shouldn't have spaces before your brackets, except for C++11 attributes + # or maybe after 'delete []', 'return []() {};', or 'auto [abc, ...] = ...;'. + if (re.search(r'\w\s+\[(?!\[)', line) and + not re.search(r'(?:auto&?|delete|return)\s+\[', line)): error(filename, linenum, 'whitespace/braces', 5, 'Extra space before [') # In range-based for, we wanted spaces before and after the colon, but # not around "::" tokens that might appear. - if (Search(r'for *\(.*[^:]:[^: ]', line) or - Search(r'for *\(.*[^: ]:[^:]', line)): + if (re.search(r'for *\(.*[^:]:[^: ]', line) or + re.search(r'for *\(.*[^: ]:[^:]', line)): error(filename, linenum, 'whitespace/forcolon', 2, 'Missing space around colon in range-based for loop') @@ -3314,7 +3922,7 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # The replacement is done repeatedly to avoid false positives from # operators that call operators. while True: - match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) + match = re.match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) if match: line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) else: @@ -3324,12 +3932,12 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) - if ((Search(r'[\w.]=', line) or - Search(r'=[\w.]', line)) - and not Search(r'\b(if|while|for) ', line) + if ((re.search(r'[\w.]=', line) or + re.search(r'=[\w.]', line)) + and not re.search(r'\b(if|while|for) ', line) # Operators taken from [lex.operators] in C++11 standard. - and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) - and not Search(r'operator=', line)): + and not re.search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) + and not re.search(r'operator=', line)): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') @@ -3348,16 +3956,17 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # # Note that && is not included here. This is because there are too # many false positives due to RValue references. - match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) + match = re.search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) if match: + # TODO: support alternate operators error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - elif not Match(r'#.*include', line): + f'Missing spaces around {match.group(1)}') + elif not re.match(r'#.*include', line): # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though # technically should should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. - match = Match(r'^(.*[^\s<])<[^\s=<,]', line) + match = re.match(r'^(.*[^\s<])<[^\s=<,]', line) if match: (_, _, end_pos) = CloseExpression( clean_lines, linenum, len(match.group(1))) @@ -3368,7 +3977,7 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # Look for > that is not surrounded by spaces. Similar to the # above, we only trigger if both sides are missing spaces to avoid # false positives with shifts. - match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) + match = re.match(r'^(.*[^-\s>])>[^\s=>,]', line) if match: (_, _, start_pos) = ReverseCloseExpression( clean_lines, linenum, len(match.group(1))) @@ -3381,7 +3990,7 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # # We also allow operators following an opening parenthesis, since # those tend to be macros that deal with operators. - match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) + match = re.search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and not (match.group(1) == 'operator' and match.group(2) == ';')): error(filename, linenum, 'whitespace/operators', 3, @@ -3399,16 +4008,16 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # follows would be part of an identifier, and there should still be # a space separating the template type and the identifier. # type> alpha - match = Search(r'>>[a-zA-Z_]', line) + match = re.search(r'>>[a-zA-Z_]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >>') # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + match = re.search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) + f'Extra space for operator {match.group(1)}') def CheckParenthesisSpacing(filename, clean_lines, linenum, error): @@ -3423,30 +4032,29 @@ def CheckParenthesisSpacing(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # No spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) + match = re.search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) + f'Missing space before ( in {match.group(1)}') # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' + match = re.search(r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', line) if match: if len(match.group(2)) != len(match.group(4)): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + not match.group(2) and re.search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) + f'Mismatching spaces inside () in {match.group(1)}') if len(match.group(2)) not in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) + f'Should have zero or one spaces inside ( and ) in {match.group(1)}') def CheckCommaSpacing(filename, clean_lines, linenum, error): @@ -3471,8 +4079,9 @@ def CheckCommaSpacing(filename, clean_lines, linenum, error): # verify that lines contain missing whitespaces, second pass on raw # lines to confirm that those missing whitespaces are not due to # elided comments. - if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and - Search(r',[^,\s]', raw[linenum])): + match = re.search(r',[^,\s]', re.sub(r'\b__VA_OPT__\s*\(,\)', '', + re.sub(r'\boperator\s*,\s*\(', 'F(', line))) + if (match and re.search(r',[^,\s]', raw[linenum])): error(filename, linenum, 'whitespace/comma', 3, 'Missing space after ,') @@ -3480,7 +4089,7 @@ def CheckCommaSpacing(filename, clean_lines, linenum, error): # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; - if Search(r';[^\s};\\)/]', line): + if re.search(r';[^\s};\\)/]', line): error(filename, linenum, 'whitespace/semicolon', 3, 'Missing space after ;') @@ -3497,7 +4106,7 @@ def _IsType(clean_lines, nesting_state, expr): True, if token looks like a type. """ # Keep only the last token in the expression - last_word = Match(r'^.*(\b\S+)$', expr) + last_word = re.match(r'^.*(\b\S+)$', expr) if last_word: token = last_word.group(1) else: @@ -3540,8 +4149,8 @@ def _IsType(clean_lines, nesting_state, expr): continue # Look for typename in the specified range - for i in xrange(first_line, last_line + 1, 1): - if Search(typename_pattern, clean_lines.elided[i]): + for i in range(first_line, last_line + 1, 1): + if re.search(typename_pattern, clean_lines.elided[i]): return True block_index -= 1 @@ -3567,7 +4176,7 @@ def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): # And since you should never have braces at the beginning of a line, # this is an easy test. Except that braces used for initialization don't # follow the same rule; we often don't want spaces before those. - match = Match(r'^(.*[^ ({>]){', line) + match = re.match(r'^(.*[^ ({>]){', line) if match: # Try a bit harder to check for brace initialization. This @@ -3604,34 +4213,34 @@ def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): trailing_text = '' if endpos > -1: trailing_text = endline[endpos:] - for offset in xrange(endlinenum + 1, + for offset in range(endlinenum + 1, min(endlinenum + 3, clean_lines.NumLines() - 1)): trailing_text += clean_lines.elided[offset] # We also suppress warnings for `uint64_t{expression}` etc., as the style # guide recommends brace initialization for integral types to avoid # overflow/truncation. - if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) + if (not re.match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) and not _IsType(clean_lines, nesting_state, leading_text)): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before {') # Make sure '} else {' has spaces. - if Search(r'}else', line): + if re.search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. - if Search(r':\s*;\s*$', line): + if re.search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): + elif re.search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): + elif (re.search(r'\s+;\s*$', line) and + not re.search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' 'statement, use {} instead.') @@ -3650,11 +4259,10 @@ def IsDecltype(clean_lines, linenum, column): (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) if start_col < 0: return False - if Search(r'\bdecltype\s*$', text[0:start_col]): + if re.search(r'\bdecltype\s*$', text[0:start_col]): return True return False - def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): """Checks for additional blank line issues related to sections. @@ -3682,7 +4290,7 @@ def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): linenum <= class_info.starting_linenum): return - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + matched = re.match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) if matched: # Issue warning if the line before public/protected/private was # not a blank line, but don't do this if the previous line contains @@ -3694,20 +4302,20 @@ def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): # common when defining classes in C macros. prev_line = clean_lines.lines[linenum - 1] if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): + not re.search(r'\b(class|struct)\b', prev_line) and + not re.search(r'\\$', prev_line)): # Try a bit harder to find the beginning of the class. This is to # account for multi-line base-specifier lists, e.g.: # class Derived # : public Base { end_class_head = class_info.starting_linenum for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): + if re.search(r'\{\s*$', clean_lines.lines[i]): end_class_head = i break if end_class_head < linenum - 1: error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) + f'"{matched.group(1)}:" should be preceded by a blank line') def GetPreviousNonBlankLine(clean_lines, linenum): @@ -3745,7 +4353,7 @@ def CheckBraces(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # get rid of comments and strings - if Match(r'\s*{\s*$', line): + if re.match(r'\s*{\s*$', line): # We allow an open brace to start a line in the case where someone is using # braces in a block to explicitly create a new scope, which is commonly used # to control the lifetime of stack-allocated variables. Braces are also @@ -3756,23 +4364,23 @@ def CheckBraces(filename, clean_lines, linenum, error): # following line if it is part of an array initialization and would not fit # within the 80 character limit of the preceding line. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline) and + if (not re.search(r'[,;:}{(]\s*$', prevline) and + not re.match(r'\s*#', prevline) and not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): + if re.match(r'\s*else\b\s*(?:if\b|\{|$)', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): + if re.match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, 'An else should appear on the same line as the preceding }') # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! - if Search(r'else if\s*\(', line): # could be multi-line if - brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) + if re.search(r'else if\s*\(', line): # could be multi-line if + brace_on_left = bool(re.search(r'}\s*else if\s*\(', line)) # find the ( after the if pos = line.find('else if') pos = line.find('(', pos) @@ -3782,17 +4390,17 @@ def CheckBraces(filename, clean_lines, linenum, error): if brace_on_left != brace_on_right: # must be brace after if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') - elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + elif re.search(r'}\s*else[^{]*$', line) or re.match(r'[^}]*else\s*{', line): error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + if re.search(r'\belse [^\s{]', line) and not re.search(r'\belse if\b', line): error(filename, linenum, 'whitespace/newline', 4, 'Else clause should never be on same line as else (use 2 lines)') # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): + if re.match(r'\s*do [^\s{]', line): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') @@ -3803,21 +4411,21 @@ def CheckBraces(filename, clean_lines, linenum, error): # its line, and the line after that should have an indent level equal to or # lower than the if. We also check for ambiguous if/else nesting without # braces. - if_else_match = Search(r'\b(if\s*\(|else\b)', line) - if if_else_match and not Match(r'\s*#', line): + if_else_match = re.search(r'\b(if\s*(|constexpr)\s*\(|else\b)', line) + if if_else_match and not re.match(r'\s*#', line): if_indent = GetIndentLevel(line) endline, endlinenum, endpos = line, linenum, if_else_match.end() - if_match = Search(r'\bif\s*\(', line) + if_match = re.search(r'\bif\s*(|constexpr)\s*\(', line) if if_match: # This could be a multiline if condition, so find the end first. pos = if_match.end() - 1 (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) # Check for an opening brace, either directly after the if or on the next # line. If found, this isn't a single-statement conditional. - if (not Match(r'\s*{', endline[endpos:]) - and not (Match(r'\s*$', endline[endpos:]) + if (not re.match(r'\s*{', endline[endpos:]) + and not (re.match(r'\s*$', endline[endpos:]) and endlinenum < (len(clean_lines.elided) - 1) - and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): + and re.match(r'\s*{', clean_lines.elided[endlinenum + 1]))): while (endlinenum < len(clean_lines.elided) and ';' not in clean_lines.elided[endlinenum][endpos:]): endlinenum += 1 @@ -3827,11 +4435,11 @@ def CheckBraces(filename, clean_lines, linenum, error): # We allow a mix of whitespace and closing braces (e.g. for one-liner # methods) and a single \ after the semicolon (for macros) endpos = endline.find(';') - if not Match(r';[\s}]*(\\?)$', endline[endpos:]): + if not re.match(r';[\s}]*(\\?)$', endline[endpos:]): # Semicolon isn't the last character, there's something trailing. # Output a warning if the semicolon is not contained inside # a lambda expression. - if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', + if not re.match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', endline): error(filename, linenum, 'readability/braces', 4, 'If/else bodies with multiple statements require braces') @@ -3842,7 +4450,7 @@ def CheckBraces(filename, clean_lines, linenum, error): # With ambiguous nested if statements, this will error out on the # if that *doesn't* match the else, regardless of whether it's the # inner one or outer one. - if (if_match and Match(r'\s*else\b', next_line) + if (if_match and re.match(r'\s*else\b', next_line) and next_indent != if_indent): error(filename, linenum, 'readability/braces', 4, 'Else clause should be indented at the same level as if. ' @@ -3908,7 +4516,7 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error): # to namespaces. For now we do not warn for this case. # # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) + match = re.match(r'^(.*\)\s*)\{', line) if match: # Matched closing parenthesis (case 1). Check the token before the # matching opening parenthesis, and don't warn if it looks like a @@ -3941,27 +4549,27 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error): clean_lines, linenum, closing_brace_pos) if opening_parenthesis[2] > -1: line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) - func = Match(r'^(.*\])\s*$', line_prefix) + macro = re.search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) + func = re.match(r'^(.*\])\s*$', line_prefix) if ((macro and macro.group(1) not in ( 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or - Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or - Search(r'\bdecltype$', line_prefix) or - Search(r'\s+=\s*$', line_prefix)): + (func and not re.search(r'\boperator\s*\[\s*\]', func.group(1))) or + re.search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or + re.search(r'\bdecltype$', line_prefix) or + re.search(r'\s+=\s*$', line_prefix)): match = None if (match and opening_parenthesis[1] > 1 and - Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): + re.search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): # Multi-line lambda-expression match = None else: # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + match = re.match(r'^(.*(?:else|\)\s*const)\s*)\{', line) if not match: # Try matching cases 4-6. These are always matched on separate lines. # @@ -3972,14 +4580,14 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error): # // blank line # } prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) + if prevline and re.search(r'[;{}]\s*$', prevline): + match = re.match(r'^(\s*)\{', line) # Check matching closing brace if match: (endline, endlinenum, endpos) = CloseExpression( clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + if endpos > -1 and re.match(r'^\s*;', endline[endpos:]): # Current {} pair is eligible for semicolon check, and we have found # the redundant semicolon, output warning here. # @@ -4016,7 +4624,7 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error): # We also check "if" blocks here, since an empty conditional block # is likely an error. line = clean_lines.elided[linenum] - matched = Match(r'\s*(for|while|if)\s*\(', line) + matched = re.match(r'\s*(for|while|if)\s*\(', line) if matched: # Find the end of the conditional expression. (end_line, end_linenum, end_pos) = CloseExpression( @@ -4025,7 +4633,7 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error): # Output warning if what follows the condition expression is a semicolon. # No warning for all other cases, including whitespace or newline, since we # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if end_pos >= 0 and re.match(r';', end_line[end_pos:]): if matched.group(1) == 'if': error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, 'Empty conditional bodies should use {}') @@ -4041,8 +4649,8 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error): opening_linenum = end_linenum opening_line_fragment = end_line[end_pos:] # Loop until EOF or find anything that's not whitespace or opening {. - while not Search(r'^\s*\{', opening_line_fragment): - if Search(r'^(?!\s*$)', opening_line_fragment): + while not re.search(r'^\s*\{', opening_line_fragment): + if re.search(r'^(?!\s*$)', opening_line_fragment): # Conditional has no brackets. return opening_linenum += 1 @@ -4072,12 +4680,12 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error): return if closing_linenum > opening_linenum: # Opening line after the {. Ignore comments here since we checked above. - body = list(opening_line[opening_pos+1:]) + bodylist = list(opening_line[opening_pos+1:]) # All lines until closing line, excluding closing line, with comments. - body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) + bodylist.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) # Closing line before the }. Won't (and can't) have comments. - body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) - body = '\n'.join(body) + bodylist.append(clean_lines.elided[closing_linenum][:closing_pos-1]) + body = '\n'.join(bodylist) else: # If statement has brackets and fits on a single line. body = opening_line[opening_pos+1:closing_pos-1] @@ -4089,8 +4697,8 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error): current_linenum = closing_linenum current_line_fragment = closing_line[closing_pos:] # Loop until EOF or find anything that's not whitespace or else clause. - while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): - if Search(r'^(?=\s*else)', current_line_fragment): + while re.search(r'^\s*$|^(?=\s*else)', current_line_fragment): + if re.search(r'^(?=\s*else)', current_line_fragment): # Found an else clause, so don't log an error. return current_linenum += 1 @@ -4119,7 +4727,7 @@ def FindCheckMacro(line): # to make sure that we are matching the expected CHECK macro, as # opposed to some other macro that happens to contain the CHECK # substring. - matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) + matched = re.match(r'^(.*\b' + macro + r'\s*)\(', line) if not matched: continue return (macro, len(matched.group(1))) @@ -4151,14 +4759,14 @@ def CheckCheck(filename, clean_lines, linenum, error): # If the check macro is followed by something other than a # semicolon, assume users will log their own custom error messages # and don't suggest any replacements. - if not Match(r'\s*;', last_line[end_pos:]): + if not re.match(r'\s*;', last_line[end_pos:]): return if linenum == end_line: expression = lines[linenum][start_pos + 1:end_pos - 1] else: expression = lines[linenum][start_pos + 1:] - for i in xrange(linenum + 1, end_line): + for i in range(linenum + 1, end_line): expression += lines[i] expression += last_line[0:end_pos - 1] @@ -4169,7 +4777,7 @@ def CheckCheck(filename, clean_lines, linenum, error): rhs = '' operator = None while expression: - matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' + matched = re.match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' r'==|!=|>=|>|<=|<|\()(.*)$', expression) if matched: token = matched.group(1) @@ -4203,9 +4811,9 @@ def CheckCheck(filename, clean_lines, linenum, error): # characters at once if possible. Trivial benchmark shows that this # is more efficient when the operands are longer than a single # character, which is generally the case. - matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) + matched = re.match(r'^([^-=!<>()&|]+)(.*)$', expression) if not matched: - matched = Match(r'^(\s*\S)(.*)$', expression) + matched = re.match(r'^(\s*\S)(.*)$', expression) if not matched: break lhs += matched.group(1) @@ -4229,7 +4837,7 @@ def CheckCheck(filename, clean_lines, linenum, error): lhs = lhs.strip() rhs = rhs.strip() match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' - if Match(match_constant, lhs) or Match(match_constant, rhs): + if re.match(match_constant, lhs) or re.match(match_constant, rhs): # Note: since we know both lhs and rhs, we can provide a more # descriptive error message like: # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) @@ -4239,9 +4847,8 @@ def CheckCheck(filename, clean_lines, linenum, error): # We are still keeping the less descriptive message because if lhs # or rhs gets long, the error message might become unreadable. error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[check_macro][operator], - check_macro, operator)) + f'Consider using {_CHECK_REPLACEMENT[check_macro][operator]}' + f' instead of {check_macro}(a {operator} b)') def CheckAltTokens(filename, clean_lines, linenum, error): @@ -4256,7 +4863,7 @@ def CheckAltTokens(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # Avoid preprocessor lines - if Match(r'^\s*#', line): + if re.match(r'^\s*#', line): return # Last ditch effort to avoid multi-line comments. This will not help @@ -4272,8 +4879,8 @@ def CheckAltTokens(filename, clean_lines, linenum, error): for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + f'Use operator {_ALT_TOKEN_REPLACEMENT[match.group(2)]}' + f' instead of {match.group(2)}') def GetLineWidth(line): @@ -4286,7 +4893,7 @@ def GetLineWidth(line): The width of the line in column positions, accounting for Unicode combining characters and wide characters. """ - if isinstance(line, unicode): + if isinstance(line, str): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): @@ -4301,7 +4908,7 @@ def GetLineWidth(line): is_low_surrogate = 0xDC00 <= ord(uc) <= 0xDFFF if not is_wide_build and is_low_surrogate: width -= 1 - + width += 1 return width else: @@ -4349,7 +4956,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' + scope_or_label_pattern = r'\s*(?:public|private|protected|signals)(?:\s+(?:slots\s*)?)?:\s*\\?$' classinfo = nesting_state.InnermostClass() initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] @@ -4360,11 +4967,11 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, # We also don't check for lines that look like continuation lines # (of lines ending in double quotes, commas, equals, or angle brackets) # because the rules for how to indent those are non-trivial. - if (not Search(r'[",=><] *$', prev) and + if (not re.search(r'[",=><] *$', prev) and (initial_spaces == 1 or initial_spaces == 3) and - not Match(scope_or_label_pattern, cleansed_line) and + not re.match(scope_or_label_pattern, cleansed_line) and not (clean_lines.raw_lines[linenum] != line and - Match(r'^\s*""', line))): + re.match(r'^\s*""', line))): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') @@ -4377,9 +4984,9 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, is_header_guard = False if IsHeaderExtension(file_extension): cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): + if (line.startswith(f'#ifndef {cppvar}') or + line.startswith(f'#define {cppvar}') or + line.startswith(f'#endif // {cppvar}')): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. @@ -4389,16 +4996,23 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, # # The "$Id:...$" comment may also get very long without it being the # developers fault. + # + # Doxygen documentation copying can get pretty long when using an overloaded + # function declaration if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^\s*//\s*[^\s]*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + not re.match(r'^\s*//.*http(s?)://\S*$', line) and + not re.match(r'^\s*//\s*[^\s]*$', line) and + not re.match(r'^// \$Id:.*#[0-9]+ \$$', line) and + not re.match(r'^\s*/// [@\\](copydoc|copydetails|copybrief) .*$', line)): line_width = GetLineWidth(line) if line_width > _line_length: error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= %i characters long' % _line_length) + f'Lines should be <= {_line_length} characters long') if (cleansed_line.count(';') > 1 and + # allow simple single line lambdas + not re.match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}\n\r]*\}', + line) and # for loops are allowed two ;'s (and may run over two lines). cleansed_line.find('for') == -1 and (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or @@ -4455,21 +5069,25 @@ def _DropCommonSuffixes(filename): Returns: The filename with the common suffix removed. """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): + for suffix in itertools.chain( + (f"{test_suffix.lstrip('_')}.{ext}" + for test_suffix, ext in itertools.product(_test_suffixes, GetNonHeaderExtensions())), + (f'{suffix}.{ext}' + for suffix, ext in itertools.product(['inl', 'imp', 'internal'], GetHeaderExtensions()))): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] return os.path.splitext(filename)[0] -def _ClassifyInclude(fileinfo, include, is_system): +def _ClassifyInclude(fileinfo, include, used_angle_brackets, include_order="default"): """Figures out what kind of header 'include' is. Args: fileinfo: The current file cpplint is running over. A FileInfo instance. include: The path to a #included file. - is_system: True if the #include used <> rather than "". + used_angle_brackets: True if the #include used <> rather than "". + include_order: "default" or other value allowed in program arguments Returns: One of the _XXX_HEADER constants. @@ -4479,6 +5097,8 @@ def _ClassifyInclude(fileinfo, include, is_system): _C_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', True, "standardcfirst") + _OTHER_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) _LIKELY_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), @@ -4489,13 +5109,24 @@ def _ClassifyInclude(fileinfo, include, is_system): """ # This is a list of all standard c++ header files, except # those already checked for above. - is_cpp_h = include in _CPP_HEADERS + is_cpp_header = include in _CPP_HEADERS + + # Mark include as C header if in list or in a known folder for standard-ish C headers. + is_std_c_header = (include_order == "default") or (include in _C_HEADERS + # additional linux glibc header folders + or re.search(rf'(?:{"|".join(C_STANDARD_HEADER_FOLDERS)})\/.*\.h', include)) + + # Headers with C++ extensions shouldn't be considered C system headers + include_ext = os.path.splitext(include)[1] + is_system = used_angle_brackets and include_ext not in ['.hh', '.hpp', '.hxx', '.h++'] if is_system: - if is_cpp_h: + if is_cpp_header: return _CPP_SYS_HEADER - else: + if is_std_c_header: return _C_SYS_HEADER + else: + return _OTHER_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include @@ -4503,9 +5134,11 @@ def _ClassifyInclude(fileinfo, include, is_system): target_dir, target_base = ( os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + target_dir_pub = os.path.normpath(target_dir + '/../public') + target_dir_pub = target_dir_pub.replace('\\', '/') if target_base == include_base and ( include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): + include_dir == target_dir_pub): return _LIKELY_MY_HEADER # If the target and include share some initial basename @@ -4547,10 +5180,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): # # We also make an exception for Lua headers, which follow google # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') + match = re.match(r'#include\s*"([^/]+\.(.*))"', line) + if match: + if (IsHeaderExtension(match.group(2)) and + not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1))): + error(filename, linenum, 'build/include_subdir', 4, + 'Include the directory when naming header files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's @@ -4558,17 +5193,33 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): match = _RE_PATTERN_INCLUDE.search(line) if match: include = match.group(2) - is_system = (match.group(1) == '<') + used_angle_brackets = match.group(1) == '<' duplicate_line = include_state.FindHeader(include) if duplicate_line >= 0: error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, duplicate_line)) - elif (include.endswith('.cc') and + f'"{include}" already included at {filename}:{duplicate_line}') + return + + for extension in GetNonHeaderExtensions(): + if (include.endswith('.' + extension) and os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): - error(filename, linenum, 'build/include', 4, - 'Do not include .cc files from other packages') - elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): + error(filename, linenum, 'build/include', 4, + 'Do not include .' + extension + ' files from other packages') + return + + # We DO want to include a 3rd party looking header if it matches the + # filename. Otherwise we get an erroneous error "...should include its + # header" error later. + third_src_header = False + for ext in GetHeaderExtensions(): + basefilename = filename[0:len(filename) - len(fileinfo.Extension())] + headerfile = basefilename + '.' + ext + headername = FileInfo(headerfile).RepositoryName() + if headername in include or include in headername: + third_src_header = True + break + + if third_src_header or not _THIRD_PARTY_HEADERS_PATTERN.match(include): include_state.include_list[-1].append((include, linenum)) # We want to ensure that headers appear in the right order: @@ -4583,16 +5234,16 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): # track of the highest type seen, and complains if we see a # lower type after that. error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) + _ClassifyInclude(fileinfo, include, used_angle_brackets, _include_order)) if error_message: error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) + f'{error_message}. Should be: {fileinfo.BaseName()}.h, c system,' + ' c++ system, other.') canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) if not include_state.IsInAlphabeticalOrder( clean_lines, linenum, canonical_include): error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) + f'Include "{include}" not in alphabetical order') include_state.SetLastHeader(canonical_include) @@ -4622,7 +5273,7 @@ def _GetTextInside(text, start_pattern): # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) + closing_punctuation = set(dict.values(matching_punctuation)) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) @@ -4712,12 +5363,10 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # Reset include state across preprocessor directives. This is meant # to silence warnings for conditional includes. - match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) + match = re.match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) if match: include_state.ResetSection(match.group(1)) - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') # Perform other checks now that we are sure that this is not an include line CheckCasts(filename, clean_lines, linenum, error) @@ -4734,15 +5383,15 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # Check if people are using the verboten C basic types. The only exception # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): + if re.search(r'\bshort port\b', line): + if not re.search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) + match = re.search(r'\b(short|long(?! +double)|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + f'Use int16/int64/etc, rather than the C type {match.group(1)}') # Check if some verboten operator overloading is going on # TODO(unknown): catch out-of-line unary operator&: @@ -4750,13 +5399,13 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # int operator&(const X& x) { return 42; } // unary operator& # The trick is it's hard to tell apart from binary operator&: # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): + if re.search(r'\boperator\s*&\s*\(\s*\)', line): error(filename, linenum, 'runtime/operator', 4, 'Unary operator& is dangerous. Do not use it.') # Check for suspicious usage of "if" like # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): + if re.search(r'\}\s*if\s*\(', line): error(filename, linenum, 'readability/braces', 4, 'Did you mean "else if"? If not, start a new line for "if".') @@ -4769,28 +5418,32 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) + match = re.match(r'([\w.\->()]+)$', printf_args) if match and match.group(1) != '__VA_ARGS__': function_name = re.search(r'\b((?:string)?printf)\s*\(', line, re.I).group(1) error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) + 'Potential format string bug. Do' + f' {function_name}("%s", {match.group(1)}) instead.') # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + match = re.search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not re.match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) + f'Did you mean "memset({match.group(1)}, 0, {match.group(2)})"?') - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') + if re.search(r'\busing namespace\b', line): + if re.search(r'\bliterals\b', line): + error(filename, linenum, 'build/namespaces_literals', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + else: + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + match = re.match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) if (match and match.group(2) != 'return' and match.group(2) != 'delete' and match.group(3).find(']') == -1): # Split the size using space and arithmetic operators as delimiters. @@ -4804,17 +5457,17 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, skip_next = False continue - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue + if re.search(r'sizeof\(.+\)', tok): continue + if re.search(r'arraysize\(\w+\)', tok): continue tok = tok.lstrip('(') tok = tok.rstrip(')') if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + if re.match(r'\d+', tok): continue + if re.match(r'0[xX][0-9a-fA-F]+', tok): continue + if re.match(r'k[A-Z0-9]\w*', tok): continue + if re.match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if re.match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. @@ -4832,9 +5485,9 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # macros are typically OK, so we allow use of "namespace {" on lines # that end with backslashes. if (IsHeaderExtension(file_extension) - and Search(r'\bnamespace\s*{', line) + and re.search(r'\bnamespace\s*{', line) and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, + error(filename, linenum, 'build/namespaces_headers', 4, 'Do not use unnamed namespaces in header files. See ' 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') @@ -4852,7 +5505,7 @@ def CheckGlobalStatic(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # Match two lines at a time to support multiline declarations - if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): + if linenum + 1 < clean_lines.NumLines() and not re.search(r'[;({]', line): line += clean_lines.elided[linenum + 1].strip() # Check for people declaring static/global STL strings at the top level. @@ -4861,7 +5514,7 @@ def CheckGlobalStatic(filename, clean_lines, linenum, error): # also because globals can be destroyed when some threads are still running. # TODO(unknown): Generalize this to also find static unique_ptr instances. # TODO(unknown): File bugs for clang-tidy to find these. - match = Match( + match = re.match( r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' r'([a-zA-Z0-9_:]+)\b(.*)', line) @@ -4883,20 +5536,19 @@ def CheckGlobalStatic(filename, clean_lines, linenum, error): # matching identifiers. # string Class::operator*() if (match and - not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and - not Search(r'\boperator\W', line) and - not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): - if Search(r'\bconst\b', line): + not re.search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and + not re.search(r'\boperator\W', line) and + not re.match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): + if re.search(r'\bconst\b', line): error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string ' - 'instead: "%schar%s %s[]".' % - (match.group(1), match.group(2) or '', match.group(3))) + 'For a static/global string constant, use a C style string instead:' + f' "{match.group(1)}char{match.group(2) or ""} {match.group(3)}[]".') else: error(filename, linenum, 'runtime/string', 4, 'Static/global string variables are not permitted.') - if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or - Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): + if (re.search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or + re.search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): error(filename, linenum, 'runtime/init', 4, 'You seem to be initializing a member variable with itself.') @@ -4913,21 +5565,21 @@ def CheckPrintf(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + match = re.search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) if match and match.group(2) != '0': # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) + error(filename, linenum, 'runtime/printf', 3, 'If you can, use' + f' sizeof({match.group(1)}) instead of {match.group(2)}' + ' as the 2nd arg to snprintf.') # Check if some verboten C functions are being used. - if Search(r'\bsprintf\s*\(', line): + if re.search(r'\bsprintf\s*\(', line): error(filename, linenum, 'runtime/printf', 5, 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\s*\(', line) + match = re.search(r'\b(strcpy|strcat)\s*\(', line) if match: error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) + f'Almost always, snprintf is better than {match.group(1)}') def IsDerivedFunction(clean_lines, linenum): @@ -4941,14 +5593,14 @@ def IsDerivedFunction(clean_lines, linenum): virt-specifier. """ # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) + for i in range(linenum, max(-1, linenum - 10), -1): + match = re.match(r'^([^()]*\w+)\(', clean_lines.elided[i]) if match: # Look for "override" after the matching closing parenthesis line, _, closing_paren = CloseExpression( clean_lines, i, len(match.group(1))) return (closing_paren >= 0 and - Search(r'\boverride\b', line[closing_paren:])) + re.search(r'\boverride\b', line[closing_paren:])) return False @@ -4962,9 +5614,9 @@ def IsOutOfLineMethodDefinition(clean_lines, linenum): True if current line contains an out-of-line method definition. """ # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): - return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None + for i in range(linenum, max(-1, linenum - 10), -1): + if re.match(r'^([^()]*\w+)\(', clean_lines.elided[i]): + return re.match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None return False @@ -4978,24 +5630,24 @@ def IsInitializerList(clean_lines, linenum): True if current line appears to be inside constructor initializer list, False otherwise. """ - for i in xrange(linenum, 1, -1): + for i in range(linenum, 1, -1): line = clean_lines.elided[i] if i == linenum: - remove_function_body = Match(r'^(.*)\{\s*$', line) + remove_function_body = re.match(r'^(.*)\{\s*$', line) if remove_function_body: line = remove_function_body.group(1) - if Search(r'\s:\s*\w+[({]', line): + if re.search(r'\s:\s*\w+[({]', line): # A lone colon tend to indicate the start of a constructor # initializer list. It could also be a ternary operator, which # also tend to appear in constructor initializer lists as # opposed to parameter lists. return True - if Search(r'\}\s*,\s*$', line): + if re.search(r'\}\s*,\s*$', line): # A closing brace followed by a comma is probably the end of a # brace-initialized member in constructor initializer list. return True - if Search(r'[{};]\s*$', line): + if re.search(r'[{};]\s*$', line): # Found one of the following: # - A closing brace or semicolon, probably the end of the previous # function. @@ -5059,13 +5711,13 @@ def CheckForNonConstReference(filename, clean_lines, linenum, # that spans more than 2 lines, please use a typedef. if linenum > 1: previous = None - if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): + if re.match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): # previous_line\n + ::current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', + previous = re.search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', clean_lines.elided[linenum - 1]) - elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): + elif re.match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): # previous_line::\n + current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', + previous = re.search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', clean_lines.elided[linenum - 1]) if previous: line = previous.group(1) + line.lstrip() @@ -5079,7 +5731,7 @@ def CheckForNonConstReference(filename, clean_lines, linenum, # Found the matching < on an earlier line, collect all # pieces up to current line. line = '' - for i in xrange(startline, linenum + 1): + for i in range(startline, linenum + 1): line += clean_lines.elided[i].strip() # Check for non-const references in function parameters. A single '&' may @@ -5103,15 +5755,15 @@ def CheckForNonConstReference(filename, clean_lines, linenum, # appear inside the second set of parentheses on the current line as # opposed to the first set. if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 10), -1): + for i in range(linenum - 1, max(0, linenum - 10), -1): previous_line = clean_lines.elided[i] - if not Search(r'[),]\s*$', previous_line): + if not re.search(r'[),]\s*$', previous_line): break - if Match(r'^\s*:\s+\S', previous_line): + if re.match(r'^\s*:\s+\S', previous_line): return # Avoid preprocessors - if Search(r'\\\s*$', line): + if re.search(r'\\\s*$', line): return # Avoid constructor initializer lists @@ -5128,25 +5780,25 @@ def CheckForNonConstReference(filename, clean_lines, linenum, r'operator\s*[<>][<>]|' r'static_assert|COMPILE_ASSERT' r')\s*\(') - if Search(allowed_functions, line): + if re.search(allowed_functions, line): return - elif not Search(r'\S+\([^)]*$', line): + elif not re.search(r'\S+\([^)]*$', line): # Don't see an allowed function on this line. Actually we # didn't see any function name on this line, so this is likely a # multi-line parameter list. Try a bit harder to catch this case. - for i in xrange(2): + for i in range(2): if (linenum > i and - Search(allowed_functions, clean_lines.elided[linenum - i - 1])): + re.search(allowed_functions, clean_lines.elided[linenum - i - 1])): return - decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body + decls = re.sub(r'{[^}]*}', ' ', line) # exclude function body for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): - if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and - not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): + if (not re.match(_RE_PATTERN_CONST_REF_PARAM, parameter) and + not re.match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): error(filename, linenum, 'runtime/references', 2, 'Is this a non-const reference? ' 'If so, make const or use a pointer: ' + - ReplaceAll(' *<', '<', parameter)) + re.sub(' *<', '<', parameter)) def CheckCasts(filename, clean_lines, linenum, error): @@ -5164,7 +5816,7 @@ def CheckCasts(filename, clean_lines, linenum, error): # I just try to capture the most common basic types, though there are more. # Parameterless conversion functions, such as bool(), are allowed as they are # probably a member operator declaration or default constructor. - match = Search( + match = re.search( r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' r'(int|float|double|bool|char|int32|uint32|int64|uint64)' r'(\([^)].*)', line) @@ -5188,7 +5840,7 @@ def CheckCasts(filename, clean_lines, linenum, error): # Avoid arrays by looking for brackets that come after the closing # parenthesis. - if Match(r'\([^()]+\)\s*\[', match.group(3)): + if re.match(r'\([^()]+\)\s*\[', match.group(3)): return # Other things to ignore: @@ -5199,19 +5851,18 @@ def CheckCasts(filename, clean_lines, linenum, error): matched_funcptr = match.group(3) if (matched_new_or_template is None and not (matched_funcptr and - (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', + (re.match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', matched_funcptr) or matched_funcptr.startswith('(*)'))) and - not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and - not Search(r'new\(\S+\)\s*' + matched_type, line)): + not re.match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and + not re.search(r'new\(\S+\)\s*' + matched_type, line)): error(filename, linenum, 'readability/casting', 4, 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - matched_type) + f'Use static_cast<{matched_type}>(...) instead') if not expecting_function: CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + r'\((int|float|double|bool|char|u?int(16|32|64)|size_t)\)', error) # This doesn't catch all cases. Consider (const char * const)"hello". # @@ -5236,7 +5887,7 @@ def CheckCasts(filename, clean_lines, linenum, error): # # This is not a cast: # reference_type&(int* function_param); - match = Search( + match = re.search( r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) if match: @@ -5244,7 +5895,7 @@ def CheckCasts(filename, clean_lines, linenum, error): # dereferenced by the casted pointer, as opposed to the casted # pointer itself. parenthesis_error = False - match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) + match = re.match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) if match: _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) if x1 >= 0 and clean_lines.elided[y1][x1] == '(': @@ -5253,7 +5904,7 @@ def CheckCasts(filename, clean_lines, linenum, error): extended_line = clean_lines.elided[y2][x2:] if y2 < clean_lines.NumLines() - 1: extended_line += clean_lines.elided[y2 + 1] - if Match(r'\s*(?:->|\[)', extended_line): + if re.match(r'\s*(?:->|\[)', extended_line): parenthesis_error = True if parenthesis_error: @@ -5285,38 +5936,38 @@ def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): False otherwise. """ line = clean_lines.elided[linenum] - match = Search(pattern, line) + match = re.search(pattern, line) if not match: return False # Exclude lines with keywords that tend to look like casts context = line[0:match.start(1) - 1] - if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): + if re.match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): return False # Try expanding current context to see if we one level of # parentheses inside a macro. if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 5), -1): + for i in range(linenum - 1, max(0, linenum - 5), -1): context = clean_lines.elided[i] + context - if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): + if re.match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): return False # operator++(int) and operator--(int) - if context.endswith(' operator++') or context.endswith(' operator--'): + if (context.endswith(' operator++') or context.endswith(' operator--') or + context.endswith('::operator++') or context.endswith('::operator--')): return False # A single unnamed argument for a function tends to look like old style cast. # If we see those, don't issue warnings for deprecated casts. remainder = line[match.end(0):] - if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', + if re.match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', remainder): return False # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) + f'Using C-style cast. Use {cast_type}<{match.group(1)}>(...) instead') return True @@ -5333,13 +5984,13 @@ def ExpectingFunctionArgs(clean_lines, linenum): of function types. """ line = clean_lines.elided[linenum] - return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + return (re.match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or (linenum >= 2 and - (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', + (re.match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', clean_lines.elided[linenum - 1]) or - Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', + re.match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', clean_lines.elided[linenum - 2]) or - Search(r'\bstd::m?function\s*\<\s*$', + re.search(r'\bstd::m?function\s*\<\s*$', clean_lines.elided[linenum - 1])))) @@ -5364,11 +6015,11 @@ _HEADERS_CONTAINING_TEMPLATES = ( )), ('', ('numeric_limits',)), ('', ('list',)), - ('', ('map', 'multimap',)), + ('', ('multimap',)), ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', 'unique_ptr', 'weak_ptr')), ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), + ('', ('multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('tuple',)), @@ -5391,17 +6042,46 @@ _HEADERS_MAYBE_TEMPLATES = ( ('', ('forward', 'make_pair', 'move', 'swap')), ) -_RE_PATTERN_STRING = re.compile(r'\bstring\b') +# Non templated types or global objects +_HEADERS_TYPES_OR_OBJS = ( + # String and others are special -- it is a non-templatized type in STL. + ('', ('string',)), + ('', ('cin', 'cout', 'cerr', 'clog', 'wcin', 'wcout', + 'wcerr', 'wclog')), + ('', ('FILE', 'fpos_t'))) + +# Non templated functions +_HEADERS_FUNCTIONS = ( + ('', ('fopen', 'freopen', + 'fclose', 'fflush', 'setbuf', 'setvbuf', 'fread', + 'fwrite', 'fgetc', 'getc', 'fgets', 'fputc', 'putc', + 'fputs', 'getchar', 'gets', 'putchar', 'puts', 'ungetc', + 'scanf', 'fscanf', 'sscanf', 'vscanf', 'vfscanf', + 'vsscanf', 'printf', 'fprintf', 'sprintf', 'snprintf', + 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf', + 'ftell', 'fgetpos', 'fseek', 'fsetpos', + 'clearerr', 'feof', 'ferror', 'perror', + 'tmpfile', 'tmpnam'),),) _re_pattern_headers_maybe_templates = [] for _header, _templates in _HEADERS_MAYBE_TEMPLATES: for _template in _templates: # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). + # 'type::max()'. _re_pattern_headers_maybe_templates.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + (re.compile(r'((\bstd::)|[^>.:])\b' + _template + r'(<.*?>)?\([^\)]'), _template, _header)) +# Match set, but not foo->set, foo.set +_re_pattern_headers_maybe_templates.append( + (re.compile(r'[^>.]\bset\s*\<'), + 'set<>', + '')) +# Match 'map var' and 'std::map(...)', but not 'map(...)'' +_re_pattern_headers_maybe_templates.append( + (re.compile(r'(std\b::\bmap\s*\<)|(^(std\b::\b)map\b\(\s*\<)'), + 'map<>', + '')) # Other scripts may reach in and modify this pattern. _re_pattern_templates = [] @@ -5412,6 +6092,23 @@ for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: _template + '<>', _header)) +_re_pattern_types_or_objs = [] +for _header, _types_or_objs in _HEADERS_TYPES_OR_OBJS: + for _type_or_obj in _types_or_objs: + _re_pattern_types_or_objs.append( + (re.compile(r'\b' + _type_or_obj + r'\b'), + _type_or_obj, + _header)) + +_re_pattern_functions = [] +for _header, _functions in _HEADERS_FUNCTIONS: + for _function in _functions: + # Match printf(..., ...), but not foo->printf, foo.printf or + # 'type::printf()'. + _re_pattern_functions.append( + (re.compile(r'([^>.]|^)\b' + _function + r'\([^\)]'), + _function, + _header)) def FilesBelongToSameModule(filename_cc, filename_h): """Check if these two filenames belong to the same module. @@ -5434,7 +6131,7 @@ def FilesBelongToSameModule(filename_cc, filename_h): some false positives. This should be sufficiently rare in practice. Args: - filename_cc: is the path for the .cc file + filename_cc: is the path for the source (e.g. .cc) file filename_h: is the path for the header path Returns: @@ -5442,20 +6139,23 @@ def FilesBelongToSameModule(filename_cc, filename_h): bool: True if filename_cc and filename_h belong to the same module. string: the additional prefix needed to open the header file. """ - - fileinfo = FileInfo(filename_cc) - if not fileinfo.IsSource(): + fileinfo_cc = FileInfo(filename_cc) + if fileinfo_cc.Extension().lstrip('.') not in GetNonHeaderExtensions(): return (False, '') - filename_cc = filename_cc[:-len(fileinfo.Extension())] - matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) + + fileinfo_h = FileInfo(filename_h) + if not IsHeaderExtension(fileinfo_h.Extension().lstrip('.')): + return (False, '') + + filename_cc = filename_cc[:-(len(fileinfo_cc.Extension()))] + matched_test_suffix = re.search(_TEST_FILE_SUFFIX, fileinfo_cc.BaseName()) if matched_test_suffix: filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] + filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] + filename_h = filename_h[:-(len(fileinfo_h.Extension()))] if filename_h.endswith('-inl'): filename_h = filename_h[:-len('-inl')] filename_h = filename_h.replace('/public/', '/') @@ -5468,33 +6168,6 @@ def FilesBelongToSameModule(filename_cc, filename_h): return files_belong_to_same_module, common_path -def UpdateIncludeState(filename, include_dict, io=codecs): - """Fill up the include_dict with new includes found from the file. - - Args: - filename: the name of the header to read. - include_dict: a dictionary in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was successfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - include_dict.setdefault(include, linenum) - return True - - def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs): """Reports for missing stl includes. @@ -5516,26 +6189,29 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } - for linenum in xrange(clean_lines.NumLines()): + for linenum in range(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') + _re_patterns = [] + _re_patterns.extend(_re_pattern_types_or_objs) + _re_patterns.extend(_re_pattern_functions) + for pattern, item, header in _re_patterns: + matched = pattern.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[header] = (linenum, item) for pattern, template, header in _re_pattern_headers_maybe_templates: if pattern.search(line): required[header] = (linenum, template) # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. + if '<' not in line: # Reduces the cpu time usage by skipping lines. continue for pattern, template, header in _re_pattern_templates: @@ -5547,46 +6223,12 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, if prefix.endswith('std::') or not prefix.endswith('::'): required[header] = (linenum, template) - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. # Let's flatten the include_state include_list and copy it into a dictionary. include_dict = dict([item for sublist in include_state.include_list for item in sublist]) - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_dict is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_dict.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_dict, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: + for required_header_unstripped in sorted(required, key=required.__getitem__): template = required[required_header_unstripped][1] if required_header_unstripped.strip('<>"') not in include_dict: error(filename, required[required_header_unstripped][0], @@ -5629,20 +6271,20 @@ def CheckRedundantVirtual(filename, clean_lines, linenum, error): """ # Look for "virtual" on current line. line = clean_lines.elided[linenum] - virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) + virtual = re.match(r'^(.*)(\bvirtual\b)(.*)$', line) if not virtual: return # Ignore "virtual" keywords that are near access-specifiers. These # are only used in class base-specifier and do not apply to member # functions. - if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or - Match(r'^\s+(public|protected|private)\b', virtual.group(3))): + if (re.search(r'\b(public|protected|private)\s+$', virtual.group(1)) or + re.match(r'^\s+(public|protected|private)\b', virtual.group(3))): return # Ignore the "virtual" keyword from virtual base classes. Usually # there is a column on the same line in these cases (virtual base # classes are rare in google3 because multiple inheritance is rare). - if Match(r'^.*[^:]:[^:].*$', line): return + if re.match(r'^.*[^:]:[^:].*$', line): return # Look for the next opening parenthesis. This is the start of the # parameter list (possibly on the next line shortly after virtual). @@ -5652,9 +6294,9 @@ def CheckRedundantVirtual(filename, clean_lines, linenum, error): end_col = -1 end_line = -1 start_col = len(virtual.group(2)) - for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): + for start_line in range(linenum, min(linenum + 3, clean_lines.NumLines())): line = clean_lines.elided[start_line][start_col:] - parameter_list = Match(r'^([^(]*)\(', line) + parameter_list = re.match(r'^([^(]*)\(', line) if parameter_list: # Match parentheses to find the end of the parameter list (_, end_line, end_col) = CloseExpression( @@ -5667,18 +6309,18 @@ def CheckRedundantVirtual(filename, clean_lines, linenum, error): # Look for "override" or "final" after the parameter list # (possibly on the next few lines). - for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): + for i in range(end_line, min(end_line + 3, clean_lines.NumLines())): line = clean_lines.elided[i][end_col:] - match = Search(r'\b(override|final)\b', line) + match = re.search(r'\b(override|final)\b', line) if match: error(filename, linenum, 'readability/inheritance', 4, ('"virtual" is redundant since function is ' - 'already declared as "%s"' % match.group(1))) + f'already declared as "{match.group(1)}"')) # Set end_col to check whole lines after we are done with the # first line. end_col = 0 - if Search(r'[^\w]\s*$', line): + if re.search(r'[^\w]\s*$', line): break @@ -5705,7 +6347,7 @@ def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): return # Check that at most one of "override" or "final" is present, not both - if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): + if re.search(r'\boverride\b', fragment) and re.search(r'\bfinal\b', fragment): error(filename, linenum, 'readability/inheritance', 4, ('"override" is redundant since function is ' 'already declared as "final"')) @@ -5725,15 +6367,17 @@ def IsBlockInNameSpace(nesting_state, is_forward_declaration): Whether or not the new block is directly in a namespace. """ if is_forward_declaration: - if len(nesting_state.stack) >= 1 and ( - isinstance(nesting_state.stack[-1], _NamespaceInfo)): - return True - else: - return False + return len(nesting_state.stack) >= 1 and ( + isinstance(nesting_state.stack[-1], _NamespaceInfo)) - return (len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.stack[-2], _NamespaceInfo)) + if len(nesting_state.stack) >= 1: + if isinstance(nesting_state.stack[-1], _NamespaceInfo): + return True + elif (len(nesting_state.stack) > 1 and + isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and + isinstance(nesting_state.stack[-2], _NamespaceInfo)): + return True + return False def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, @@ -5772,14 +6416,14 @@ def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, error): line = raw_lines_no_comments[linenum] - if Match(r'^\s+', line): - error(filename, linenum, 'runtime/indentation_namespace', 4, - 'Do not indent within a namespace') + if re.match(r'^\s+', line): + error(filename, linenum, 'whitespace/indent_namespace', 4, + 'Do not indent within a namespace.') def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, - extra_check_functions=[]): + extra_check_functions=None): """Processes a single line in the file. Args: @@ -5818,11 +6462,13 @@ def ProcessLine(filename, file_extension, clean_lines, line, CheckMakePairUsesDeduction(filename, clean_lines, line, error) CheckRedundantVirtual(filename, clean_lines, line, error) CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) + if extra_check_functions: + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) -def FlagCxx11Features(filename, clean_lines, linenum, error): - """Flag those c++11 features that we only allow in certain places. + +def FlagCxxHeaders(filename, clean_lines, linenum, error): + """Flag C++ headers that the styleguide restricts. Args: filename: The name of the current file. @@ -5832,68 +6478,24 @@ def FlagCxx11Features(filename, clean_lines, linenum, error): """ line = clean_lines.elided[linenum] - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++ TR1 headers. - if include and include.group(1).startswith('tr1/'): - error(filename, linenum, 'build/c++tr1', 5, - ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) + include = re.match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) # Flag unapproved C++11 headers. if include and include.group(1) in ('cfenv', - 'condition_variable', 'fenv.h', - 'future', - 'mutex', - 'thread', - 'chrono', 'ratio', - 'regex', - 'system_error', ): error(filename, linenum, 'build/c++11', 5, - ('<%s> is an unapproved C++11 header.') % include.group(1)) + f"<{include.group(1)}> is an unapproved C++11 header.") - # The only place where we need to worry about C++11 keywords and library - # features in preprocessor directives is in macro definitions. - if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return - - # These are classes and free functions. The classes are always - # mentioned as std::*, but we only catch the free functions if - # they're not found by ADL. They're alphabetical by header. - for top_name in ( - # type_traits - 'alignment_of', - 'aligned_union', - ): - if Search(r'\bstd::%s\b' % top_name, line): - error(filename, linenum, 'build/c++11', 5, - ('std::%s is an unapproved C++11 class or function. Send c-style ' - 'an example of where it would make your code more readable, and ' - 'they may let you use it.') % top_name) - - -def FlagCxx14Features(filename, clean_lines, linenum, error): - """Flag those C++14 features that we restrict. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++14 headers. - if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): - error(filename, linenum, 'build/c++14', 5, - ('<%s> is an unapproved C++14 header.') % include.group(1)) + # filesystem is the only unapproved C++17 header + if include and include.group(1) == 'filesystem': + error(filename, linenum, 'build/c++17', 5, + " is an unapproved C++17 header.") def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): + extra_check_functions=None): """Performs lint checks and reports any errors to the given error function. Args: @@ -5917,18 +6519,21 @@ def ProcessFileData(filename, file_extension, lines, error, ResetNolintSuppressions() CheckForCopyright(filename, lines, error) - ProcessGlobalSuppresions(lines) + ProcessGlobalSuppressions(lines) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines) if IsHeaderExtension(file_extension): CheckForHeaderGuard(filename, clean_lines, error) - for line in xrange(clean_lines.NumLines()): + for line in range(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions) - FlagCxx11Features(filename, clean_lines, line, error) + FlagCxxHeaders(filename, clean_lines, line, error) + if _error_suppressions.HasOpenBlock(): + error(filename, _error_suppressions.GetOpenBlockStart(), 'readability/nolint', 5, + 'NONLINT block never ended') nesting_state.CheckCompletedBlocks(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) @@ -5961,13 +6566,13 @@ def ProcessConfigOverrides(filename): if not base_name: break # Reached the root directory. - cfg_file = os.path.join(abs_path, "CPPLINT.cfg") + cfg_file = os.path.join(abs_path, _config_filename) abs_filename = abs_path if not os.path.isfile(cfg_file): continue try: - with open(cfg_file) as file_handle: + with codecs.open(cfg_file, 'r', 'utf8', 'replace') as file_handle: for line in file_handle: line, _, _ = line.partition('#') # Remove comments. if not line.strip(): @@ -5993,42 +6598,45 @@ def ProcessConfigOverrides(filename): if _cpplint_state.quiet: # Suppress "Ignoring file" warning when using --quiet. return False - sys.stderr.write('Ignoring "%s": file excluded by "%s". ' + _cpplint_state.PrintInfo(f'Ignoring "{filename}": file excluded by "{cfg_file}". ' 'File path component "%s" matches ' 'pattern "%s"\n' % - (filename, cfg_file, base_name, val)) + (base_name, val)) return False elif name == 'linelength': global _line_length try: - _line_length = int(val) + _line_length = int(val) except ValueError: - sys.stderr.write('Line length must be numeric.') + _cpplint_state.PrintError('Line length must be numeric.') + elif name == 'extensions': + ProcessExtensionsOption(val) elif name == 'root': global _root # root directories are specified relative to CPPLINT.cfg dir. _root = os.path.join(os.path.dirname(cfg_file), val) elif name == 'headers': ProcessHppHeadersOption(val) + elif name == 'includeorder': + ProcessIncludeOrderOption(val) else: - sys.stderr.write( - 'Invalid configuration option (%s) in file %s\n' % - (name, cfg_file)) + _cpplint_state.PrintError( + f'Invalid configuration option ({name}) in file {cfg_file}\n') except IOError: - sys.stderr.write( - "Skipping config file '%s': Can't open for reading\n" % cfg_file) + _cpplint_state.PrintError( + f"Skipping config file '{cfg_file}': Can't open for reading\n") keep_looking = False # Apply all the accumulated filters in reverse order (top-level directory # config options having the least priority). - for filter in reversed(cfg_filters): - _AddFilters(filter) + for cfg_filter in reversed(cfg_filters): + _AddFilters(cfg_filter) return True -def ProcessFile(filename, vlevel, extra_check_functions=[]): +def ProcessFile(filename, vlevel, extra_check_functions=None): """Does google-lint on a single file. Args: @@ -6066,7 +6674,8 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): codecs.getwriter('utf8'), 'replace').read().split('\n') else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + with codecs.open(filename, 'r', 'utf8', 'replace') as target_file: + lines = target_file.read().split('\n') # Remove trailing '\r'. # The -1 accounts for the extra trailing blank line we get from split() @@ -6078,8 +6687,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): lf_lines.append(linenum + 1) except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) + # TODO: Maybe make this have an exit code of 2 after all is done + _cpplint_state.PrintError( + f"Skipping input '{filename}': Can't open for reading\n") _RestoreFilters() return @@ -6088,9 +6698,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. - if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) + if filename != '-' and file_extension not in GetAllExtensions(): + _cpplint_state.PrintError(f'Ignoring {filename}; not a valid file name' + f' ({(", ".join(GetAllExtensions()))})\n') else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) @@ -6116,7 +6726,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # Suppress printing anything if --quiet was passed unless the error # count has increased after processing this file. if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: - sys.stdout.write('Done processing %s\n' % filename) + _cpplint_state.PrintInfo(f'Done processing {filename}\n') _RestoreFilters() @@ -6126,19 +6736,28 @@ def PrintUsage(message): Args: message: The optional error message. """ - sys.stderr.write(_USAGE) + sys.stderr.write(_USAGE % (sorted(list(GetAllExtensions())), + ','.join(sorted(list(GetAllExtensions()))), + sorted(GetHeaderExtensions()), + ','.join(sorted(GetHeaderExtensions())))) + if message: sys.exit('\nFATAL ERROR: ' + message) else: - sys.exit(1) + sys.exit(0) +def PrintVersion(): + sys.stdout.write('Cpplint fork (https://github.com/cpplint/cpplint)\n') + sys.stdout.write('cpplint ' + __VERSION__ + '\n') + sys.stdout.write('Python ' + sys.version + '\n') + sys.exit(0) def PrintCategories(): """Prints a list of all the error-categories used by error messages. These are the categories used to filter messages via --filter. """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.stderr.write(''.join(f' {cat}\n' for cat in _ERROR_CATEGORIES)) sys.exit(0) @@ -6155,12 +6774,19 @@ def ParseArguments(args): """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'v=', + 'version', 'counting=', 'filter=', 'root=', + 'repository=', 'linelength=', 'extensions=', + 'exclude=', + 'recursive', 'headers=', + 'includeorder=', + 'config=', 'quiet']) except getopt.GetoptError: PrintUsage('Invalid arguments.') @@ -6170,17 +6796,21 @@ def ParseArguments(args): filters = '' quiet = _Quiet() counting_style = '' + recursive = False for (opt, val) in opts: if opt == '--help': PrintUsage(None) + if opt == '--version': + PrintVersion() elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + if val not in ('emacs', 'vs7', 'eclipse', 'junit', 'sed', 'gsed'): + PrintUsage('The only allowed output formats are emacs, vs7, eclipse ' + 'sed, gsed and junit.') output_format = val elif opt == '--quiet': quiet = True - elif opt == '--verbose': + elif opt == '--verbose' or opt == '--v': verbosity = int(val) elif opt == '--filter': filters = val @@ -6193,49 +6823,157 @@ def ParseArguments(args): elif opt == '--root': global _root _root = val + elif opt == '--repository': + global _repository + _repository = val elif opt == '--linelength': global _line_length try: - _line_length = int(val) + _line_length = int(val) except ValueError: - PrintUsage('Line length must be digits.') + PrintUsage('Line length must be digits.') + elif opt == '--exclude': + global _excludes + if not _excludes: + _excludes = set() + _excludes.update(glob.glob(val)) elif opt == '--extensions': - global _valid_extensions - try: - _valid_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma separated list.') + ProcessExtensionsOption(val) elif opt == '--headers': ProcessHppHeadersOption(val) + elif opt == '--recursive': + recursive = True + elif opt == '--includeorder': + ProcessIncludeOrderOption(val) + elif opt == '--config': + global _config_filename + _config_filename = val + if os.path.basename(_config_filename) != _config_filename: + PrintUsage('Config file name must not include directory components.') if not filenames: PrintUsage('No files were specified.') + if recursive: + filenames = _ExpandDirectories(filenames) + + if _excludes: + filenames = _FilterExcludedFiles(filenames) + _SetOutputFormat(output_format) _SetQuiet(quiet) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) + filenames.sort() return filenames +def _ParseFilterSelector(parameter): + """Parses the given command line parameter for file- and line-specific + exclusions. + readability/casting:file.cpp + readability/casting:file.cpp:43 + + Args: + parameter: The parameter value of --filter + + Returns: + [category, filename, line]. + Category is always given. + Filename is either a filename or empty if all files are meant. + Line is either a line in filename or -1 if all lines are meant. + """ + colon_pos = parameter.find(":") + if colon_pos == -1: + return parameter, "", -1 + category = parameter[:colon_pos] + second_colon_pos = parameter.find(":", colon_pos + 1) + if second_colon_pos == -1: + return category, parameter[colon_pos + 1:], -1 + else: + return category, parameter[colon_pos + 1: second_colon_pos], \ + int(parameter[second_colon_pos + 1:]) + +def _ExpandDirectories(filenames): + """Searches a list of filenames and replaces directories in the list with + all files descending from those directories. Files with extensions not in + the valid extensions list are excluded. + + Args: + filenames: A list of files or directories + + Returns: + A list of all files that are members of filenames or descended from a + directory in filenames + """ + expanded = set() + for filename in filenames: + if not os.path.isdir(filename): + expanded.add(filename) + continue + + for root, _, files in os.walk(filename): + for loopfile in files: + fullname = os.path.join(root, loopfile) + if fullname.startswith('.' + os.path.sep): + fullname = fullname[len('.' + os.path.sep):] + expanded.add(fullname) + + filtered = [] + for filename in expanded: + if os.path.splitext(filename)[1][1:] in GetAllExtensions(): + filtered.append(filename) + return filtered + +def _FilterExcludedFiles(fnames): + """Filters out files listed in the --exclude command line switch. File paths + in the switch are evaluated relative to the current working directory + """ + exclude_paths = [os.path.abspath(f) for f in _excludes] + # because globbing does not work recursively, exclude all subpath of all excluded entries + return [f for f in fnames + if not any(e for e in exclude_paths + if _IsParentOrSame(e, os.path.abspath(f)))] + +def _IsParentOrSame(parent, child): + """Return true if child is subdirectory of parent. + Assumes both paths are absolute and don't contain symlinks. + """ + parent = os.path.normpath(parent) + child = os.path.normpath(child) + if parent == child: + return True + + prefix = os.path.commonprefix([parent, child]) + if prefix != parent: + return False + # Note: os.path.commonprefix operates on character basis, so + # take extra care of situations like '/foo/ba' and '/foo/bar/baz' + child_suffix = child[len(prefix):] + child_suffix = child_suffix.lstrip(os.sep) + return child == os.path.join(prefix, child_suffix) def main(): filenames = ParseArguments(sys.argv[1:]) + backup_err = sys.stderr + try: + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReader(sys.stderr, 'replace') - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + # If --quiet is passed, suppress printing error count unless there are errors. + if not _cpplint_state.quiet or _cpplint_state.error_count > 0: + _cpplint_state.PrintErrorCounts() - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - # If --quiet is passed, suppress printing error count unless there are errors. - if not _cpplint_state.quiet or _cpplint_state.error_count > 0: - _cpplint_state.PrintErrorCounts() + if _cpplint_state.output_format == 'junit': + sys.stderr.write(_cpplint_state.FormatJUnitXML()) + + finally: + sys.stderr = backup_err sys.exit(_cpplint_state.error_count > 0) diff --git a/trunk/Arduino/libraries/SdFat/extras/cpplint.sh b/trunk/Arduino/libraries/SdFat/extras/cpplint.sh index daf45aa2..0b9b14e9 100644 --- a/trunk/Arduino/libraries/SdFat/extras/cpplint.sh +++ b/trunk/Arduino/libraries/SdFat/extras/cpplint.sh @@ -1,4 +1,2 @@ -#!/bin/sh -export PATH=/cygdrive/c/Python27:/cygdrive/c/Python27/DLLs:/cygdrive/c/Python27/Scripts:$PATH -echo $PATH -python cpplint.py --filter=-build/include,-runtime/references,-build/header_guard ../src/*.* ../src/*/*.* 2>cpplint.txt \ No newline at end of file +#! /usr/bin/bash +./cpplint.py ../src/*.cpp ../src/*.h ../src/*/*.cpp ../src/*/*.h ../src/SdCard/*/*.cpp ../src/SdCard/*/*.h 2>cpplint.txt \ No newline at end of file diff --git a/trunk/Arduino/libraries/SdFat/library.properties b/trunk/Arduino/libraries/SdFat/library.properties index b5738f7b..e8b2932e 100644 --- a/trunk/Arduino/libraries/SdFat/library.properties +++ b/trunk/Arduino/libraries/SdFat/library.properties @@ -1,5 +1,5 @@ name=SdFat -version=2.2.2 +version=2.3.0 license=MIT author=Bill Greiman maintainer=Bill Greiman diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.cpp b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.cpp index 6d1fb43f..8aa185bb 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.cpp +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -197,7 +197,7 @@ fail: } //------------------------------------------------------------------------------ bool FatFile::dirEntry(DirFat_t* dst) { - DirFat_t* dir; + const DirFat_t* dir; // Make sure fields on device are correct. if (!sync()) { DBG_FAIL_MACRO; @@ -237,7 +237,7 @@ uint32_t FatFile::dirSize() { return 512UL * n; } //------------------------------------------------------------------------------ -int FatFile::fgets(char* str, int num, char* delim) { +int FatFile::fgets(char* str, int num, const char* delim) { char ch; int n = 0; int r = -1; @@ -354,7 +354,7 @@ bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) { goto fail; } } - tmpDir = *this; + tmpDir.copy(this); parent = &tmpDir; close(); } @@ -477,7 +477,7 @@ bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) { DBG_WARN_MACRO; goto fail; } - tmpDir = *this; + tmpDir.copy(this); dirFile = &tmpDir; close(); } @@ -495,7 +495,7 @@ bool FatFile::open(uint16_t index, oflag_t oflag) { bool FatFile::open(FatFile* dirFile, uint16_t index, oflag_t oflag) { if (index) { // Find start of LFN. - DirLfn_t* ldir; + const DirLfn_t* ldir; uint8_t n = index < 20 ? index : 20; for (uint8_t i = 1; i <= n; i++) { ldir = reinterpret_cast(dirFile->cacheDir(index - i)); @@ -632,7 +632,7 @@ bool FatFile::openCwd() { DBG_FAIL_MACRO; goto fail; } - *this = *FatVolume::cwv()->vwd(); + this->copy(FatVolume::cwv()->vwd()); rewind(); return true; @@ -642,7 +642,7 @@ fail: //------------------------------------------------------------------------------ bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) { uint8_t checksum = 0; - DirLfn_t* ldir; + const DirLfn_t* ldir; uint8_t lfnOrd = 0; uint16_t index; @@ -837,7 +837,7 @@ int FatFile::read(void* buf, size_t nbyte) { DBG_FAIL_MACRO; goto fail; } - uint8_t* src = pc + offset; + const uint8_t* src = pc + offset; memcpy(dst, src, n); #if USE_MULTI_SECTOR_IO } else if (toRead >= 2 * m_vol->bytesPerSector()) { @@ -965,7 +965,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { } // sync() and cache directory entry sync(); - oldFile = *this; + oldFile.copy(this); dir = cacheDirEntry(FsCache::CACHE_FOR_READ); if (!dir) { DBG_FAIL_MACRO; @@ -1060,7 +1060,7 @@ bool FatFile::rmdir() { // make sure directory is empty while (1) { - DirFat_t* dir = readDirCache(true); + const DirFat_t* dir = readDirCache(true); if (!dir) { // EOF if no error. if (!getError()) { @@ -1104,7 +1104,7 @@ bool FatFile::rmRfStar() { // remember position index = m_curPosition / FS_DIR_SIZE; - DirFat_t* dir = readDirCache(); + const DirFat_t* dir = readDirCache(); if (!dir) { // At EOF if no error. if (!getError()) { @@ -1281,8 +1281,9 @@ bool FatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, uint16_t dirTime; DirFat_t* dir; - if (!isFile() || year < 1980 || year > 2107 || month < 1 || month > 12 || - day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) { + if (!isFileOrSubDir() || year < 1980 || year > 2107 || month < 1 || + month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || + second > 59) { DBG_FAIL_MACRO; goto fail; } diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.h b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.h index fa34d28e..1d42f39b 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.h +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFile.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef FatFile_h -#define FatFile_h +#pragma once /** * \file * \brief FatFile class @@ -117,14 +116,80 @@ class FatFile { * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). */ FatFile(const char* path, oflag_t oflag) { open(path, oflag); } -#if DESTRUCTOR_CLOSES_FILE + + /** Copy from to this. + * \param[in] from Source file. + */ + void copy(const FatFile* from) { + if (from != this) { +#if FILE_COPY_CONSTRUCTOR_SELECT + *this = *from; +#else // FILE_COPY_CONSTRUCTOR_SELECT + memcpy(this, from, sizeof(FatFile)); +#endif // FILE_COPY_CONSTRUCTOR_SELECT + } + } + /** move from to this. + * \param[in] from Source file. + */ + void move(FatFile* from) { + if (from != this) { + copy(from); + from->m_attributes = FILE_ATTR_CLOSED; + } + } + +#if FILE_COPY_CONSTRUCTOR_SELECT == FILE_COPY_CONSTRUCTOR_PUBLIC + /** Copy constructor. + * \param[in] from Move from file. + * + */ + FatFile(const FatFile& from) = default; + /** Copy assignment operator. + * \param[in] from Move from file. + * \return Copied file. + */ + FatFile& operator=(const FatFile& from) = default; +#elif FILE_COPY_CONSTRUCTOR_SELECT == FILE_COPY_CONSTRUCTOR_PRIVATE + + private: + FatFile(const FatFile& from) = default; + FatFile& operator=(const FatFile& from) = default; + + public: +#else // FILE_COPY_CONSTRUCTOR_SELECT + FatFile(const FatFile& from) = delete; + FatFile& operator=(const FatFile& from) = delete; +#endif // FILE_COPY_CONSTRUCTOR_SELECT + +#if FILE_MOVE_CONSTRUCTOR_SELECT + /** Move constructor. + * \param[in] from Move from file. + */ + FatFile(FatFile&& from) { move(&from); } + /** Move assignment operator. + * \param[in] from Move from file. + * \return Moved file. + */ + FatFile& operator=(FatFile&& from) { + move(&from); + return *this; + } +#else // FILE_MOVE_CONSTRUCTOR_SELECT + FatFile(FatFile&& from) = delete; + FatFile& operator=(FatFile&& from) = delete; +#endif /** Destructor */ +#if DESTRUCTOR_CLOSES_FILE ~FatFile() { if (isOpen()) { close(); } } +#else // DESTRUCTOR_CLOSES_FILE + ~FatFile() = default; #endif // DESTRUCTOR_CLOSES_FILE + /** The parenthesis operator. * * \return true if a file is open. @@ -264,7 +329,7 @@ class FatFile { * If no data is read, fgets() returns zero for EOF or -1 if an error * occurred. */ - int fgets(char* str, int num, char* delim = nullptr); + int fgets(char* str, int num, const char* delim = nullptr); /** \return The total number of bytes in a file. */ uint32_t fileSize() const { return m_fileSize; } /** \return first sector of file or zero for empty file. */ @@ -323,8 +388,7 @@ class FatFile { * * \param[out] name An array of characters for the file's name. * \param[in] size The size of the array in bytes. The array - * must be at least 13 bytes long. The file's name will be - * truncated if the file's name is too long. + * must be at least 13 bytes long. * \return length for success or zero for failure. */ size_t getName(char* name, size_t size); @@ -333,7 +397,7 @@ class FatFile { * * \param[out] name An array of characters for the file's name. * \param[in] size The size of the array in characters. - * \return the name length. + * \return length for success or zero for failure. */ size_t getName7(char* name, size_t size); /** @@ -341,7 +405,7 @@ class FatFile { * * \param[out] name An array of characters for the file's name. * \param[in] size The size of the array in characters. - * \return the name length. + * \return length for success or zero for failure. */ size_t getName8(char* name, size_t size); #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -784,7 +848,7 @@ class FatFile { */ bool rename(FatFile* dirFile, const char* newPath); /** Set the file's current position to zero. */ - void rewind() { seekSet(0); } + void rewind() { seekSet(0UL); } /** Remove a directory file. * * The directory file will be removed only if it is empty and is not the @@ -965,7 +1029,7 @@ class FatFile { DirFat_t* cacheDirEntry(uint8_t action); bool cmpName(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd); bool createLFN(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd); - uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i); + uint16_t getLfnChar(const DirLfn_t* ldir, uint8_t i); uint8_t lfnChecksum(const uint8_t* name) { uint8_t sum = 0; for (uint8_t i = 0; i < 11; i++) { @@ -980,8 +1044,8 @@ class FatFile { bool parsePathName(const char* str, FatSfn_t* fname, const char** ptr); bool mkdir(FatFile* parent, FatName_t* fname); bool open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag); - bool open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag); - bool openSFN(FatSfn_t* fname); + bool open(FatFile* dirFile, const FatSfn_t* fname, oflag_t oflag); + bool openSFN(const FatSfn_t* fname); bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, oflag_t oflag, uint8_t lfnOrd); DirFat_t* readDirCache(bool skipReadOk = false); @@ -1033,4 +1097,3 @@ class File32 : public StreamFile { return tmpFile; } }; -#endif // FatFile_h diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileLFN.cpp b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileLFN.cpp index 54326dd4..049b99f3 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileLFN.cpp +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileLFN.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -54,8 +54,9 @@ static void putLfnChar(DirLfn_t* ldir, uint8_t i, uint16_t c) { } //============================================================================== bool FatFile::cmpName(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd) { - FatFile dir = *this; - DirLfn_t* ldir; + FatFile dir; + dir.copy(this); + const DirLfn_t* ldir; fname->reset(); for (uint8_t order = 1; order <= lfnOrd; order++) { ldir = reinterpret_cast(dir.cacheDir(index - order)); @@ -92,7 +93,8 @@ fail: } //------------------------------------------------------------------------------ bool FatFile::createLFN(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd) { - FatFile dir = *this; + FatFile dir; + dir.copy(this); DirLfn_t* ldir; uint8_t checksum = lfnChecksum(fname->sfn); uint8_t fc = 0; @@ -216,7 +218,7 @@ fail: bool FatFile::makeUniqueSfn(FatLfn_t* fname) { const uint8_t FIRST_HASH_SEQ = 2; // min value is 2 uint8_t pos = fname->seqPos; - DirFat_t* dir; + const DirFat_t* dir; uint16_t hex = 0; DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS)); @@ -280,7 +282,7 @@ bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) { uint16_t freeTotal; uint16_t time; DirFat_t* dir; - DirLfn_t* ldir; + const DirLfn_t* ldir; auto vol = dirFile->m_vol; if (!dirFile->isDir() || isOpen()) { @@ -376,7 +378,6 @@ create: if (freeFound == 0) { freeIndex = curIndex; } - while (freeFound < freeNeed) { dir = dirFile->readDirCache(); if (!dir) { diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFilePrint.cpp b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFilePrint.cpp index 34f65946..12a171db 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFilePrint.cpp +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFilePrint.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileSFN.cpp b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileSFN.cpp index c8deef50..2535854b 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileSFN.cpp +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatFileSFN.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,7 +28,7 @@ //------------------------------------------------------------------------------ // open with filename in fname #define SFN_OPEN_USES_CHKSUM 0 -bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) { +bool FatFile::open(FatFile* dirFile, const FatSfn_t* fname, oflag_t oflag) { uint16_t date; uint16_t time; uint8_t ms10; @@ -40,7 +40,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) { uint16_t emptyIndex = 0; uint16_t index = 0; DirFat_t* dir; - DirLfn_t* ldir; + const DirLfn_t* ldir; dirFile->rewind(); while (true) { @@ -156,7 +156,7 @@ bool FatFile::openExistingSFN(const char* path) { if (*path == 0) { return openRoot(vol); } - *this = *vol->vwd(); + this->copy(vol->vwd()); do { if (!parsePathName(path, &fname, &path)) { DBG_FAIL_MACRO; @@ -173,9 +173,9 @@ fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::openSFN(FatSfn_t* fname) { +bool FatFile::openSFN(const FatSfn_t* fname) { DirFat_t dir; - DirLfn_t* ldir; + const DirLfn_t* ldir; auto vol = m_vol; uint8_t lfnOrd = 0; if (!isDir()) { @@ -183,7 +183,7 @@ bool FatFile::openSFN(FatSfn_t* fname) { goto fail; } while (true) { - if (read(&dir, 32) != 32) { + if (read(&dir, sizeof(dir)) != sizeof(dir)) { DBG_FAIL_MACRO; goto fail; } @@ -192,7 +192,7 @@ bool FatFile::openSFN(FatSfn_t* fname) { goto fail; } if (isFatFileOrSubdir(&dir) && memcmp(fname->sfn, dir.name, 11) == 0) { - uint16_t saveDirIndex = (m_curPosition - 32) >> 5; + uint16_t saveDirIndex = (m_curPosition - sizeof(dir)) >> 5; uint32_t saveDirCluster = m_firstCluster; memset(this, 0, sizeof(FatFile)); m_attributes = dir.attributes & FS_ATTRIB_COPY; diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatLib.h b/trunk/Arduino/libraries/SdFat/src/FatLib/FatLib.h index 057d4041..ab3dfe99 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatLib.h +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatLib.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef FatLib_h -#define FatLib_h +#pragma once #include "FatFormatter.h" #include "FatVolume.h" -#endif // FatLib_h diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.cpp b/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.cpp index 62537603..cc054183 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.cpp +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -37,7 +37,7 @@ bool FatVolume::chdir(const char* path) { DBG_FAIL_MACRO; goto fail; } - m_vwd = dir; + m_vwd.copy(&dir); return true; fail: diff --git a/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.h b/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.h index daf64c39..3cf34146 100644 --- a/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.h +++ b/trunk/Arduino/libraries/SdFat/src/FatLib/FatVolume.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef FatVolume_h -#define FatVolume_h +#pragma once #include "FatFile.h" /** * \file @@ -344,4 +343,3 @@ class FatVolume : public FatPartition { static FatVolume* m_cwv; FatFile m_vwd; }; -#endif // FatVolume_h diff --git a/trunk/Arduino/libraries/SdFat/src/FreeStack.h b/trunk/Arduino/libraries/SdFat/src/FreeStack.h index 60729fac..d165fc7e 100644 --- a/trunk/Arduino/libraries/SdFat/src/FreeStack.h +++ b/trunk/Arduino/libraries/SdFat/src/FreeStack.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef FreeStack_h -#define FreeStack_h +#pragma once /** * \file * \brief FreeStack() function. @@ -41,7 +40,7 @@ extern char __bss_end; * \return The number of free bytes. */ inline int FreeStack() { - char* sp = reinterpret_cast(SP); + const char* sp = reinterpret_cast(SP); return __brkval ? sp - __brkval : sp - &__bss_end; } #elif defined(ARDUINO_ARCH_APOLLO3) @@ -87,4 +86,3 @@ int UnusedStack(); inline void FillStack() {} inline int UnusedStack() { return 0; } #endif // defined(HAS_UNUSED_STACK) -#endif // FreeStack_h diff --git a/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.cpp b/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.cpp index 9d08a3cd..5dd06fbe 100644 --- a/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.cpp +++ b/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -120,9 +120,11 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { #endif // CRC_CCITT #endif // USE_SD_CRC //============================================================================== -// SharedSpiCard member functions +// SdSpiCard member functions //------------------------------------------------------------------------------ -bool SharedSpiCard::begin(SdSpiConfig spiConfig) { +bool SdSpiCard::begin(SdSpiConfig spiConfig) { + uint8_t cardType; + uint32_t arg; Timeout timeout; // Restore state to creator. initSharedSpiCard(); @@ -140,7 +142,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { spiSetSckSpeed(1000UL * SD_MAX_INIT_RATE_KHZ); spiBegin(spiConfig); m_beginCalled = true; - uint32_t arg; + spiStart(); // must supply min of 74 clock cycles with CS high. @@ -169,7 +171,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { // check SD version while (true) { if (cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND) { - type(SD_CARD_TYPE_SD1); + cardType = SD_CARD_TYPE_SD1; break; } // Skip first three bytes. @@ -177,7 +179,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { m_status = spiReceive(); } if (m_status == 0XAA) { - type(SD_CARD_TYPE_SD2); + cardType = SD_CARD_TYPE_SD2; break; } if (timeout.timedOut()) { @@ -186,7 +188,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { } } // initialize card and send host supports SDHC if SD2 - arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; + arg = cardType == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { // check for timeout if (timeout.timedOut()) { @@ -195,13 +197,13 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { } } // if SD2 read OCR register to check for SDHC card - if (type() == SD_CARD_TYPE_SD2) { + if (cardType == SD_CARD_TYPE_SD2) { if (cardCommand(CMD58, 0)) { error(SD_CARD_ERROR_CMD58); goto fail; } if ((spiReceive() & 0XC0) == 0XC0) { - type(SD_CARD_TYPE_SDHC); + cardType = SD_CARD_TYPE_SDHC; } // Discard rest of ocr - contains allowed voltage range. for (uint8_t i = 0; i < 3; i++) { @@ -210,6 +212,10 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) { } spiStop(); spiSetSckSpeed(spiConfig.maxSck); + m_type = cardType; +#if ENABLE_DEDICATED_SPI + m_dedicatedSpi = spiOptionDedicated(spiConfig.options); +#endif return true; fail: @@ -217,7 +223,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::cardCMD6(uint32_t arg, uint8_t* status) { +bool SdSpiCard::cardCMD6(uint32_t arg, uint8_t* status) { if (cardCommand(CMD6, arg)) { error(SD_CARD_ERROR_CMD6); goto fail; @@ -234,7 +240,7 @@ fail: } //------------------------------------------------------------------------------ // send command and return error code. Return zero for OK -uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { +uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { if (!syncDevice()) { return 0XFF; } @@ -264,7 +270,7 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { spiSend(cmd | 0x40); // send argument - uint8_t* pa = reinterpret_cast(&arg); + const uint8_t* pa = reinterpret_cast(&arg); for (int8_t i = 3; i >= 0; i--) { spiSend(pa[i]); } @@ -284,7 +290,7 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { return m_status; } //------------------------------------------------------------------------------ -void SharedSpiCard::end() { +void SdSpiCard::end() { if (m_beginCalled) { syncDevice(); spiEnd(); @@ -292,7 +298,7 @@ void SharedSpiCard::end() { } } //------------------------------------------------------------------------------ -bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) { +bool SdSpiCard::erase(uint32_t firstSector, uint32_t lastSector) { csd_t csd; if (!readCSD(&csd)) { goto fail; @@ -307,7 +313,7 @@ bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) { goto fail; } } - if (m_type != SD_CARD_TYPE_SDHC) { + if (type() != SD_CARD_TYPE_SDHC) { firstSector <<= 9; lastSector <<= 9; } @@ -328,12 +334,12 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::eraseSingleSectorEnable() { +bool SdSpiCard::eraseSingleSectorEnable() { csd_t csd; return readCSD(&csd) ? csd.eraseSingleBlock() : false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::isBusy() { +bool SdSpiCard::isBusy() { if (m_state == READ_STATE) { return false; } @@ -348,9 +354,9 @@ bool SharedSpiCard::isBusy() { return rtn; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readData(uint8_t* dst) { return readData(dst, 512); } +bool SdSpiCard::readData(uint8_t* dst) { return readData(dst, 512); } //------------------------------------------------------------------------------ -bool SharedSpiCard::readData(uint8_t* dst, size_t count) { +bool SdSpiCard::readData(uint8_t* dst, size_t count) { #if USE_SD_CRC uint16_t crc; #endif // USE_SD_CRC @@ -368,6 +374,8 @@ bool SharedSpiCard::readData(uint8_t* dst, size_t count) { goto fail; } // transfer data + // cppcheck wrong - Due can return non-zero. + // cppcheck-suppress knownConditionTrueFalse if ((m_status = spiReceive(dst, count))) { error(SD_CARD_ERROR_DMA); goto fail; @@ -392,7 +400,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readOCR(uint32_t* ocr) { +bool SdSpiCard::readOCR(uint32_t* ocr) { uint8_t* p = reinterpret_cast(ocr); if (cardCommand(CMD58, 0)) { error(SD_CARD_ERROR_CMD58); @@ -414,7 +422,7 @@ fail: } //------------------------------------------------------------------------------ /** read CID or CSR register */ -bool SharedSpiCard::readRegister(uint8_t cmd, void* buf) { +bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { uint8_t* dst = reinterpret_cast(buf); if (cardCommand(cmd, 0)) { error(SD_CARD_ERROR_READ_REG); @@ -431,7 +439,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readSCR(scr_t* scr) { +bool SdSpiCard::readSCR(scr_t* scr) { uint8_t* dst = reinterpret_cast(scr); if (cardAcmd(ACMD51, 0)) { error(SD_CARD_ERROR_ACMD51); @@ -448,7 +456,10 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readSector(uint32_t sector, uint8_t* dst) { +bool SdSpiCard::readSector(uint32_t sector, uint8_t* dst) { +#if ENABLE_DEDICATED_SPI + return readSectors(sector, dst, 1); +#else // use address if not SDHC card if (type() != SD_CARD_TYPE_SDHC) { sector <<= 9; @@ -466,9 +477,25 @@ bool SharedSpiCard::readSector(uint32_t sector, uint8_t* dst) { fail: spiStop(); return false; +#endif } //------------------------------------------------------------------------------ -bool SharedSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) { +bool SdSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) { +#if ENABLE_DEDICATED_SPI + if (sdState() != READ_STATE || sector != m_curSector) { + if (!readStart(sector)) { + goto fail; + } + m_curSector = sector; + } + for (size_t i = 0; i < ns; i++, dst += 512) { + if (!readData(dst)) { + goto fail; + } + } + m_curSector += ns; + return m_dedicatedSpi ? true : readStop(); +#else if (!readStart(sector)) { goto fail; } @@ -478,11 +505,12 @@ bool SharedSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) { } } return readStop(); +#endif fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readStart(uint32_t sector) { +bool SdSpiCard::readStart(uint32_t sector) { if (type() != SD_CARD_TYPE_SDHC) { sector <<= 9; } @@ -498,7 +526,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readSDS(sds_t* sds) { +bool SdSpiCard::readSDS(sds_t* sds) { uint8_t* dst = reinterpret_cast(sds); // retrun is R2 so read extra status byte. if (cardAcmd(ACMD13, 0) || spiReceive()) { @@ -516,7 +544,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::readStop() { +bool SdSpiCard::readStop() { m_state = IDLE_STATE; if (cardCommand(CMD12, 0)) { error(SD_CARD_ERROR_CMD12); @@ -530,12 +558,25 @@ fail: return false; } //------------------------------------------------------------------------------ -uint32_t SharedSpiCard::sectorCount() { +uint32_t SdSpiCard::sectorCount() { csd_t csd; return readCSD(&csd) ? csd.capacity() : 0; } //------------------------------------------------------------------------------ -void SharedSpiCard::spiStart() { +bool SdSpiCard::setDedicatedSpi(bool value) { +#if ENABLE_DEDICATED_SPI + if (!syncDevice()) { + return false; + } + m_dedicatedSpi = value; + return true; +#else // ENABLE_DEDICATED_SPI + (void)value; + return false; +#endif // ENABLE_DEDICATED_SPI +} +//------------------------------------------------------------------------------ +void SdSpiCard::spiStart() { SPI_ASSERT_NOT_ACTIVE; if (!m_spiActive) { spiActivate(); @@ -546,7 +587,7 @@ void SharedSpiCard::spiStart() { } } //------------------------------------------------------------------------------ -void SharedSpiCard::spiStop() { +void SdSpiCard::spiStop() { SPI_ASSERT_ACTIVE; if (m_spiActive) { spiUnselect(); @@ -557,7 +598,7 @@ void SharedSpiCard::spiStop() { } } //------------------------------------------------------------------------------ -bool SharedSpiCard::syncDevice() { +bool SdSpiCard::syncDevice() { if (m_state == WRITE_STATE) { return writeStop(); } @@ -567,7 +608,7 @@ bool SharedSpiCard::syncDevice() { return true; } //------------------------------------------------------------------------------ -bool SharedSpiCard::waitReady(uint16_t ms) { +bool SdSpiCard::waitReady(uint16_t ms) { Timeout timeout(ms); while (spiReceive() != 0XFF) { if (timeout.timedOut()) { @@ -577,7 +618,7 @@ bool SharedSpiCard::waitReady(uint16_t ms) { return true; } //------------------------------------------------------------------------------ -bool SharedSpiCard::writeData(const uint8_t* src) { +bool SdSpiCard::writeData(const uint8_t* src) { // wait for previous write to finish if (!waitReady(SD_WRITE_TIMEOUT)) { error(SD_CARD_ERROR_WRITE_TIMEOUT); @@ -594,7 +635,7 @@ fail: } //------------------------------------------------------------------------------ // send one sector of data for write sector or write multiple sectors -bool SharedSpiCard::writeData(uint8_t token, const uint8_t* src) { +bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { #if USE_SD_CRC uint16_t crc = CRC_CCITT(src, 512); #else // USE_SD_CRC @@ -617,7 +658,14 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::writeSector(uint32_t sector, const uint8_t* src) { +bool SdSpiCard::writeSector(uint32_t sector, const uint8_t* src) { +#ifndef OLD_WAY_WRITE_SECTOR +#if ENABLE_DEDICATED_SPI + if (m_dedicatedSpi) { + return writeSectors(sector, src, 1); + } +#endif +#endif // OLD_WAY_WRITE_SECTOR // use address if not SDHC card if (type() != SD_CARD_TYPE_SDHC) { sector <<= 9; @@ -651,8 +699,22 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::writeSectors(uint32_t sector, const uint8_t* src, - size_t ns) { +bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) { +#if ENABLE_DEDICATED_SPI + if (sdState() != WRITE_STATE || m_curSector != sector) { + if (!writeStart(sector)) { + goto fail; + } + m_curSector = sector; + } + for (size_t i = 0; i < ns; i++, src += 512) { + if (!writeData(src)) { + goto fail; + } + } + m_curSector += ns; + return m_dedicatedSpi ? true : writeStop(); +#else if (!writeStart(sector)) { goto fail; } @@ -662,13 +724,13 @@ bool SharedSpiCard::writeSectors(uint32_t sector, const uint8_t* src, } } return writeStop(); - +#endif fail: spiStop(); return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::writeStart(uint32_t sector) { +bool SdSpiCard::writeStart(uint32_t sector) { // use address if not SDHC card if (type() != SD_CARD_TYPE_SDHC) { sector <<= 9; @@ -685,7 +747,7 @@ fail: return false; } //------------------------------------------------------------------------------ -bool SharedSpiCard::writeStop() { +bool SdSpiCard::writeStop() { if (!waitReady(SD_WRITE_TIMEOUT)) { goto fail; } @@ -699,69 +761,3 @@ fail: spiStop(); return false; } -//============================================================================== -bool DedicatedSpiCard::begin(SdSpiConfig spiConfig) { - if (!SharedSpiCard::begin(spiConfig)) { - return false; - } - m_dedicatedSpi = spiOptionDedicated(spiConfig.options); - return true; -} -//------------------------------------------------------------------------------ -bool DedicatedSpiCard::readSector(uint32_t sector, uint8_t* dst) { - return readSectors(sector, dst, 1); -} -//------------------------------------------------------------------------------ -bool DedicatedSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) { - if (sdState() != READ_STATE || sector != m_curSector) { - if (!readStart(sector)) { - goto fail; - } - m_curSector = sector; - } - for (size_t i = 0; i < ns; i++, dst += 512) { - if (!readData(dst)) { - goto fail; - } - } - m_curSector += ns; - return m_dedicatedSpi ? true : readStop(); - -fail: - return false; -} -//------------------------------------------------------------------------------ -bool DedicatedSpiCard::setDedicatedSpi(bool value) { - if (!syncDevice()) { - return false; - } - m_dedicatedSpi = value; - return true; -} -//------------------------------------------------------------------------------ -bool DedicatedSpiCard::writeSector(uint32_t sector, const uint8_t* src) { - if (m_dedicatedSpi) { - return writeSectors(sector, src, 1); - } - return SharedSpiCard::writeSector(sector, src); -} -//------------------------------------------------------------------------------ -bool DedicatedSpiCard::writeSectors(uint32_t sector, const uint8_t* src, - size_t ns) { - if (sdState() != WRITE_STATE || m_curSector != sector) { - if (!writeStart(sector)) { - goto fail; - } - m_curSector = sector; - } - for (size_t i = 0; i < ns; i++, src += 512) { - if (!writeData(src)) { - goto fail; - } - } - m_curSector += ns; - return m_dedicatedSpi ? true : writeStop(); - -fail: - return false; -} diff --git a/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.h b/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.h index abaed59c..7ed9cf6b 100644 --- a/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.h +++ b/trunk/Arduino/libraries/SdFat/src/SdCard/SdSpiCard.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -26,8 +26,7 @@ * \file * \brief Classes for SPI access to SD/SDHC cards. */ -#ifndef SdSpiCard_h -#define SdSpiCard_h +#pragma once #include #include "../SpiDriver/SdSpiDriver.h" @@ -43,8 +42,8 @@ if (!m_spiActive) { \ Serial.print(F("SPI_ASSERT_ACTIVE")); \ Serial.println(__LINE__); \ - while (true) \ - ; \ + while (true) { \ + } \ } \ } #define SPI_ASSERT_NOT_ACTIVE \ @@ -52,8 +51,8 @@ if (m_spiActive) { \ Serial.print(F("SPI_ASSERT_NOT_ACTIVE")); \ Serial.println(__LINE__); \ - while (true) \ - ; \ + while (true) { \ + } \ } \ } #else // CHECK_SPI_ACTIVE @@ -64,15 +63,15 @@ #endif // CHECK_SPI_ACTIVE //============================================================================== /** - * \class SharedSpiCard + * \class SdSpiCard * \brief Raw access to SD and SDHC flash memory cards via shared SPI port. */ #if HAS_SDIO_CLASS -class SharedSpiCard : public SdCardInterface { +class SdSpiCard : public SdCardInterface { #elif USE_BLOCK_DEVICE_INTERFACE -class SharedSpiCard : public FsBlockDeviceInterface { +class SdSpiCard : public FsBlockDeviceInterface { #else // HAS_SDIO_CLASS -class SharedSpiCard { +class SdSpiCard { #endif // HAS_SDIO_CLASS public: /** SD is in idle state */ @@ -81,8 +80,8 @@ class SharedSpiCard { static const uint8_t READ_STATE = 1; /** SD is in multi-sector write state. */ static const uint8_t WRITE_STATE = 2; - /** Construct an instance of SharedSpiCard. */ - SharedSpiCard() { initSharedSpiCard(); } + /** Construct an instance of SdSpiCard. */ + SdSpiCard() { initSharedSpiCard(); } /** Initialize the SD card. * \param[in] spiConfig SPI card configuration. * \return true for success or false for failure. @@ -131,16 +130,26 @@ class SharedSpiCard { uint8_t errorCode() const { return m_errorCode; } /** \return error data for last error. */ uint32_t errorData() const { return m_status; } - /** \return false for shared class. */ +/** \return false for shared class. */ +#if ENABLE_DEDICATED_SPI + bool hasDedicatedSpi() { return true; } +#else bool hasDedicatedSpi() { return false; } +#endif /** * Check for busy. MISO low indicates the card is busy. * * \return true if busy else false. */ bool isBusy(); - /** \return false, can't be in dedicated state. */ + /** \return true if in dedicated SPI state. */ +#if ENABLE_DEDICATED_SPI + bool isDedicatedSpi() { return m_dedicatedSpi; } +#else // ENABLE_DEDICATED_SPI bool isDedicatedSpi() { return false; } +#endif // ENABLE_DEDICATED_SPI + /** \return true if card is on SPI bus. */ + bool isSpi() { return true; } /** * Read a card's CID register. The CID contains card identification * information such as Manufacturer ID, Product name, Product serial @@ -196,7 +205,6 @@ class SharedSpiCard { * \return true for success or false for failure. */ bool readSectors(uint32_t sector, uint8_t* dst, size_t ns); - /** Start a read multiple sector sequence. * * \param[in] sector Address of first sector in sequence. @@ -226,19 +234,12 @@ class SharedSpiCard { * or zero if an error occurs. */ uint32_t sectorCount(); -#ifndef DOXYGEN_SHOULD_SKIP_THIS - // Use sectorCount(). cardSize() will be removed in the future. - uint32_t __attribute__((error("use sectorCount()"))) cardSize(); -#endif // DOXYGEN_SHOULD_SKIP_THIS /** Set SPI sharing state * \param[in] value desired state. - * \return false for shared card + * \return true for success. */ - bool setDedicatedSpi(bool value) { - (void)value; - return false; - } - /** end a mult-sector transfer. + bool setDedicatedSpi(bool value); + /** end a multi-sector transfer. * * \return true for success or false for failure. */ @@ -301,7 +302,6 @@ class SharedSpiCard { void spiStart(); void spiStop(); void spiUnselect() { sdCsWrite(m_csPin, true); } - void type(uint8_t value) { m_type = value; } bool waitReady(uint16_t ms); bool writeData(uint8_t token, const uint8_t* src); #if SPI_DRIVER_SELECT < 2 @@ -361,6 +361,10 @@ class SharedSpiCard { m_status = 0; m_type = 0; } +#if ENABLE_DEDICATED_SPI + uint32_t m_curSector = 0; + bool m_dedicatedSpi = false; +#endif // ENABLE_DEDICATED_SPI bool m_beginCalled; SdCsPin_t m_csPin; uint8_t m_errorCode; @@ -369,74 +373,3 @@ class SharedSpiCard { uint8_t m_status; uint8_t m_type; }; -//============================================================================== -/** - * \class DedicatedSpiCard - * \brief Raw access to SD and SDHC flash memory cards via dedicate SPI port. - */ -class DedicatedSpiCard : public SharedSpiCard { - public: - /** Construct an instance of DedicatedSpiCard. */ - DedicatedSpiCard() = default; - /** Initialize the SD card. - * \param[in] spiConfig SPI card configuration. - * \return true for success or false for failure. - */ - bool begin(SdSpiConfig spiConfig); - /** \return true, can be in dedicaded state. */ - bool hasDedicatedSpi() { return true; } - /** \return true if in dedicated SPI state. */ - bool isDedicatedSpi() { return m_dedicatedSpi; } - /** - * Read a 512 byte sector from an SD card. - * - * \param[in] sector Logical sector to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return true for success or false for failure. - */ - bool readSector(uint32_t sector, uint8_t* dst); - /** - * Read multiple 512 byte sectors from an SD card. - * - * \param[in] sector Logical sector to be read. - * \param[in] ns Number of sectors to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return true for success or false for failure. - */ - bool readSectors(uint32_t sector, uint8_t* dst, size_t ns); - /** Set SPI sharing state - * \param[in] value desired state. - * \return true for success else false; - */ - bool setDedicatedSpi(bool value); - /** - * Write a 512 byte sector to an SD card. - * - * \param[in] sector Logical sector to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return true for success or false for failure. - */ - bool writeSector(uint32_t sector, const uint8_t* src); - /** - * Write multiple 512 byte sectors to an SD card. - * - * \param[in] sector Logical sector to be written. - * \param[in] ns Number of sectors to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return true for success or false for failure. - */ - bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns); - - private: - uint32_t m_curSector = 0; - bool m_dedicatedSpi = false; -}; -//============================================================================== -#if ENABLE_DEDICATED_SPI -/** typedef for dedicated SPI. */ -typedef DedicatedSpiCard SdSpiCard; -#else -/** typedef for shared SPI. */ -typedef SharedSpiCard SdSpiCard; -#endif -#endif // SdSpiCard_h diff --git a/trunk/Arduino/libraries/SdFat/src/SdCard/SdioCard.h b/trunk/Arduino/libraries/SdFat/src/SdCard/SdioCard.h index 55859d79..42b89e0f 100644 --- a/trunk/Arduino/libraries/SdFat/src/SdCard/SdioCard.h +++ b/trunk/Arduino/libraries/SdFat/src/SdCard/SdioCard.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -26,34 +26,18 @@ * \file * \brief Classes for SDIO cards. */ -#ifndef SdioCard_h -#define SdioCard_h +#pragma once #include "../common/SysCall.h" #include "SdCardInterface.h" -/** Use programmed I/O with FIFO. */ -#define FIFO_SDIO 0 -/** Use programmed I/O with DMA. */ -#define DMA_SDIO 1 +#ifdef SDIO_CONFIG_INCLUDE +#include SDIO_CONFIG_INCLUDE +#else // SDIO_CONFIG_INCLUDE /** * \class SdioConfig - * \brief SDIO card configuration. + * \brief Empty SDIO card configuration. */ -class SdioConfig { - public: - SdioConfig() {} - /** - * SdioConfig constructor. - * \param[in] opt SDIO options. - */ - explicit SdioConfig(uint8_t opt) : m_options(opt) {} - /** \return SDIO card options. */ - uint8_t options() { return m_options; } - /** \return true if DMA_SDIO. */ - bool useDma() { return m_options & DMA_SDIO; } - - private: - uint8_t m_options = FIFO_SDIO; -}; +class SdioConfig {}; +#endif // SDIO_CONFIG_INCLUDE //------------------------------------------------------------------------------ /** * \class SdioCard @@ -62,10 +46,10 @@ class SdioConfig { class SdioCard : public SdCardInterface { public: /** Initialize the SD card. - * \param[in] sdioConfig SDIO card configuration. + * \param[in] config SDIO card configuration. * \return true for success or false for failure. */ - bool begin(SdioConfig sdioConfig); + bool begin(SdioConfig config); /** CMD6 Switch mode: Check Function Set Function. * \param[in] arg CMD6 argument. * \param[out] status return status data. @@ -76,7 +60,7 @@ class SdioCard : public SdCardInterface { /** Disable an SDIO card. * not implemented. */ - void end() {} + void end(); #ifndef DOXYGEN_SHOULD_SKIP_THIS uint32_t __attribute__((error("use sectorCount()"))) cardSize(); @@ -176,21 +160,11 @@ class SdioCard : public SdCardInterface { * \param[in] sector Address of first sector in sequence. * * \note This function is used with readData() and readStop() for optimized - * multiple sector reads. SPI chipSelect must be low for the entire sequence. + * multiple sector reads. * * \return true for success or false for failure. */ bool readStart(uint32_t sector); - /** Start a read multiple sectors sequence. - * - * \param[in] sector Address of first sector in sequence. - * \param[in] count Maximum sector count. - * \note This function is used with readData() and readStop() for optimized - * multiple sector reads. SPI chipSelect must be low for the entire sequence. - * - * \return true for success or false for failure. - */ - bool readStart(uint32_t sector, uint32_t count); /** End a read multiple sectors sequence. * * \return true for success or false for failure. @@ -251,16 +225,6 @@ class SdioCard : public SdCardInterface { * \return true for success or false for failure. */ bool writeStart(uint32_t sector); - /** Start a write multiple sectors sequence. - * - * \param[in] sector Address of first sector in sequence. - * \param[in] count Maximum sector count. - * \note This function is used with writeData() and writeStop() - * for optimized multiple sector writes. - * - * \return true for success or false for failure. - */ - bool writeStart(uint32_t sector, uint32_t count); /** End a write multiple sectors sequence. * @@ -273,7 +237,5 @@ class SdioCard : public SdCardInterface { static const uint8_t READ_STATE = 1; static const uint8_t WRITE_STATE = 2; uint32_t m_curSector; - SdioConfig m_sdioConfig; uint8_t m_curState = IDLE_STATE; }; -#endif // SdioCard_h diff --git a/trunk/Arduino/libraries/SdFat/src/SdFat.h b/trunk/Arduino/libraries/SdFat/src/SdFat.h index d906ea5f..b9bc875f 100644 --- a/trunk/Arduino/libraries/SdFat/src/SdFat.h +++ b/trunk/Arduino/libraries/SdFat/src/SdFat.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SdFat_h -#define SdFat_h +#pragma once /** * \file * \brief main SdFs include file. @@ -38,9 +37,9 @@ #endif // INCLUDE_SDIOS //------------------------------------------------------------------------------ /** SdFat version for cpp use. */ -#define SD_FAT_VERSION 20202 +#define SD_FAT_VERSION 20300 /** SdFat version as string. */ -#define SD_FAT_VERSION_STR "2.2.2" +#define SD_FAT_VERSION_STR "2.3.0" //============================================================================== /** * \class SdBase @@ -80,7 +79,7 @@ class SdBase : public Vol { * \return true for success or false for failure. */ bool begin(SdSpiConfig spiConfig) { - return cardBegin(spiConfig) && Vol::begin(m_card); + return cardBegin(spiConfig) && volumeBegin(); } //--------------------------------------------------------------------------- /** Initialize SD card and file system for SDIO mode. @@ -89,7 +88,7 @@ class SdBase : public Vol { * \return true for success or false for failure. */ bool begin(SdioConfig sdioConfig) { - return cardBegin(sdioConfig) && Vol::begin(m_card); + return cardBegin(sdioConfig) && volumeBegin(); } //---------------------------------------------------------------------------- /** \return Pointer to SD card object. */ @@ -347,7 +346,9 @@ class SdBase : public Vol { * * \return true for success or false for failure. */ - bool volumeBegin() { return Vol::begin(m_card); } + bool volumeBegin() { + return Vol::begin(m_card) || Vol::begin(m_card, true, 0); + } #if ENABLE_ARDUINO_SERIAL /** Print error details after begin() fails. */ void initErrorPrint() { initErrorPrint(&Serial); } @@ -445,7 +446,6 @@ typedef FsBaseFile SdBaseFile; #if defined __has_include #if __has_include() #define HAS_INCLUDE_FS_H -#warning File not defined because __has_include(FS.h) #endif // __has_include() #endif // defined __has_include #ifndef HAS_INCLUDE_FS_H @@ -457,10 +457,12 @@ typedef ExFile File; #elif SDFAT_FILE_TYPE == 3 typedef FsFile File; #endif // SDFAT_FILE_TYPE +#elif !defined(DISABLE_FS_H_WARNING) +#warning File not defined because __has_include(FS.h) #endif // HAS_INCLUDE_FS_H /** * \class SdFile - * \brief FAT16/FAT32 file with Print. + * \brief File with Print. */ class SdFile : public PrintFile { public: @@ -503,4 +505,3 @@ class SdFile : public PrintFile { /** Cancel the date/time callback function. */ static void dateTimeCallbackCancel() { FsDateTime::clearCallback(); } }; -#endif // SdFat_h diff --git a/trunk/Arduino/libraries/SdFat/src/SdFatConfig.h b/trunk/Arduino/libraries/SdFat/src/SdFatConfig.h index 946bab19..4b64f9d0 100644 --- a/trunk/Arduino/libraries/SdFat/src/SdFatConfig.h +++ b/trunk/Arduino/libraries/SdFat/src/SdFatConfig.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2022 Bill Greiman + * Copyright (c) 2011-2024 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -26,15 +26,15 @@ * \file * \brief configuration definitions */ -#ifndef SdFatConfig_h -#define SdFatConfig_h +#pragma once #include #ifdef __AVR__ #include #endif // __AVR__ -// + +// #include "SdFatDebugConfig.h" // To try UTF-8 encoded filenames. -// #define USE_UTF8_LONG_NAMES 1 +// #define USE_UTF8_LONG_NAMES 1 // // For minimum flash size use these settings: // #define USE_FAT_FILE_FLAG_CONTIGUOUS 0 @@ -46,12 +46,59 @@ // Options can be set in a makefile or an IDE like platformIO // if they are in a #ifndef/#endif block below. //------------------------------------------------------------------------------ -/** For Debug - must be one */ +/* + * Options for file class constructors, assignment operators and destructors. + * + * By default file copy constructors and copy assignment operators are + * private to prevent multiple copies of a instance for a file. + * + * File move constructors and move assignment operators are public to permit + * return of a file instance for compilers that aren't able to use copy elision. + * + */ +/** File copy constructors and copy assignment operators are deleted */ +#define FILE_COPY_CONSTRUCTOR_DELETED 0 +/** File copy constructors and copy assignment operators are private */ +#define FILE_COPY_CONSTRUCTOR_PRIVATE 1 +/** File copy constructors and copy assignment operators are public */ +#define FILE_COPY_CONSTRUCTOR_PUBLIC 2 + +#ifndef FILE_COPY_CONSTRUCTOR_SELECT +/** Specify kind of file copy constructors and copy assignment operators */ +#define FILE_COPY_CONSTRUCTOR_SELECT FILE_COPY_CONSTRUCTOR_PRIVATE +#endif // FILE_COPY_CONSTRUCTOR_SELECT +/** File move constructors and move assignment operators are deleted. */ +#define FILE_MOVE_CONSTRUCTOR_DELETED 0 +/** File move constructors and move assignment operators are public. */ +#define FILE_MOVE_CONSTRUCTOR_PUBLIC 1 + +#ifndef FILE_MOVE_CONSTRUCTOR_SELECT +/** Specify kind of file move constructors and move assignment operators */ +#define FILE_MOVE_CONSTRUCTOR_SELECT FILE_MOVE_CONSTRUCTOR_PUBLIC +#endif // FILE_MOVE_CONSTRUCTOR_SELECT + +#if FILE_MOVE_CONSTRUCTOR_SELECT != FILE_MOVE_CONSTRUCTOR_PUBLIC && \ + FILE_COPY_CONSTRUCTOR_SELECT != FILE_COPY_CONSTRUCTOR_PUBLIC +#error "No public move or copy assign operators" +#endif // FILE_MOVE_CONSTRUCTOR_SELECT && FILE_MOVE_CONSTRUCTOR_SELECT +/** + * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. */ +#ifndef DESTRUCTOR_CLOSES_FILE +#define DESTRUCTOR_CLOSES_FILE 0 +#endif // DESTRUCTOR_CLOSES_FILE +//------------------------------------------------------------------------------ +/** For Debug - must be one on Arduino */ +#ifndef ENABLE_ARDUINO_FEATURES #define ENABLE_ARDUINO_FEATURES 1 -/** For Debug - must be one */ +#endif //ENABLE_ARDUINO_FEATURES +/** For Debug - must be one on Arduino */ +#ifndef ENABLE_ARDUINO_SERIAL #define ENABLE_ARDUINO_SERIAL 1 -/** For Debug - must be one */ +#endif //ENABLE_ARDUINO_SERIAL +/** For Debug - must be one on Arduino */ +#ifndef ENABLE_ARDUINO_STRING #define ENABLE_ARDUINO_STRING 1 +#endif //ENABLE_ARDUINO_STRING //------------------------------------------------------------------------------ #if ENABLE_ARDUINO_FEATURES #include "Arduino.h" @@ -141,7 +188,11 @@ * receive and transfer(buf, rxTmp, count) for send. Try this with STM32. */ #ifndef USE_SPI_ARRAY_TRANSFER +#if defined(ARDUINO_ARCH_RP2040) +#define USE_SPI_ARRAY_TRANSFER 2 +#else // defined(ARDUINO_ARCH_RP2040) #define USE_SPI_ARRAY_TRANSFER 0 +#endif // defined(ARDUINO_ARCH_RP2040) #endif // USE_SPI_ARRAY_TRANSFER //------------------------------------------------------------------------------ /** @@ -327,15 +378,6 @@ typedef uint8_t SdCsPin_t; #define FAT12_SUPPORT 0 #endif // FAT12_SUPPORT //------------------------------------------------------------------------------ -/** - * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. - * - * Causes use of lots of heap in ARM. - */ -#ifndef DESTRUCTOR_CLOSES_FILE -#define DESTRUCTOR_CLOSES_FILE 0 -#endif // DESTRUCTOR_CLOSES_FILE -//------------------------------------------------------------------------------ /** * Call flush for endl if ENDL_CALLS_FLUSH is nonzero * @@ -402,6 +444,12 @@ typedef uint8_t SdCsPin_t; #endif // RAMEND //------------------------------------------------------------------------------ /** Enable SDIO driver if available. */ +#if defined(ARDUINO_ARCH_RP2040) +#define HAS_RP2040_SDIO 1 +#define HAS_SDIO_CLASS 1 +#define SDIO_CONFIG_INCLUDE "Rp2040Sdio/Rp2040SdioConfig.h" +#endif // defined(ARDUINO_ARCH_RP2040) + #if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Pseudo pin select for SDIO. #ifndef BUILTIN_SDCARD @@ -415,10 +463,11 @@ typedef uint8_t SdCsPin_t; #define SDCARD_SCK_PIN 60 #define SDCARD_SS_PIN 62 #endif // SDCARD_SPI -#define HAS_SDIO_CLASS 1 #endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) -#if defined(__IMXRT1062__) +#if defined(__IMXRT1062__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) #define HAS_SDIO_CLASS 1 +#define HAS_TEENSY_SDIO 1 +#define SDIO_CONFIG_INCLUDE "TeensySdio/TeensySdioConfig.h" #endif // defined(__IMXRT1062__) //------------------------------------------------------------------------------ /** @@ -427,9 +476,8 @@ typedef uint8_t SdCsPin_t; #if defined(ARDUINO_ARCH_APOLLO3) || \ (defined(__AVR__) && defined(SPDR) && defined(SPSR) && defined(SPIF)) || \ (defined(__AVR__) && defined(SPI0) && defined(SPI_RXCIF_bm)) || \ - defined(ESP8266) || defined(ESP32) || defined(PLATFORM_ID) || \ defined(ARDUINO_SAM_DUE) || defined(STM32_CORE_VERSION) || \ - defined(__STM32F1__) || defined(__STM32F4__) || \ + defined(__STM32F1__) || defined(__STM32F4__) || defined(PLATFORM_ID) || \ (defined(CORE_TEENSY) && defined(__arm__)) #define SD_HAS_CUSTOM_SPI 1 #else // SD_HAS_CUSTOM_SPI @@ -441,5 +489,3 @@ typedef uint8_t SdCsPin_t; /** Default is no SDIO. */ #define HAS_SDIO_CLASS 0 #endif // HAS_SDIO_CLASS - -#endif // SdFatConfig_h diff --git a/trunk/Arduino/libraries/Servo/docs/api.md b/trunk/Arduino/libraries/Servo/docs/api.md index fc54c9df..f8ae38a5 100644 --- a/trunk/Arduino/libraries/Servo/docs/api.md +++ b/trunk/Arduino/libraries/Servo/docs/api.md @@ -4,12 +4,12 @@ ### `attach()` -Attach the Servo variable to a pin. Note that in Arduino 0016 and earlier, the Servo library supports servos on only two pins: 9 and 10. +Attach the Servo variable to a pin. Note that in Arduino IDE 0016 and earlier, the Servo library supports servos on only two pins: 9 and 10. #### Syntax ``` -servo.attach(pin) +servo.attach(pin) servo.attach(pin, min, max) ``` @@ -23,16 +23,16 @@ servo.attach(pin, min, max) #### Example ``` -#include +#include Servo myservo; -void setup() -{ +void setup() +{ myservo.attach(9); -} +} -void loop() {} +void loop() {} ``` #### See also @@ -58,17 +58,17 @@ servo.write(angle) #### Example ```` -#include +#include Servo myservo; -void setup() -{ +void setup() +{ myservo.attach(9); myservo.write(90); // set servo to mid-point -} +} -void loop() {} +void loop() {} ```` #### See also @@ -81,7 +81,7 @@ Writes a value in microseconds (us) to the servo, controlling the shaft accordin Note that some manufactures do not follow this standard very closely so that servos often respond to values between 700 and 2300. Feel free to increase these endpoints until the servo no longer continues to increase its range. Note however that attempting to drive a servo past its endpoints (often indicated by a growling sound) is a high-current state, and should be avoided. -Continuous-rotation servos will respond to the writeMicrosecond function in an analogous manner to the write function. +Continuous-rotation servos will respond to the writeMicrosecond function in an manner analogous to the write function. #### Syntax @@ -97,17 +97,17 @@ servo.writeMicroseconds(us) #### Example ```` -#include +#include Servo myservo; -void setup() -{ +void setup() +{ myservo.attach(9); myservo.writeMicroseconds(1500); // set servo to mid-point -} +} -void loop() {} +void loop() {} ```` #### See also @@ -118,7 +118,9 @@ void loop() {} ### `read()` -Read the current angle of the servo (the value passed to the last call to [write()](#write)). +Read the current setpoint of the servo (the angle passed to the last call to [write()](#write)). + +Note that the servo has no way of reporting its current physical orientation. This method returns the angle to which the sketch program has requested the servo to move, regardless of whether the servo has already reached that angle. #### Syntax @@ -132,7 +134,7 @@ servo.read() #### Returns -The angle of the servo, from 0 to 180 degrees. +The setpoint of the servo, as an angle from 0 to 180 degrees. #### See also diff --git a/trunk/Arduino/libraries/Servo/docs/readme.md b/trunk/Arduino/libraries/Servo/docs/readme.md index b986b140..2fd4a078 100644 --- a/trunk/Arduino/libraries/Servo/docs/readme.md +++ b/trunk/Arduino/libraries/Servo/docs/readme.md @@ -3,7 +3,7 @@ This library allows an Arduino board to control RC (hobby) servo motors. Servos have integrated gears and a shaft that can be precisely controlled. Standard servos allow the shaft to be positioned at various angles, usually between 0 and 180 degrees. Continuous rotation servos allow the rotation of the shaft to be set to various speeds. -The Servo library supports up to 12 motors on most Arduino boards and 48 on the Arduino Mega. On boards other than the Mega, use of the library disables `analogWrite()` (PWM) functionality on pins 9 and 10, whether or not there is a Servo on those pins. On the Mega, up to 12 servos can be used without interfering with PWM functionality; use of 12 to 23 motors will disable PWM on pins 11 and 12. +The Servo library supports up to 12 motors on most Arduino boards and 48 on the Arduino Mega. On boards other than the Mega, use of the library disables `analogWrite()` (PWM) functionality on pins 9 and 10, whether or not there is a Servo on those pins. On the Mega, up to 12 servos can be used without interfering with PWM functionality; use of 12 to 23 motors will disable PWM on pins 11 and 12. To use this library: diff --git a/trunk/Arduino/libraries/Servo/examples/Knob/Knob.ino b/trunk/Arduino/libraries/Servo/examples/Knob/Knob.ino index 0015a468..613a1adf 100644 --- a/trunk/Arduino/libraries/Servo/examples/Knob/Knob.ino +++ b/trunk/Arduino/libraries/Servo/examples/Knob/Knob.ino @@ -9,13 +9,13 @@ #include -Servo myservo; // create servo object to control a servo +Servo myservo; // create Servo object to control a servo int potpin = A0; // analog pin used to connect the potentiometer int val; // variable to read the value from the analog pin void setup() { - myservo.attach(9); // attaches the servo on pin 9 to the servo object + myservo.attach(9); // attaches the servo on pin 9 to the Servo object } void loop() { diff --git a/trunk/Arduino/libraries/Servo/examples/Knob/readme.md b/trunk/Arduino/libraries/Servo/examples/Knob/readme.md index 7dc37ddb..4184c0d4 100644 --- a/trunk/Arduino/libraries/Servo/examples/Knob/readme.md +++ b/trunk/Arduino/libraries/Servo/examples/Knob/readme.md @@ -1,6 +1,6 @@ # Knob -Control the position of a RC (hobby) [servo motor](http://en.wikipedia.org/wiki/Servo_motor#RC_servos) with your Arduino and a potentiometer. +Control the position of an RC (hobby) [servo motor](http://en.wikipedia.org/wiki/Servo_motor#RC_servos) with your Arduino and a potentiometer. This example makes use of the Arduino `Servo` library. @@ -15,7 +15,7 @@ This example makes use of the Arduino `Servo` library. Servo motors have three wires: power, ground, and signal. The power wire is typically red, and should be connected to the 5V pin on the Arduino board. The ground wire is typically black or brown and should be connected to a ground pin on the board. The signal pin is typically yellow or orange and should be connected to pin 9 on the board. -The potentiometer should be wired so that its two outer pins are connected to power (+5V) and ground, and its middle pin is connected to analog input 0 on the board. +The potentiometer should be wired so that its two outer pins are connected to power (5V) and ground, and its middle pin is connected to analog input 0 on the board. ![](images/knob_BB.png) diff --git a/trunk/Arduino/libraries/Servo/examples/Sweep/Sweep.ino b/trunk/Arduino/libraries/Servo/examples/Sweep/Sweep.ino index e988bbd8..c86e37bb 100644 --- a/trunk/Arduino/libraries/Servo/examples/Sweep/Sweep.ino +++ b/trunk/Arduino/libraries/Servo/examples/Sweep/Sweep.ino @@ -9,13 +9,13 @@ #include -Servo myservo; // create servo object to control a servo -// twelve servo objects can be created on most boards +Servo myservo; // create Servo object to control a servo +// twelve Servo objects can be created on most boards int pos = 0; // variable to store the servo position void setup() { - myservo.attach(9); // attaches the servo on pin 9 to the servo object + myservo.attach(9); // attaches the servo on pin 9 to the Servo object } void loop() { diff --git a/trunk/Arduino/libraries/Servo/examples/Sweep/readme.md b/trunk/Arduino/libraries/Servo/examples/Sweep/readme.md index bb07d5a1..81cbec8b 100644 --- a/trunk/Arduino/libraries/Servo/examples/Sweep/readme.md +++ b/trunk/Arduino/libraries/Servo/examples/Sweep/readme.md @@ -1,6 +1,6 @@ # Sweep -Sweeps the shaft of a RC [servo motor](http://en.wikipedia.org/wiki/Servo_motor#RC_servos) back and forth across 180 degrees. +Sweeps the shaft of an RC [servo motor](http://en.wikipedia.org/wiki/Servo_motor#RC_servos) back and forth across 180 degrees. ## Hardware Required diff --git a/trunk/Arduino/libraries/Servo/library.properties b/trunk/Arduino/libraries/Servo/library.properties index 30d58923..be03fd11 100644 --- a/trunk/Arduino/libraries/Servo/library.properties +++ b/trunk/Arduino/libraries/Servo/library.properties @@ -1,9 +1,9 @@ name=Servo -version=1.2.1 +version=1.3.0 author=Michael Margolis, Arduino maintainer=Arduino sentence=Allows Arduino boards to control a variety of servo motors. paragraph=This library can control a great number of servos.
It makes careful use of timers: the library can control 12 servos using only 1 timer.
On the Arduino Due you can control up to 60 servos. category=Device Control url=https://www.arduino.cc/reference/en/libraries/servo/ -architectures=avr,megaavr,sam,samd,nrf52,stm32f4,mbed,mbed_nano,mbed_portenta,mbed_rp2040,renesas,renesas_portenta,renesas_uno +architectures=avr,megaavr,sam,samd,nrf52,stm32f4,mbed,mbed_nano,mbed_portenta,mbed_rp2040,renesas,renesas_portenta,renesas_uno,zephyr diff --git a/trunk/Arduino/libraries/Servo/src/Servo.h b/trunk/Arduino/libraries/Servo/src/Servo.h index aab9c16a..20abb2c2 100644 --- a/trunk/Arduino/libraries/Servo/src/Servo.h +++ b/trunk/Arduino/libraries/Servo/src/Servo.h @@ -1,6 +1,6 @@ /* - Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 - Copyright (c) 2009 Michael Margolis. All right reserved. + Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers - Version 2 + Copyright (c) 2009 Michael Margolis. 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 @@ -17,15 +17,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -/* - A servo is activated by creating an instance of the Servo class passing +/* + A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method. - The servos are pulsed in the background using the value most recently + The servos are pulsed in the background using the value most recently written using the write() method. - Note that analogWrite of PWM on pins associated with the timer are + Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached. - Timers are seized as needed in groups of 12 servos - 24 servos use two + Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four. The sequence used to seize timers is defined in timers.h @@ -33,16 +33,16 @@ Servo - Class for manipulating servo motors connected to Arduino pins. - attach(pin ) - Attaches a servo motor to an I/O pin. + attach(pin ) - Attaches a servo motor to an I/O pin. attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds - default min is 544, max is 2400 - - write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) - writeMicroseconds() - Sets the servo pulse width in microseconds - read() - Gets the last written servo pulse width as an angle between 0 and 180. + default min is 544, max is 2400 + + write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) + writeMicroseconds() - Sets the servo pulse width in microseconds + read() - Gets the last written servo pulse width as an angle between 0 and 180. readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release) - attached() - Returns true if there is a servo attached. - detach() - Stops an attached servos from pulsing its I/O pin. + attached() - Returns true if there is a servo attached. + detach() - Stops an attached servos from pulsing its I/O pin. */ #ifndef Servo_h @@ -50,8 +50,8 @@ #include -/* - * Defines for 16 bit timers used with Servo library +/* + * Defines for 16 bit timers used with Servo library * * If _useTimerX is defined then TimerX is a 16 bit timer on the current board * timer16_Sequence_t enumerates the sequence that the timers should be allocated @@ -75,28 +75,34 @@ #include "mbed/ServoTimers.h" #elif defined(ARDUINO_ARCH_RENESAS) #include "renesas/ServoTimers.h" +#elif defined(ARDUINO_ARCH_XMC) +#include "xmc/ServoTimers.h" +#elif defined(ARDUINO_ARCH_ESP32) +#include "esp32/ServoTimers.h" +#elif defined(ARDUINO_ARCH_ZEPHYR) +#include "zephyr/ServoTimers.h" #else -#error "This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor." +#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, STM32F4, Renesas, XMC, ESP32 or Zephyr core." #endif #define Servo_VERSION 2 // software version of this library -#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo -#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached -#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds +#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds -#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer +#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer #define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) #define INVALID_SERVO 255 // flag indicating an invalid servo index -#if !defined(ARDUINO_ARCH_STM32F4) +#if !defined(ARDUINO_ARCH_STM32F4) && !defined(ARDUINO_ARCH_XMC) typedef struct { uint8_t nbr :6 ; // a pin number from 0 to 63 - uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false -} ServoPin_t ; + uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false +} ServoPin_t ; typedef struct { ServoPin_t Pin; @@ -108,17 +114,17 @@ class Servo public: Servo(); uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or INVALID_SERVO if failure - uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. + uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. void detach(); - void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds - void writeMicroseconds(int value); // Write pulse width in microseconds + void write(int value); // if value is < 200 it's treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds int read(); // returns current pulse width as an angle between 0 and 180 degrees int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) - bool attached(); // return true if this servo is attached, otherwise false + bool attached(); // return true if this servo is attached, otherwise false private: uint8_t servoIndex; // index into the channel data for this servo - int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH - int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH + int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH }; #endif diff --git a/trunk/Arduino/libraries/Servo/src/avr/Servo.cpp b/trunk/Arduino/libraries/Servo/src/avr/Servo.cpp index 11fecc58..27f53a8c 100644 --- a/trunk/Arduino/libraries/Servo/src/avr/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/avr/Servo.cpp @@ -1,5 +1,5 @@ /* - Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers - Version 2 Copyright (c) 2009 Michael Margolis. All right reserved. This library is free software; you can redistribute it and/or @@ -24,7 +24,7 @@ #include "Servo.h" -#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009 +#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to ticks (assumes prescaler of 8) // 12 Aug 2009 #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds @@ -62,7 +62,7 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks; if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated - digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // it's an active channel so pulse it high } else { // finished all channels so wait for the refresh period to expire before starting over diff --git a/trunk/Arduino/libraries/Servo/src/avr/ServoTimers.h b/trunk/Arduino/libraries/Servo/src/avr/ServoTimers.h index 8a35c59a..6189ea39 100644 --- a/trunk/Arduino/libraries/Servo/src/avr/ServoTimers.h +++ b/trunk/Arduino/libraries/Servo/src/avr/ServoTimers.h @@ -1,5 +1,5 @@ /* - Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers - Version 2 Copyright (c) 2009 Michael Margolis. All right reserved. This library is free software; you can redistribute it and/or diff --git a/trunk/Arduino/libraries/Servo/src/megaavr/Servo.cpp b/trunk/Arduino/libraries/Servo/src/megaavr/Servo.cpp index 59b3e447..7147636e 100644 --- a/trunk/Arduino/libraries/Servo/src/megaavr/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/megaavr/Servo.cpp @@ -3,7 +3,7 @@ #include #include -#define usToTicks(_us) ((clockCyclesPerMicrosecond() / 16 * _us) / 4) // converts microseconds to tick +#define usToTicks(_us) ((clockCyclesPerMicrosecond() / 16 * _us) / 4) // converts microseconds to ticks #define ticksToUs(_ticks) (((unsigned) _ticks * 16) / (clockCyclesPerMicrosecond() / 4)) // converts from ticks back to microseconds #define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays diff --git a/trunk/Arduino/libraries/Servo/src/nrf52/Servo.cpp b/trunk/Arduino/libraries/Servo/src/nrf52/Servo.cpp index 72bd504a..fe9876a9 100644 --- a/trunk/Arduino/libraries/Servo/src/nrf52/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/nrf52/Servo.cpp @@ -35,7 +35,7 @@ Servo::Servo() { if (ServoCount < MAX_SERVOS) { this->servoIndex = ServoCount++; // assign a servo index to this instance - } else { + } else { this->servoIndex = INVALID_SERVO; // too many servos } @@ -43,7 +43,7 @@ Servo::Servo() uint8_t Servo::attach(int pin) { - + return this->attach(pin, 0, 2500); } @@ -59,9 +59,9 @@ uint8_t Servo::attach(int pin, int min, int max) if (max > servo_max) max = servo_max; this->min = min; this->max = max; - + servos[this->servoIndex].Pin.isActive = true; - + } return this->servoIndex; } @@ -73,13 +73,13 @@ void Servo::detach() void Servo::write(int value) -{ +{ if (value < 0) value = 0; else if (value > 180) value = 180; value = map(value, 0, 180, MIN_PULSE, MAX_PULSE); - + writeMicroseconds(value); } @@ -117,7 +117,7 @@ int Servo::read() // return the value as degrees } int Servo::readMicroseconds() -{ +{ uint8_t channel, instance; uint8_t pin=servos[this->servoIndex].Pin.nbr; instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16; diff --git a/trunk/Arduino/libraries/Servo/src/nrf52/ServoTimers.h b/trunk/Arduino/libraries/Servo/src/nrf52/ServoTimers.h index f4fc1768..eba5a1ed 100644 --- a/trunk/Arduino/libraries/Servo/src/nrf52/ServoTimers.h +++ b/trunk/Arduino/libraries/Servo/src/nrf52/ServoTimers.h @@ -17,7 +17,7 @@ */ /* - * nRF52 doesn't use timer, but PWM. This file include definitions to keep + * nRF52 doesn't use timer, but PWM. This file includes definitions to keep * compatibility with the Servo library standards. */ diff --git a/trunk/Arduino/libraries/Servo/src/sam/Servo.cpp b/trunk/Arduino/libraries/Servo/src/sam/Servo.cpp index df5058f6..9984c69d 100644 --- a/trunk/Arduino/libraries/Servo/src/sam/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/sam/Servo.cpp @@ -21,7 +21,7 @@ #include #include -#define usToTicks(_us) (( clockCyclesPerMicrosecond() * _us) / 32) // converts microseconds to tick +#define usToTicks(_us) (( clockCyclesPerMicrosecond() * _us) / 32) // converts microseconds to ticks #define ticksToUs(_ticks) (( (unsigned)_ticks * 32)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds #define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays @@ -89,7 +89,7 @@ void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel) if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer,Channel[timer]).ticks; if(SERVO(timer,Channel[timer]).Pin.isActive == true) { // check if activated - digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // it's an active channel so pulse it high } } else { diff --git a/trunk/Arduino/libraries/Servo/src/samd/Servo.cpp b/trunk/Arduino/libraries/Servo/src/samd/Servo.cpp index d8e2ec40..04af43ef 100644 --- a/trunk/Arduino/libraries/Servo/src/samd/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/samd/Servo.cpp @@ -21,7 +21,7 @@ #include #include -#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 16) // converts microseconds to tick +#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 16) // converts microseconds to ticks #define ticksToUs(_ticks) (((unsigned) _ticks * 16) / clockCyclesPerMicrosecond()) // converts from ticks back to microseconds #define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays @@ -133,7 +133,7 @@ static void _initISR(Tc *tc, uint8_t channel, uint32_t id, IRQn_Type irqn, uint8 // Set timer counter mode as normal PWM tc->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM; - // Set the prescaler factor to GCLK_TC/16. At nominal 48 MHz GCLK_TC this is 3000 ticks per millisecond + // Set the prescaler factor to GCLK_TC/16. At nominal 48 MHz GCLK_TC this is 3000 ticks per millisecond tc->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16; // Count up diff --git a/trunk/Arduino/libraries/Servo/src/stm32f4/Servo.cpp b/trunk/Arduino/libraries/Servo/src/stm32f4/Servo.cpp index 01d05d98..8687c518 100644 --- a/trunk/Arduino/libraries/Servo/src/stm32f4/Servo.cpp +++ b/trunk/Arduino/libraries/Servo/src/stm32f4/Servo.cpp @@ -33,7 +33,7 @@ #include "pwm.h" #include "math.h" -// 20 millisecond period config. For a 1-based prescaler, +// 20 millisecond period config. For a 1-based prescaler, // // (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec // => prescaler * overflow = 20 * CYC_MSEC diff --git a/trunk/Arduino/libraries/Servo/src/stm32f4/ServoTimers.h b/trunk/Arduino/libraries/Servo/src/stm32f4/ServoTimers.h index 53bd6479..d595879c 100644 --- a/trunk/Arduino/libraries/Servo/src/stm32f4/ServoTimers.h +++ b/trunk/Arduino/libraries/Servo/src/stm32f4/ServoTimers.h @@ -49,17 +49,17 @@ * * This implementation only allows Servo instances to attach() to pins * that already have a timer channel associated with them, and just - * uses pwmWrite() to drive the wave. + * uses analogWrite() to drive the wave. * * This introduces an incompatibility: while the Arduino * implementation of attach() returns the affected channel on success * and 0 on failure, this one returns true on success and false on * failure. * - * RC Servos expect a pulse every 20 ms. Since periods are set for + * RC Servos expect a pulse every 20 ms. Since periods are set for * entire timers, rather than individual channels, attach()ing a Servo * to a pin can interfere with other pins associated with the same - * timer. As always, your board's pin map is your friend. + * timer. As always, your board's pin map is your friend. */ // Pin number of unattached pins @@ -70,7 +70,7 @@ // Default min/max pulse widths (in microseconds) and angles (in -// degrees). Values chosen for Arduino compatibility. These values +// degrees). Values chosen for Arduino compatibility. These values // are part of the public API; DO NOT CHANGE THEM. #define MIN_ANGLE 0 #define MAX_ANGLE 180 @@ -101,21 +101,21 @@ public: * pin must be capable of PWM output. * * @param minPulseWidth Minimum pulse width to write to pin, in - * microseconds. This will be associated - * with a minAngle degree angle. Defaults to + * microseconds. This will be associated + * with a minAngle degree angle. Defaults to * SERVO_DEFAULT_MIN_PW = 544. * * @param maxPulseWidth Maximum pulse width to write to pin, in - * microseconds. This will be associated + * microseconds. This will be associated * with a maxAngle degree angle. Defaults to * SERVO_DEFAULT_MAX_PW = 2400. * * @param minAngle Target angle (in degrees) associated with - * minPulseWidth. Defaults to + * minPulseWidth. Defaults to * SERVO_DEFAULT_MIN_ANGLE = 0. * * @param maxAngle Target angle (in degrees) associated with - * maxPulseWidth. Defaults to + * maxPulseWidth. Defaults to * SERVO_DEFAULT_MAX_ANGLE = 180. * * @sideeffect May set pinMode(pin, PWM). @@ -140,7 +140,7 @@ public: /** * @brief Set the servomotor target angle. * - * @param angle Target angle, in degrees. If the target angle is + * @param angle Target angle, in degrees. If the target angle is * outside the range specified at attach() time, it * will be clamped to lie in that range. * @@ -161,7 +161,7 @@ public: void writeMicroseconds(uint16 pulseWidth); /** - * Get the servomotor's target angle, in degrees. This will + * Get the servomotor's target angle, in degrees. This will * lie inside the range specified at attach() time. * * @see Servo::attach() @@ -169,7 +169,7 @@ public: int read() const; /** - * Get the current pulse width, in microseconds. This will + * Get the current pulse width, in microseconds. This will * lie within the range specified at attach() time. * * @see Servo::attach()