├── configuration ├── custom_components │ └── axp192 │ │ ├── __init__.py │ │ ├── sensor.py │ │ ├── axp192.h │ │ └── axp192.cpp ├── fonts │ └── arial.ttf └── m5stickc_env.yaml ├── .gitignore ├── M5StickC Holder.stl ├── LICENSE └── README.md /configuration/custom_components/axp192/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | secrets.yaml 2 | **/.esphome/ 3 | **/__pycache__/ 4 | -------------------------------------------------------------------------------- /M5StickC Holder.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpcornil-git/HA-M5StickC/HEAD/M5StickC Holder.stl -------------------------------------------------------------------------------- /configuration/fonts/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpcornil-git/HA-M5StickC/HEAD/configuration/fonts/arial.ttf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 jpcornil-git 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /configuration/custom_components/axp192/sensor.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.components import i2c, sensor 4 | from esphome.const import CONF_ID,\ 5 | CONF_BATTERY_LEVEL, CONF_BRIGHTNESS, UNIT_PERCENT, ICON_BATTERY 6 | 7 | DEPENDENCIES = ['i2c'] 8 | 9 | axp192_ns = cg.esphome_ns.namespace('axp192') 10 | 11 | AXP192Component = axp192_ns.class_('AXP192Component', cg.PollingComponent, i2c.I2CDevice) 12 | 13 | CONFIG_SCHEMA = cv.Schema({ 14 | cv.GenerateID(): cv.declare_id(AXP192Component), 15 | cv.Optional(CONF_BATTERY_LEVEL): 16 | sensor.sensor_schema(unit_of_measurement=UNIT_PERCENT, icon=ICON_BATTERY, accuracy_decimals=1).extend({ 17 | }), 18 | cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, 19 | }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) 20 | 21 | 22 | def to_code(config): 23 | var = cg.new_Pvariable(config[CONF_ID]) 24 | yield cg.register_component(var, config) 25 | yield i2c.register_i2c_device(var, config) 26 | 27 | if CONF_BATTERY_LEVEL in config: 28 | conf = config[CONF_BATTERY_LEVEL] 29 | sens = yield sensor.new_sensor(conf) 30 | cg.add(var.set_batterylevel_sensor(sens)) 31 | 32 | if CONF_BRIGHTNESS in config: 33 | conf = config[CONF_BRIGHTNESS] 34 | cg.add(var.set_brightness(conf)) 35 | -------------------------------------------------------------------------------- /configuration/custom_components/axp192/axp192.h: -------------------------------------------------------------------------------- 1 | #ifndef __AXP192_H__ 2 | #define __AXP192_H__ 3 | 4 | #include "esphome/core/component.h" 5 | #include "esphome/components/sensor/sensor.h" 6 | #include "esphome/components/i2c/i2c.h" 7 | 8 | namespace esphome { 9 | namespace axp192 { 10 | 11 | #define SLEEP_MSEC(us) (((uint64_t)us) * 1000L) 12 | #define SLEEP_SEC(us) (((uint64_t)us) * 1000000L) 13 | #define SLEEP_MIN(us) (((uint64_t)us) * 60L * 1000000L) 14 | #define SLEEP_HR(us) (((uint64_t)us) * 60L * 60L * 1000000L) 15 | 16 | #define CURRENT_100MA (0b0000) 17 | #define CURRENT_190MA (0b0001) 18 | #define CURRENT_280MA (0b0010) 19 | #define CURRENT_360MA (0b0011) 20 | #define CURRENT_450MA (0b0100) 21 | #define CURRENT_550MA (0b0101) 22 | #define CURRENT_630MA (0b0110) 23 | #define CURRENT_700MA (0b0111) 24 | 25 | class AXP192Component : public PollingComponent, public i2c::I2CDevice { 26 | public: 27 | void set_batterylevel_sensor(sensor::Sensor *batterylevel_sensor) { batterylevel_sensor_ = batterylevel_sensor; } 28 | void set_brightness(float brightness) { brightness_ = brightness; } 29 | 30 | // ========== INTERNAL METHODS ========== 31 | // (In most use cases you won't need these) 32 | void setup() override; 33 | void dump_config() override; 34 | float get_setup_priority() const override; 35 | void update() override; 36 | 37 | protected: 38 | sensor::Sensor *batterylevel_sensor_; 39 | float brightness_{1.0f}; 40 | float curr_brightness_{-1.0f}; 41 | 42 | /** 43 | * LDO2: Display backlight 44 | * LDO3: Display Control 45 | * RTC: Don't set GPIO1 as LDO 46 | * DCDC1: Main rail. When not set the controller shuts down. 47 | * DCDC3: Use unknown 48 | */ 49 | void begin(bool disableLDO2 = false, bool disableLDO3 = false, bool disableRTC = false, bool disableDCDC1 = false, bool disableDCDC3 = false); 50 | void UpdateBrightness(); 51 | bool GetBatState(); 52 | uint8_t GetBatData(); 53 | 54 | void EnableCoulombcounter(void); 55 | void DisableCoulombcounter(void); 56 | void StopCoulombcounter(void); 57 | void ClearCoulombcounter(void); 58 | uint32_t GetCoulombchargeData(void); 59 | uint32_t GetCoulombdischargeData(void); 60 | float GetCoulombData(void); 61 | 62 | uint16_t GetVbatData(void) __attribute__((deprecated)); 63 | uint16_t GetIchargeData(void) __attribute__((deprecated)); 64 | uint16_t GetIdischargeData(void) __attribute__((deprecated)); 65 | uint16_t GetTempData(void) __attribute__((deprecated)); 66 | uint32_t GetPowerbatData(void) __attribute__((deprecated)); 67 | uint16_t GetVinData(void) __attribute__((deprecated)); 68 | uint16_t GetIinData(void) __attribute__((deprecated)); 69 | uint16_t GetVusbinData(void) __attribute__((deprecated)); 70 | uint16_t GetIusbinData(void) __attribute__((deprecated)); 71 | uint16_t GetVapsData(void) __attribute__((deprecated)); 72 | uint8_t GetBtnPress(void); 73 | 74 | // -- sleep 75 | void SetSleep(void); 76 | void DeepSleep(uint64_t time_in_us = 0); 77 | void LightSleep(uint64_t time_in_us = 0); 78 | 79 | // void SetChargeVoltage( uint8_t ); 80 | void SetChargeCurrent( uint8_t ); 81 | float GetBatVoltage(); 82 | float GetBatCurrent(); 83 | float GetVinVoltage(); 84 | float GetVinCurrent(); 85 | float GetVBusVoltage(); 86 | float GetVBusCurrent(); 87 | float GetTempInAXP192(); 88 | float GetBatPower(); 89 | float GetBatChargeCurrent(); 90 | float GetAPSVoltage(); 91 | float GetBatCoulombInput(); 92 | float GetBatCoulombOut(); 93 | uint8_t GetWarningLevel(void); 94 | void SetCoulombClear(); 95 | void SetLDO2( bool State ); 96 | void SetLDO3( bool State ); 97 | void SetAdcState(bool State); 98 | 99 | void PowerOff(); 100 | 101 | 102 | void Write1Byte( uint8_t Addr , uint8_t Data ); 103 | uint8_t Read8bit( uint8_t Addr ); 104 | uint16_t Read12Bit( uint8_t Addr); 105 | uint16_t Read13Bit( uint8_t Addr); 106 | uint16_t Read16bit( uint8_t Addr ); 107 | uint32_t Read24bit( uint8_t Addr ); 108 | uint32_t Read32bit( uint8_t Addr ); 109 | void ReadBuff( uint8_t Addr , uint8_t Size , uint8_t *Buff ); 110 | }; 111 | 112 | } 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HA-M5StickC - Wifi temperature/humidity sensor for HomeAssistant 2 | M5StickC Plus-based wifi temperature/humidity sensor for HomeAssistant using ESPHome infrastructure. 3 | 4 | ![M5StickC](https://user-images.githubusercontent.com/40644331/150952300-0b41d46b-c639-4111-89eb-ae7e53106b5b.png) 5 | 6 | Once configured, the following entities will be available within Home Assistant: 7 | * sensor 8 | * Temperature (°C) 9 | * Humidity (%) 10 | * Battery level (%) 11 | * Wifi RSSI (dBm) 12 | * Uptime (s) 13 | * binary_sensor 14 | * Button A (front, display control) 15 | * Button B (bottom) 16 | * switch 17 | * Led 18 | 19 | M5StickC will also fetch temperature and humidity corrections from HomeAssistant to compensate for M5Stick heat leaking into ENV III Hat (see following [comment/measurement data](https://github.com/esphome/issues/issues/2887#issuecomment-1015253415)). This correction is zero-order/a simple offset as of today. 20 | 21 | A typical application uses this device as a temperature or humidity sensor for the [HA Generic Thermostat](https://www.home-assistant.io/integrations/generic_thermostat/) (target_sensor parameter) to control a heater/cooler/fan switch. Led can be used within HomeAssistant to indicate e.g. switch state and button B is available to switch an auxiliary equipment or HA state. 22 | 23 | # Hardware description 24 | * [M5StickC Plus](https://docs.m5stack.com/en/core/m5stickc_plus) 25 | * [ENV III Hat](https://docs.m5stack.com/en/hat/hat_envIII) 26 | * [Holder for 3D printing](https://github.com/jpcornil-git/HA-M5StickC/blob/main/M5StickC%20Holder.stl) 27 | 28 | # M5StickC configuration 29 | * Install [ESPHome](https://esphome.io/guides/getting_started_command_line.html), e.g. using docker 30 | ``` 31 | docker pull esphome/esphome 32 | ``` 33 | * Clone this repo 34 | ``` 35 | git clone https://github.com/jpcornil-git/HA-M5StickC.git 36 | ``` 37 | * Start ESPhome with configuration folder set to this one, e.g. using docker (replace __\__ with the actual path and, if required, adapt ttyUSB0 [on Windows, replace --device /dev/ttyUSB0 with --privileged]). 38 | ``` 39 | docker run --device /dev/ttyUSB0 -p 6052:6052 -v "/HA-M5StickC/configuration:/config" esphome/esphome 40 | ``` 41 | * Start a web browser and navigate to http://localhost:6052/ to access ESPHome dashboard, you should see a new m5stickc_env device. 42 | 43 | ![ESPHome](https://user-images.githubusercontent.com/40644331/150975974-32f76b0c-ee18-437d-9910-80420ca39890.png) 44 | 45 | * Edit secrets.yaml (1) and edit/add following credentials ("myOtaPassword" will be used to secure firmware update Over-The-Air/OTA): 46 | ``` yaml 47 | # Your Wi-Fi SSID and password 48 | wifi_ssid: "myWifiSSID" 49 | wifi_password: "myWifiPassword" 50 | # Your OTA password 51 | ota_password: "myOtaPassword" 52 | ``` 53 | * Build and install firmware on your M5StickC (2) 54 | * Wireless update is only possible once M5StickC is on the network, i.e. after initial (wired) configuration and correct Wifi SSID/password configured. 55 | 56 | * Once uploaded, you should see a splash screen on the M5StickC displaying the name of the device for 3 seconds followed by a black screen (screen off by default). Cycle thru available screens using the M5/front button to select a different one: 57 | 58 | [Off] -> [Corrected Temp/Humidity sensor] -> [Raw Temp/Humidity sensor] -> [Network parameters] -> [Off] -> ... 59 | 60 | | ![HA Zoom](https://user-images.githubusercontent.com/40644331/151000476-269d5fe0-8ff0-4daa-aa97-5433fab35a71.png) | 61 | | :---: | 62 | | Corrected Temp/Humidity sensor screen | 63 | 64 | # HomeAssistant configuration 65 | * Edit HA configuration.yaml 66 | 67 | Add/adapt following content to control temperature and humidity corrections (entity names, e.g. m5stickc_env_1_offset_temperature below, have to match **devicename** in m5stickc_env.yaml). If you have multiple M5StickC, each of them needs two such entries. 68 | 69 | ``` yaml 70 | input_number: 71 | m5stickc_env_1_offset_temperature: 72 | name: Température offset 73 | unit_of_measurement: "°C" 74 | mode: box 75 | min: -5.0 76 | max: +5.0 77 | step: 0.1 78 | m5stickc_env_1_offset_humidity: 79 | name: Humidity offset 80 | unit_of_measurement: "%" 81 | mode: box 82 | min: -10.0 83 | max: +10.0 84 | step: 0.5 85 | ``` 86 | * Restart HA 87 | 88 | You should see a new ESPHome integration. 89 | 90 | ![HA integrationpng](https://user-images.githubusercontent.com/40644331/150993327-281525f8-aca9-4efc-a8a5-195ec5fab00d.png) 91 | 92 | Example of a lovelace dashboard illustrating M5StickC entities and associated temperature and humidity correction controls. 93 | 94 | ![HA overview](https://user-images.githubusercontent.com/40644331/150995882-91f921ec-5f07-4787-a00e-24ceff94937e.png) 95 | 96 | **Note**: When you update correction data, it will take up to 30s (update cycle) for these to become effective. 97 | 98 | # References 99 | 100 | * [1] AXP192 Power Management unit (PMU) is reused from [esphome-m5stickC](https://github.com/airy10/esphome-m5stickC) 101 | -------------------------------------------------------------------------------- /configuration/m5stickc_env.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | device_id: "1" 3 | devicename: "m5stickc_env_${device_id}" 4 | friendly_devicename: "M5StickC ENV ${device_id}" 5 | 6 | # - Hardware description ------------------------------------------------------- 7 | 8 | esp32: 9 | board: m5stick-c 10 | framework: 11 | type: arduino 12 | 13 | i2c: 14 | - id: bus_a 15 | # Internal/system i2c bus 16 | sda: GPIO21 17 | scl: GPIO22 18 | - id: bus_b 19 | # external/second i2c bus 20 | sda: GPIO0 21 | scl: GPIO26 22 | 23 | spi: 24 | clk_pin: GPIO13 25 | mosi_pin: GPIO15 26 | 27 | output: 28 | - platform: ledc 29 | id: builtin_led 30 | pin: 10 31 | inverted: true 32 | 33 | # - System configuration ------------------------------------------------------- 34 | 35 | # Password for Over-The-Air (OTA) firmware update 36 | ota: 37 | password: !secret ota_password 38 | 39 | # Enable logging 40 | logger: 41 | level: DEBUG 42 | 43 | # Enable Home Assistant API 44 | api: 45 | 46 | # Wifi settings 47 | wifi: 48 | ssid: !secret wifi_ssid 49 | password: !secret wifi_password 50 | 51 | # - Global variables ----------------------------------------------------------- 52 | 53 | globals: 54 | - id: menu_id 55 | type: int 56 | restore_value: no 57 | initial_value: "0" 58 | - id: offset_temperature 59 | type: float 60 | restore_value: yes 61 | initial_value: "0.0" 62 | - id: offset_humidity 63 | type: float 64 | restore_value: yes 65 | initial_value: "0.0" 66 | 67 | # Fonts 68 | font: 69 | - file: 'fonts/arial.ttf' 70 | id: font1 71 | size: 14 72 | - file: 'fonts/arial.ttf' 73 | id: font2 74 | size: 24 75 | - file: 'fonts/arial.ttf' 76 | id: font3 77 | size: 36 78 | 79 | # Colors 80 | color: 81 | - id: red 82 | red: 100% 83 | green: 0% 84 | blue: 0% 85 | - id: green 86 | red: 0% 87 | green: 100% 88 | blue: 0% 89 | - id: blue 90 | red: 0% 91 | green: 0% 92 | blue: 100% 93 | - id: gray 94 | red: 50% 95 | green: 50% 96 | blue: 50% 97 | 98 | # - ESPHome component configuration -------------------------------------------- 99 | 100 | esphome: 101 | name: $devicename 102 | on_boot: 103 | # Splash screen for 3s then display menu 1 104 | priority: 800 105 | then: 106 | - delay: 3s 107 | - lambda: |- 108 | id(menu_id) = 1; 109 | - component.update: tft_display 110 | 111 | # - Home assistant components -------------------------------------------------- 112 | 113 | # Button configuration 114 | binary_sensor: 115 | # Front/main button: Change displayed menu 116 | - platform: gpio 117 | pin: 118 | number: GPIO37 119 | inverted: true 120 | name: ${friendly_devicename} Button A 121 | on_press: 122 | then: 123 | - lambda: |- 124 | id(menu_id) += 1; 125 | if (id(menu_id) > 4) 126 | id(menu_id) = 1; 127 | - component.update: tft_display 128 | 129 | # Bottom button: Make it available for HA usage 130 | - platform: gpio 131 | pin: 132 | number: GPIO39 133 | inverted: true 134 | name: ${friendly_devicename} Button B 135 | 136 | sensor: 137 | # Temperature offset from HA (entity_id have to match) 138 | - platform: homeassistant 139 | id: ha_offset_temperature 140 | entity_id: input_number.${devicename}_offset_temperature 141 | on_value: 142 | then: 143 | # Store new offset into nvram (restore_value=true) only if new value differs 144 | - lambda: |- 145 | if ( (!isnan(x)) && (x != id(offset_temperature)) ) { 146 | ESP_LOGI("{$friendly_devicename}", "Stored new offset value for temperature = %+.1f °C (was %+.1f °C)", x, id(offset_temperature)); 147 | id(offset_temperature)=x; 148 | } 149 | # Humidity offset from HA (entity_id have to match) 150 | - platform: homeassistant 151 | id: ha_offset_humidity 152 | entity_id: input_number.${devicename}_offset_humidity 153 | on_value: 154 | then: 155 | # Store new offset into nvram only if new value differs 156 | - lambda: |- 157 | - lambda: |- 158 | if ( (!isnan(x)) && (x != id(offset_humidity)) ) { 159 | ESP_LOGI("{$friendly_devicename}", "Stored new offset value for humidity = %+.1f %% (was %+.1f %%)", x, id(offset_humidity)); 160 | id(offset_humidity)=x; 161 | } 162 | # Temperature & Humidity sensors from ENV HAT 163 | - platform: sht3xd 164 | i2c_id: bus_b 165 | address: 0x44 166 | update_interval: 30s 167 | temperature: 168 | name: ${friendly_devicename} Room Temperature 169 | id: sht3xd_temp 170 | filters: 171 | - lambda: |- 172 | return x+id(offset_temperature); 173 | humidity: 174 | name: ${friendly_devicename} Room Humidity 175 | id: sht3xd_hum 176 | filters: 177 | - lambda: |- 178 | return x+id(offset_humidity); 179 | 180 | # AXP192 power management unit (PMU) - must be present to initialize TFT power on 181 | - platform: axp192 182 | id: pmu 183 | address: 0x34 184 | i2c_id: bus_a 185 | update_interval: 30s 186 | 187 | # Battery sensor 188 | battery_level: 189 | name: ${friendly_devicename} Battery Level 190 | id: m5stick_batterylevel 191 | brightness: 0.5 192 | 193 | # Wifi RSSI sensor 194 | - platform: wifi_signal 195 | name: ${friendly_devicename} WiFi Signal 196 | id: wifi_dbm 197 | 198 | # Uptime sensor 199 | - platform: uptime 200 | name: ${friendly_devicename} Uptime 201 | 202 | # Internal LED available for HA usage 203 | light: 204 | - platform: monochromatic 205 | id: led1 206 | output: builtin_led 207 | name: ${friendly_devicename} Led 208 | 209 | # - Display configuration and management --------------------------------------- 210 | 211 | # 135x240 TFT LCD 212 | display: 213 | - platform: st7789v 214 | id: tft_display 215 | cs_pin: GPIO5 216 | dc_pin: GPIO23 217 | reset_pin: GPIO18 218 | rotation: 270 219 | lambda: |- 220 | float brightness=0.5; 221 | if (id(menu_id) == 0) { 222 | // Splash display 223 | it.print(0, 20, id(font2), green, TextAlign::TOP_LEFT, "ID = ${friendly_devicename}"); 224 | 225 | } else if (id(menu_id) == 1) { 226 | // Display off 227 | brightness=0.0; 228 | 229 | } else { 230 | // Display header 231 | it.print(0, 0, id(font1), gray, TextAlign::TOP_LEFT, "ID = ${friendly_devicename}"); 232 | if ((WiFi.status() == WL_CONNECTED)) { 233 | it.printf(0, 20, id(font1), blue, TextAlign::LEFT, "RSSI: %.0f dBm", id(wifi_dbm).state); 234 | } else { 235 | it.print(0, 20, id(font1), red, TextAlign::LEFT, "No Wifi"); 236 | } 237 | it.printf(120, 20, id(font1), blue, TextAlign::TOP_LEFT, "Battery: %.0f %%", id(m5stick_batterylevel).state); 238 | 239 | if (id(menu_id) == 2) { 240 | // Corrected temperature/humidity values 241 | it.print(0, 40, id(font3), green, TextAlign::TOP_LEFT, "Temp."); 242 | it.printf(100, 40, id(font3), green, TextAlign::TOP_LEFT, ": %.1f °C", id(sht3xd_temp).state); 243 | it.print(0, 90, id(font3), green, TextAlign::TOP_LEFT, "Hum."); 244 | it.printf(100, 90, id(font3), green, TextAlign::TOP_LEFT, ": %.1f %%", id(sht3xd_hum).state); 245 | 246 | } else if (id(menu_id) == 3) { 247 | // Raw temperature/humidity values 248 | it.print(240, 0, id(font1), red, TextAlign::TOP_RIGHT, "RAW"); 249 | it.print(0, 40, id(font3), green, TextAlign::TOP_LEFT, "Temp."); 250 | it.printf(100, 40, id(font3), red, TextAlign::TOP_LEFT, ": %.1f °C", id(sht3xd_temp).raw_state); 251 | it.print(0, 90, id(font3), green, TextAlign::TOP_LEFT, "Hum."); 252 | it.printf(100, 90, id(font3), red, TextAlign::TOP_LEFT, ": %.1f %%", id(sht3xd_hum).raw_state); 253 | 254 | } else if (id(menu_id) == 4) { 255 | // Network/wifi parameters 256 | it.printf(0, 40, id(font1), green, TextAlign::TOP_LEFT, "Hostname: %s", WiFi.getHostname()); 257 | it.printf(0, 60, id(font1), green, TextAlign::TOP_LEFT, "MAC: %s", WiFi.macAddress().c_str()); 258 | if ((WiFi.status() == WL_CONNECTED)) { 259 | it.printf(0, 80, id(font1), green, TextAlign::TOP_LEFT, "IP: %s", WiFi.localIP().toString().c_str()); 260 | it.printf(0, 100, id(font1), green, TextAlign::TOP_LEFT, "SSID: %s", WiFi.SSID().c_str()); 261 | } 262 | } 263 | } 264 | // Update display brightness 265 | id(pmu).set_brightness(brightness); 266 | id(pmu).update(); 267 | 268 | -------------------------------------------------------------------------------- /configuration/custom_components/axp192/axp192.cpp: -------------------------------------------------------------------------------- 1 | #include "axp192.h" 2 | #include "esphome/core/log.h" 3 | #include "esp_sleep.h" 4 | 5 | namespace esphome { 6 | namespace axp192 { 7 | 8 | static const char *TAG = "axp192.sensor"; 9 | 10 | void AXP192Component::setup() 11 | { 12 | begin(false, false, false, false, false); 13 | } 14 | 15 | void AXP192Component::dump_config() { 16 | ESP_LOGCONFIG(TAG, "AXP192:"); 17 | LOG_I2C_DEVICE(this); 18 | LOG_SENSOR(" ", "Battery Level", this->batterylevel_sensor_); 19 | } 20 | 21 | float AXP192Component::get_setup_priority() const { return setup_priority::DATA; } 22 | 23 | void AXP192Component::update() { 24 | 25 | if (this->batterylevel_sensor_ != nullptr) { 26 | // To be fixed 27 | // This is not giving the right value - mostly there to have some sample sensor... 28 | float vbat = GetBatVoltage(); 29 | float batterylevel = 100.0 * ((vbat - 3.0) / (4.1 - 3.0)); 30 | 31 | ESP_LOGD(TAG, "Got Battery Level=%f (%f)", batterylevel, vbat); 32 | if (batterylevel > 100.) { 33 | batterylevel = 100; 34 | } 35 | this->batterylevel_sensor_->publish_state(batterylevel); 36 | } 37 | 38 | UpdateBrightness(); 39 | } 40 | 41 | 42 | void AXP192Component::begin(bool disableLDO2, bool disableLDO3, bool disableRTC, bool disableDCDC1, bool disableDCDC3) 43 | { 44 | // Set LDO2 & LDO3(TFT_LED & TFT) 3.0V 45 | Write1Byte(0x28, 0xcc); 46 | 47 | // Set ADC sample rate to 200hz 48 | Write1Byte(0x84, 0b11110010); 49 | 50 | // Set ADC to All Enable 51 | Write1Byte(0x82, 0xff); 52 | 53 | // Bat charge voltage to 4.2, Current 100MA 54 | Write1Byte(0x33, 0xc0); 55 | 56 | // Depending on configuration enable LDO2, LDO3, DCDC1, DCDC3. 57 | uint8_t buf = (Read8bit(0x12) & 0xef) | 0x4D; 58 | if(disableLDO3) buf &= ~(1<<3); 59 | if(disableLDO2) buf &= ~(1<<2); 60 | if(disableDCDC3) buf &= ~(1<<1); 61 | if(disableDCDC1) buf &= ~(1<<0); 62 | Write1Byte(0x12, buf); 63 | 64 | // 128ms power on, 4s power off 65 | Write1Byte(0x36, 0x0C); 66 | 67 | if(!disableRTC) 68 | { 69 | // Set RTC voltage to 3.3V 70 | Write1Byte(0x91, 0xF0); 71 | 72 | // Set GPIO0 to LDO 73 | Write1Byte(0x90, 0x02); 74 | } 75 | 76 | // Disable vbus hold limit 77 | Write1Byte(0x30, 0x80); 78 | 79 | // Set temperature protection 80 | Write1Byte(0x39, 0xfc); 81 | 82 | // Enable RTC BAT charge 83 | Write1Byte(0x35, 0xa2 & (disableRTC ? 0x7F : 0xFF)); 84 | 85 | // Enable bat detection 86 | Write1Byte(0x32, 0x46); 87 | } 88 | 89 | void AXP192Component::Write1Byte( uint8_t Addr , uint8_t Data ) 90 | { 91 | this->write_byte(Addr, Data); 92 | } 93 | 94 | uint8_t AXP192Component::Read8bit( uint8_t Addr ) 95 | { 96 | uint8_t data; 97 | this->read_byte(Addr, &data); 98 | return data; 99 | } 100 | 101 | uint16_t AXP192Component::Read12Bit( uint8_t Addr) 102 | { 103 | uint16_t Data = 0; 104 | uint8_t buf[2]; 105 | ReadBuff(Addr,2,buf); 106 | Data = ((buf[0] << 4) + buf[1]); // 107 | return Data; 108 | } 109 | 110 | uint16_t AXP192Component::Read13Bit( uint8_t Addr) 111 | { 112 | uint16_t Data = 0; 113 | uint8_t buf[2]; 114 | ReadBuff(Addr,2,buf); 115 | Data = ((buf[0] << 5) + buf[1]); // 116 | return Data; 117 | } 118 | 119 | uint16_t AXP192Component::Read16bit( uint8_t Addr ) 120 | { 121 | uint32_t ReData = 0; 122 | uint8_t Buff[2]; 123 | this->read_bytes(Addr, Buff, sizeof(Buff)); 124 | for( int i = 0 ; i < sizeof(Buff) ; i++ ) 125 | { 126 | ReData <<= 8; 127 | ReData |= Buff[i]; 128 | } 129 | return ReData; 130 | } 131 | 132 | uint32_t AXP192Component::Read24bit( uint8_t Addr ) 133 | { 134 | uint32_t ReData = 0; 135 | uint8_t Buff[3]; 136 | this->read_bytes(Addr, Buff, sizeof(Buff)); 137 | for( int i = 0 ; i < sizeof(Buff) ; i++ ) 138 | { 139 | ReData <<= 8; 140 | ReData |= Buff[i]; 141 | } 142 | return ReData; 143 | } 144 | 145 | uint32_t AXP192Component::Read32bit( uint8_t Addr ) 146 | { 147 | uint32_t ReData = 0; 148 | uint8_t Buff[4]; 149 | this->read_bytes(Addr, Buff, sizeof(Buff)); 150 | for( int i = 0 ; i < sizeof(Buff) ; i++ ) 151 | { 152 | ReData <<= 8; 153 | ReData |= Buff[i]; 154 | } 155 | return ReData; 156 | } 157 | 158 | void AXP192Component::ReadBuff( uint8_t Addr , uint8_t Size , uint8_t *Buff ) 159 | { 160 | this->read_bytes(Addr, Buff, Size); 161 | } 162 | 163 | void AXP192Component::UpdateBrightness() 164 | { 165 | ESP_LOGD(TAG, "Brightness=%f (Curr: %f)", brightness_, curr_brightness_); 166 | if (brightness_ == curr_brightness_) 167 | { 168 | return; 169 | } 170 | curr_brightness_ = brightness_; 171 | 172 | const uint8_t c_min = 7; 173 | const uint8_t c_max = 12; 174 | auto ubri = c_min + static_cast(brightness_ * (c_max - c_min)); 175 | 176 | if (ubri > c_max) 177 | { 178 | ubri = c_max; 179 | } 180 | uint8_t buf = Read8bit( 0x28 ); 181 | Write1Byte( 0x28 , ((buf & 0x0f) | (ubri << 4)) ); 182 | } 183 | 184 | bool AXP192Component::GetBatState() 185 | { 186 | if( Read8bit(0x01) | 0x20 ) 187 | return true; 188 | else 189 | return false; 190 | } 191 | 192 | uint8_t AXP192Component::GetBatData() 193 | { 194 | return Read8bit(0x75); 195 | } 196 | //---------coulombcounter_from_here--------- 197 | //enable: void EnableCoulombcounter(void); 198 | //disable: void DisableCOulombcounter(void); 199 | //stop: void StopCoulombcounter(void); 200 | //clear: void ClearCoulombcounter(void); 201 | //get charge data: uint32_t GetCoulombchargeData(void); 202 | //get discharge data: uint32_t GetCoulombdischargeData(void); 203 | //get coulomb val affter calculation: float GetCoulombData(void); 204 | //------------------------------------------ 205 | void AXP192Component::EnableCoulombcounter(void) 206 | { 207 | Write1Byte( 0xB8 , 0x80 ); 208 | } 209 | 210 | void AXP192Component::DisableCoulombcounter(void) 211 | { 212 | Write1Byte( 0xB8 , 0x00 ); 213 | } 214 | 215 | void AXP192Component::StopCoulombcounter(void) 216 | { 217 | Write1Byte( 0xB8 , 0xC0 ); 218 | } 219 | 220 | void AXP192Component::ClearCoulombcounter(void) 221 | { 222 | Write1Byte( 0xB8 , 0xA0 ); 223 | } 224 | 225 | uint32_t AXP192Component::GetCoulombchargeData(void) 226 | { 227 | return Read32bit(0xB0); 228 | } 229 | 230 | uint32_t AXP192Component::GetCoulombdischargeData(void) 231 | { 232 | return Read32bit(0xB4); 233 | } 234 | 235 | float AXP192Component::GetCoulombData(void) 236 | { 237 | 238 | uint32_t coin = 0; 239 | uint32_t coout = 0; 240 | 241 | coin = GetCoulombchargeData(); 242 | coout = GetCoulombdischargeData(); 243 | 244 | //c = 65536 * current_LSB * (coin - coout) / 3600 / ADC rate 245 | //Adc rate can be read from 84H ,change this variable if you change the ADC reate 246 | float ccc = 65536 * 0.5 * (coin - coout) / 3600.0 / 25.0; 247 | return ccc; 248 | 249 | } 250 | //----------coulomb_end_at_here---------- 251 | 252 | uint16_t AXP192Component::GetVbatData(void){ 253 | 254 | uint16_t vbat = 0; 255 | uint8_t buf[2]; 256 | ReadBuff(0x78,2,buf); 257 | vbat = ((buf[0] << 4) + buf[1]); // V 258 | return vbat; 259 | } 260 | 261 | uint16_t AXP192Component::GetVinData(void) 262 | { 263 | uint16_t vin = 0; 264 | uint8_t buf[2]; 265 | ReadBuff(0x56,2,buf); 266 | vin = ((buf[0] << 4) + buf[1]); // V 267 | return vin; 268 | } 269 | 270 | uint16_t AXP192Component::GetIinData(void) 271 | { 272 | uint16_t iin = 0; 273 | uint8_t buf[2]; 274 | ReadBuff(0x58,2,buf); 275 | iin = ((buf[0] << 4) + buf[1]); 276 | return iin; 277 | } 278 | 279 | uint16_t AXP192Component::GetVusbinData(void) 280 | { 281 | uint16_t vin = 0; 282 | uint8_t buf[2]; 283 | ReadBuff(0x5a,2,buf); 284 | vin = ((buf[0] << 4) + buf[1]); // V 285 | return vin; 286 | } 287 | 288 | uint16_t AXP192Component::GetIusbinData(void) 289 | { 290 | uint16_t iin = 0; 291 | uint8_t buf[2]; 292 | ReadBuff(0x5C,2,buf); 293 | iin = ((buf[0] << 4) + buf[1]); 294 | return iin; 295 | } 296 | 297 | uint16_t AXP192Component::GetIchargeData(void) 298 | { 299 | uint16_t icharge = 0; 300 | uint8_t buf[2]; 301 | ReadBuff(0x7A,2,buf); 302 | icharge = ( buf[0] << 5 ) + buf[1] ; 303 | return icharge; 304 | } 305 | 306 | uint16_t AXP192Component::GetIdischargeData(void) 307 | { 308 | uint16_t idischarge = 0; 309 | uint8_t buf[2]; 310 | ReadBuff(0x7C,2,buf); 311 | idischarge = ( buf[0] << 5 ) + buf[1] ; 312 | return idischarge; 313 | } 314 | 315 | uint16_t AXP192Component::GetTempData(void) 316 | { 317 | uint16_t temp = 0; 318 | uint8_t buf[2]; 319 | ReadBuff(0x5e,2,buf); 320 | temp = ((buf[0] << 4) + buf[1]); 321 | return temp; 322 | } 323 | 324 | uint32_t AXP192Component::GetPowerbatData(void) 325 | { 326 | uint32_t power = 0; 327 | uint8_t buf[3]; 328 | ReadBuff(0x70,2,buf); 329 | power = (buf[0] << 16) + (buf[1] << 8) + buf[2]; 330 | return power; 331 | } 332 | 333 | uint16_t AXP192Component::GetVapsData(void) 334 | { 335 | uint16_t vaps = 0; 336 | uint8_t buf[2]; 337 | ReadBuff(0x7e,2,buf); 338 | vaps = ((buf[0] << 4) + buf[1]); 339 | return vaps; 340 | } 341 | 342 | void AXP192Component::SetSleep(void) 343 | { 344 | Write1Byte(0x31 , Read8bit(0x31) | ( 1 << 3)); // Power off voltag 3.0v 345 | Write1Byte(0x90 , Read8bit(0x90) | 0x07); // GPIO1 floating 346 | Write1Byte(0x82, 0x00); // Disable ADCs 347 | Write1Byte(0x12, Read8bit(0x12) & 0xA1); // Disable all outputs but DCDC1 348 | } 349 | 350 | // -- sleep 351 | void AXP192Component::DeepSleep(uint64_t time_in_us) 352 | { 353 | SetSleep(); 354 | esp_sleep_enable_ext0_wakeup((gpio_num_t)37, 0 /* LOW */); 355 | if (time_in_us > 0) 356 | { 357 | esp_sleep_enable_timer_wakeup(time_in_us); 358 | } 359 | else 360 | { 361 | esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); 362 | } 363 | (time_in_us == 0) ? esp_deep_sleep_start() : esp_deep_sleep(time_in_us); 364 | } 365 | 366 | void AXP192Component::LightSleep(uint64_t time_in_us) 367 | { 368 | if (time_in_us > 0) 369 | { 370 | esp_sleep_enable_timer_wakeup(time_in_us); 371 | } 372 | else 373 | { 374 | esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); 375 | } 376 | esp_light_sleep_start(); 377 | } 378 | 379 | // 0 not press, 0x01 long press, 0x02 press 380 | uint8_t AXP192Component::GetBtnPress() 381 | { 382 | uint8_t state = Read8bit(0x46); 383 | if(state) 384 | { 385 | Write1Byte( 0x46 , 0x03 ); 386 | } 387 | return state; 388 | } 389 | 390 | uint8_t AXP192Component::GetWarningLevel(void) 391 | { 392 | return Read8bit(0x47) & 0x01; 393 | } 394 | 395 | float AXP192Component::GetBatVoltage() 396 | { 397 | float ADCLSB = 1.1 / 1000.0; 398 | uint16_t ReData = Read12Bit( 0x78 ); 399 | return ReData * ADCLSB; 400 | } 401 | 402 | float AXP192Component::GetBatCurrent() 403 | { 404 | float ADCLSB = 0.5; 405 | uint16_t CurrentIn = Read13Bit( 0x7A ); 406 | uint16_t CurrentOut = Read13Bit( 0x7C ); 407 | return ( CurrentIn - CurrentOut ) * ADCLSB; 408 | } 409 | 410 | float AXP192Component::GetVinVoltage() 411 | { 412 | float ADCLSB = 1.7 / 1000.0; 413 | uint16_t ReData = Read12Bit( 0x56 ); 414 | return ReData * ADCLSB; 415 | } 416 | 417 | float AXP192Component::GetVinCurrent() 418 | { 419 | float ADCLSB = 0.625; 420 | uint16_t ReData = Read12Bit( 0x58 ); 421 | return ReData * ADCLSB; 422 | } 423 | 424 | float AXP192Component::GetVBusVoltage() 425 | { 426 | float ADCLSB = 1.7 / 1000.0; 427 | uint16_t ReData = Read12Bit( 0x5A ); 428 | return ReData * ADCLSB; 429 | } 430 | 431 | float AXP192Component::GetVBusCurrent() 432 | { 433 | float ADCLSB = 0.375; 434 | uint16_t ReData = Read12Bit( 0x5C ); 435 | return ReData * ADCLSB; 436 | } 437 | 438 | float AXP192Component::GetTempInAXP192() 439 | { 440 | float ADCLSB = 0.1; 441 | const float OFFSET_DEG_C = -144.7; 442 | uint16_t ReData = Read12Bit( 0x5E ); 443 | return OFFSET_DEG_C + ReData * ADCLSB; 444 | } 445 | 446 | float AXP192Component::GetBatPower() 447 | { 448 | float VoltageLSB = 1.1; 449 | float CurrentLCS = 0.5; 450 | uint32_t ReData = Read24bit( 0x70 ); 451 | return VoltageLSB * CurrentLCS * ReData/ 1000.0; 452 | } 453 | 454 | float AXP192Component::GetBatChargeCurrent() 455 | { 456 | float ADCLSB = 0.5; 457 | uint16_t ReData = Read13Bit( 0x7A ); 458 | return ReData * ADCLSB; 459 | } 460 | 461 | float AXP192Component::GetAPSVoltage() 462 | { 463 | float ADCLSB = 1.4 / 1000.0; 464 | uint16_t ReData = Read12Bit( 0x7E ); 465 | return ReData * ADCLSB; 466 | } 467 | 468 | float AXP192Component::GetBatCoulombInput() 469 | { 470 | uint32_t ReData = Read32bit( 0xB0 ); 471 | return ReData * 65536 * 0.5 / 3600 /25.0; 472 | } 473 | 474 | float AXP192Component::GetBatCoulombOut() 475 | { 476 | uint32_t ReData = Read32bit( 0xB4 ); 477 | return ReData * 65536 * 0.5 / 3600 /25.0; 478 | } 479 | 480 | void AXP192Component::SetCoulombClear() 481 | { 482 | Write1Byte(0xB8,0x20); 483 | } 484 | 485 | void AXP192Component::SetLDO2( bool State ) 486 | { 487 | uint8_t buf = Read8bit(0x12); 488 | if( State == true ) 489 | { 490 | buf = (1<<2) | buf; 491 | } 492 | else 493 | { 494 | buf = ~(1<<2) & buf; 495 | } 496 | Write1Byte( 0x12 , buf ); 497 | } 498 | 499 | void AXP192Component::SetLDO3(bool State) 500 | { 501 | uint8_t buf = Read8bit(0x12); 502 | if( State == true ) 503 | { 504 | buf = (1<<3) | buf; 505 | } 506 | else 507 | { 508 | buf = ~(1<<3) & buf; 509 | } 510 | Write1Byte( 0x12 , buf ); 511 | } 512 | 513 | 514 | void AXP192Component::SetChargeCurrent(uint8_t current) 515 | { 516 | uint8_t buf = Read8bit(0x33); 517 | buf = (buf & 0xf0) | (current & 0x07); 518 | Write1Byte(0x33, buf); 519 | } 520 | 521 | void AXP192Component::PowerOff() 522 | { 523 | Write1Byte(0x32, Read8bit(0x32) | 0x80); 524 | } 525 | 526 | void AXP192Component::SetAdcState(bool state) 527 | { 528 | Write1Byte(0x82, state ? 0xff : 0x00); 529 | } 530 | } 531 | } 532 | 533 | --------------------------------------------------------------------------------