Req. Arduino Core 3.0.2
This commit is contained in:
parent
21f4d6c054
commit
825e48086b
|
|
@ -40,7 +40,8 @@ void checkAndUpdateWiFiCredentials() {
|
|||
}
|
||||
}
|
||||
|
||||
IRAM_ATTR void checkWiFi(unsigned long tickMillis) {
|
||||
IRAM_ATTR
|
||||
void checkWiFi(unsigned long tickMillis) {
|
||||
static unsigned long lastAttempt = 0;
|
||||
static bool bConnecting = false;
|
||||
|
||||
|
|
|
|||
39
HermitCrab.h
39
HermitCrab.h
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef DEBUG
|
||||
#define DEBUG 1 // Set to 0 to disable debug output
|
||||
#endif
|
||||
#undef DEBUG
|
||||
|
||||
|
||||
//#define BLE_DEBUG
|
||||
#ifdef BLE_DEBUG
|
||||
|
|
@ -96,28 +96,27 @@
|
|||
// End of PIN Definition
|
||||
// =======================
|
||||
|
||||
|
||||
// Resolution for all Channels
|
||||
#define PWM_RESOLUTION 10 // 10-bit resolution
|
||||
#define PWM_MOTOR_RESOLUTION 8
|
||||
|
||||
#define PWM_AP_CHANNEL 9
|
||||
#define PWM_SC_CHANNEL 10
|
||||
#define PWM_AP_FREQ 1
|
||||
#define PWM_SC_FREQ 2
|
||||
|
||||
#define PWM_MIST_CHANNEL 8 // MIST
|
||||
#define PWM_MIST_FREQ 1 // MIST
|
||||
|
||||
#define PWM_HEATER1_CHANNEL 2
|
||||
#define PWM_HEATER2_CHANNEL 3
|
||||
#define PWM_LIGHT_CHANNEL 4
|
||||
#define PWM_MOTOR_CHANNEL 5
|
||||
#define PWM_FAN_CHANNEL 6
|
||||
|
||||
#define PWM_1KHZ_FREQ 1000 // 1 kHz frequency
|
||||
#define PWM_25KHZ_FREQ 25000 // 25KHz
|
||||
|
||||
#define PWM_FULL 1023
|
||||
#define PWM_OFF 0
|
||||
|
||||
// High Speed Group - Motor and Fan (26KHz)
|
||||
#define PWM_MOTOR_CHANNEL 0
|
||||
#define PWM_FAN_CHANNEL 1
|
||||
// Medium Speed Group - LED & Heater (1KHz)
|
||||
#define PWM_LIGHT_CHANNEL 2
|
||||
#define PWM_HEATER1_CHANNEL 3
|
||||
#define PWM_HEATER2_CHANNEL 4
|
||||
// Low Speed Group - Mist, WifiLED (1Hz)
|
||||
#define PWM_MIST_CHANNEL 8
|
||||
#define PWM_WIFI_LED_CHANNEL 9
|
||||
|
||||
#define PWM_1HZ_FREQ 1 // 1 Hz
|
||||
#define PWM_1KHZ_FREQ 1221 // 1 kHz
|
||||
#define PWM_26KHZ_FREQ 26042 // 26KHz
|
||||
|
||||
#define TAG "HC"
|
||||
|
||||
enum EVENT_TYPE {
|
||||
|
|
|
|||
6
LED0.cpp
6
LED0.cpp
|
|
@ -15,7 +15,7 @@ void CLED0::setup(uint8_t _pin, uint16_t _freq, uint16_t _channel) {
|
|||
setDuty(duty);
|
||||
};
|
||||
|
||||
MY_IRAM_ATTR void CLED0::setFreq(uint16_t _freq) {
|
||||
void CLED0::setFreq(uint16_t _freq) {
|
||||
if (freq != _freq) {
|
||||
if (_freq == 0) {
|
||||
ledcDetach(pin);
|
||||
|
|
@ -31,7 +31,7 @@ MY_IRAM_ATTR void CLED0::setFreq(uint16_t _freq) {
|
|||
}
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CLED0::setDuty() {
|
||||
void CLED0::setDuty() {
|
||||
uint16_t _duty;
|
||||
|
||||
if (bAC) _duty = LED0_DUTY_AC;
|
||||
|
|
@ -41,7 +41,7 @@ MY_IRAM_ATTR void CLED0::setDuty() {
|
|||
ledcWrite(PIN_LED_WIFI, PWM_FULL * (100 - _duty) / 100); // Light Blink
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CLED0::setDuty(uint16_t _duty) {
|
||||
void CLED0::setDuty(uint16_t _duty) {
|
||||
if (duty != _duty) {
|
||||
if (bPWMMode)
|
||||
ledcWrite(PIN_LED_WIFI, PWM_FULL * (100 - _duty) / 100); // Light Blink
|
||||
|
|
|
|||
965
Libraries/Adafruit_SSD1306.cpp
Normal file
965
Libraries/Adafruit_SSD1306.cpp
Normal file
|
|
@ -0,0 +1,965 @@
|
|||
/*!
|
||||
* @file Adafruit_SSD1306.cpp
|
||||
*
|
||||
* @mainpage Arduino library for monochrome OLEDs based on SSD1306 drivers.
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is documentation for Adafruit's SSD1306 library for monochrome
|
||||
* OLED displays: http://www.adafruit.com/category/63_98
|
||||
*
|
||||
* These displays use I2C or SPI to communicate. I2C requires 2 pins
|
||||
* (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
|
||||
* select, data/command) and optionally a reset pin. Hardware SPI or
|
||||
* 'bitbang' software SPI are both supported.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* @section dependencies Dependencies
|
||||
*
|
||||
* This library depends on <a
|
||||
* href="https://github.com/adafruit/Adafruit-GFX-Library"> Adafruit_GFX</a>
|
||||
* being present on your system. Please make sure you have installed the latest
|
||||
* version before using this library.
|
||||
*
|
||||
* @section author Author
|
||||
*
|
||||
* Written by Limor Fried/Ladyada for Adafruit Industries, with
|
||||
* contributions from the open source community.
|
||||
*
|
||||
* @section license License
|
||||
*
|
||||
* BSD license, all text above, and the splash screen included below,
|
||||
* must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include "Adafruit_SSD1306.h"
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
// SOME DEFINES AND STATIC VARIABLES USED INTERNALLY -----------------------
|
||||
|
||||
#if defined(I2C_BUFFER_LENGTH)
|
||||
#define WIRE_MAX min(256, I2C_BUFFER_LENGTH) ///< Particle or similar Wire lib
|
||||
#else
|
||||
#define WIRE_MAX 32 ///< Use common Arduino core default
|
||||
#endif
|
||||
|
||||
#define ssd1306_swap(a, b) \
|
||||
(((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#define WIRE_WRITE wire->write ///< Wire write function in recent Arduino lib
|
||||
#else
|
||||
#define WIRE_WRITE wire->send ///< Wire write function in older Arduino lib
|
||||
#endif
|
||||
|
||||
#define SSD1306_SELECT digitalWrite(csPin, LOW); ///< Device select
|
||||
#define SSD1306_DESELECT digitalWrite(csPin, HIGH); ///< Device deselect
|
||||
#define SSD1306_MODE_COMMAND digitalWrite(dcPin, LOW); ///< Command mode
|
||||
#define SSD1306_MODE_DATA digitalWrite(dcPin, HIGH); ///< Data mode
|
||||
|
||||
#if (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER)
|
||||
#define SETWIRECLOCK wire->setClock(wireClk) ///< Set before I2C transfer
|
||||
#define RESWIRECLOCK wire->setClock(restoreClk) ///< Restore after I2C xfer
|
||||
#else // setClock() is not present in older Arduino Wire lib (or WICED)
|
||||
#define SETWIRECLOCK ///< Dummy stand-in define
|
||||
#define RESWIRECLOCK ///< keeps compiler happy
|
||||
#endif
|
||||
|
||||
// The definition of 'transaction' is broadened a bit in the context of
|
||||
// this library -- referring not just to SPI transactions (if supported
|
||||
// in the version of the SPI library being used), but also chip select
|
||||
// (if SPI is being used, whether hardware or soft), and also to the
|
||||
// beginning and end of I2C transfers (the Wire clock may be sped up before
|
||||
// issuing data to the display, then restored to the default rate afterward
|
||||
// so other I2C device types still work). All of these are encapsulated
|
||||
// in the TRANSACTION_* macros.
|
||||
|
||||
// Check first if Wire, then hardware SPI, then soft SPI:
|
||||
#define TRANSACTION_START SETWIRECLOCK;
|
||||
#define TRANSACTION_END RESWIRECLOCK; \
|
||||
|
||||
// CONSTRUCTORS, DESTRUCTOR ------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Constructor for I2C-interfaced SSD1306 displays.
|
||||
@param w
|
||||
Display width in pixels
|
||||
@param h
|
||||
Display height in pixels
|
||||
@param twi
|
||||
Pointer to an existing TwoWire instance (e.g. &Wire, the
|
||||
microcontroller's primary I2C bus).
|
||||
@param rst_pin
|
||||
Reset pin (using Arduino pin numbering), or -1 if not used
|
||||
(some displays might be wired to share the microcontroller's
|
||||
reset pin).
|
||||
@param clkDuring
|
||||
Speed (in Hz) for Wire transmissions in SSD1306 library calls.
|
||||
Defaults to 400000 (400 KHz), a known 'safe' value for most
|
||||
microcontrollers, and meets the SSD1306 datasheet spec.
|
||||
Some systems can operate I2C faster (800 KHz for ESP32, 1 MHz
|
||||
for many other 32-bit MCUs), and some (perhaps not all)
|
||||
SSD1306's can work with this -- so it's optionally be specified
|
||||
here and is not a default behavior. (Ignored if using pre-1.5.7
|
||||
Arduino software, which operates I2C at a fixed 100 KHz.)
|
||||
@param clkAfter
|
||||
Speed (in Hz) for Wire transmissions following SSD1306 library
|
||||
calls. Defaults to 100000 (100 KHz), the default Arduino Wire
|
||||
speed. This is done rather than leaving it at the 'during' speed
|
||||
because other devices on the I2C bus might not be compatible
|
||||
with the faster rate. (Ignored if using pre-1.5.7 Arduino
|
||||
software, which operates I2C at a fixed 100 KHz.)
|
||||
@return Adafruit_SSD1306 object.
|
||||
@note Call the object's begin() function before use -- buffer
|
||||
allocation is performed there!
|
||||
*/
|
||||
Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi,
|
||||
int8_t rst_pin, uint32_t clkDuring,
|
||||
uint32_t clkAfter)
|
||||
: Adafruit_GFX(w, h), spi(NULL), wire(twi ? twi : &Wire), buffer(NULL),
|
||||
mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1), rstPin(rst_pin)
|
||||
#if ARDUINO >= 157
|
||||
,
|
||||
wireClk(clkDuring), restoreClk(clkAfter)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Constructor for SPI SSD1306 displays, using software (bitbang)
|
||||
SPI.
|
||||
@param w
|
||||
Display width in pixels
|
||||
@param h
|
||||
Display height in pixels
|
||||
@param mosi_pin
|
||||
MOSI (master out, slave in) pin (using Arduino pin numbering).
|
||||
This transfers serial data from microcontroller to display.
|
||||
@param sclk_pin
|
||||
SCLK (serial clock) pin (using Arduino pin numbering).
|
||||
This clocks each bit from MOSI.
|
||||
@param dc_pin
|
||||
Data/command pin (using Arduino pin numbering), selects whether
|
||||
display is receiving commands (low) or data (high).
|
||||
@param rst_pin
|
||||
Reset pin (using Arduino pin numbering), or -1 if not used
|
||||
(some displays might be wired to share the microcontroller's
|
||||
reset pin).
|
||||
@param cs_pin
|
||||
Chip-select pin (using Arduino pin numbering) for sharing the
|
||||
bus with other devices. Active low.
|
||||
@return Adafruit_SSD1306 object.
|
||||
@note Call the object's begin() function before use -- buffer
|
||||
allocation is performed there!
|
||||
*/
|
||||
Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin,
|
||||
int8_t sclk_pin, int8_t dc_pin,
|
||||
int8_t rst_pin, int8_t cs_pin)
|
||||
: Adafruit_GFX(w, h), spi(NULL), wire(NULL), buffer(NULL),
|
||||
mosiPin(mosi_pin), clkPin(sclk_pin), dcPin(dc_pin), csPin(cs_pin),
|
||||
rstPin(rst_pin) {}
|
||||
|
||||
/*!
|
||||
@brief Constructor for SPI SSD1306 displays, using native hardware SPI.
|
||||
@param w
|
||||
Display width in pixels
|
||||
@param h
|
||||
Display height in pixels
|
||||
@param spi_ptr
|
||||
Pointer to an existing SPIClass instance (e.g. &SPI, the
|
||||
microcontroller's primary SPI bus).
|
||||
@param dc_pin
|
||||
Data/command pin (using Arduino pin numbering), selects whether
|
||||
display is receiving commands (low) or data (high).
|
||||
@param rst_pin
|
||||
Reset pin (using Arduino pin numbering), or -1 if not used
|
||||
(some displays might be wired to share the microcontroller's
|
||||
reset pin).
|
||||
@param cs_pin
|
||||
Chip-select pin (using Arduino pin numbering) for sharing the
|
||||
bus with other devices. Active low.
|
||||
@param bitrate
|
||||
SPI clock rate for transfers to this display. Default if
|
||||
unspecified is 8000000UL (8 MHz).
|
||||
@return Adafruit_SSD1306 object.
|
||||
@note Call the object's begin() function before use -- buffer
|
||||
allocation is performed there!
|
||||
*/
|
||||
Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi_ptr,
|
||||
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin,
|
||||
uint32_t bitrate)
|
||||
: Adafruit_GFX(w, h), spi(spi_ptr ? spi_ptr : &SPI), wire(NULL),
|
||||
buffer(NULL), mosiPin(-1), clkPin(-1), dcPin(dc_pin), csPin(cs_pin),
|
||||
rstPin(rst_pin) {
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
spiSettings = SPISettings(bitrate, MSBFIRST, SPI_MODE0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Issue single command to SSD1306, using I2C or hard/soft SPI as
|
||||
needed. Because command calls are often grouped, SPI transaction and
|
||||
selection must be started/ended in calling function for efficiency. This is a
|
||||
protected function, not exposed (see ssd1306_command() instead).
|
||||
|
||||
@param c
|
||||
the command character to send to the display.
|
||||
Refer to ssd1306 data sheet for commands
|
||||
@return None (void).
|
||||
@note
|
||||
*/
|
||||
void Adafruit_SSD1306::ssd1306_command1(uint8_t c) {
|
||||
{ // I2C
|
||||
wire->beginTransmission(i2caddr);
|
||||
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
||||
WIRE_WRITE(c);
|
||||
wire->endTransmission();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Issue list of commands to SSD1306, same rules as above re:
|
||||
transactions. This is a protected function, not exposed.
|
||||
@param c
|
||||
pointer to list of commands
|
||||
|
||||
@param n
|
||||
number of commands in the list
|
||||
|
||||
@return None (void).
|
||||
@note
|
||||
*/
|
||||
void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) {
|
||||
{ // I2C
|
||||
wire->beginTransmission(i2caddr);
|
||||
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
||||
uint16_t bytesOut = 1;
|
||||
while (n--) {
|
||||
if (bytesOut >= WIRE_MAX) {
|
||||
wire->endTransmission();
|
||||
wire->beginTransmission(i2caddr);
|
||||
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
||||
bytesOut = 1;
|
||||
}
|
||||
WIRE_WRITE(pgm_read_byte(c++));
|
||||
bytesOut++;
|
||||
}
|
||||
wire->endTransmission();
|
||||
}
|
||||
}
|
||||
|
||||
// A public version of ssd1306_command1(), for existing user code that
|
||||
// might rely on that function. This encapsulates the command transfer
|
||||
// in a transaction start/end, similar to old library's handling of it.
|
||||
/*!
|
||||
@brief Issue a single low-level command directly to the SSD1306
|
||||
display, bypassing the library.
|
||||
@param c
|
||||
Command to issue (0x00 to 0xFF, see datasheet).
|
||||
@return None (void).
|
||||
*/
|
||||
void Adafruit_SSD1306::ssd1306_command(uint8_t c) {
|
||||
TRANSACTION_START
|
||||
ssd1306_command1(c);
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
// ALLOCATE & INIT DISPLAY -------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Allocate RAM for image buffer, initialize peripherals and pins.
|
||||
@param vcs
|
||||
VCC selection. Pass SSD1306_SWITCHCAPVCC to generate the display
|
||||
voltage (step up) from the 3.3V source, or SSD1306_EXTERNALVCC
|
||||
otherwise. Most situations with Adafruit SSD1306 breakouts will
|
||||
want SSD1306_SWITCHCAPVCC.
|
||||
@param addr
|
||||
I2C address of corresponding SSD1306 display (or pass 0 to use
|
||||
default of 0x3C for 128x32 display, 0x3D for all others).
|
||||
SPI displays (hardware or software) do not use addresses, but
|
||||
this argument is still required (pass 0 or any value really,
|
||||
it will simply be ignored). Default if unspecified is 0.
|
||||
@param reset
|
||||
If true, and if the reset pin passed to the constructor is
|
||||
valid, a hard reset will be performed before initializing the
|
||||
display. If using multiple SSD1306 displays on the same bus, and
|
||||
if they all share the same reset pin, you should only pass true
|
||||
on the first display being initialized, false on all others,
|
||||
else the already-initialized displays would be reset. Default if
|
||||
unspecified is true.
|
||||
@param periphBegin
|
||||
If true, and if a hardware peripheral is being used (I2C or SPI,
|
||||
but not software SPI), call that peripheral's begin() function,
|
||||
else (false) it has already been done in one's sketch code.
|
||||
Cases where false might be used include multiple displays or
|
||||
other devices sharing a common bus, or situations on some
|
||||
platforms where a nonstandard begin() function is available
|
||||
(e.g. a TwoWire interface on non-default pins, as can be done
|
||||
on the ESP8266 and perhaps others).
|
||||
@return true on successful allocation/init, false otherwise.
|
||||
Well-behaved code should check the return value before
|
||||
proceeding.
|
||||
@note MUST call this function before any drawing or updates!
|
||||
*/
|
||||
bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr, bool reset,
|
||||
bool periphBegin) {
|
||||
|
||||
if ((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8))))
|
||||
return false;
|
||||
|
||||
clearDisplay();
|
||||
|
||||
vccstate = vcs;
|
||||
|
||||
// Setup pin directions
|
||||
{ // Using I2C
|
||||
// If I2C address is unspecified, use default
|
||||
// (0x3C for 32-pixel-tall displays, 0x3D for all others).
|
||||
i2caddr = addr ? addr : ((HEIGHT == 32) ? 0x3C : 0x3D);
|
||||
// TwoWire begin() function might be already performed by the calling
|
||||
// function if it has unusual circumstances (e.g. TWI variants that
|
||||
// can accept different SDA/SCL pins, or if two SSD1306 instances
|
||||
// with different addresses -- only a single begin() is needed).
|
||||
if (periphBegin)
|
||||
wire->begin();
|
||||
}
|
||||
|
||||
// Reset SSD1306 if requested and reset pin specified in constructor
|
||||
if (reset && (rstPin >= 0)) {
|
||||
pinMode(rstPin, OUTPUT);
|
||||
digitalWrite(rstPin, HIGH);
|
||||
delay(1); // VDD goes high at start, pause for 1 ms
|
||||
digitalWrite(rstPin, LOW); // Bring reset low
|
||||
delay(10); // Wait 10 ms
|
||||
digitalWrite(rstPin, HIGH); // Bring out of reset
|
||||
}
|
||||
|
||||
TRANSACTION_START
|
||||
|
||||
// Init sequence
|
||||
static const uint8_t init1[] = {SSD1306_DISPLAYOFF, // 0xAE
|
||||
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
|
||||
0x80, // the suggested ratio 0x80
|
||||
SSD1306_SETMULTIPLEX}; // 0xA8
|
||||
ssd1306_commandList(init1, sizeof(init1));
|
||||
ssd1306_command1(HEIGHT - 1);
|
||||
|
||||
static const uint8_t init2[] = {SSD1306_SETDISPLAYOFFSET, // 0xD3
|
||||
0x0, // no offset
|
||||
SSD1306_SETSTARTLINE | 0x0, // line #0
|
||||
SSD1306_CHARGEPUMP}; // 0x8D
|
||||
ssd1306_commandList(init2, sizeof(init2));
|
||||
|
||||
ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14);
|
||||
|
||||
static const uint8_t init3[] = {SSD1306_MEMORYMODE, // 0x20
|
||||
0x00, // 0x0 act like ks0108
|
||||
SSD1306_SEGREMAP | 0x1,
|
||||
SSD1306_COMSCANDEC};
|
||||
ssd1306_commandList(init3, sizeof(init3));
|
||||
|
||||
uint8_t comPins = 0x02;
|
||||
contrast = 0x8F;
|
||||
|
||||
if ((WIDTH == 128) && (HEIGHT == 32)) {
|
||||
comPins = 0x02;
|
||||
contrast = 0x8F;
|
||||
} else if ((WIDTH == 128) && (HEIGHT == 64)) {
|
||||
comPins = 0x12;
|
||||
contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF;
|
||||
} else if ((WIDTH == 96) && (HEIGHT == 16)) {
|
||||
comPins = 0x2; // ada x12
|
||||
contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0xAF;
|
||||
} else {
|
||||
// Other screen varieties -- TBD
|
||||
}
|
||||
|
||||
ssd1306_command1(SSD1306_SETCOMPINS);
|
||||
ssd1306_command1(comPins);
|
||||
ssd1306_command1(SSD1306_SETCONTRAST);
|
||||
ssd1306_command1(contrast);
|
||||
|
||||
ssd1306_command1(SSD1306_SETPRECHARGE); // 0xd9
|
||||
ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1);
|
||||
static const uint8_t init5[] = {
|
||||
SSD1306_SETVCOMDETECT, // 0xDB
|
||||
0x40,
|
||||
SSD1306_DISPLAYALLON_RESUME, // 0xA4
|
||||
SSD1306_NORMALDISPLAY, // 0xA6
|
||||
SSD1306_DEACTIVATE_SCROLL,
|
||||
SSD1306_DISPLAYON}; // Main screen turn on
|
||||
ssd1306_commandList(init5, sizeof(init5));
|
||||
|
||||
TRANSACTION_END
|
||||
|
||||
return true; // Success
|
||||
}
|
||||
|
||||
// DRAWING FUNCTIONS -------------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Set/clear/invert a single pixel. This is also invoked by the
|
||||
Adafruit_GFX library in generating many higher-level graphics
|
||||
primitives.
|
||||
@param x
|
||||
Column of display -- 0 at left to (screen width - 1) at right.
|
||||
@param y
|
||||
Row of display -- 0 at top to (screen height -1) at bottom.
|
||||
@param color
|
||||
Pixel color, one of: SSD1306_BLACK, SSD1306_WHITE or
|
||||
SSD1306_INVERSE.
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
||||
if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
|
||||
// Pixel is in-bounds. Rotate coordinates if needed.
|
||||
switch (getRotation()) {
|
||||
case 1:
|
||||
ssd1306_swap(x, y);
|
||||
x = WIDTH - x - 1;
|
||||
break;
|
||||
case 2:
|
||||
x = WIDTH - x - 1;
|
||||
y = HEIGHT - y - 1;
|
||||
break;
|
||||
case 3:
|
||||
ssd1306_swap(x, y);
|
||||
y = HEIGHT - y - 1;
|
||||
break;
|
||||
}
|
||||
switch (color) {
|
||||
case SSD1306_WHITE:
|
||||
buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Clear contents of display buffer (set all pixels to off).
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::clearDisplay(void) {
|
||||
memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Draw a horizontal line. This is also invoked by the Adafruit_GFX
|
||||
library in generating many higher-level graphics primitives.
|
||||
@param x
|
||||
Leftmost column -- 0 at left to (screen width - 1) at right.
|
||||
@param y
|
||||
Row of display -- 0 at top to (screen height -1) at bottom.
|
||||
@param w
|
||||
Width of line, in pixels.
|
||||
@param color
|
||||
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w,
|
||||
uint16_t color) {
|
||||
bool bSwap = false;
|
||||
switch (rotation) {
|
||||
case 1:
|
||||
// 90 degree rotation, swap x & y for rotation, then invert x
|
||||
bSwap = true;
|
||||
ssd1306_swap(x, y);
|
||||
x = WIDTH - x - 1;
|
||||
break;
|
||||
case 2:
|
||||
// 180 degree rotation, invert x and y, then shift y around for height.
|
||||
x = WIDTH - x - 1;
|
||||
y = HEIGHT - y - 1;
|
||||
x -= (w - 1);
|
||||
break;
|
||||
case 3:
|
||||
// 270 degree rotation, swap x & y for rotation,
|
||||
// then invert y and adjust y for w (not to become h)
|
||||
bSwap = true;
|
||||
ssd1306_swap(x, y);
|
||||
y = HEIGHT - y - 1;
|
||||
y -= (w - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (bSwap)
|
||||
drawFastVLineInternal(x, y, w, color);
|
||||
else
|
||||
drawFastHLineInternal(x, y, w, color);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Draw a horizontal line with a width and color. Used by public
|
||||
methods drawFastHLine,drawFastVLine
|
||||
@param x
|
||||
Leftmost column -- 0 at left to (screen width - 1) at right.
|
||||
@param y
|
||||
Row of display -- 0 at top to (screen height -1) at bottom.
|
||||
@param w
|
||||
Width of line, in pixels.
|
||||
@param color
|
||||
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or
|
||||
SSD1306_INVERSE.
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
|
||||
uint16_t color) {
|
||||
|
||||
if ((y >= 0) && (y < HEIGHT)) { // Y coord in bounds?
|
||||
if (x < 0) { // Clip left
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
if ((x + w) > WIDTH) { // Clip right
|
||||
w = (WIDTH - x);
|
||||
}
|
||||
if (w > 0) { // Proceed only if width is positive
|
||||
uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x], mask = 1 << (y & 7);
|
||||
switch (color) {
|
||||
case SSD1306_WHITE:
|
||||
while (w--) {
|
||||
*pBuf++ |= mask;
|
||||
};
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
mask = ~mask;
|
||||
while (w--) {
|
||||
*pBuf++ &= mask;
|
||||
};
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
while (w--) {
|
||||
*pBuf++ ^= mask;
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Draw a vertical line. This is also invoked by the Adafruit_GFX
|
||||
library in generating many higher-level graphics primitives.
|
||||
@param x
|
||||
Column of display -- 0 at left to (screen width -1) at right.
|
||||
@param y
|
||||
Topmost row -- 0 at top to (screen height - 1) at bottom.
|
||||
@param h
|
||||
Height of line, in pixels.
|
||||
@param color
|
||||
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h,
|
||||
uint16_t color) {
|
||||
bool bSwap = false;
|
||||
switch (rotation) {
|
||||
case 1:
|
||||
// 90 degree rotation, swap x & y for rotation,
|
||||
// then invert x and adjust x for h (now to become w)
|
||||
bSwap = true;
|
||||
ssd1306_swap(x, y);
|
||||
x = WIDTH - x - 1;
|
||||
x -= (h - 1);
|
||||
break;
|
||||
case 2:
|
||||
// 180 degree rotation, invert x and y, then shift y around for height.
|
||||
x = WIDTH - x - 1;
|
||||
y = HEIGHT - y - 1;
|
||||
y -= (h - 1);
|
||||
break;
|
||||
case 3:
|
||||
// 270 degree rotation, swap x & y for rotation, then invert y
|
||||
bSwap = true;
|
||||
ssd1306_swap(x, y);
|
||||
y = HEIGHT - y - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bSwap)
|
||||
drawFastHLineInternal(x, y, h, color);
|
||||
else
|
||||
drawFastVLineInternal(x, y, h, color);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Draw a vertical line with a width and color. Used by public method
|
||||
drawFastHLine,drawFastVLine
|
||||
@param x
|
||||
Leftmost column -- 0 at left to (screen width - 1) at right.
|
||||
@param __y
|
||||
Row of display -- 0 at top to (screen height -1) at bottom.
|
||||
@param __h height of the line in pixels
|
||||
@param color
|
||||
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or
|
||||
SSD1306_INVERSE.
|
||||
@return None (void).
|
||||
@note Changes buffer contents only, no immediate effect on display.
|
||||
Follow up with a call to display(), or with other graphics
|
||||
commands as needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y,
|
||||
int16_t __h, uint16_t color) {
|
||||
|
||||
if ((x >= 0) && (x < WIDTH)) { // X coord in bounds?
|
||||
if (__y < 0) { // Clip top
|
||||
__h += __y;
|
||||
__y = 0;
|
||||
}
|
||||
if ((__y + __h) > HEIGHT) { // Clip bottom
|
||||
__h = (HEIGHT - __y);
|
||||
}
|
||||
if (__h > 0) { // Proceed only if height is now positive
|
||||
// this display doesn't need ints for coordinates,
|
||||
// use local byte registers for faster juggling
|
||||
uint8_t y = __y, h = __h;
|
||||
uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x];
|
||||
|
||||
// do the first partial byte, if necessary - this requires some masking
|
||||
uint8_t mod = (y & 7);
|
||||
if (mod) {
|
||||
// mask off the high n bits we want to set
|
||||
mod = 8 - mod;
|
||||
// note - lookup table results in a nearly 10% performance
|
||||
// improvement in fill* functions
|
||||
// uint8_t mask = ~(0xFF >> mod);
|
||||
static const uint8_t PROGMEM premask[8] = {0x00, 0x80, 0xC0, 0xE0,
|
||||
0xF0, 0xF8, 0xFC, 0xFE};
|
||||
uint8_t mask = pgm_read_byte(&premask[mod]);
|
||||
// adjust the mask if we're not going to reach the end of this byte
|
||||
if (h < mod)
|
||||
mask &= (0XFF >> (mod - h));
|
||||
|
||||
switch (color) {
|
||||
case SSD1306_WHITE:
|
||||
*pBuf |= mask;
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
*pBuf &= ~mask;
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
*pBuf ^= mask;
|
||||
break;
|
||||
}
|
||||
pBuf += WIDTH;
|
||||
}
|
||||
|
||||
if (h >= mod) { // More to go?
|
||||
h -= mod;
|
||||
// Write solid bytes while we can - effectively 8 rows at a time
|
||||
if (h >= 8) {
|
||||
if (color == SSD1306_INVERSE) {
|
||||
// separate copy of the code so we don't impact performance of
|
||||
// black/white write version with an extra comparison per loop
|
||||
do {
|
||||
*pBuf ^= 0xFF; // Invert byte
|
||||
pBuf += WIDTH; // Advance pointer 8 rows
|
||||
h -= 8; // Subtract 8 rows from height
|
||||
} while (h >= 8);
|
||||
} else {
|
||||
// store a local value to work with
|
||||
uint8_t val = (color != SSD1306_BLACK) ? 255 : 0;
|
||||
do {
|
||||
*pBuf = val; // Set byte
|
||||
pBuf += WIDTH; // Advance pointer 8 rows
|
||||
h -= 8; // Subtract 8 rows from height
|
||||
} while (h >= 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (h) { // Do the final partial byte, if necessary
|
||||
mod = h & 7;
|
||||
// this time we want to mask the low bits of the byte,
|
||||
// vs the high bits we did above
|
||||
// uint8_t mask = (1 << mod) - 1;
|
||||
// note - lookup table results in a nearly 10% performance
|
||||
// improvement in fill* functions
|
||||
static const uint8_t PROGMEM postmask[8] = {0x00, 0x01, 0x03, 0x07,
|
||||
0x0F, 0x1F, 0x3F, 0x7F};
|
||||
uint8_t mask = pgm_read_byte(&postmask[mod]);
|
||||
switch (color) {
|
||||
case SSD1306_WHITE:
|
||||
*pBuf |= mask;
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
*pBuf &= ~mask;
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
*pBuf ^= mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // endif positive height
|
||||
} // endif x in bounds
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Return color of a single pixel in display buffer.
|
||||
@param x
|
||||
Column of display -- 0 at left to (screen width - 1) at right.
|
||||
@param y
|
||||
Row of display -- 0 at top to (screen height -1) at bottom.
|
||||
@return true if pixel is set (usually SSD1306_WHITE, unless display invert
|
||||
mode is enabled), false if clear (SSD1306_BLACK).
|
||||
@note Reads from buffer contents; may not reflect current contents of
|
||||
screen if display() has not been called.
|
||||
*/
|
||||
bool Adafruit_SSD1306::getPixel(int16_t x, int16_t y) {
|
||||
if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
|
||||
// Pixel is in-bounds. Rotate coordinates if needed.
|
||||
switch (getRotation()) {
|
||||
case 1:
|
||||
ssd1306_swap(x, y);
|
||||
x = WIDTH - x - 1;
|
||||
break;
|
||||
case 2:
|
||||
x = WIDTH - x - 1;
|
||||
y = HEIGHT - y - 1;
|
||||
break;
|
||||
case 3:
|
||||
ssd1306_swap(x, y);
|
||||
y = HEIGHT - y - 1;
|
||||
break;
|
||||
}
|
||||
return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7)));
|
||||
}
|
||||
return false; // Pixel out of bounds
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Get base address of display buffer for direct reading or writing.
|
||||
@return Pointer to an unsigned 8-bit array, column-major, columns padded
|
||||
to full byte boundary if needed.
|
||||
*/
|
||||
uint8_t *Adafruit_SSD1306::getBuffer(void) { return buffer; }
|
||||
|
||||
// REFRESH DISPLAY ---------------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Push data currently in RAM to SSD1306 display.
|
||||
@return None (void).
|
||||
@note Drawing operations are not visible until this function is
|
||||
called. Call after each graphics command, or after a whole set
|
||||
of graphics commands, as best needed by one's own application.
|
||||
*/
|
||||
void Adafruit_SSD1306::display(void) {
|
||||
TRANSACTION_START
|
||||
static const uint8_t dlist1[] = {
|
||||
SSD1306_PAGEADDR,
|
||||
0, // Page start address
|
||||
0xFF, // Page end (not really, but works here)
|
||||
SSD1306_COLUMNADDR, 0}; // Column start address
|
||||
ssd1306_commandList(dlist1, sizeof(dlist1));
|
||||
ssd1306_command1(WIDTH - 1); // Column end address
|
||||
|
||||
#if defined(ESP8266)
|
||||
// ESP8266 needs a periodic yield() call to avoid watchdog reset.
|
||||
// With the limited size of SSD1306 displays, and the fast bitrate
|
||||
// being used (1 MHz or more), I think one yield() immediately before
|
||||
// a screen write and one immediately after should cover it. But if
|
||||
// not, if this becomes a problem, yields() might be added in the
|
||||
// 32-byte transfer condition below.
|
||||
yield();
|
||||
#endif
|
||||
uint16_t count = WIDTH * ((HEIGHT + 7) / 8);
|
||||
uint8_t *ptr = buffer;
|
||||
if (wire) { // I2C
|
||||
wire->beginTransmission(i2caddr);
|
||||
WIRE_WRITE((uint8_t)0x40);
|
||||
uint16_t bytesOut = 1;
|
||||
while (count--) {
|
||||
if (bytesOut >= WIRE_MAX) {
|
||||
wire->endTransmission();
|
||||
wire->beginTransmission(i2caddr);
|
||||
WIRE_WRITE((uint8_t)0x40);
|
||||
bytesOut = 1;
|
||||
}
|
||||
WIRE_WRITE(*ptr++);
|
||||
bytesOut++;
|
||||
}
|
||||
wire->endTransmission();
|
||||
} else { // SPI
|
||||
SSD1306_MODE_DATA
|
||||
while (count--)
|
||||
SPIwrite(*ptr++);
|
||||
}
|
||||
TRANSACTION_END
|
||||
#if defined(ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
// SCROLLING FUNCTIONS -----------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Activate a right-handed scroll for all or part of the display.
|
||||
@param start
|
||||
First row.
|
||||
@param stop
|
||||
Last row.
|
||||
@return None (void).
|
||||
*/
|
||||
// To scroll the whole display, run: display.startscrollright(0x00, 0x0F)
|
||||
void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop) {
|
||||
TRANSACTION_START
|
||||
static const uint8_t PROGMEM scrollList1a[] = {
|
||||
SSD1306_RIGHT_HORIZONTAL_SCROLL, 0X00};
|
||||
ssd1306_commandList(scrollList1a, sizeof(scrollList1a));
|
||||
ssd1306_command1(start);
|
||||
ssd1306_command1(0X00);
|
||||
ssd1306_command1(stop);
|
||||
static const uint8_t PROGMEM scrollList1b[] = {0X00, 0XFF,
|
||||
SSD1306_ACTIVATE_SCROLL};
|
||||
ssd1306_commandList(scrollList1b, sizeof(scrollList1b));
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Activate a left-handed scroll for all or part of the display.
|
||||
@param start
|
||||
First row.
|
||||
@param stop
|
||||
Last row.
|
||||
@return None (void).
|
||||
*/
|
||||
// To scroll the whole display, run: display.startscrollleft(0x00, 0x0F)
|
||||
void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop) {
|
||||
TRANSACTION_START
|
||||
static const uint8_t PROGMEM scrollList2a[] = {SSD1306_LEFT_HORIZONTAL_SCROLL,
|
||||
0X00};
|
||||
ssd1306_commandList(scrollList2a, sizeof(scrollList2a));
|
||||
ssd1306_command1(start);
|
||||
ssd1306_command1(0X00);
|
||||
ssd1306_command1(stop);
|
||||
static const uint8_t PROGMEM scrollList2b[] = {0X00, 0XFF,
|
||||
SSD1306_ACTIVATE_SCROLL};
|
||||
ssd1306_commandList(scrollList2b, sizeof(scrollList2b));
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Activate a diagonal scroll for all or part of the display.
|
||||
@param start
|
||||
First row.
|
||||
@param stop
|
||||
Last row.
|
||||
@return None (void).
|
||||
*/
|
||||
// display.startscrolldiagright(0x00, 0x0F)
|
||||
void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop) {
|
||||
TRANSACTION_START
|
||||
static const uint8_t PROGMEM scrollList3a[] = {
|
||||
SSD1306_SET_VERTICAL_SCROLL_AREA, 0X00};
|
||||
ssd1306_commandList(scrollList3a, sizeof(scrollList3a));
|
||||
ssd1306_command1(HEIGHT);
|
||||
static const uint8_t PROGMEM scrollList3b[] = {
|
||||
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL, 0X00};
|
||||
ssd1306_commandList(scrollList3b, sizeof(scrollList3b));
|
||||
ssd1306_command1(start);
|
||||
ssd1306_command1(0X00);
|
||||
ssd1306_command1(stop);
|
||||
static const uint8_t PROGMEM scrollList3c[] = {0X01, SSD1306_ACTIVATE_SCROLL};
|
||||
ssd1306_commandList(scrollList3c, sizeof(scrollList3c));
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Activate alternate diagonal scroll for all or part of the display.
|
||||
@param start
|
||||
First row.
|
||||
@param stop
|
||||
Last row.
|
||||
@return None (void).
|
||||
*/
|
||||
// To scroll the whole display, run: display.startscrolldiagleft(0x00, 0x0F)
|
||||
void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop) {
|
||||
TRANSACTION_START
|
||||
static const uint8_t PROGMEM scrollList4a[] = {
|
||||
SSD1306_SET_VERTICAL_SCROLL_AREA, 0X00};
|
||||
ssd1306_commandList(scrollList4a, sizeof(scrollList4a));
|
||||
ssd1306_command1(HEIGHT);
|
||||
static const uint8_t PROGMEM scrollList4b[] = {
|
||||
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL, 0X00};
|
||||
ssd1306_commandList(scrollList4b, sizeof(scrollList4b));
|
||||
ssd1306_command1(start);
|
||||
ssd1306_command1(0X00);
|
||||
ssd1306_command1(stop);
|
||||
static const uint8_t PROGMEM scrollList4c[] = {0X01, SSD1306_ACTIVATE_SCROLL};
|
||||
ssd1306_commandList(scrollList4c, sizeof(scrollList4c));
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Cease a previously-begun scrolling action.
|
||||
@return None (void).
|
||||
*/
|
||||
void Adafruit_SSD1306::stopscroll(void) {
|
||||
TRANSACTION_START
|
||||
ssd1306_command1(SSD1306_DEACTIVATE_SCROLL);
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
// OTHER HARDWARE SETTINGS -------------------------------------------------
|
||||
|
||||
/*!
|
||||
@brief Enable or disable display invert mode (white-on-black vs
|
||||
black-on-white).
|
||||
@param i
|
||||
If true, switch to invert mode (black-on-white), else normal
|
||||
mode (white-on-black).
|
||||
@return None (void).
|
||||
@note This has an immediate effect on the display, no need to call the
|
||||
display() function -- buffer contents are not changed, rather a
|
||||
different pixel mode of the display hardware is used. When
|
||||
enabled, drawing SSD1306_BLACK (value 0) pixels will actually draw
|
||||
white, SSD1306_WHITE (value 1) will draw black.
|
||||
*/
|
||||
void Adafruit_SSD1306::invertDisplay(bool i) {
|
||||
TRANSACTION_START
|
||||
ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
|
||||
TRANSACTION_END
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Dim the display.
|
||||
@param dim
|
||||
true to enable lower brightness mode, false for full brightness.
|
||||
@return None (void).
|
||||
@note This has an immediate effect on the display, no need to call the
|
||||
display() function -- buffer contents are not changed.
|
||||
*/
|
||||
void Adafruit_SSD1306::dim(bool dim) {
|
||||
// the range of contrast to too small to be really useful
|
||||
// it is useful to dim the display
|
||||
TRANSACTION_START
|
||||
ssd1306_command1(SSD1306_SETCONTRAST);
|
||||
ssd1306_command1(dim ? 0 : contrast);
|
||||
TRANSACTION_END
|
||||
}
|
||||
167
Libraries/Adafruit_SSD1306.h
Normal file
167
Libraries/Adafruit_SSD1306.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*!
|
||||
* @file Adafruit_SSD1306.h
|
||||
*
|
||||
* This is part of for Adafruit's SSD1306 library for monochrome
|
||||
* OLED displays: http://www.adafruit.com/category/63_98
|
||||
*
|
||||
* These displays use I2C or SPI to communicate. I2C requires 2 pins
|
||||
* (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
|
||||
* select, data/command) and optionally a reset pin. Hardware SPI or
|
||||
* 'bitbang' software SPI are both supported.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Limor Fried/Ladyada for Adafruit Industries, with
|
||||
* contributions from the open source community.
|
||||
*
|
||||
* BSD license, all text above, and the splash screen header file,
|
||||
* must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _Adafruit_SSD1306_H_
|
||||
#define _Adafruit_SSD1306_H_
|
||||
|
||||
// ONE of the following three lines must be #defined:
|
||||
//#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen
|
||||
#define SSD1306_128_32 ///< DEPRECATED: old way to specify 128x32 screen
|
||||
//#define SSD1306_96_16 ///< DEPRECATED: old way to specify 96x16 screen
|
||||
// This establishes the screen dimensions in old Adafruit_SSD1306 sketches
|
||||
// (NEW CODE SHOULD IGNORE THIS, USE THE CONSTRUCTORS THAT ACCEPT WIDTH
|
||||
// AND HEIGHT ARGUMENTS).
|
||||
|
||||
// Uncomment to disable Adafruit splash logo
|
||||
//#define SSD1306_NO_SPLASH
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Wire.h>
|
||||
|
||||
/// The following "raw" color names are kept for backwards client compatability
|
||||
/// They can be disabled by predefining this macro before including the Adafruit
|
||||
/// header client code will then need to be modified to use the scoped enum
|
||||
/// values directly
|
||||
#ifndef NO_ADAFRUIT_SSD1306_COLOR_COMPATIBILITY
|
||||
#define BLACK SSD1306_BLACK ///< Draw 'off' pixels
|
||||
#define WHITE SSD1306_WHITE ///< Draw 'on' pixels
|
||||
#define INVERSE SSD1306_INVERSE ///< Invert pixels
|
||||
#endif
|
||||
/// fit into the SSD1306_ naming scheme
|
||||
#define SSD1306_BLACK 0 ///< Draw 'off' pixels
|
||||
#define SSD1306_WHITE 1 ///< Draw 'on' pixels
|
||||
#define SSD1306_INVERSE 2 ///< Invert pixels
|
||||
|
||||
#define SSD1306_MEMORYMODE 0x20 ///< See datasheet
|
||||
#define SSD1306_COLUMNADDR 0x21 ///< See datasheet
|
||||
#define SSD1306_PAGEADDR 0x22 ///< See datasheet
|
||||
#define SSD1306_SETCONTRAST 0x81 ///< See datasheet
|
||||
#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet
|
||||
#define SSD1306_SEGREMAP 0xA0 ///< See datasheet
|
||||
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet
|
||||
#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used
|
||||
#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet
|
||||
#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet
|
||||
#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet
|
||||
#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet
|
||||
#define SSD1306_DISPLAYON 0xAF ///< See datasheet
|
||||
#define SSD1306_COMSCANINC 0xC0 ///< Not currently used
|
||||
#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet
|
||||
#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet
|
||||
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet
|
||||
#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet
|
||||
#define SSD1306_SETCOMPINS 0xDA ///< See datasheet
|
||||
#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet
|
||||
|
||||
#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used
|
||||
#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used
|
||||
#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet
|
||||
|
||||
#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source
|
||||
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V
|
||||
|
||||
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll
|
||||
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll
|
||||
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll
|
||||
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll
|
||||
#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll
|
||||
#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll
|
||||
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range
|
||||
|
||||
// Deprecated size stuff for backwards compatibility with old sketches
|
||||
#if defined SSD1306_128_64
|
||||
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_64 defined
|
||||
#define SSD1306_LCDHEIGHT 64 ///< DEPRECATED: height w/SSD1306_128_64 defined
|
||||
#endif
|
||||
#if defined SSD1306_128_32
|
||||
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_32 defined
|
||||
#define SSD1306_LCDHEIGHT 32 ///< DEPRECATED: height w/SSD1306_128_32 defined
|
||||
#endif
|
||||
#if defined SSD1306_96_16
|
||||
#define SSD1306_LCDWIDTH 96 ///< DEPRECATED: width w/SSD1306_96_16 defined
|
||||
#define SSD1306_LCDHEIGHT 16 ///< DEPRECATED: height w/SSD1306_96_16 defined
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief Class that stores state and functions for interacting with
|
||||
SSD1306 OLED displays.
|
||||
*/
|
||||
class Adafruit_SSD1306 : public Adafruit_GFX {
|
||||
public:
|
||||
// NEW CONSTRUCTORS -- recommended for new projects
|
||||
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
|
||||
int8_t rst_pin = -1, uint32_t clkDuring = 400000UL,
|
||||
uint32_t clkAfter = 100000UL);
|
||||
Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin,
|
||||
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
|
||||
Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi, int8_t dc_pin,
|
||||
int8_t rst_pin, int8_t cs_pin, uint32_t bitrate = 8000000UL);
|
||||
|
||||
~Adafruit_SSD1306(void);
|
||||
|
||||
bool begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = 0,
|
||||
bool reset = true, bool periphBegin = true);
|
||||
void display(void);
|
||||
void clearDisplay(void);
|
||||
void invertDisplay(bool i);
|
||||
void dim(bool dim);
|
||||
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||
void startscrollright(uint8_t start, uint8_t stop);
|
||||
void startscrollleft(uint8_t start, uint8_t stop);
|
||||
void startscrolldiagright(uint8_t start, uint8_t stop);
|
||||
void startscrolldiagleft(uint8_t start, uint8_t stop);
|
||||
void stopscroll(void);
|
||||
void ssd1306_command(uint8_t c);
|
||||
bool getPixel(int16_t x, int16_t y);
|
||||
uint8_t *getBuffer(void);
|
||||
|
||||
protected:
|
||||
void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||
void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||
void ssd1306_command1(uint8_t c);
|
||||
void ssd1306_commandList(const uint8_t *c, uint8_t n);
|
||||
|
||||
SPIClass *spi; ///< Initialized during construction when using SPI. See
|
||||
///< SPI.cpp, SPI.h
|
||||
TwoWire *wire; ///< Initialized during construction when using I2C. See
|
||||
///< Wire.cpp, Wire.h
|
||||
uint8_t *buffer; ///< Buffer data used for display buffer. Allocated when
|
||||
///< begin method is called.
|
||||
int8_t i2caddr; ///< I2C address initialized when begin method is called.
|
||||
int8_t vccstate; ///< VCC selection, set by begin method.
|
||||
int8_t page_end; ///< not used
|
||||
int8_t mosiPin; ///< (Master Out Slave In) set when using SPI set during
|
||||
///< construction.
|
||||
int8_t clkPin; ///< (Clock Pin) set when using SPI set during construction.
|
||||
int8_t dcPin; ///< (Data Pin) set when using SPI set during construction.
|
||||
int8_t
|
||||
csPin; ///< (Chip Select Pin) set when using SPI set during construction.
|
||||
int8_t rstPin; ///< Display reset pin assignment. Set during construction.
|
||||
|
||||
uint32_t wireClk; ///< Wire speed for SSD1306 transfers
|
||||
uint32_t restoreClk; ///< Wire speed following SSD1306 transfers
|
||||
uint8_t contrast; ///< normal contrast setting for this device
|
||||
};
|
||||
|
||||
endif // _Adafruit_SSD1306_H_
|
||||
680
Libraries/NetworkClient.cpp
Normal file
680
Libraries/NetworkClient.cpp
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
Client.h - Client class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "NetworkClient.h"
|
||||
#include "NetworkManager.h"
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) ((((__const uint32_t *)(a))[0] == 0) && (((__const uint32_t *)(a))[1] == 0) && (((__const uint32_t *)(a))[2] == htonl(0xffff)))
|
||||
|
||||
#define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000)
|
||||
#define WIFI_CLIENT_MAX_WRITE_RETRY (10)
|
||||
#define WIFI_CLIENT_SELECT_TIMEOUT_US (1000000)
|
||||
#define WIFI_CLIENT_FLUSH_BUFFER_SIZE (1024)
|
||||
|
||||
#undef connect
|
||||
#undef write
|
||||
#undef read
|
||||
|
||||
class NetworkClientRxBuffer {
|
||||
private:
|
||||
size_t _size;
|
||||
uint8_t *_buffer;
|
||||
size_t _pos;
|
||||
size_t _fill;
|
||||
int _fd;
|
||||
bool _failed;
|
||||
|
||||
size_t r_available() {
|
||||
if (_fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
int count;
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
int res = lwip_ioctl(_fd, FIONREAD, &count);
|
||||
#else
|
||||
int res = lwip_ioctl_r(_fd, FIONREAD, &count);
|
||||
#endif
|
||||
if (res < 0) {
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t fillBuffer() {
|
||||
if (!_buffer) {
|
||||
_buffer = (uint8_t *)malloc(_size);
|
||||
if (!_buffer) {
|
||||
log_e("Not enough memory to allocate buffer");
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_fill && _pos == _fill) {
|
||||
_fill = 0;
|
||||
_pos = 0;
|
||||
}
|
||||
if (!_buffer || _size <= _fill || !r_available()) {
|
||||
return 0;
|
||||
}
|
||||
int res = recv(_fd, _buffer + _fill, _size - _fill, MSG_DONTWAIT);
|
||||
if (res < 0) {
|
||||
if (errno != EWOULDBLOCK) {
|
||||
_failed = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
_fill += res;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
NetworkClientRxBuffer(int fd, size_t size = 1436) : _size(size), _buffer(NULL), _pos(0), _fill(0), _fd(fd), _failed(false) {
|
||||
//_buffer = (uint8_t *)malloc(_size);
|
||||
}
|
||||
|
||||
~NetworkClientRxBuffer() {
|
||||
free(_buffer);
|
||||
}
|
||||
|
||||
bool failed() {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
int read(uint8_t *dst, size_t len) {
|
||||
if (!dst || !len || (_pos == _fill && !fillBuffer())) {
|
||||
return _failed ? -1 : 0;
|
||||
}
|
||||
size_t a = _fill - _pos;
|
||||
if (len <= a || ((len - a) <= (_size - _fill) && fillBuffer() >= (len - a))) {
|
||||
if (len == 1) {
|
||||
*dst = _buffer[_pos];
|
||||
} else {
|
||||
memcpy(dst, _buffer + _pos, len);
|
||||
}
|
||||
_pos += len;
|
||||
return len;
|
||||
}
|
||||
size_t left = len;
|
||||
size_t toRead = a;
|
||||
uint8_t *buf = dst;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
while (left) {
|
||||
if (!fillBuffer()) {
|
||||
return len - left;
|
||||
}
|
||||
a = _fill - _pos;
|
||||
toRead = (a > left) ? left : a;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int peek() {
|
||||
if (_pos == _fill && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
return _buffer[_pos];
|
||||
}
|
||||
|
||||
size_t available() {
|
||||
return _fill - _pos + r_available();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (r_available()) {
|
||||
_pos = _fill;
|
||||
while (fillBuffer()) {
|
||||
_pos = _fill;
|
||||
}
|
||||
}
|
||||
_pos = 0;
|
||||
_fill = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class NetworkClientSocketHandle {
|
||||
private:
|
||||
int sockfd;
|
||||
|
||||
public:
|
||||
NetworkClientSocketHandle(int fd) : sockfd(fd) {}
|
||||
|
||||
~NetworkClientSocketHandle() {
|
||||
close();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (sockfd >= 0) {
|
||||
::close(sockfd);
|
||||
sockfd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fd() {
|
||||
return sockfd;
|
||||
}
|
||||
};
|
||||
|
||||
NetworkClient::NetworkClient() : _rxBuffer(nullptr), _connected(false), _sse(false), _timeout(WIFI_CLIENT_DEF_CONN_TIMEOUT_MS), next(NULL) {}
|
||||
|
||||
NetworkClient::NetworkClient(int fd) : _connected(true), _timeout(WIFI_CLIENT_DEF_CONN_TIMEOUT_MS), next(NULL) {
|
||||
clientSocketHandle.reset(new NetworkClientSocketHandle(fd));
|
||||
_rxBuffer.reset(new NetworkClientRxBuffer(fd));
|
||||
}
|
||||
|
||||
NetworkClient::~NetworkClient() {}
|
||||
|
||||
void NetworkClient::stop() {
|
||||
if (clientSocketHandle) {
|
||||
clientSocketHandle->close();
|
||||
}
|
||||
clientSocketHandle = NULL;
|
||||
_rxBuffer = NULL;
|
||||
_connected = false;
|
||||
_lastReadTimeout = 0;
|
||||
_lastWriteTimeout = 0;
|
||||
}
|
||||
|
||||
int NetworkClient::connect(IPAddress ip, uint16_t port) {
|
||||
return connect(ip, port, _timeout);
|
||||
}
|
||||
|
||||
int NetworkClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms) {
|
||||
struct sockaddr_storage serveraddr = {};
|
||||
_timeout = timeout_ms;
|
||||
int sockfd = -1;
|
||||
|
||||
if (ip.type() == IPv6) {
|
||||
struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
|
||||
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
tmpaddr->sin6_family = AF_INET6;
|
||||
memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16);
|
||||
tmpaddr->sin6_port = htons(port);
|
||||
tmpaddr->sin6_scope_id = ip.zone();
|
||||
} else {
|
||||
struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
tmpaddr->sin_family = AF_INET;
|
||||
tmpaddr->sin_addr.s_addr = ip;
|
||||
tmpaddr->sin_port = htons(port);
|
||||
}
|
||||
if (sockfd < 0) {
|
||||
log_e("socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sockfd, &fdset);
|
||||
tv.tv_sec = _timeout / 1000;
|
||||
tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
int res = lwip_connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
|
||||
#else
|
||||
int res = lwip_connect_r(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
|
||||
#endif
|
||||
if (res < 0 && errno != EINPROGRESS) {
|
||||
log_e("connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = select(sockfd + 1, nullptr, &fdset, nullptr, _timeout < 0 ? nullptr : &tv);
|
||||
if (res < 0) {
|
||||
log_e("select on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
} else if (res == 0) {
|
||||
log_i("select returned due to timeout %d ms for fd %d", _timeout, sockfd);
|
||||
close(sockfd);
|
||||
return 0;
|
||||
} else {
|
||||
int sockerr;
|
||||
socklen_t len = (socklen_t)sizeof(int);
|
||||
res = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sockerr, &len);
|
||||
|
||||
if (res < 0) {
|
||||
log_e("getsockopt on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sockerr != 0) {
|
||||
log_e("socket error on fd %d, errno: %d, \"%s\"", sockfd, sockerr, strerror(sockerr));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define ROE_WIFICLIENT(x, msg) \
|
||||
{ \
|
||||
if (((x) < 0)) { \
|
||||
log_e("Setsockopt '" msg "'' on fd %d failed. errno: %d, \"%s\"", sockfd, errno, strerror(errno)); \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
ROE_WIFICLIENT(setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), "SO_SNDTIMEO");
|
||||
ROE_WIFICLIENT(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), "SO_RCVTIMEO");
|
||||
|
||||
// These are also set in NetworkClientSecure, should be set here too?
|
||||
//ROE_WIFICLIENT(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY");
|
||||
//ROE_WIFICLIENT (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE");
|
||||
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) & (~O_NONBLOCK));
|
||||
clientSocketHandle.reset(new NetworkClientSocketHandle(sockfd));
|
||||
_rxBuffer.reset(new NetworkClientRxBuffer(sockfd));
|
||||
|
||||
_connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NetworkClient::connect(const char *host, uint16_t port) {
|
||||
return connect(host, port, _timeout);
|
||||
}
|
||||
|
||||
int NetworkClient::connect(const char *host, uint16_t port, int32_t timeout_ms) {
|
||||
IPAddress srv((uint32_t)0);
|
||||
if (!Network.hostByName(host, srv)) {
|
||||
return 0;
|
||||
}
|
||||
return connect(srv, port, timeout_ms);
|
||||
}
|
||||
|
||||
int NetworkClient::setSocketOption(int option, char *value, size_t len) {
|
||||
return setSocketOption(SOL_SOCKET, option, (const void *)value, len);
|
||||
}
|
||||
|
||||
int NetworkClient::setSocketOption(int level, int option, const void *value, size_t len) {
|
||||
int res = setsockopt(fd(), level, option, value, len);
|
||||
if (res < 0) {
|
||||
log_e("fail on %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::getSocketOption(int level, int option, const void *value, size_t size) {
|
||||
int res = getsockopt(fd(), level, option, (char *)value, (socklen_t *)&size);
|
||||
if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::setOption(int option, int *value) {
|
||||
return setSocketOption(IPPROTO_TCP, option, (const void *)value, sizeof(int));
|
||||
}
|
||||
|
||||
int NetworkClient::getOption(int option, int *value) {
|
||||
socklen_t size = sizeof(int);
|
||||
int res = getsockopt(fd(), IPPROTO_TCP, option, (char *)value, &size);
|
||||
if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NetworkClient::setConnectionTimeout(uint32_t milliseconds) {
|
||||
_timeout = milliseconds;
|
||||
}
|
||||
|
||||
int NetworkClient::setNoDelay(bool nodelay) {
|
||||
int flag = nodelay;
|
||||
return setOption(TCP_NODELAY, &flag);
|
||||
}
|
||||
|
||||
bool NetworkClient::getNoDelay() {
|
||||
int flag = 0;
|
||||
getOption(TCP_NODELAY, &flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
|
||||
int NetworkClient::read() {
|
||||
uint8_t data = 0;
|
||||
int res = read(&data, 1);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) { // No data available.
|
||||
return -1;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void NetworkClient::flush() {
|
||||
clear();
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(const uint8_t *buf, size_t size) {
|
||||
int res = 0;
|
||||
int retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
||||
int socketFileDescriptor = fd();
|
||||
size_t totalBytesSent = 0;
|
||||
size_t bytesRemaining = size;
|
||||
|
||||
if (!_connected || (socketFileDescriptor < 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (retry) {
|
||||
//use select to make sure the socket is ready for writing
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&set); // empties the set
|
||||
FD_SET(socketFileDescriptor, &set); // adds FD to the set
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = WIFI_CLIENT_SELECT_TIMEOUT_US;
|
||||
retry--;
|
||||
|
||||
if (_lastWriteTimeout != _timeout) {
|
||||
if (fd() >= 0) {
|
||||
struct timeval timeout_tv;
|
||||
timeout_tv.tv_sec = _timeout / 1000;
|
||||
timeout_tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
if (setSocketOption(SO_SNDTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) {
|
||||
_lastWriteTimeout = _timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FD_ISSET(socketFileDescriptor, &set)) {
|
||||
res = send(socketFileDescriptor, (void *)buf, bytesRemaining, MSG_DONTWAIT);
|
||||
if (res > 0) {
|
||||
totalBytesSent += res;
|
||||
if (totalBytesSent >= size) {
|
||||
//completed successfully
|
||||
retry = 0;
|
||||
} else {
|
||||
buf += res;
|
||||
bytesRemaining -= res;
|
||||
retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
||||
}
|
||||
} else if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
if (errno != EAGAIN) {
|
||||
//if resource was busy, can try again, otherwise give up
|
||||
stop();
|
||||
res = 0;
|
||||
retry = 0;
|
||||
}
|
||||
} else {
|
||||
// Try again
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalBytesSent;
|
||||
}
|
||||
|
||||
size_t NetworkClient::write_P(PGM_P buf, size_t size) {
|
||||
return write(buf, size);
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(Stream &stream) {
|
||||
uint8_t *buf = (uint8_t *)malloc(1360);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
size_t toRead = 0, toWrite = 0, written = 0;
|
||||
size_t available = stream.available();
|
||||
while (available) {
|
||||
toRead = (available > 1360) ? 1360 : available;
|
||||
toWrite = stream.readBytes(buf, toRead);
|
||||
written += write(buf, toWrite);
|
||||
available = stream.available();
|
||||
}
|
||||
free(buf);
|
||||
return written;
|
||||
}
|
||||
|
||||
int NetworkClient::read(uint8_t *buf, size_t size) {
|
||||
if (_lastReadTimeout != _timeout) {
|
||||
if (fd() >= 0) {
|
||||
struct timeval timeout_tv;
|
||||
timeout_tv.tv_sec = _timeout / 1000;
|
||||
timeout_tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
if (setSocketOption(SO_RCVTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) {
|
||||
_lastReadTimeout = _timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int res = -1;
|
||||
if (_rxBuffer) {
|
||||
res = _rxBuffer->read(buf, size);
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t NetworkClient::readBytes(char *buffer, size_t length) {
|
||||
size_t left = length, sofar = 0;
|
||||
int r = 0, to = millis() + getTimeout();
|
||||
while (left) {
|
||||
r = read((uint8_t *)buffer + sofar, left);
|
||||
if (r < 0) {
|
||||
// Error has occurred
|
||||
break;
|
||||
}
|
||||
if (r > 0) {
|
||||
// We got some data
|
||||
left -= r;
|
||||
sofar += r;
|
||||
to = millis() + getTimeout();
|
||||
} else {
|
||||
// We got no data
|
||||
if (millis() >= to) {
|
||||
// We have waited for data enough
|
||||
log_w("Timeout waiting for data on fd %d", fd());
|
||||
break;
|
||||
}
|
||||
// Allow other tasks to run
|
||||
delay(2);
|
||||
}
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
int NetworkClient::peek() {
|
||||
int res = -1;
|
||||
if (fd() >= 0 && _rxBuffer) {
|
||||
res = _rxBuffer->peek();
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::available() {
|
||||
if (fd() < 0 || !_rxBuffer) {
|
||||
return 0;
|
||||
}
|
||||
int res = _rxBuffer->available();
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NetworkClient::clear() {
|
||||
if (_rxBuffer != nullptr) {
|
||||
_rxBuffer->clear();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NetworkClient::connected() {
|
||||
if (fd() == -1 && _connected) {
|
||||
stop();
|
||||
}
|
||||
if (_connected) {
|
||||
uint8_t dummy;
|
||||
int res = recv(fd(), &dummy, 0, MSG_DONTWAIT);
|
||||
// avoid unused var warning by gcc
|
||||
(void)res;
|
||||
// recv only sets errno if res is <= 0
|
||||
if (res <= 0) {
|
||||
switch (errno) {
|
||||
case EWOULDBLOCK:
|
||||
case ENOENT: //caused by vfs
|
||||
_connected = true;
|
||||
break;
|
||||
case ENOTCONN:
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
case ECONNREFUSED:
|
||||
case ECONNABORTED:
|
||||
_connected = false;
|
||||
log_d("Disconnected: RES: %d, ERR: %d", res, errno);
|
||||
break;
|
||||
default:
|
||||
log_i("Unexpected: RES: %d, ERR: %d", res, errno);
|
||||
_connected = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_connected = true;
|
||||
}
|
||||
}
|
||||
return _connected;
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::remoteIP(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getpeername(fd, (struct sockaddr *)&addr, &len);
|
||||
|
||||
// IPv4 socket, old way
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
// IPv6, but it might be IPv4 mapped address
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||
return IPAddress(IPv4, (uint8_t *)saddr6->sin6_addr.s6_addr + IPADDRESS_V4_BYTES_INDEX);
|
||||
} else {
|
||||
return IPAddress(IPv6, (uint8_t *)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||
}
|
||||
}
|
||||
log_e("NetworkClient::remoteIP Not AF_INET or AF_INET6?");
|
||||
return (IPAddress(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::remotePort(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getpeername(fd, (struct sockaddr *)&addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::remoteIP() const {
|
||||
return remoteIP(fd());
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::remotePort() const {
|
||||
return remotePort(fd());
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::localIP(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getsockname(fd, (struct sockaddr *)&addr, &len);
|
||||
|
||||
// IPv4 socket, old way
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
// IPv6, but it might be IPv4 mapped address
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||
return IPAddress(IPv4, (uint8_t *)saddr6->sin6_addr.s6_addr + IPADDRESS_V4_BYTES_INDEX);
|
||||
} else {
|
||||
return IPAddress(IPv6, (uint8_t *)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||
}
|
||||
}
|
||||
log_e("NetworkClient::localIP Not AF_INET or AF_INET6?");
|
||||
return (IPAddress(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::localPort(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getsockname(fd, (struct sockaddr *)&addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::localIP() const {
|
||||
return localIP(fd());
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::localPort() const {
|
||||
return localPort(fd());
|
||||
}
|
||||
|
||||
bool NetworkClient::operator==(const NetworkClient &rhs) {
|
||||
return clientSocketHandle == rhs.clientSocketHandle && remotePort() == rhs.remotePort() && remoteIP() == rhs.remoteIP();
|
||||
}
|
||||
|
||||
int NetworkClient::fd() const {
|
||||
if (clientSocketHandle == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
return clientSocketHandle->fd();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkClient::setSSE(bool sse) {
|
||||
_sse = sse;
|
||||
}
|
||||
|
||||
bool NetworkClient::isSSE() {
|
||||
return _sse;
|
||||
}
|
||||
111
Libraries/NetworkClient.h
Normal file
111
Libraries/NetworkClient.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Client.h - Base class that provides Client
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Client.h"
|
||||
#include <memory>
|
||||
|
||||
class NetworkClientSocketHandle;
|
||||
class NetworkClientRxBuffer;
|
||||
|
||||
class ESPLwIPClient : public Client {
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0;
|
||||
virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0;
|
||||
virtual void setConnectionTimeout(uint32_t milliseconds) = 0;
|
||||
};
|
||||
|
||||
class NetworkClient : public ESPLwIPClient {
|
||||
protected:
|
||||
std::shared_ptr<NetworkClientSocketHandle> clientSocketHandle;
|
||||
std::shared_ptr<NetworkClientRxBuffer> _rxBuffer;
|
||||
bool _connected;
|
||||
bool _sse;
|
||||
int _timeout;
|
||||
int _lastWriteTimeout;
|
||||
int _lastReadTimeout;
|
||||
|
||||
public:
|
||||
NetworkClient *next;
|
||||
NetworkClient();
|
||||
NetworkClient(int fd);
|
||||
~NetworkClient();
|
||||
int connect(IPAddress ip, uint16_t port);
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout_ms);
|
||||
int connect(const char *host, uint16_t port);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout_ms);
|
||||
size_t write(uint8_t data);
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
size_t write_P(PGM_P buf, size_t size);
|
||||
size_t write(Stream &stream);
|
||||
[[deprecated("Use clear() instead.")]]
|
||||
void flush(); // Print::flush tx
|
||||
int available();
|
||||
int read();
|
||||
int read(uint8_t *buf, size_t size);
|
||||
size_t readBytes(char *buffer, size_t length);
|
||||
size_t readBytes(uint8_t *buffer, size_t length) {
|
||||
return readBytes((char *)buffer, length);
|
||||
}
|
||||
int peek();
|
||||
void clear(); // clear rx
|
||||
void stop();
|
||||
uint8_t connected();
|
||||
void setSSE(bool sse);
|
||||
bool isSSE();
|
||||
|
||||
operator bool() {
|
||||
return connected();
|
||||
}
|
||||
bool operator==(const bool value) {
|
||||
return bool() == value;
|
||||
}
|
||||
bool operator!=(const bool value) {
|
||||
return bool() != value;
|
||||
}
|
||||
bool operator==(const NetworkClient &);
|
||||
bool operator!=(const NetworkClient &rhs) {
|
||||
return !this->operator==(rhs);
|
||||
};
|
||||
|
||||
virtual int fd() const;
|
||||
|
||||
int setSocketOption(int option, char *value, size_t len);
|
||||
int setSocketOption(int level, int option, const void *value, size_t len);
|
||||
int getSocketOption(int level, int option, const void *value, size_t size);
|
||||
int setOption(int option, int *value);
|
||||
int getOption(int option, int *value);
|
||||
void setConnectionTimeout(uint32_t milliseconds);
|
||||
int setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
|
||||
IPAddress remoteIP() const;
|
||||
IPAddress remoteIP(int fd) const;
|
||||
uint16_t remotePort() const;
|
||||
uint16_t remotePort(int fd) const;
|
||||
IPAddress localIP() const;
|
||||
IPAddress localIP(int fd) const;
|
||||
uint16_t localPort() const;
|
||||
uint16_t localPort(int fd) const;
|
||||
|
||||
//friend class NetworkServer;
|
||||
using Print::write;
|
||||
};
|
||||
828
Libraries/NimBLE-Arduino_modified/NimBLEAdvertisedDevice.cpp
Normal file
828
Libraries/NimBLE-Arduino_modified/NimBLEAdvertisedDevice.cpp
Normal file
|
|
@ -0,0 +1,828 @@
|
|||
/*
|
||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
|
||||
# include "NimBLEDevice.h"
|
||||
# include "NimBLEAdvertisedDevice.h"
|
||||
# include "NimBLEUtils.h"
|
||||
# include "NimBLELog.h"
|
||||
|
||||
# include <climits>
|
||||
|
||||
static const char* LOG_TAG = "NimBLEAdvertisedDevice";
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param [in] event The advertisement event data.
|
||||
*/
|
||||
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType)
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
: m_address{event->ext_disc.addr},
|
||||
m_advType{eventType},
|
||||
m_rssi{event->ext_disc.rssi},
|
||||
m_callbackSent{0},
|
||||
m_advLength{event->ext_disc.length_data},
|
||||
m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)},
|
||||
m_sid{event->ext_disc.sid},
|
||||
m_primPhy{event->ext_disc.prim_phy},
|
||||
m_secPhy{event->ext_disc.sec_phy},
|
||||
m_periodicItvl{event->ext_disc.periodic_adv_itvl},
|
||||
m_payload(event->ext_disc.data, event->ext_disc.data + event->ext_disc.length_data) {
|
||||
# else
|
||||
: m_address{event->disc.addr},
|
||||
m_advType{eventType},
|
||||
m_rssi{event->disc.rssi},
|
||||
m_callbackSent{0},
|
||||
m_advLength{event->disc.length_data},
|
||||
m_payload(event->disc.data, event->disc.data + event->disc.length_data) {
|
||||
# endif
|
||||
m_payload.reserve(256);
|
||||
} // NimBLEAdvertisedDevice
|
||||
|
||||
/**
|
||||
* @brief Update the advertisement data.
|
||||
* @param [in] event The advertisement event data.
|
||||
*/
|
||||
void NimBLEAdvertisedDevice::update(const ble_gap_event* event, uint8_t eventType) {
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
const auto& disc = event->ext_disc;
|
||||
m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
||||
# else
|
||||
const auto& disc = event->disc;
|
||||
# endif
|
||||
|
||||
m_rssi = disc.rssi;
|
||||
if (eventType == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP && isLegacyAdvertisement()) {
|
||||
m_payload.insert(m_payload.end(), disc.data, disc.data + disc.length_data);
|
||||
return;
|
||||
}
|
||||
m_advLength = disc.length_data;
|
||||
m_payload.assign(disc.data, disc.data + disc.length_data);
|
||||
m_callbackSent = 0; // new data, reset callback sent flag
|
||||
} // update
|
||||
|
||||
/**
|
||||
* @brief Get the address of the advertising device.
|
||||
* @return The address of the advertised device.
|
||||
*/
|
||||
const NimBLEAddress& NimBLEAdvertisedDevice::getAddress() const {
|
||||
return m_address;
|
||||
} // getAddress
|
||||
|
||||
/**
|
||||
* @brief Get the advertisement type.
|
||||
* @return The advertising type the device is reporting:
|
||||
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
|
||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertising - high duty cycle
|
||||
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
|
||||
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertising - not connectable
|
||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getAdvType() const {
|
||||
return m_advType;
|
||||
} // getAdvType
|
||||
|
||||
/**
|
||||
* @brief Get the advertisement flags.
|
||||
* @return The advertisement flags, a bitmask of:
|
||||
* BLE_HS_ADV_F_DISC_LTD (0x01) - limited discoverability
|
||||
* BLE_HS_ADV_F_DISC_GEN (0x02) - general discoverability
|
||||
* BLE_HS_ADV_F_BREDR_UNSUP - BR/EDR not supported
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getAdvFlags() const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_FLAGS, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_FLAGS_LEN + 1) {
|
||||
return *field->value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // getAdvFlags
|
||||
|
||||
/**
|
||||
* @brief Get the appearance.
|
||||
*
|
||||
* A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user
|
||||
* typically in the form of an icon.
|
||||
*
|
||||
* @return The appearance of the advertised device.
|
||||
*/
|
||||
uint16_t NimBLEAdvertisedDevice::getAppearance() const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) {
|
||||
return *field->value | *(field->value + 1) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // getAppearance
|
||||
|
||||
/**
|
||||
* @brief Get the advertisement interval.
|
||||
* @return The advertisement interval in 0.625ms units.
|
||||
*/
|
||||
uint16_t NimBLEAdvertisedDevice::getAdvInterval() const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) {
|
||||
return *field->value | *(field->value + 1) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // getAdvInterval
|
||||
|
||||
/**
|
||||
* @brief Get the preferred min connection interval.
|
||||
* @return The preferred min connection interval in 1.25ms units.
|
||||
*/
|
||||
uint16_t NimBLEAdvertisedDevice::getMinInterval() const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
|
||||
return *field->value | *(field->value + 1) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // getMinInterval
|
||||
|
||||
/**
|
||||
* @brief Get the preferred max connection interval.
|
||||
* @return The preferred max connection interval in 1.25ms units.
|
||||
*/
|
||||
uint16_t NimBLEAdvertisedDevice::getMaxInterval() const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
|
||||
return *(field->value + 2) | *(field->value + 3) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // getMaxInterval
|
||||
|
||||
/**
|
||||
* @brief Get the manufacturer data.
|
||||
* @param [in] index The index of the of the manufacturer data set to get.
|
||||
* @return The manufacturer data.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getManufacturerData(uint8_t index) const {
|
||||
return getPayloadByType(BLE_HS_ADV_TYPE_MFG_DATA, index);
|
||||
} // getManufacturerData
|
||||
|
||||
/**
|
||||
* @brief Get the count of manufacturer data sets.
|
||||
* @return The number of manufacturer data sets.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getManufacturerDataCount() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA);
|
||||
} // getManufacturerDataCount
|
||||
|
||||
/**
|
||||
* @brief Get the URI from the advertisement.
|
||||
* @return The URI data.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getURI() const {
|
||||
return getPayloadByType(BLE_HS_ADV_TYPE_URI);
|
||||
} // getURI
|
||||
|
||||
/**
|
||||
* @brief Get the data from any type available in the advertisement.
|
||||
* @param [in] type The advertised data type BLE_HS_ADV_TYPE.
|
||||
* @param [in] index The index of the data type.
|
||||
* @return The data available under the type `type`.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type, uint8_t index) const {
|
||||
size_t data_loc;
|
||||
if (findAdvField(type, index, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length > 1) {
|
||||
return std::string((char*)field->value, field->length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
} // getPayloadByType
|
||||
|
||||
/**
|
||||
* @brief Get the advertised name.
|
||||
* @return The name of the advertised device.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getName() const {
|
||||
return getPayloadByType(BLE_HS_ADV_TYPE_COMP_NAME);
|
||||
} // getName
|
||||
|
||||
/**
|
||||
* @brief Get the RSSI.
|
||||
* @return The RSSI of the advertised device.
|
||||
*/
|
||||
int8_t NimBLEAdvertisedDevice::getRSSI() const {
|
||||
return m_rssi;
|
||||
} // getRSSI
|
||||
|
||||
/**
|
||||
* @brief Get the scan object that created this advertised device.
|
||||
* @return The scan object.
|
||||
*/
|
||||
NimBLEScan* NimBLEAdvertisedDevice::getScan() const {
|
||||
return NimBLEDevice::getScan();
|
||||
} // getScan
|
||||
|
||||
/**
|
||||
* @brief Get the number of target addresses.
|
||||
* @return The number of addresses.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() const {
|
||||
uint8_t count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the target address at the index.
|
||||
* @param [in] index The index of the target address.
|
||||
* @return The target address.
|
||||
*/
|
||||
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) const {
|
||||
size_t data_loc = ULONG_MAX;
|
||||
uint8_t count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
|
||||
if (count < index + 1) {
|
||||
index -= count;
|
||||
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
|
||||
}
|
||||
|
||||
if (count > 0 && data_loc != ULONG_MAX) {
|
||||
index++;
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
|
||||
// In the case of more than one field of target addresses we need to adjust the index
|
||||
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
|
||||
}
|
||||
if (field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
|
||||
return NimBLEAddress{field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN, field->type};
|
||||
}
|
||||
}
|
||||
|
||||
return NimBLEAddress{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the service data.
|
||||
* @param [in] index The index of the service data requested.
|
||||
* @return The advertised service data or empty string if no data.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) const {
|
||||
uint8_t bytes;
|
||||
size_t data_loc = findServiceData(index, &bytes);
|
||||
if (data_loc != ULONG_MAX) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length > bytes) {
|
||||
const char* field_data = reinterpret_cast<const char*>(field->value + bytes);
|
||||
return std::string(field_data, field->length - bytes - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
} // getServiceData
|
||||
|
||||
/**
|
||||
* @brief Get the service data.
|
||||
* @param [out] len The length of the service data requested.
|
||||
* @return The advertised service data or empty string if no data.
|
||||
*/
|
||||
uint8_t* NimBLEAdvertisedDevice::getServiceData(uint16_t* len) const {
|
||||
uint8_t bytes;
|
||||
size_t data_loc = findServiceData(0, &bytes); // Assuming index 0 for the first service data
|
||||
if (data_loc != ULONG_MAX) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length > bytes) {
|
||||
*len = field->length - bytes - 1;
|
||||
return const_cast<uint8_t*>(field->value + bytes);
|
||||
}
|
||||
}
|
||||
*len = 0;
|
||||
return nullptr;
|
||||
} // getServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service data.
|
||||
* @param [in] uuid The uuid of the service data requested.
|
||||
* @return The advertised service data or empty string if no data.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID& uuid) const {
|
||||
uint8_t bytes;
|
||||
uint8_t index = 0;
|
||||
size_t data_loc = findServiceData(index, &bytes);
|
||||
size_t pl_size = m_payload.size() - 2;
|
||||
uint8_t uuid_bytes = uuid.bitSize() / 8;
|
||||
|
||||
while (data_loc < pl_size) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (bytes == uuid_bytes && NimBLEUUID(field->value, bytes) == uuid) {
|
||||
const char* field_data = reinterpret_cast<const char*>(field->value + bytes);
|
||||
return std::string(field_data, field->length - bytes - 1);
|
||||
}
|
||||
|
||||
index++;
|
||||
data_loc = findServiceData(index, &bytes);
|
||||
}
|
||||
|
||||
NIMBLE_LOGI(LOG_TAG, "No service data found");
|
||||
return "";
|
||||
} // getServiceData
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the service data at the index.
|
||||
* @param [in] index The index of the service data UUID requested.
|
||||
* @return The advertised service data UUID or an empty UUID if not found.
|
||||
*/
|
||||
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) const {
|
||||
uint8_t bytes;
|
||||
size_t data_loc = findServiceData(index, &bytes);
|
||||
if (data_loc != ULONG_MAX) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length >= bytes) {
|
||||
return NimBLEUUID(field->value, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return NimBLEUUID("");
|
||||
} // getServiceDataUUID
|
||||
|
||||
/**
|
||||
* @brief Find the service data at the index.
|
||||
* @param [in] index The index of the service data to find.
|
||||
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
|
||||
* @return The index in the vector where the data is located, ULONG_MAX if not found.
|
||||
*/
|
||||
size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t* bytes) const {
|
||||
*bytes = 0;
|
||||
|
||||
size_t data_loc = 0;
|
||||
uint8_t found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc);
|
||||
if (found > index) {
|
||||
*bytes = 2;
|
||||
return data_loc;
|
||||
}
|
||||
|
||||
index -= found;
|
||||
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc);
|
||||
if (found > index) {
|
||||
*bytes = 4;
|
||||
return data_loc;
|
||||
}
|
||||
|
||||
index -= found;
|
||||
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc);
|
||||
if (found > index) {
|
||||
*bytes = 16;
|
||||
return data_loc;
|
||||
}
|
||||
|
||||
return ULONG_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the count of advertised service data UUIDS
|
||||
* @return The number of service data UUIDS in the vector.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getServiceDataCount() const {
|
||||
uint8_t count = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128);
|
||||
|
||||
return count;
|
||||
} // getServiceDataCount
|
||||
|
||||
/**
|
||||
* @brief Get the Service UUID.
|
||||
* @param [in] index The index of the service UUID requested.
|
||||
* @return The Service UUID of the advertised service, or an empty UUID if not found.
|
||||
*/
|
||||
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) const {
|
||||
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
|
||||
size_t data_loc = 0;
|
||||
uint8_t uuid_bytes = 0;
|
||||
uint8_t count = 0;
|
||||
|
||||
do {
|
||||
count = findAdvField(type, index, &data_loc);
|
||||
if (count > index) {
|
||||
if (type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) {
|
||||
uuid_bytes = 2;
|
||||
} else if (type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) {
|
||||
uuid_bytes = 4;
|
||||
} else {
|
||||
uuid_bytes = 16;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
type++;
|
||||
index -= count;
|
||||
}
|
||||
|
||||
} while (type <= BLE_HS_ADV_TYPE_COMP_UUIDS128);
|
||||
|
||||
if (uuid_bytes > 0) {
|
||||
index++;
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
// In the case of more than one field of service uuid's we need to adjust
|
||||
// the index to account for the uuids of the previous fields.
|
||||
if (field->length < index * uuid_bytes) {
|
||||
index -= count - field->length / uuid_bytes;
|
||||
}
|
||||
|
||||
if (field->length > uuid_bytes * index) {
|
||||
return NimBLEUUID(field->value + uuid_bytes * (index - 1), uuid_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return NimBLEUUID("");
|
||||
} // getServiceUUID
|
||||
|
||||
/**
|
||||
* @brief Get the number of services advertised
|
||||
* @return The count of services in the advertising packet.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() const {
|
||||
uint8_t count = findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
|
||||
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128);
|
||||
|
||||
return count;
|
||||
} // getServiceUUIDCount
|
||||
|
||||
/**
|
||||
* @brief Check advertised services for existence of the required UUID
|
||||
* @param [in] uuid The service uuid to look for in the advertisement.
|
||||
* @return Return true if service is advertised
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID& uuid) const {
|
||||
size_t count = getServiceUUIDCount();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (uuid == getServiceUUID(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} // isAdvertisingService
|
||||
|
||||
/**
|
||||
* @brief Get the TX Power.
|
||||
* @return The TX Power of the advertised device.
|
||||
*/
|
||||
int8_t NimBLEAdvertisedDevice::getTXPower() const {
|
||||
size_t data_loc = 0;
|
||||
if (findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data_loc]);
|
||||
if (field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) {
|
||||
return *(int8_t*)field->value;
|
||||
}
|
||||
}
|
||||
|
||||
return -99;
|
||||
} // getTXPower
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have preferred connection parameters?
|
||||
* @return True if connection parameters are present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveConnParams() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0;
|
||||
} // haveConnParams
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have have the advertising interval?
|
||||
* @return True if the advertisement interval is present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveAdvInterval() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0;
|
||||
} // haveAdvInterval
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have an appearance value?
|
||||
* @return True if there is an appearance value present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveAppearance() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0;
|
||||
} // haveAppearance
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have manufacturer data?
|
||||
* @return True if there is manufacturer data present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveManufacturerData() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0;
|
||||
} // haveManufacturerData
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a URI?
|
||||
* @return True if there is a URI present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveURI() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
|
||||
} // haveURI
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a adv type `type`?
|
||||
* @return True if there is a `type` present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveType(uint16_t type) const {
|
||||
return findAdvField(type) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does the advertisement contain a target address?
|
||||
* @return True if an address is present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveTargetAddress() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 || findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a name value?
|
||||
* @return True if there is a name value present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveName() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0;
|
||||
} // haveName
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service data value?
|
||||
* @return True if there is a service data value present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveServiceData() const {
|
||||
return getServiceDataCount() > 0;
|
||||
} // haveServiceData
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service UUID value?
|
||||
* @return True if there is a service UUID value present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveServiceUUID() const {
|
||||
return getServiceUUIDCount() > 0;
|
||||
} // haveServiceUUID
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a transmission power value?
|
||||
* @return True if there is a transmission power value present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveTXPower() const {
|
||||
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
|
||||
} // haveTXPower
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
/**
|
||||
* @brief Get the set ID of the extended advertisement.
|
||||
* @return The set ID.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getSetId() const {
|
||||
return m_sid;
|
||||
} // getSetId
|
||||
|
||||
/**
|
||||
* @brief Get the primary PHY used by this advertisement.
|
||||
* @return The PHY type, one of:
|
||||
* * BLE_HCI_LE_PHY_1M
|
||||
* * BLE_HCI_LE_PHY_CODED
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() const {
|
||||
return m_primPhy;
|
||||
} // getPrimaryPhy
|
||||
|
||||
/**
|
||||
* @brief Get the primary PHY used by this advertisement.
|
||||
* @return The PHY type, one of:
|
||||
* * BLE_HCI_LE_PHY_1M
|
||||
* * BLE_HCI_LE_PHY_2M
|
||||
* * BLE_HCI_LE_PHY_CODED
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() const {
|
||||
return m_secPhy;
|
||||
} // getSecondaryPhy
|
||||
|
||||
/**
|
||||
* @brief Get the periodic interval of the advertisement.
|
||||
* @return The periodic advertising interval, 0 if not periodic advertising.
|
||||
*/
|
||||
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const {
|
||||
return m_periodicItvl;
|
||||
} // getPeriodicInterval
|
||||
# endif
|
||||
|
||||
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const {
|
||||
size_t length = m_payload.size();
|
||||
size_t data = 0;
|
||||
uint8_t count = 0;
|
||||
|
||||
while (length > 2) {
|
||||
const ble_hs_adv_field* field = reinterpret_cast<const ble_hs_adv_field*>(&m_payload[data]);
|
||||
if (field->length >= length) {
|
||||
return count;
|
||||
}
|
||||
|
||||
if (field->type == type || (type == BLE_HS_ADV_TYPE_COMP_NAME && field->type == BLE_HS_ADV_TYPE_INCOMP_NAME)) {
|
||||
switch (type) {
|
||||
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
|
||||
case BLE_HS_ADV_TYPE_COMP_UUIDS16:
|
||||
count += field->length / 2;
|
||||
break;
|
||||
|
||||
case BLE_HS_ADV_TYPE_INCOMP_UUIDS32:
|
||||
case BLE_HS_ADV_TYPE_COMP_UUIDS32:
|
||||
count += field->length / 4;
|
||||
break;
|
||||
|
||||
case BLE_HS_ADV_TYPE_INCOMP_UUIDS128:
|
||||
case BLE_HS_ADV_TYPE_COMP_UUIDS128:
|
||||
count += field->length / 16;
|
||||
break;
|
||||
|
||||
case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR:
|
||||
case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR:
|
||||
count += field->length / 6;
|
||||
break;
|
||||
|
||||
case BLE_HS_ADV_TYPE_COMP_NAME:
|
||||
// keep looking for complete name, else use this
|
||||
if (data_loc != nullptr && field->type == BLE_HS_ADV_TYPE_INCOMP_NAME) {
|
||||
*data_loc = data;
|
||||
index++;
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (data_loc != nullptr) {
|
||||
if (count > index) { // assumes index values default to 0
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
length -= 1 + field->length;
|
||||
data += 1 + field->length;
|
||||
}
|
||||
|
||||
if (data_loc != nullptr && count > index) {
|
||||
*data_loc = data;
|
||||
}
|
||||
|
||||
return count;
|
||||
} // findAdvField
|
||||
|
||||
/**
|
||||
* @brief Create a string representation of this device.
|
||||
* @return A string representation of this device.
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::toString() const {
|
||||
std::string res = "Name: " + getName() + ", Address: " + getAddress().toString();
|
||||
|
||||
if (haveAppearance()) {
|
||||
char val[6];
|
||||
snprintf(val, sizeof(val), "%d", getAppearance());
|
||||
res += ", appearance: ";
|
||||
res += val;
|
||||
}
|
||||
|
||||
if (haveManufacturerData()) {
|
||||
auto mfgData = getManufacturerData();
|
||||
res += ", manufacturer data: ";
|
||||
res += NimBLEUtils::dataToHexString(reinterpret_cast<const uint8_t*>(mfgData.data()), mfgData.length());
|
||||
}
|
||||
|
||||
if (haveServiceUUID()) {
|
||||
res += ", serviceUUID: " + getServiceUUID().toString();
|
||||
}
|
||||
|
||||
if (haveTXPower()) {
|
||||
char val[5];
|
||||
snprintf(val, sizeof(val), "%d", getTXPower());
|
||||
res += ", txPower: ";
|
||||
res += val;
|
||||
}
|
||||
|
||||
if (haveServiceData()) {
|
||||
uint8_t count = getServiceDataCount();
|
||||
res += "\nService Data:";
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
res += "\nUUID: " + std::string(getServiceDataUUID(i));
|
||||
res += ", Data: " + getServiceData(i);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
} // toString
|
||||
|
||||
/**
|
||||
* @brief Get the length of the advertisement data in the payload.
|
||||
* @return The number of bytes in the payload that is from the advertisement.
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getAdvLength() const {
|
||||
return m_advLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the advertised device address type.
|
||||
* @return The device address type:
|
||||
* * BLE_ADDR_PUBLIC (0x00)
|
||||
* * BLE_ADDR_RANDOM (0x01)
|
||||
* * BLE_ADDR_PUBLIC_ID (0x02)
|
||||
* * BLE_ADDR_RANDOM_ID (0x03)
|
||||
*/
|
||||
uint8_t NimBLEAdvertisedDevice::getAddressType() const {
|
||||
return m_address.getType();
|
||||
} // getAddressType
|
||||
|
||||
/**
|
||||
* @brief Check if this device is advertising as connectable.
|
||||
* @return True if the device is connectable.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::isConnectable() const {
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
if (m_isLegacyAdv) {
|
||||
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
|
||||
}
|
||||
# endif
|
||||
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
|
||||
} // isConnectable
|
||||
|
||||
/**
|
||||
* @brief Check if this device is advertising as scannable.
|
||||
* @return True if the device is scannable.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::isScannable() const {
|
||||
return isLegacyAdvertisement() && (m_advType == BLE_HCI_ADV_TYPE_ADV_IND || m_advType == BLE_HCI_ADV_TYPE_ADV_SCAN_IND);
|
||||
} // isScannable
|
||||
|
||||
/**
|
||||
* @brief Check if this advertisement is a legacy or extended type
|
||||
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const {
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
return m_isLegacyAdv;
|
||||
# else
|
||||
return true;
|
||||
# endif
|
||||
} // isLegacyAdvertisement
|
||||
|
||||
/**
|
||||
* @brief Get the payload advertised by the device.
|
||||
* @return The advertisement payload.
|
||||
*/
|
||||
const std::vector<uint8_t>& NimBLEAdvertisedDevice::getPayload() const {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the begin iterator for the payload.
|
||||
* @return A read only iterator pointing to the first byte in the payload.
|
||||
*/
|
||||
const std::vector<uint8_t>::const_iterator NimBLEAdvertisedDevice::begin() const {
|
||||
return m_payload.cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the end iterator for the payload.
|
||||
* @return A read only iterator pointing to one past the last byte of the payload.
|
||||
*/
|
||||
const std::vector<uint8_t>::const_iterator NimBLEAdvertisedDevice::end() const {
|
||||
return m_payload.cend();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||
197
Libraries/NimBLE-Arduino_modified/NimBLEAdvertisedDevice.h
Normal file
197
Libraries/NimBLE-Arduino_modified/NimBLEAdvertisedDevice.h
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
||||
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
|
||||
# include "NimBLEAddress.h"
|
||||
# include "NimBLEScan.h"
|
||||
# include "NimBLEUUID.h"
|
||||
|
||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "host/ble_hs_adv.h"
|
||||
# include "host/ble_gap.h"
|
||||
# else
|
||||
# include "nimble/nimble/host/include/host/ble_hs_adv.h"
|
||||
# include "nimble/nimble/host/include/host/ble_gap.h"
|
||||
# endif
|
||||
|
||||
# include <vector>
|
||||
|
||||
class NimBLEScan;
|
||||
/**
|
||||
* @brief A representation of a %BLE advertised device found by a scan.
|
||||
*
|
||||
* When we perform a %BLE scan, the result will be a set of devices that are advertising. This
|
||||
* class provides a model of a detected device.
|
||||
*/
|
||||
class NimBLEAdvertisedDevice {
|
||||
public:
|
||||
NimBLEAdvertisedDevice() = default;
|
||||
|
||||
uint8_t getAdvType() const;
|
||||
uint8_t getAdvFlags() const;
|
||||
uint16_t getAppearance() const;
|
||||
uint16_t getAdvInterval() const;
|
||||
uint16_t getMinInterval() const;
|
||||
uint16_t getMaxInterval() const;
|
||||
uint8_t getManufacturerDataCount() const;
|
||||
const NimBLEAddress& getAddress() const;
|
||||
std::string getManufacturerData(uint8_t index = 0) const;
|
||||
std::string getURI() const;
|
||||
std::string getPayloadByType(uint16_t type, uint8_t index = 0) const;
|
||||
std::string getName() const;
|
||||
int8_t getRSSI() const;
|
||||
NimBLEScan* getScan() const;
|
||||
uint8_t getServiceDataCount() const;
|
||||
std::string getServiceData(uint8_t index = 0) const;
|
||||
uint8_t * getServiceData(uint16_t *len) const;
|
||||
std::string getServiceData(const NimBLEUUID& uuid) const;
|
||||
NimBLEUUID getServiceDataUUID(uint8_t index = 0) const;
|
||||
NimBLEUUID getServiceUUID(uint8_t index = 0) const;
|
||||
uint8_t getServiceUUIDCount() const;
|
||||
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
|
||||
uint8_t getTargetAddressCount() const;
|
||||
int8_t getTXPower() const;
|
||||
uint8_t getAdvLength() const;
|
||||
uint8_t getAddressType() const;
|
||||
bool isAdvertisingService(const NimBLEUUID& uuid) const;
|
||||
bool haveAppearance() const;
|
||||
bool haveManufacturerData() const;
|
||||
bool haveName() const;
|
||||
bool haveServiceData() const;
|
||||
bool haveServiceUUID() const;
|
||||
bool haveTXPower() const;
|
||||
bool haveConnParams() const;
|
||||
bool haveAdvInterval() const;
|
||||
bool haveTargetAddress() const;
|
||||
bool haveURI() const;
|
||||
bool haveType(uint16_t type) const;
|
||||
std::string toString() const;
|
||||
bool isConnectable() const;
|
||||
bool isScannable() const;
|
||||
bool isLegacyAdvertisement() const;
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
uint8_t getSetId() const;
|
||||
uint8_t getPrimaryPhy() const;
|
||||
uint8_t getSecondaryPhy() const;
|
||||
uint16_t getPeriodicInterval() const;
|
||||
# endif
|
||||
|
||||
const std::vector<uint8_t>& getPayload() const;
|
||||
const std::vector<uint8_t>::const_iterator begin() const;
|
||||
const std::vector<uint8_t>::const_iterator end() const;
|
||||
|
||||
/**
|
||||
* @brief A template to convert the service data to <type\>.
|
||||
* @tparam T The type to convert the data to.
|
||||
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getManufacturerData(bool skipSizeCheck = false) const {
|
||||
std::string data = getManufacturerData();
|
||||
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
|
||||
const char* pData = data.data();
|
||||
return *((T*)pData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A template to convert the service data to <tt><type\></tt>.
|
||||
* @tparam T The type to convert the data to.
|
||||
* @param [in] index The vector index of the service data requested.
|
||||
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) const {
|
||||
std::string data = getServiceData(index);
|
||||
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
|
||||
const char* pData = data.data();
|
||||
return *((T*)pData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A template to convert the service data to <tt><type\></tt>.
|
||||
* @tparam T The type to convert the data to.
|
||||
* @param [in] uuid The uuid of the service data requested.
|
||||
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getServiceData(const NimBLEUUID& uuid, bool skipSizeCheck = false) const {
|
||||
std::string data = getServiceData(uuid);
|
||||
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
|
||||
const char* pData = data.data();
|
||||
return *((T*)pData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A template to convert the service data to <tt><type></tt>.
|
||||
* @tparam T The type to convert the data to.
|
||||
* @param [in] uuid The UUID of the service data requested.
|
||||
* @param [in] skipSizeCheck If true, skips checking if the data size is less than <tt>sizeof(<type>)</tt>.
|
||||
* @return The data converted to <type> or a default-constructed <type> if size check fails.
|
||||
* @details <b>Usage:</b> <tt>getServiceData<Type>(uuid, skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getServiceData(uint16_t *len) const {
|
||||
uint8_t* data = getServiceData(len);
|
||||
|
||||
if (data == nullptr) {
|
||||
len = 0;
|
||||
return T();
|
||||
}
|
||||
return *reinterpret_cast<T*>(data);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class NimBLEScan;
|
||||
|
||||
NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType);
|
||||
void update(const ble_gap_event* event, uint8_t eventType);
|
||||
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t* data_loc = nullptr) const;
|
||||
size_t findServiceData(uint8_t index, uint8_t* bytes) const;
|
||||
|
||||
NimBLEAddress m_address{};
|
||||
uint8_t m_advType{};
|
||||
int8_t m_rssi{};
|
||||
uint8_t m_callbackSent{};
|
||||
uint8_t m_advLength{};
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
bool m_isLegacyAdv{};
|
||||
uint8_t m_sid{};
|
||||
uint8_t m_primPhy{};
|
||||
uint8_t m_secPhy{};
|
||||
uint16_t m_periodicItvl{};
|
||||
# endif
|
||||
|
||||
std::vector<uint8_t> m_payload;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
|
||||
#endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */
|
||||
625
Libraries/NimBLE-Arduino_modified/NimBLEScan.cpp
Normal file
625
Libraries/NimBLE-Arduino_modified/NimBLEScan.cpp
Normal file
|
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
|
||||
# include "NimBLEScan.h"
|
||||
# include "NimBLEDevice.h"
|
||||
# include "NimBLELog.h"
|
||||
|
||||
# include <string>
|
||||
# include <climits>
|
||||
|
||||
static const char* LOG_TAG = "NimBLEScan";
|
||||
static NimBLEScanCallbacks defaultScanCallbacks;
|
||||
|
||||
/**
|
||||
* @brief Scan constructor.
|
||||
*/
|
||||
NimBLEScan::NimBLEScan()
|
||||
: m_pScanCallbacks{&defaultScanCallbacks},
|
||||
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
|
||||
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
|
||||
m_pTaskData{nullptr},
|
||||
m_maxResults{0xFF},
|
||||
m_bDiscovered(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Scan destructor, release any allocated resources.
|
||||
*/
|
||||
NimBLEScan::~NimBLEScan() {
|
||||
clearResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle GAP events related to scans.
|
||||
* @param [in] event The event type for this event.
|
||||
* @param [in] param Parameter data for this event.
|
||||
*/
|
||||
int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
||||
(void)arg;
|
||||
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_EXT_DISC:
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
//const auto& disc = event->disc;
|
||||
//const bool isLegacyAdv = true;
|
||||
//const auto event_type = event->disc.event_type;
|
||||
NimBLEAddress advertisedAddress(event->disc.addr);
|
||||
NimBLEAdvertisedDevice tempDevice(event, event->disc.event_type);
|
||||
|
||||
// Process if the address does match the target
|
||||
if (advertisedAddress == pScan->m_targetAddress) {
|
||||
pScan->m_pScanCallbacks->onResult(&tempDevice);
|
||||
return 0;
|
||||
} else {
|
||||
// SSID & PW Broadcast
|
||||
// Get manufacturer data as a string
|
||||
const std::string& manufacturerData = tempDevice.getManufacturerData();
|
||||
if (!manufacturerData.empty() && manufacturerData.length() >= 2) {
|
||||
// Check if the manufacturer data is valid and the first 2 bytes matches the manufacturer ID
|
||||
uint16_t manufacturerID = * (uint16_t *) &manufacturerData[0];
|
||||
if (manufacturerID == 0x4E80 || manufacturerID == 0x4E81) {
|
||||
pScan->m_pScanCallbacks->onDiscovered(&tempDevice);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Call onDiscovered only once
|
||||
// if (!pScan->m_bDiscovered) {
|
||||
// pScan->m_device = NimBLEAdvertisedDevice(event, event->disc.event_type);
|
||||
// //pScan->m_pScanCallbacks->onDiscovered(&pScan->m_device);
|
||||
// pScan->m_bDiscovered = true;
|
||||
// } else {
|
||||
// // Update the single device instance with new data
|
||||
// pScan->m_device.update(event, event->disc.event_type);
|
||||
// }
|
||||
|
||||
// // Always call onResult
|
||||
// pScan->m_pScanCallbacks->onResult(&pScan->m_device);
|
||||
// //pScan->erase(&pScan->m_device);
|
||||
// return 0;
|
||||
}
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE: {
|
||||
NIMBLE_LOGD(LOG_TAG, "Discovery complete; reason=%d", event->disc_complete.reason);
|
||||
|
||||
//pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
|
||||
|
||||
if (pScan->m_pTaskData != nullptr) {
|
||||
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
||||
(void)arg;
|
||||
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_EXT_DISC:
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
const auto& disc = event->ext_disc;
|
||||
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
||||
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
|
||||
# else
|
||||
const auto& disc = event->disc;
|
||||
const bool isLegacyAdv = true;
|
||||
const auto event_type = disc.event_type;
|
||||
# endif
|
||||
NimBLEAddress advertisedAddress(disc.addr);
|
||||
|
||||
# ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL
|
||||
// stop processing if already connected
|
||||
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress);
|
||||
if (pClient != nullptr && pClient->isConnected()) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s, already connected", advertisedAddress.toString().c_str());
|
||||
return 0;
|
||||
}
|
||||
# endif
|
||||
NimBLEAdvertisedDevice* advertisedDevice = nullptr;
|
||||
|
||||
// If we've seen this device before get a pointer to it from the vector
|
||||
for (const auto& dev : pScan->m_scanResults.m_deviceVec) {
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
// Same address but different set ID should create a new advertised device.
|
||||
if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid)
|
||||
# else
|
||||
if (dev->getAddress() == advertisedAddress)
|
||||
# endif
|
||||
{
|
||||
advertisedDevice = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't seen this device before; create a new instance and insert it in the vector.
|
||||
// Otherwise just update the relevant parameters of the already known device.
|
||||
if (advertisedDevice == nullptr) {
|
||||
// Check if we have reach the scan results limit, ignore this one if so.
|
||||
// We still need to store each device when maxResults is 0 to be able to append the scan results
|
||||
if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
|
||||
(pScan->m_scanResults.m_deviceVec.size() >= pScan->m_maxResults)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Scan response without advertisement: %s", advertisedAddress.toString().c_str());
|
||||
}
|
||||
|
||||
advertisedDevice = new NimBLEAdvertisedDevice(event, event_type);
|
||||
pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice);
|
||||
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
|
||||
} else {
|
||||
advertisedDevice->update(event, event_type);
|
||||
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
|
||||
} else {
|
||||
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!advertisedDevice->m_callbackSent) {
|
||||
advertisedDevice->m_callbackSent++;
|
||||
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
|
||||
}
|
||||
|
||||
// If not active scanning or scan response is not available
|
||||
// or extended advertisement scanning, report the result to the callback now.
|
||||
if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
|
||||
advertisedDevice->m_callbackSent++;
|
||||
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
||||
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
||||
advertisedDevice->m_callbackSent++;
|
||||
// got the scan response report the full data.
|
||||
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
||||
}
|
||||
|
||||
// If not storing results and we have invoked the callback, delete the device.
|
||||
if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) {
|
||||
pScan->erase(advertisedDevice);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE: {
|
||||
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason);
|
||||
|
||||
if (pScan->m_maxResults == 0) {
|
||||
pScan->clearResults();
|
||||
}
|
||||
|
||||
pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason);
|
||||
|
||||
if (pScan->m_pTaskData != nullptr) {
|
||||
NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
} // handleGapEvent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Should we perform an active or passive scan?
|
||||
* The default is a passive scan. An active scan means that we will request a scan response.
|
||||
* @param [in] active If true, we perform an active scan otherwise a passive scan.
|
||||
*/
|
||||
void NimBLEScan::setActiveScan(bool active) {
|
||||
m_scanParams.passive = !active;
|
||||
} // setActiveScan
|
||||
|
||||
/**
|
||||
* @brief Set whether or not the BLE controller should only report results
|
||||
* from devices it has not already seen.
|
||||
* @param [in] enabled If set to 1 (true), scanned devices will only be reported once.
|
||||
* If set to 0 duplicates will be reported each time they are seen.
|
||||
* If using extended scanning this can be set to 2 which will reset the duplicate filter
|
||||
* at the end of each scan period if the scan period is set.
|
||||
* @note The controller has a limited buffer and will start reporting
|
||||
duplicate devices once the limit is reached.
|
||||
*/
|
||||
void NimBLEScan::setDuplicateFilter(uint8_t enabled) {
|
||||
m_scanParams.filter_duplicates = enabled;
|
||||
} // setDuplicateFilter
|
||||
|
||||
/**
|
||||
* @brief Set whether or not the BLE controller only reports scan results
|
||||
* from devices advertising in limited discovery mode.
|
||||
* @param [in] enabled If true, only limited discovery devices will be in scan results.
|
||||
*/
|
||||
void NimBLEScan::setLimitedOnly(bool enabled) {
|
||||
m_scanParams.limited = enabled;
|
||||
} // setLimited
|
||||
|
||||
/**
|
||||
* @brief Sets the scan filter policy.
|
||||
* @param [in] filter Can be one of:
|
||||
* * BLE_HCI_SCAN_FILT_NO_WL (0)
|
||||
* Scanner processes all advertising packets (white list not used) except\n
|
||||
* directed, connectable advertising packets not sent to the scanner.
|
||||
* * BLE_HCI_SCAN_FILT_USE_WL (1)
|
||||
* Scanner processes advertisements from white list only. A connectable,\n
|
||||
* directed advertisement is ignored unless it contains scanners address.
|
||||
* * BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
|
||||
* Scanner process all advertising packets (white list not used). A\n
|
||||
* connectable, directed advertisement shall not be ignored if the InitA
|
||||
* is a resolvable private address.
|
||||
* * BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
|
||||
* Scanner process advertisements from white list only. A connectable,\n
|
||||
* directed advertisement shall not be ignored if the InitA is a
|
||||
* resolvable private address.
|
||||
*/
|
||||
void NimBLEScan::setFilterPolicy(uint8_t filter) {
|
||||
m_scanParams.filter_policy = filter;
|
||||
} // setFilterPolicy
|
||||
|
||||
/**
|
||||
* @brief Sets the max number of results to store.
|
||||
* @param [in] maxResults The number of results to limit storage to\n
|
||||
* 0 == none (callbacks only) 0xFF == unlimited, any other value is the limit.
|
||||
*/
|
||||
void NimBLEScan::setMaxResults(uint8_t maxResults) {
|
||||
m_maxResults = maxResults;
|
||||
} // setMaxResults
|
||||
|
||||
/**
|
||||
* @brief Set the call backs to be invoked.
|
||||
* @param [in] pScanCallbacks Call backs to be invoked.
|
||||
* @param [in] wantDuplicates True if we wish to be called back with duplicates, default: false.
|
||||
*/
|
||||
void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates) {
|
||||
setDuplicateFilter(!wantDuplicates);
|
||||
if (pScanCallbacks == nullptr) {
|
||||
m_pScanCallbacks = &defaultScanCallbacks;
|
||||
return;
|
||||
}
|
||||
m_pScanCallbacks = pScanCallbacks;
|
||||
} // setScanCallbacks
|
||||
|
||||
/**
|
||||
* @brief Set the interval to scan.
|
||||
* @param [in] intervalMs The scan interval in milliseconds.
|
||||
* @details The interval is the time between the start of two consecutive scan windows.
|
||||
* When a new interval starts the controller changes the channel it's scanning on.
|
||||
*/
|
||||
void NimBLEScan::setInterval(uint16_t intervalMs) {
|
||||
m_scanParams.itvl = (intervalMs * 16) / 10;
|
||||
} // setInterval
|
||||
|
||||
/**
|
||||
* @brief Set the window to actively scan.
|
||||
* @param [in] windowMs How long during the interval to actively scan in milliseconds.
|
||||
*/
|
||||
void NimBLEScan::setWindow(uint16_t windowMs) {
|
||||
m_scanParams.window = (windowMs * 16) / 10;
|
||||
} // setWindow
|
||||
|
||||
/**
|
||||
* @brief Get the status of the scanner.
|
||||
* @return true if scanning or scan starting.
|
||||
*/
|
||||
bool NimBLEScan::isScanning() {
|
||||
return ble_gap_disc_active();
|
||||
}
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
/**
|
||||
* @brief Set the PHYs to scan.
|
||||
* @param [in] phyMask The PHYs to scan, a bit mask of:
|
||||
* * NIMBLE_CPP_SCAN_1M
|
||||
* * NIMBLE_CPP_SCAN_CODED
|
||||
*/
|
||||
void NimBLEScan::setPhy(Phy phyMask) {
|
||||
m_phy = phyMask;
|
||||
} // setScanPhy
|
||||
|
||||
/**
|
||||
* @brief Set the extended scanning period.
|
||||
* @param [in] periodMs The scan period in milliseconds
|
||||
* @details The period is the time between the start of two consecutive scan periods.
|
||||
* This works as a timer to restart scanning at the specified amount of time in periodMs.
|
||||
* @note The duration used when this is set must be less than period.
|
||||
*/
|
||||
void NimBLEScan::setPeriod(uint32_t periodMs) {
|
||||
m_period = (periodMs + 500) / 1280; // round up 1.28 second units
|
||||
} // setScanPeriod
|
||||
# endif
|
||||
|
||||
/**
|
||||
* @brief Start scanning.
|
||||
* @param [in] duration The duration in milliseconds for which to scan. 0 == scan forever.
|
||||
* @param [in] isContinue Set to true to save previous scan results, false to clear them.
|
||||
* @param [in] restart Set to true to restart the scan if already in progress.
|
||||
* this is useful to clear the duplicate filter so all devices are reported again.
|
||||
* @return True if scan started or false if there was an error.
|
||||
*/
|
||||
bool NimBLEScan::start(uint32_t duration, bool isContinue, bool restart) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration);
|
||||
if (isScanning()) {
|
||||
if (restart) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Scan already in progress, restarting it");
|
||||
if (!stop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isContinue) {
|
||||
clearResults();
|
||||
}
|
||||
}
|
||||
} else { // Don't clear results while scanning is active
|
||||
if (!isContinue) {
|
||||
clearResults();
|
||||
}
|
||||
}
|
||||
|
||||
// If scanning is already active, call the functions anyway as the parameters can be changed.
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
ble_gap_ext_disc_params scan_params;
|
||||
scan_params.passive = m_scanParams.passive;
|
||||
scan_params.itvl = m_scanParams.itvl;
|
||||
scan_params.window = m_scanParams.window;
|
||||
int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType,
|
||||
duration / 10, // 10ms units
|
||||
m_period,
|
||||
m_scanParams.filter_duplicates,
|
||||
m_scanParams.filter_policy,
|
||||
m_scanParams.limited,
|
||||
m_phy & SCAN_1M ? &scan_params : NULL,
|
||||
m_phy & SCAN_CODED ? &scan_params : NULL,
|
||||
NimBLEScan::handleGapEvent,
|
||||
NULL);
|
||||
# else
|
||||
int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType,
|
||||
duration ? duration : BLE_HS_FOREVER,
|
||||
&m_scanParams,
|
||||
NimBLEScan::handleGapEvent,
|
||||
NULL);
|
||||
# endif
|
||||
switch (rc) {
|
||||
case 0:
|
||||
case BLE_HS_EALREADY:
|
||||
NIMBLE_LOGD(LOG_TAG, "Scan started");
|
||||
break;
|
||||
|
||||
case BLE_HS_EBUSY:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
|
||||
break;
|
||||
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to scan - Host Reset");
|
||||
break;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error starting scan; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< start()");
|
||||
return rc == 0 || rc == BLE_HS_EALREADY;
|
||||
} // start
|
||||
|
||||
/**
|
||||
* @brief Stop an in progress scan.
|
||||
* @return True if successful.
|
||||
*/
|
||||
bool NimBLEScan::stop() {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> stop()");
|
||||
|
||||
int rc = ble_gap_disc_cancel();
|
||||
if (rc != 0 && rc != BLE_HS_EALREADY) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_maxResults == 0) {
|
||||
clearResults();
|
||||
}
|
||||
|
||||
if (m_pTaskData != nullptr) {
|
||||
NimBLEUtils::taskRelease(*m_pTaskData);
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< stop()");
|
||||
return true;
|
||||
} // stop
|
||||
|
||||
/**
|
||||
* @brief Delete peer device from the scan results vector.
|
||||
* @param [in] address The address of the device to delete from the results.
|
||||
*/
|
||||
void NimBLEScan::erase(const NimBLEAddress& address) {
|
||||
NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str());
|
||||
for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) {
|
||||
if ((*it)->getAddress() == address) {
|
||||
delete *it;
|
||||
m_scanResults.m_deviceVec.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete peer device from the scan results vector.
|
||||
* @param [in] device The device to delete from the results.
|
||||
*/
|
||||
void NimBLEScan::erase(const NimBLEAdvertisedDevice* device) {
|
||||
NIMBLE_LOGD(LOG_TAG, "erase device: %s", device->getAddress().toString().c_str());
|
||||
for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) {
|
||||
if ((*it) == device) {
|
||||
delete *it;
|
||||
m_scanResults.m_deviceVec.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If the host reset and re-synced this is called.
|
||||
* If the application was scanning indefinitely with a callback, restart it.
|
||||
*/
|
||||
void NimBLEScan::onHostSync() {
|
||||
m_pScanCallbacks->onScanEnd(m_scanResults, BLE_HS_ENOTSYNCED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start scanning and block until scanning has been completed.
|
||||
* @param [in] duration The duration in milliseconds for which to scan.
|
||||
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
|
||||
* @return The scan results.
|
||||
*/
|
||||
NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) {
|
||||
if (duration == 0) {
|
||||
NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever");
|
||||
}
|
||||
|
||||
if (m_pTaskData != nullptr) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
|
||||
return m_scanResults;
|
||||
}
|
||||
|
||||
NimBLETaskData taskData;
|
||||
m_pTaskData = &taskData;
|
||||
|
||||
if (start(duration, is_continue)) {
|
||||
NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
|
||||
}
|
||||
|
||||
m_pTaskData = nullptr;
|
||||
return m_scanResults;
|
||||
} // getResults
|
||||
|
||||
/**
|
||||
* @brief Get the results of the scan.
|
||||
* @return NimBLEScanResults object.
|
||||
*/
|
||||
NimBLEScanResults NimBLEScan::getResults() {
|
||||
return m_scanResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the stored results of the scan.
|
||||
*/
|
||||
void NimBLEScan::clearResults() {
|
||||
if (m_scanResults.m_deviceVec.size()) {
|
||||
std::vector<NimBLEAdvertisedDevice*> vSwap{};
|
||||
ble_npl_hw_enter_critical();
|
||||
vSwap.swap(m_scanResults.m_deviceVec);
|
||||
ble_npl_hw_exit_critical(0);
|
||||
for (const auto& dev : vSwap) {
|
||||
delete dev;
|
||||
}
|
||||
}
|
||||
} // clearResults
|
||||
|
||||
/**
|
||||
* @brief Dump the scan results to the log.
|
||||
*/
|
||||
void NimBLEScanResults::dump() const {
|
||||
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >=3
|
||||
for (const auto& dev : m_deviceVec) {
|
||||
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
|
||||
}
|
||||
#endif
|
||||
} // dump
|
||||
|
||||
/**
|
||||
* @brief Get the count of devices found in the last scan.
|
||||
* @return The number of devices found in the last scan.
|
||||
*/
|
||||
int NimBLEScanResults::getCount() const {
|
||||
return m_deviceVec.size();
|
||||
} // getCount
|
||||
|
||||
/**
|
||||
* @brief Return the specified device at the given index.
|
||||
* The index should be between 0 and getCount()-1.
|
||||
* @param [in] idx The index of the device.
|
||||
* @return The device at the specified index.
|
||||
*/
|
||||
const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(uint32_t idx) const {
|
||||
return m_deviceVec[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get iterator to the beginning of the vector of advertised device pointers.
|
||||
* @return An iterator to the beginning of the vector of advertised device pointers.
|
||||
*/
|
||||
std::vector<NimBLEAdvertisedDevice*>::const_iterator NimBLEScanResults::begin() const {
|
||||
return m_deviceVec.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get iterator to the end of the vector of advertised device pointers.
|
||||
* @return An iterator to the end of the vector of advertised device pointers.
|
||||
*/
|
||||
std::vector<NimBLEAdvertisedDevice*>::const_iterator NimBLEScanResults::end() const {
|
||||
return m_deviceVec.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the specified device at the given address.
|
||||
* If the address is not found a nullptr is returned.
|
||||
* @param [in] address The address of the device.
|
||||
* @return A pointer to the device at the specified address.
|
||||
*/
|
||||
const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(const NimBLEAddress& address) const {
|
||||
for (const auto& dev : m_deviceVec) {
|
||||
if (dev->getAddress() == address) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* CB_TAG = "NimBLEScanCallbacks";
|
||||
|
||||
void NimBLEScanCallbacks::onDiscovered(const NimBLEAdvertisedDevice* pAdvertisedDevice) {
|
||||
NIMBLE_LOGD(CB_TAG, "Discovered: %s", pAdvertisedDevice->toString().c_str());
|
||||
}
|
||||
|
||||
void NimBLEScanCallbacks::onResult(const NimBLEAdvertisedDevice* pAdvertisedDevice) {
|
||||
NIMBLE_LOGD(CB_TAG, "Result: %s", pAdvertisedDevice->toString().c_str());
|
||||
}
|
||||
|
||||
void NimBLEScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason) {
|
||||
NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount());
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
|
||||
146
Libraries/NimBLE-Arduino_modified/NimBLEScan.h
Normal file
146
Libraries/NimBLE-Arduino_modified/NimBLEScan.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NIMBLE_CPP_SCAN_H_
|
||||
#define NIMBLE_CPP_SCAN_H_
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
|
||||
# include "NimBLEAdvertisedDevice.h"
|
||||
# include "NimBLEUtils.h"
|
||||
|
||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "host/ble_gap.h"
|
||||
# else
|
||||
# include "nimble/nimble/host/include/host/ble_gap.h"
|
||||
# endif
|
||||
|
||||
# include <vector>
|
||||
|
||||
class NimBLEDevice;
|
||||
class NimBLEScan;
|
||||
class NimBLEAdvertisedDevice;
|
||||
class NimBLEScanCallbacks;
|
||||
class NimBLEAddress;
|
||||
|
||||
/**
|
||||
* @brief A class that contains and operates on the results of a BLE scan.
|
||||
* @details When a scan completes, we have a set of found devices. Each device is described
|
||||
* by a NimBLEAdvertisedDevice object. The number of items in the set is given by
|
||||
* getCount(). We can retrieve a device by calling getDevice() passing in the
|
||||
* index (starting at 0) of the desired device.
|
||||
*/
|
||||
class NimBLEScanResults {
|
||||
public:
|
||||
void dump() const;
|
||||
int getCount() const;
|
||||
const NimBLEAdvertisedDevice* getDevice(uint32_t idx) const;
|
||||
const NimBLEAdvertisedDevice* getDevice(const NimBLEAddress& address) const;
|
||||
std::vector<NimBLEAdvertisedDevice*>::const_iterator begin() const;
|
||||
std::vector<NimBLEAdvertisedDevice*>::const_iterator end() const;
|
||||
|
||||
private:
|
||||
friend NimBLEScan;
|
||||
std::vector<NimBLEAdvertisedDevice*> m_deviceVec;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Perform and manage %BLE scans.
|
||||
*
|
||||
* Scanning is associated with a %BLE client that is attempting to locate BLE servers.
|
||||
*/
|
||||
class NimBLEScan {
|
||||
public:
|
||||
bool m_bDiscovered;
|
||||
NimBLEAdvertisedDevice m_device;
|
||||
NimBLEAddress m_targetAddress;
|
||||
void setTargetAddress(NimBLEAddress& addr) { m_targetAddress = addr; };
|
||||
|
||||
public:
|
||||
bool start(uint32_t duration, bool isContinue = false, bool restart = true);
|
||||
bool isScanning();
|
||||
void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false);
|
||||
void setActiveScan(bool active);
|
||||
void setInterval(uint16_t intervalMs);
|
||||
void setWindow(uint16_t windowMs);
|
||||
void setDuplicateFilter(uint8_t enabled);
|
||||
void setLimitedOnly(bool enabled);
|
||||
void setFilterPolicy(uint8_t filter);
|
||||
bool stop();
|
||||
void clearResults();
|
||||
NimBLEScanResults getResults();
|
||||
NimBLEScanResults getResults(uint32_t duration, bool is_continue = false);
|
||||
void setMaxResults(uint8_t maxResults);
|
||||
void erase(const NimBLEAddress& address);
|
||||
void erase(const NimBLEAdvertisedDevice* device);
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
enum Phy { SCAN_1M = 0x01, SCAN_CODED = 0x02, SCAN_ALL = 0x03 };
|
||||
void setPhy(Phy phyMask);
|
||||
void setPeriod(uint32_t periodMs);
|
||||
# endif
|
||||
|
||||
private:
|
||||
friend class NimBLEDevice;
|
||||
|
||||
NimBLEScan();
|
||||
~NimBLEScan();
|
||||
static int handleGapEvent(ble_gap_event* event, void* arg);
|
||||
void onHostSync();
|
||||
|
||||
NimBLEScanCallbacks* m_pScanCallbacks;
|
||||
ble_gap_disc_params m_scanParams;
|
||||
NimBLEScanResults m_scanResults;
|
||||
NimBLETaskData* m_pTaskData;
|
||||
uint8_t m_maxResults;
|
||||
|
||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
uint8_t m_phy{SCAN_ALL};
|
||||
uint16_t m_period{0};
|
||||
# endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A callback handler for callbacks associated device scanning.
|
||||
*/
|
||||
class NimBLEScanCallbacks {
|
||||
public:
|
||||
virtual ~NimBLEScanCallbacks() {}
|
||||
|
||||
/**
|
||||
* @brief Called when a new device is discovered, before the scan result is received (if applicable).
|
||||
* @param [in] advertisedDevice The device which was discovered.
|
||||
*/
|
||||
virtual void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice);
|
||||
|
||||
/**
|
||||
* @brief Called when a new scan result is complete, including scan response data (if applicable).
|
||||
* @param [in] advertisedDevice The device for which the complete result is available.
|
||||
*/
|
||||
virtual void onResult(const NimBLEAdvertisedDevice* advertisedDevice);
|
||||
|
||||
/**
|
||||
* @brief Called when a scan operation ends.
|
||||
* @param [in] scanResults The results of the scan that ended.
|
||||
* @param [in] reason The reason code for why the scan ended.
|
||||
*/
|
||||
virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason);
|
||||
};
|
||||
|
||||
#endif // CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER
|
||||
#endif // NIMBLE_CPP_SCAN_H_
|
||||
2
OTA.cpp
2
OTA.cpp
|
|
@ -17,7 +17,7 @@
|
|||
// OTA
|
||||
//
|
||||
// ==============================================================
|
||||
const char *HC__VERSION = "20250405001";
|
||||
const char *HC__VERSION = "20260413001";
|
||||
#define UPDATE_PORT ((uint16_t) 443)
|
||||
String url = "visionsoft.kr";
|
||||
String uri = "/hc/hc_firmware_update.php";
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
|
||||
dependancy: NimBLE-Arduino library, maintained by h2zero (Ryan Powell)
|
||||
requires Arduino core 3.0.2
|
||||
83
Setup.cpp
83
Setup.cpp
|
|
@ -9,7 +9,6 @@
|
|||
#include "TimeManager.h"
|
||||
#include "OTA.h"
|
||||
#include "UI.h"
|
||||
#include "LED0.h"
|
||||
#include "BLEScan.h"
|
||||
#include <esp_wifi.h>
|
||||
#include "esp_coexist.h"
|
||||
|
|
@ -57,19 +56,24 @@ void setup() {
|
|||
g_nSecond = 0;
|
||||
bShowSensor = false;
|
||||
|
||||
led0.setup(PIN_LED_WIFI, PWM_AP_FREQ, PWM_AP_CHANNEL);
|
||||
led0.setDuty(20);
|
||||
|
||||
DPRINTLN("1");
|
||||
setupConfig();
|
||||
DPRINTLN("2");
|
||||
setupStatus();
|
||||
|
||||
DPRINTLN("3");
|
||||
setupPins();
|
||||
DPRINTLN("4");
|
||||
scanI2C();
|
||||
DPRINTLN("5");
|
||||
setupSensor();
|
||||
|
||||
DPRINTLN("6");
|
||||
ui.setup();
|
||||
DPRINTLN("7");
|
||||
ui.message(0, (char *) "WiFi...");
|
||||
DPRINTLN("8");
|
||||
setupWiFi();
|
||||
DPRINTLN("9");
|
||||
|
||||
if (aht25.sensor() || aht10_0x39.sensor()) {
|
||||
ui.message(4, (char *) "Sensor... OK!");
|
||||
|
|
@ -88,13 +92,14 @@ void setup() {
|
|||
// Restore Status
|
||||
restoreStatus();
|
||||
|
||||
|
||||
// Create a task pinned to core 0
|
||||
xTaskCreatePinnedToCore(
|
||||
core0Task, // Function to run as a task
|
||||
"Task0", // Task name
|
||||
10240, // Stack size in words
|
||||
NULL, // Task input parameter
|
||||
0, // Priority of the task
|
||||
1, // Priority of the task
|
||||
&TaskHandle_0, // Task handle
|
||||
0 // Core 0
|
||||
);
|
||||
|
|
@ -193,30 +198,70 @@ void setupPostWiFi(bool bBoot = false) {
|
|||
}
|
||||
}
|
||||
|
||||
#include "driver/ledc.h"
|
||||
|
||||
void setupPins() {
|
||||
// --- Basic Digital Pin Initialization ---
|
||||
pinMode(PIN_HEATER1, OUTPUT);
|
||||
pinMode(PIN_HEATER2, OUTPUT);
|
||||
digitalWrite(PIN_HEATER1, HEATER_OFF);
|
||||
digitalWrite(PIN_HEATER2, HEATER_OFF);
|
||||
|
||||
//ledcAttachChannel(PIN_LED_WIFI, PWM_AP_FREQ, PWM_RESOLUTION, PWM_AP_CHANNEL);
|
||||
//ledcWrite(PIN_LED_WIFI, PWM_FULL * 4 / 5); // Light Blink
|
||||
|
||||
ledcAttachChannel(PIN_MIST, PWM_MIST_FREQ, PWM_RESOLUTION, PWM_MIST_CHANNEL);
|
||||
ledcWrite(PIN_MIST, PWM_OFF);
|
||||
// --- 1. High Speed Group: Motor and Fan (~26kHz) ---
|
||||
// Core 2.0.17 uses APB (80MHz). 80MHz / (3 * 1024) ≈ 26kHz.
|
||||
ledcAttachChannel(PIN_MOTOR, PWM_26KHZ_FREQ, PWM_RESOLUTION, PWM_MOTOR_CHANNEL);
|
||||
ledcAttachChannel(PIN_FAN, PWM_26KHZ_FREQ, PWM_RESOLUTION, PWM_FAN_CHANNEL);
|
||||
|
||||
// --- 2. Medium Speed Group: LED & Heaters (1221Hz) ---
|
||||
// 80MHz / (64 * 1024) = 1220.7Hz.
|
||||
ledcAttachChannel(PIN_LIGHT, PWM_1KHZ_FREQ, PWM_RESOLUTION, PWM_LIGHT_CHANNEL);
|
||||
ledcAttachChannel(PIN_LED_HEATER1, PWM_1KHZ_FREQ, PWM_RESOLUTION, PWM_HEATER1_CHANNEL);
|
||||
ledcAttachChannel(PIN_LED_HEATER2, PWM_1KHZ_FREQ, PWM_RESOLUTION, PWM_HEATER2_CHANNEL);
|
||||
ledcAttachChannel(PIN_LIGHT, PWM_1KHZ_FREQ, PWM_RESOLUTION, PWM_LIGHT_CHANNEL);
|
||||
|
||||
ledcAttachChannel(PIN_MOTOR, PWM_25KHZ_FREQ, PWM_RESOLUTION, PWM_MOTOR_CHANNEL);
|
||||
ledcAttachChannel(PIN_FAN, PWM_25KHZ_FREQ, PWM_RESOLUTION, PWM_FAN_CHANNEL);
|
||||
// --- 3. Low Speed Group: Mist & WiFi LED (1.19Hz) ---
|
||||
// Initialized at 32Hz to bypass the slow divider calculation/WDT panic.
|
||||
ledcAttachChannel(PIN_MIST, PWM_1HZ_FREQ, PWM_RESOLUTION, PWM_MIST_CHANNEL);
|
||||
ledcAttachChannel(PIN_LED_WIFI, PWM_1HZ_FREQ, PWM_RESOLUTION, PWM_WIFI_LED_CHANNEL);
|
||||
|
||||
ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF);
|
||||
ledcWrite(PIN_LED_HEATER2, PWM_FULL - PWM_OFF);
|
||||
ledcWrite(PIN_LIGHT, PWM_OFF);
|
||||
// MANUALLY RECONFIGURE TIMER FOR 1.19Hz
|
||||
// Source: LEDC_REF_TICK (1MHz)
|
||||
// Divider: 819 (Integer part)
|
||||
// Result: 1,000,000 / (819 * 1024) ≈ 1.192 Hz
|
||||
// Note: On Core 2.0.17, LEDC_USE_REF_TICK is the correct constant.
|
||||
/*
|
||||
ledc_timer_set(
|
||||
LEDC_LOW_SPEED_MODE,
|
||||
LEDC_TIMER_0,
|
||||
(uint32_t)(819 << 8), // 18.8 bit register: 819 integer, 0 fractional
|
||||
(uint32_t)PWM_RESOLUTION,
|
||||
LEDC_REF_TICK
|
||||
);
|
||||
*/
|
||||
/*
|
||||
ledc_timer_config_t mist_timer_conf = {
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_8_BIT,
|
||||
.timer_num = LEDC_TIMER_0,
|
||||
.freq_hz = 2, // The driver will target 1Hz
|
||||
.clk_cfg = LEDC_USE_REF_TICK, // Forces the 1MHz source
|
||||
.deconfigure = false
|
||||
};
|
||||
esp_err_t err = ledc_timer_config(&mist_timer_conf);
|
||||
if (err == ESP_OK) {
|
||||
// Apply the change and reset the counter
|
||||
ledc_timer_rst(LEDC_LOW_SPEED_MODE, LEDC_TIMER_0);
|
||||
}
|
||||
*/
|
||||
// --- Initial State Writes ---
|
||||
ledcWrite(PIN_MOTOR, PWM_OFF);
|
||||
ledcWrite(PIN_FAN, PWM_OFF);
|
||||
ledcWrite(PIN_LIGHT, PWM_OFF);
|
||||
ledcWrite(PIN_MIST, PWM_OFF);
|
||||
ledcWrite(PIN_LED_WIFI, (PWM_FULL * 4) / 5); // 80% Initial Blink
|
||||
|
||||
// Heaters (Inverted logic for MOSFET safety)
|
||||
ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF);
|
||||
ledcWrite(PIN_LED_HEATER2, PWM_FULL - PWM_OFF);
|
||||
}
|
||||
|
||||
void setupStatus() {
|
||||
|
|
@ -243,13 +288,15 @@ void restoreStatus() {
|
|||
time_t now;
|
||||
time(&now);
|
||||
uint32_t gap = (uint32_t)now - config.statusSave.now;
|
||||
DPRINTF("Reboot in %.1f seconds\n", gap / 1000.0f);
|
||||
DPRINTF("Reboot within %.1f seconds after last reset... \n", gap / 1000.0f);
|
||||
if (gap < 60000 && config.bStatusSaved) {
|
||||
status = config.statusSave;
|
||||
status.nFlags |= (uint16_t)(config.statusSave.nFlags & 0xFF);
|
||||
status.nLightDuty = 0;
|
||||
config.bStatusSaved = false;
|
||||
DPRINTLN(" Status Restored!");
|
||||
} else {
|
||||
DPRINTLN(" Status NOT Restored!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ void setup_BLE();
|
|||
// ==================================================================================
|
||||
MY_IRAM_ATTR void core0Task(void *pvParameters) {
|
||||
ESP_LOGI(TAG_TASK0,"Core 0 Task Started");
|
||||
DPRINTLN("Core 0 Task Started");
|
||||
wl_status_t lastWiFiStatus = WL_DISCONNECTED;
|
||||
unsigned long tickMillis = millis();
|
||||
unsigned long tickSecond;
|
||||
|
|
|
|||
10
UI.cpp
10
UI.cpp
|
|
@ -160,7 +160,7 @@ enum UI_ITEM {
|
|||
ITEM_COUNT
|
||||
};
|
||||
|
||||
char *title[] = {
|
||||
const char *title[] = {
|
||||
"Time:",
|
||||
|
||||
"Temp1", "Temp2", "Temp3",
|
||||
|
|
@ -691,7 +691,7 @@ MY_IRAM_ATTR bool CUI::displayBottom() {
|
|||
int lastC = 0;
|
||||
char sz[32];
|
||||
static uint8_t bLastNTP = 0xFF;
|
||||
static uint8_t bLastBLE = ~FLAG_BLE_NODATA;
|
||||
static uint8_t bLastBLE = (uint8_t)(~FLAG_BLE_NODATA);
|
||||
static uint8_t bLastConn = 0xFF;
|
||||
static uint8_t bLastWiFi = 0xFF;
|
||||
|
||||
|
|
@ -1133,20 +1133,20 @@ void CUI::checkButtonStates(unsigned long currentMillis) {
|
|||
}
|
||||
|
||||
// ISR for the Set button handling
|
||||
IRAM_ATTR void buttonSetISR() {
|
||||
ARDUINO_ISR_ATTR void buttonSetISR() {
|
||||
// Record the time of the button interrupt and set a flag
|
||||
ui.buttonSetChangeTime = millis();
|
||||
ui.bButtonSetChanged = true; // Flag for main loop to process
|
||||
}
|
||||
|
||||
// ISR for the Up button handling
|
||||
IRAM_ATTR void buttonUpISR() {
|
||||
ARDUINO_ISR_ATTR void buttonUpISR() {
|
||||
ui.buttonUpChangeTime = millis();
|
||||
ui.bButtonUpChanged = true; // Flag for main loop to process
|
||||
}
|
||||
|
||||
// ISR for the Down button handling
|
||||
IRAM_ATTR void buttonDownISR() {
|
||||
ARDUINO_ISR_ATTR void buttonDownISR() {
|
||||
ui.buttonDownChangeTime = millis();
|
||||
ui.bButtonDownChanged = true; // Flag for main loop to process
|
||||
}
|
||||
48
WiFiHost.cpp
48
WiFiHost.cpp
|
|
@ -157,14 +157,16 @@ MY_IRAM_ATTR void CWiFiHost::Stop() {
|
|||
udpExternal.stop(); // or udpExternal.end(); depending on your preference
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CWiFiHost::CloseConnection()
|
||||
MY_IRAM_ATTR
|
||||
void CWiFiHost::CloseConnection()
|
||||
{
|
||||
if (wifiClient && wifiClient.connected()) wifiClient.stop();
|
||||
m_bClientConnected = false;
|
||||
m_nMode = MODE_WAITING;
|
||||
}
|
||||
|
||||
IRAM_ATTR void CWiFiHost::Loop(unsigned long clock)
|
||||
IRAM_ATTR
|
||||
void CWiFiHost::Loop(unsigned long clock)
|
||||
{
|
||||
if (!isWiFiConnected()) return;
|
||||
|
||||
|
|
@ -250,7 +252,8 @@ IRAM_ATTR void CWiFiHost::Loop(unsigned long clock)
|
|||
}
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CWiFiHost::SendHeartBeat(unsigned long clock) {
|
||||
MY_IRAM_ATTR
|
||||
void CWiFiHost::SendHeartBeat(unsigned long clock) {
|
||||
if (!isWiFiConnected()) {
|
||||
//ESP_LOGI(TAG_WIFI_HOST,"WiFiHost - SendHeartBeat() called while not connected!");
|
||||
return;
|
||||
|
|
@ -302,7 +305,8 @@ MY_IRAM_ATTR void CWiFiHost::SendHeartBeat(unsigned long clock) {
|
|||
}
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CWiFiHost::MonitorUDP() {
|
||||
MY_IRAM_ATTR
|
||||
void CWiFiHost::MonitorUDP() {
|
||||
// Listen for UDP External port
|
||||
IPAddress ip;
|
||||
int size = udpExternal.parsePacket();
|
||||
|
|
@ -355,7 +359,8 @@ MY_IRAM_ATTR void CWiFiHost::MonitorUDP() {
|
|||
}
|
||||
}
|
||||
|
||||
IRAM_ATTR void CWiFiHost::CheckClient(unsigned long clock)
|
||||
IRAM_ATTR
|
||||
void CWiFiHost::CheckClient(unsigned long clock)
|
||||
{
|
||||
// 1. Quick exit if no data is ready to be read
|
||||
int available = wifiClient.available();
|
||||
|
|
@ -386,7 +391,8 @@ IRAM_ATTR void CWiFiHost::CheckClient(unsigned long clock)
|
|||
}
|
||||
}
|
||||
|
||||
IRAM_ATTR void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
|
||||
IRAM_ATTR
|
||||
void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
|
||||
{
|
||||
switch (pkt.cmd)
|
||||
{
|
||||
|
|
@ -463,10 +469,6 @@ IRAM_ATTR void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case CMD_PAUSE:
|
||||
break;
|
||||
case CMD_RESUME:
|
||||
break;
|
||||
case CMD_RESET_SENSOR:
|
||||
aht25.setScanFlag(true);
|
||||
aht10_0x39.setScanFlag(true);
|
||||
|
|
@ -515,11 +517,6 @@ IRAM_ATTR void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
|
|||
ESP_LOGI(TAG_WIFI_HOST,"WiFi - Send Config initiated...");
|
||||
}
|
||||
break;
|
||||
case CMD_SEND_CONFIG_SERVER:
|
||||
|
||||
case CMD_LOAD_CONFIG_SERVER:
|
||||
break;
|
||||
|
||||
// PID
|
||||
case CMD_INIT_PID_PARAM:
|
||||
history.savePID();
|
||||
|
|
@ -713,7 +710,8 @@ IRAM_ATTR void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
|
|||
}
|
||||
}
|
||||
|
||||
IRAM_ATTR int CWiFiHost::SendPacket(TCP_PACKET& pkt)
|
||||
IRAM_ATTR
|
||||
int CWiFiHost::SendPacket(TCP_PACKET& pkt)
|
||||
{
|
||||
size_t sent = 0;
|
||||
if (m_bClientConnected && wifiClient && wifiClient.connected())
|
||||
|
|
@ -723,7 +721,8 @@ IRAM_ATTR int CWiFiHost::SendPacket(TCP_PACKET& pkt)
|
|||
return sent;
|
||||
}
|
||||
|
||||
IRAM_ATTR size_t CWiFiHost::SendData(const uint8_t* data, size_t size) {
|
||||
IRAM_ATTR
|
||||
size_t CWiFiHost::SendData(const uint8_t* data, size_t size) {
|
||||
if (data != nullptr) {
|
||||
m_nMode = MODE_SEND;
|
||||
m_pDataSend_data = (char *) data;
|
||||
|
|
@ -735,7 +734,8 @@ IRAM_ATTR size_t CWiFiHost::SendData(const uint8_t* data, size_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
IRAM_ATTR // This function sends a small "chunk" then returns false.
|
||||
IRAM_ATTR
|
||||
// This function sends a small "chunk" then returns false.
|
||||
// It returns true ONLY when the entire buffer is finished.
|
||||
bool CWiFiHost::SendData(unsigned long clock)
|
||||
{
|
||||
|
|
@ -767,7 +767,8 @@ bool CWiFiHost::SendData(unsigned long clock)
|
|||
return true;
|
||||
}
|
||||
|
||||
IRAM_ATTR size_t CWiFiHost::ReceiveData(uint8_t* data, size_t size)
|
||||
IRAM_ATTR
|
||||
size_t CWiFiHost::ReceiveData(uint8_t* data, size_t size)
|
||||
{
|
||||
m_nMode = MODE_RECV;
|
||||
m_pDataReceive_data = (char *) data;
|
||||
|
|
@ -779,7 +780,8 @@ IRAM_ATTR size_t CWiFiHost::ReceiveData(uint8_t* data, size_t size)
|
|||
return size;
|
||||
}
|
||||
|
||||
IRAM_ATTR bool CWiFiHost::ReceiveData(unsigned long clock)
|
||||
IRAM_ATTR
|
||||
bool CWiFiHost::ReceiveData(unsigned long clock)
|
||||
{
|
||||
// Receive Data
|
||||
size_t nSize = 0;
|
||||
|
|
@ -822,7 +824,8 @@ IRAM_ATTR bool CWiFiHost::ReceiveData(unsigned long clock)
|
|||
return false;
|
||||
}
|
||||
|
||||
IRAM_ATTR void CWiFiHost::SendHeartBeat() {
|
||||
IRAM_ATTR
|
||||
void CWiFiHost::SendHeartBeat() {
|
||||
if (m_bHelloSent) {
|
||||
hostPacket.cmd = CMD_HEARTBEAT;
|
||||
time_t now = time(NULL); // Get current time in seconds
|
||||
|
|
@ -833,7 +836,8 @@ IRAM_ATTR void CWiFiHost::SendHeartBeat() {
|
|||
}
|
||||
}
|
||||
|
||||
MY_IRAM_ATTR void CWiFiHost::Restart() {
|
||||
MY_IRAM_ATTR
|
||||
void CWiFiHost::Restart() {
|
||||
if (isWiFiConnected()) {
|
||||
if (m_bClientConnected && m_bClientConnected && wifiClient && wifiClient.connected()) {
|
||||
hostPacket.cmd = CMD_DROP_CONNECTION;
|
||||
|
|
|
|||
|
|
@ -271,7 +271,10 @@ private:
|
|||
volatile bool m_bClientConnected;
|
||||
//uint32_t m_dwPublicIP;
|
||||
//uint16_t m_nPublicPort;
|
||||
|
||||
// Send Status to VisionSoft HeartBeat service
|
||||
IPAddress m_cExternalServerIPAddress;
|
||||
|
||||
WiFiUDP udpLocal, udpExternal;
|
||||
WiFiServer wifiServer; //, wifiExternal;
|
||||
WiFiClient wifiClient;
|
||||
|
|
|
|||
4
zcd.cpp
4
zcd.cpp
|
|
@ -40,7 +40,7 @@ short getHeater2Duty() {
|
|||
}
|
||||
|
||||
// Function to set the duty based on percentage (0 to 10000)
|
||||
IRAM_ATTR void setHeater1Duty(short duty) {
|
||||
ARDUINO_ISR_ATTR void setHeater1Duty(short duty) {
|
||||
if (duty <= 0) {
|
||||
dutyHeater1 = 0; // If 0% duty, no pulse (turn off TRIAC)
|
||||
} else if (duty >= 10000) {
|
||||
|
|
@ -66,7 +66,7 @@ IRAM_ATTR void setHeater1Duty(short duty) {
|
|||
}
|
||||
|
||||
// Function to set the duty based on percentage (0 to 10000)
|
||||
IRAM_ATTR void setHeater2Duty(short duty) {
|
||||
ARDUINO_ISR_ATTR void setHeater2Duty(short duty) {
|
||||
if (config.bAC2_OnOff) {
|
||||
if (duty >= 10000) {
|
||||
digitalWrite(PIN_HEATER2, HEATER_ON);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user