mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	ads1118 component (#5711)
Co-authored-by: Solomon <solomon.gorkhover@finnpartners.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -22,6 +22,7 @@ esphome/components/ade7880/* @kpfleming | |||||||
| esphome/components/ade7953/* @angelnu | esphome/components/ade7953/* @angelnu | ||||||
| esphome/components/ade7953_i2c/* @angelnu | esphome/components/ade7953_i2c/* @angelnu | ||||||
| esphome/components/ade7953_spi/* @angelnu | esphome/components/ade7953_spi/* @angelnu | ||||||
|  | esphome/components/ads1118/* @solomondg1 | ||||||
| esphome/components/ags10/* @mak-42 | esphome/components/ags10/* @mak-42 | ||||||
| esphome/components/airthings_ble/* @jeromelaban | esphome/components/airthings_ble/* @jeromelaban | ||||||
| esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau | esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								esphome/components/ads1118/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/ads1118/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import spi | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@solomondg1"] | ||||||
|  | DEPENDENCIES = ["spi"] | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | CONF_ADS1118_ID = "ads1118_id" | ||||||
|  |  | ||||||
|  | ads1118_ns = cg.esphome_ns.namespace("ads1118") | ||||||
|  | ADS1118 = ads1118_ns.class_("ADS1118", cg.Component, spi.SPIDevice) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(ADS1118), | ||||||
|  |     } | ||||||
|  | ).extend(spi.spi_device_schema(cs_pin_required=True)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await spi.register_spi_device(var, config) | ||||||
							
								
								
									
										126
									
								
								esphome/components/ads1118/ads1118.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								esphome/components/ads1118/ads1118.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | |||||||
|  | #include "ads1118.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ads1118 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ads1118"; | ||||||
|  | static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111; | ||||||
|  |  | ||||||
|  | void ADS1118::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up ads1118"); | ||||||
|  |   this->spi_setup(); | ||||||
|  |  | ||||||
|  |   this->config_ = 0; | ||||||
|  |   // Setup multiplexer | ||||||
|  |   //        0bx000xxxxxxxxxxxx | ||||||
|  |   this->config_ |= ADS1118_MULTIPLEXER_P0_NG << 12; | ||||||
|  |  | ||||||
|  |   // Setup Gain | ||||||
|  |   //        0bxxxx000xxxxxxxxx | ||||||
|  |   this->config_ |= ADS1118_GAIN_6P144 << 9; | ||||||
|  |  | ||||||
|  |   // Set singleshot mode | ||||||
|  |   //        0bxxxxxxx1xxxxxxxx | ||||||
|  |   this->config_ |= 0b0000000100000000; | ||||||
|  |  | ||||||
|  |   // Set data rate - 860 samples per second (we're in singleshot mode) | ||||||
|  |   //        0bxxxxxxxx100xxxxx | ||||||
|  |   this->config_ |= ADS1118_DATA_RATE_860_SPS << 5; | ||||||
|  |  | ||||||
|  |   // Set temperature sensor mode - ADC | ||||||
|  |   //        0bxxxxxxxxxxx0xxxx | ||||||
|  |   this->config_ |= 0b0000000000000000; | ||||||
|  |  | ||||||
|  |   // Set DOUT pull up - enable | ||||||
|  |   //        0bxxxxxxxxxxxx0xxx | ||||||
|  |   this->config_ |= 0b0000000000001000; | ||||||
|  |  | ||||||
|  |   // NOP - must be 01 | ||||||
|  |   //        0bxxxxxxxxxxxxx01x | ||||||
|  |   this->config_ |= 0b0000000000000010; | ||||||
|  |  | ||||||
|  |   // Not used - can be 0 or 1, lets be positive | ||||||
|  |   //        0bxxxxxxxxxxxxxxx1 | ||||||
|  |   this->config_ |= 0b0000000000000001; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ADS1118::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "ADS1118:"); | ||||||
|  |   LOG_PIN("  CS Pin:", this->cs_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode) { | ||||||
|  |   uint16_t temp_config = this->config_; | ||||||
|  |   // Multiplexer | ||||||
|  |   //        0bxBBBxxxxxxxxxxxx | ||||||
|  |   temp_config &= 0b1000111111111111; | ||||||
|  |   temp_config |= (multiplexer & 0b111) << 12; | ||||||
|  |  | ||||||
|  |   // Gain | ||||||
|  |   //        0bxxxxBBBxxxxxxxxx | ||||||
|  |   temp_config &= 0b1111000111111111; | ||||||
|  |   temp_config |= (gain & 0b111) << 9; | ||||||
|  |  | ||||||
|  |   if (temperature_mode) { | ||||||
|  |     // Set temperature sensor mode | ||||||
|  |     //        0bxxxxxxxxxxx1xxxx | ||||||
|  |     temp_config |= 0b0000000000010000; | ||||||
|  |   } else { | ||||||
|  |     // Set ADC mode | ||||||
|  |     //        0bxxxxxxxxxxx0xxxx | ||||||
|  |     temp_config &= 0b1111111111101111; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Start conversion | ||||||
|  |   temp_config |= 0b1000000000000000; | ||||||
|  |  | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(temp_config); | ||||||
|  |   this->disable(); | ||||||
|  |  | ||||||
|  |   // about 1.2 ms with 860 samples per second | ||||||
|  |   delay(2); | ||||||
|  |  | ||||||
|  |   this->enable(); | ||||||
|  |   uint8_t adc_first_byte = this->read_byte(); | ||||||
|  |   uint8_t adc_second_byte = this->read_byte(); | ||||||
|  |   this->disable(); | ||||||
|  |   uint16_t raw_conversion = encode_uint16(adc_first_byte, adc_second_byte); | ||||||
|  |  | ||||||
|  |   auto signed_conversion = static_cast<int16_t>(raw_conversion); | ||||||
|  |  | ||||||
|  |   if (temperature_mode) { | ||||||
|  |     return (signed_conversion >> 2) * 0.03125f; | ||||||
|  |   } else { | ||||||
|  |     float millivolts; | ||||||
|  |     float divider = 32768.0f; | ||||||
|  |     switch (gain) { | ||||||
|  |       case ADS1118_GAIN_6P144: | ||||||
|  |         millivolts = (signed_conversion * 6144) / divider; | ||||||
|  |         break; | ||||||
|  |       case ADS1118_GAIN_4P096: | ||||||
|  |         millivolts = (signed_conversion * 4096) / divider; | ||||||
|  |         break; | ||||||
|  |       case ADS1118_GAIN_2P048: | ||||||
|  |         millivolts = (signed_conversion * 2048) / divider; | ||||||
|  |         break; | ||||||
|  |       case ADS1118_GAIN_1P024: | ||||||
|  |         millivolts = (signed_conversion * 1024) / divider; | ||||||
|  |         break; | ||||||
|  |       case ADS1118_GAIN_0P512: | ||||||
|  |         millivolts = (signed_conversion * 512) / divider; | ||||||
|  |         break; | ||||||
|  |       case ADS1118_GAIN_0P256: | ||||||
|  |         millivolts = (signed_conversion * 256) / divider; | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         millivolts = NAN; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return millivolts / 1e3f; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ads1118 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										46
									
								
								esphome/components/ads1118/ads1118.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								esphome/components/ads1118/ads1118.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/spi/spi.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ads1118 { | ||||||
|  |  | ||||||
|  | enum ADS1118Multiplexer { | ||||||
|  |   ADS1118_MULTIPLEXER_P0_N1 = 0b000, | ||||||
|  |   ADS1118_MULTIPLEXER_P0_N3 = 0b001, | ||||||
|  |   ADS1118_MULTIPLEXER_P1_N3 = 0b010, | ||||||
|  |   ADS1118_MULTIPLEXER_P2_N3 = 0b011, | ||||||
|  |   ADS1118_MULTIPLEXER_P0_NG = 0b100, | ||||||
|  |   ADS1118_MULTIPLEXER_P1_NG = 0b101, | ||||||
|  |   ADS1118_MULTIPLEXER_P2_NG = 0b110, | ||||||
|  |   ADS1118_MULTIPLEXER_P3_NG = 0b111, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum ADS1118Gain { | ||||||
|  |   ADS1118_GAIN_6P144 = 0b000, | ||||||
|  |   ADS1118_GAIN_4P096 = 0b001, | ||||||
|  |   ADS1118_GAIN_2P048 = 0b010, | ||||||
|  |   ADS1118_GAIN_1P024 = 0b011, | ||||||
|  |   ADS1118_GAIN_0P512 = 0b100, | ||||||
|  |   ADS1118_GAIN_0P256 = 0b101, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ADS1118 : public Component, | ||||||
|  |                 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING, | ||||||
|  |                                       spi::DATA_RATE_1MHZ> { | ||||||
|  |  public: | ||||||
|  |   ADS1118() = default; | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |   /// Helper method to request a measurement from a sensor. | ||||||
|  |   float request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint16_t config_{0}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ads1118 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										97
									
								
								esphome/components/ads1118/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								esphome/components/ads1118/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, voltage_sampler | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_GAIN, | ||||||
|  |     CONF_MULTIPLEXER, | ||||||
|  |     DEVICE_CLASS_VOLTAGE, | ||||||
|  |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_CELSIUS, | ||||||
|  |     UNIT_VOLT, | ||||||
|  |     CONF_TYPE, | ||||||
|  | ) | ||||||
|  | from .. import ads1118_ns, ADS1118, CONF_ADS1118_ID | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ["voltage_sampler"] | ||||||
|  | DEPENDENCIES = ["ads1118"] | ||||||
|  |  | ||||||
|  | ADS1118Multiplexer = ads1118_ns.enum("ADS1118Multiplexer") | ||||||
|  | MUX = { | ||||||
|  |     "A0_A1": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N1, | ||||||
|  |     "A0_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N3, | ||||||
|  |     "A1_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_N3, | ||||||
|  |     "A2_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_N3, | ||||||
|  |     "A0_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_NG, | ||||||
|  |     "A1_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_NG, | ||||||
|  |     "A2_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_NG, | ||||||
|  |     "A3_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P3_NG, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADS1118Gain = ads1118_ns.enum("ADS1118Gain") | ||||||
|  | GAIN = { | ||||||
|  |     "6.144": ADS1118Gain.ADS1118_GAIN_6P144, | ||||||
|  |     "4.096": ADS1118Gain.ADS1118_GAIN_4P096, | ||||||
|  |     "2.048": ADS1118Gain.ADS1118_GAIN_2P048, | ||||||
|  |     "1.024": ADS1118Gain.ADS1118_GAIN_1P024, | ||||||
|  |     "0.512": ADS1118Gain.ADS1118_GAIN_0P512, | ||||||
|  |     "0.256": ADS1118Gain.ADS1118_GAIN_0P256, | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ADS1118Sensor = ads1118_ns.class_( | ||||||
|  |     "ADS1118Sensor", | ||||||
|  |     cg.PollingComponent, | ||||||
|  |     sensor.Sensor, | ||||||
|  |     voltage_sampler.VoltageSampler, | ||||||
|  |     cg.Parented.template(ADS1118), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | TYPE_ADC = "adc" | ||||||
|  | TYPE_TEMPERATURE = "temperature" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.typed_schema( | ||||||
|  |     { | ||||||
|  |         TYPE_ADC: sensor.sensor_schema( | ||||||
|  |             ADS1118Sensor, | ||||||
|  |             unit_of_measurement=UNIT_VOLT, | ||||||
|  |             accuracy_decimals=3, | ||||||
|  |             device_class=DEVICE_CLASS_VOLTAGE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ) | ||||||
|  |         .extend( | ||||||
|  |             { | ||||||
|  |                 cv.GenerateID(CONF_ADS1118_ID): cv.use_id(ADS1118), | ||||||
|  |                 cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"), | ||||||
|  |                 cv.Required(CONF_GAIN): cv.enum(GAIN, string=True), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         .extend(cv.polling_component_schema("60s")), | ||||||
|  |         TYPE_TEMPERATURE: sensor.sensor_schema( | ||||||
|  |             ADS1118Sensor, | ||||||
|  |             unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ) | ||||||
|  |         .extend( | ||||||
|  |             { | ||||||
|  |                 cv.GenerateID(CONF_ADS1118_ID): cv.use_id(ADS1118), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         .extend(cv.polling_component_schema("60s")), | ||||||
|  |     }, | ||||||
|  |     default_type=TYPE_ADC, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = await sensor.new_sensor(config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await cg.register_parented(var, config[CONF_ADS1118_ID]) | ||||||
|  |  | ||||||
|  |     if config[CONF_TYPE] == TYPE_ADC: | ||||||
|  |         cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) | ||||||
|  |         cg.add(var.set_gain(config[CONF_GAIN])) | ||||||
|  |     if config[CONF_TYPE] == TYPE_TEMPERATURE: | ||||||
|  |         cg.add(var.set_temperature_mode(True)) | ||||||
							
								
								
									
										29
									
								
								esphome/components/ads1118/sensor/ads1118_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/ads1118/sensor/ads1118_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | #include "ads1118_sensor.h" | ||||||
|  |  | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ads1118 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ads1118.sensor"; | ||||||
|  |  | ||||||
|  | void ADS1118Sensor::dump_config() { | ||||||
|  |   LOG_SENSOR("  ", "ADS1118 Sensor", this); | ||||||
|  |   ESP_LOGCONFIG(TAG, "    Multiplexer: %u", this->multiplexer_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "    Gain: %u", this->gain_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ADS1118Sensor::sample() { | ||||||
|  |   return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->temperature_mode_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ADS1118Sensor::update() { | ||||||
|  |   float v = this->sample(); | ||||||
|  |   if (!std::isnan(v)) { | ||||||
|  |     ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); | ||||||
|  |     this->publish_state(v); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ads1118 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										36
									
								
								esphome/components/ads1118/sensor/ads1118_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/ads1118/sensor/ads1118_sensor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/voltage_sampler/voltage_sampler.h" | ||||||
|  |  | ||||||
|  | #include "../ads1118.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ads1118 { | ||||||
|  |  | ||||||
|  | class ADS1118Sensor : public PollingComponent, | ||||||
|  |                       public sensor::Sensor, | ||||||
|  |                       public voltage_sampler::VoltageSampler, | ||||||
|  |                       public Parented<ADS1118> { | ||||||
|  |  public: | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |   void set_multiplexer(ADS1118Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } | ||||||
|  |   void set_gain(ADS1118Gain gain) { this->gain_ = gain; } | ||||||
|  |   void set_temperature_mode(bool temp) { this->temperature_mode_ = temp; } | ||||||
|  |  | ||||||
|  |   float sample() override; | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   ADS1118Multiplexer multiplexer_{ADS1118_MULTIPLEXER_P0_NG}; | ||||||
|  |   ADS1118Gain gain_{ADS1118_GAIN_6P144}; | ||||||
|  |   bool temperature_mode_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ads1118 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -322,6 +322,12 @@ ads1115: | |||||||
|   address: 0x48 |   address: 0x48 | ||||||
|   i2c_id: i2c_bus |   i2c_id: i2c_bus | ||||||
|  |  | ||||||
|  | ads1118: | ||||||
|  |   spi_id: spi_bus | ||||||
|  |   cs_pin: | ||||||
|  |     allow_other_uses: true | ||||||
|  |     number: GPIO12 | ||||||
|  |  | ||||||
| as5600: | as5600: | ||||||
|   i2c_id: i2c_bus |   i2c_id: i2c_bus | ||||||
|   dir_pin: |   dir_pin: | ||||||
| @@ -571,6 +577,14 @@ sensor: | |||||||
|     state_topic: hi/me |     state_topic: hi/me | ||||||
|     retain: false |     retain: false | ||||||
|     availability: |     availability: | ||||||
|  |   - platform: ads1118 | ||||||
|  |     name: ads1118 adc | ||||||
|  |     multiplexer: A0_A1 | ||||||
|  |     gain: 1.024 | ||||||
|  |     type: adc | ||||||
|  |   - platform: ads1118 | ||||||
|  |     name: ads1118 temperature | ||||||
|  |     type: temperature | ||||||
|   - platform: as5600 |   - platform: as5600 | ||||||
|     name: AS5600 Position |     name: AS5600 Position | ||||||
|     raw_position: |     raw_position: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user