#ifndef WLED_PIN_MANAGER_H #define WLED_PIN_MANAGER_H /* * Registers pins so there is no attempt for two interfaces to use the same pin */ #include #ifdef ARDUINO_ARCH_ESP32 #include "driver/ledc.h" // needed for analog/LEDC channel counts #endif #include "const.h" // for USERMOD_* values typedef struct PinManagerPinType { int8_t pin; bool isOutput; } managed_pin_type; /* * Allows PinManager to "lock" an allocation to a specific * owner, so someone else doesn't accidentally de-allocate * a pin it hasn't allocated. Also enhances debugging. * * RAM Cost: * 17 bytes on ESP8266 * 40 bytes on ESP32 */ enum struct PinOwner : uint8_t { None = 0, // default == legacy == unspecified owner // High bit is set for all built-in pin owners Ethernet = 0x81, BusDigital = 0x82, BusOnOff = 0x83, BusPwm = 0x84, // 'BusP' == PWM output using BusPwm Button = 0x85, // 'Butn' == button from configuration IR = 0x86, // 'IR' == IR receiver pin from configuration Relay = 0x87, // 'Rly' == Relay pin from configuration SPI_RAM = 0x88, // 'SpiR' == SPI RAM DebugOut = 0x89, // 'Dbg' == debug output always IO1 DMX = 0x8A, // 'DMX' == hard-coded to IO2 HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32) HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32) // Use UserMod IDs from const.h here UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h" UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.h" // #define USERMOD_ID_FIXNETSERVICES // 0x04 // Usermod "usermod_Fix_unreachable_netservices.h" -- Does not allocate pins UM_PIR = USERMOD_ID_PIRSWITCH, // 0x05 // Usermod "usermod_PIR_sensor_switch.h" UM_IMU = USERMOD_ID_IMU, // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Interrupt pin UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h -- May use "standard" HW_I2C pins UM_RotaryEncoderUI = USERMOD_ID_ROTARY_ENC_UI, // 0x08 // Usermod "usermod_v2_rotary_encoder_ui.h" // #define USERMOD_ID_AUTO_SAVE // 0x09 // Usermod "usermod_v2_auto_save.h" -- Does not allocate pins // #define USERMOD_ID_DHT // 0x0A // Usermod "usermod_dht.h" -- Statically allocates pins, not compatible with pinManager? // #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins UM_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h" UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h" UM_Battery = USERMOD_ID_BATTERY, // // #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins // #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h // #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager UM_BH1750 = USERMOD_ID_BH1750, // 0x14 // Usermod "bh1750.h -- Uses "standard" HW_I2C pins UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h" UM_BME280 = USERMOD_ID_BME280, // 0x1E // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE, // 0x20 // Usermod "audio_reactive.h" UM_SdCard = USERMOD_ID_SD_CARD, // 0x25 // Usermod "usermod_sd_card.h" UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS, // 0x26 // Usermod "usermod_pwm_outputs.h" UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h" UM_MAX17048 = USERMOD_ID_MAX17048, // 0x2F // Usermod "usermod_max17048.h" UM_BME68X = USERMOD_ID_BME68X, // 0x31 // Usermod "usermod_bme68x.h -- Uses "standard" HW_I2C pins UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY // 0x35 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); class PinManager { private: #ifdef ESP8266 #define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17) static uint32_t pinAlloc; // 1 bit per pin, we use first 17bits #else #define WLED_NUM_PINS (GPIO_PIN_COUNT) static uint64_t pinAlloc; // 1 bit per pin, we use 50 bits on ESP32-S3 static uint16_t ledcAlloc; // up to 16 LEDC channels (WLED_MAX_ANALOG_CHANNELS) #endif static uint8_t i2cAllocCount; // allow multiple allocation of I2C bus pins but keep track of allocations static uint8_t spiAllocCount; // allow multiple allocation of SPI bus pins but keep track of allocations static PinOwner ownerTag[WLED_NUM_PINS]; public: // De-allocates a single pin static bool deallocatePin(byte gpio, PinOwner tag); // De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified) static bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag); static bool deallocateMultiplePins(const managed_pin_type *pinArray, byte arrayElementCount, PinOwner tag); // Allocates a single pin, with an owner tag. // De-allocation requires the same owner tag (or override) static bool allocatePin(byte gpio, bool output, PinOwner tag); // Allocates all the pins, or allocates none of the pins, with owner tag. // Provided to simplify error condition handling in clients // using more than one pin, such as I2C, SPI, rotary encoders, // ethernet, etc.. static bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag ); [[deprecated("Replaced by three-parameter allocatePin(gpio, output, ownerTag), for improved debugging")]] static inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); } [[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]] static inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } // will return true for reserved pins static bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); // will return false for reserved pins static bool isPinOk(byte gpio, bool output = true); static bool isReadOnlyPin(byte gpio); static PinOwner getPinOwner(byte gpio); #ifdef ARDUINO_ARCH_ESP32 static byte allocateLedc(byte channels); static void deallocateLedc(byte pos, byte channels); #endif }; //extern PinManager pinManager; #endif