mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
AM2320: negative temperature fix... (#312)
* switched the library, now uses a library which correctly handles negative temperatures * minor change
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
.DS_Store
|
||||
|
||||
+53
-124
@@ -1,130 +1,59 @@
|
||||
/**
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2016 Ratthanan Nalintasnai
|
||||
**/
|
||||
|
||||
#include "AM2320.h"
|
||||
|
||||
#include <Wire.h>
|
||||
//
|
||||
// AM2321 Temperature & Humidity Sensor library for Arduino
|
||||
// Сделана Тимофеевым Е.Н. из AM2320-master
|
||||
|
||||
AM2320::AM2320() {
|
||||
// do nothing
|
||||
unsigned int CRC16(byte *ptr, byte length)
|
||||
{
|
||||
unsigned int crc = 0xFFFF;
|
||||
uint8_t s = 0x00;
|
||||
|
||||
while(length--) {
|
||||
crc ^= *ptr++;
|
||||
for(s = 0; s < 8; s++) {
|
||||
if((crc & 0x01) != 0) {
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
} else crc >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
AM2320::AM2320()
|
||||
{
|
||||
}
|
||||
|
||||
void AM2320::begin() {
|
||||
Wire.begin();
|
||||
int AM2320::Read()
|
||||
{
|
||||
byte buf[8];
|
||||
for(int s = 0; s < 8; s++) buf[s] = 0x00;
|
||||
|
||||
Wire.beginTransmission(AM2320_address);
|
||||
Wire.endTransmission();
|
||||
// запрос 4 байт (температуры и влажности)
|
||||
Wire.beginTransmission(AM2320_address);
|
||||
Wire.write(0x03);// запрос
|
||||
Wire.write(0x00); // с 0-го адреса
|
||||
Wire.write(0x04); // 4 байта
|
||||
if (Wire.endTransmission(1) != 0) return 1;
|
||||
delayMicroseconds(1600); //>1.5ms
|
||||
// считываем результаты запроса
|
||||
Wire.requestFrom(AM2320_address, 0x08);
|
||||
for (int i = 0; i < 0x08; i++) buf[i] = Wire.read();
|
||||
|
||||
// CRC check
|
||||
unsigned int Rcrc = buf[7] << 8;
|
||||
Rcrc += buf[6];
|
||||
if (Rcrc == CRC16(buf, 6)) {
|
||||
unsigned int temperature = ((buf[4] & 0x7F) << 8) + buf[5];
|
||||
t = temperature / 10.0;
|
||||
t = ((buf[4] & 0x80) >> 7) == 1 ? t * (-1) : t;
|
||||
|
||||
unsigned int humidity = (buf[2] << 8) + buf[3];
|
||||
h = humidity / 10.0;
|
||||
return 0;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
void AM2320::begin(int sda, int scl) {
|
||||
Wire.begin(sda, scl);
|
||||
}
|
||||
#endif
|
||||
|
||||
float AM2320::getTemperature() {
|
||||
return _temperature;
|
||||
}
|
||||
|
||||
float AM2320::getHumidity() {
|
||||
return _humidity;
|
||||
}
|
||||
|
||||
bool AM2320::measure() {
|
||||
_errorCode = 0;
|
||||
|
||||
if ( ! _read_registers(0x00, 4)) {
|
||||
_errorCode = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int receivedCrc = 0; // allocate 16 bits for storing crc from sensor
|
||||
receivedCrc = ((receivedCrc | _buf[7]) << 8 | _buf[6]); // pack high and low byte together
|
||||
|
||||
if (receivedCrc == crc16(_buf, 6)) {
|
||||
int humudity = ((_buf[2] << 8) | _buf[3]);
|
||||
_humidity = humudity / 10.0;
|
||||
|
||||
int temperature = ((_buf[4] & 0x7F) << 8) | _buf[5];
|
||||
if ((_buf[2] & 0x80) >> 8 == 1) { // negative temperature
|
||||
_temperature = (temperature / 10.0) * -1; // devide data by 10 according to the datasheet
|
||||
}
|
||||
else { // positive temperature
|
||||
_temperature = temperature / 10.0; // devide data by 10 according to the datasheet
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
_errorCode = 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int AM2320::getErrorCode() {
|
||||
return _errorCode;
|
||||
}
|
||||
|
||||
bool AM2320::_read_registers(int startAddress, int numByte) {
|
||||
_wake(); // wake up sensor
|
||||
|
||||
Wire.beginTransmission(AM2320_ADDR);
|
||||
Wire.write(0x03); // function code: 0x03 - read register data
|
||||
Wire.write(startAddress); // begin address
|
||||
Wire.write(numByte); // number of bytes to read
|
||||
|
||||
// send and check result if not success, return error code
|
||||
if (Wire.endTransmission(true) != 0) {
|
||||
return false; // return sensor not ready code
|
||||
}
|
||||
delayMicroseconds(1500); // as specified in datasheet
|
||||
Wire.requestFrom(AM2320_ADDR, numByte + 4); // request bytes from sensor
|
||||
// see function code description in datasheet
|
||||
for ( int i = 0; i < numByte + 4; i++) { // read
|
||||
_buf[i] = Wire.read();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AM2320::_wake() {
|
||||
Wire.beginTransmission(AM2320_ADDR);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
unsigned int crc16(byte *byte, unsigned int numByte) {
|
||||
unsigned int crc = 0xFFFF; // 16-bit crc register
|
||||
|
||||
while (numByte > 0) { // loop until process all bytes
|
||||
crc ^= *byte; // exclusive-or crc with first byte
|
||||
|
||||
for (int i = 0; i < 8; i++) { // perform 8 shifts
|
||||
unsigned int lsb = crc & 0x01; // extract LSB from crc
|
||||
crc >>= 1; // shift be one position to the right
|
||||
|
||||
if (lsb == 0) { // LSB is 0
|
||||
continue; // repete the process
|
||||
}
|
||||
else { // LSB is 1
|
||||
crc ^= 0xA001; // exclusive-or with 1010 0000 0000 0001
|
||||
}
|
||||
}
|
||||
|
||||
numByte--; // decrement number of byte left to be processed
|
||||
byte++; // move to next byte
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
+11
-57
@@ -1,63 +1,17 @@
|
||||
/**
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2016 Ratthanan Nalintasnai
|
||||
**/
|
||||
|
||||
#ifndef AM2303_H
|
||||
#define AM2303_H
|
||||
#ifndef AM2320_H
|
||||
#define AM2320_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// address of AM2320
|
||||
#define AM2320_ADDR 0x5C
|
||||
|
||||
// maximum number of bytes that can be read consequtively before
|
||||
// sensor splits out error
|
||||
#define MAX_BYTES_READ 10
|
||||
|
||||
class AM2320 {
|
||||
public:
|
||||
AM2320();
|
||||
|
||||
// initialize AM2320
|
||||
void begin();
|
||||
|
||||
// initialize AM2320 for ESP8266 (ESP-01) which requires manual
|
||||
// specification of sda and scl pins
|
||||
#ifdef ESP8266
|
||||
void begin(int sda, int scl);
|
||||
#endif
|
||||
|
||||
bool measure();
|
||||
|
||||
float getTemperature();
|
||||
float getHumidity();
|
||||
|
||||
int getErrorCode();
|
||||
|
||||
private:
|
||||
byte _buf[ MAX_BYTES_READ ];
|
||||
float _temperature;
|
||||
float _humidity;
|
||||
int _errorCode;
|
||||
void _wake();
|
||||
bool _read_registers(int startAddress, int numByte);
|
||||
#define AM2320_address (0xB8 >> 1)
|
||||
|
||||
class AM2320
|
||||
{
|
||||
public:
|
||||
AM2320();
|
||||
float t;
|
||||
float h;
|
||||
int Read(void);
|
||||
};
|
||||
|
||||
// compute CRC16
|
||||
unsigned int crc16(byte *byte, unsigned int numByte);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
# AM2320
|
||||
Arduino library for Aosong AM2320 temperature and humidity sensor.
|
||||
|
||||
# Downloads
|
||||
|
||||
[Latest Version](https://github.com/hibikiledo/AM2320/releases)
|
||||
|
||||
# API Reference
|
||||
|
||||
## Constructor
|
||||
A constructor should be called first. This is where you can obtain a variable of the sensor library.
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
AM2320();
|
||||
```
|
||||
|
||||
### Parameters
|
||||
None
|
||||
|
||||
### Usage Example
|
||||
```cpp
|
||||
#include "AM2320.h"
|
||||
|
||||
// create a variable of sensor library
|
||||
AM2320 sensor;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## Initialize the sensor
|
||||
This should be called once in `void setup()` of an Arduino sketch.
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
void begin(void);
|
||||
```
|
||||
### Parameters
|
||||
None
|
||||
|
||||
### Return Value
|
||||
None
|
||||
|
||||
### Usage Example
|
||||
```cpp
|
||||
#include "AM2320.h"
|
||||
AM2320 sensor;
|
||||
|
||||
void setup() {
|
||||
// initialize sensor library
|
||||
sensor.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## Initialize the sensor with specific SDA and SCL pin numbers
|
||||
Same as [Initialize the sensor](#initialize-the-sensor) with support for ESP8266 Arduino core.
|
||||
This is useful on ESP-01 where SDA and SCL pins aren't default ones.
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
void begin(int sda, int scl);
|
||||
```
|
||||
|
||||
### Parameters
|
||||
- `int sda` - pin number of sda line of I<sup>2</sup>C bus
|
||||
- `int scl` - pin number of scl line of I<sup>2</sup>C bus
|
||||
|
||||
### Return Value
|
||||
None
|
||||
|
||||
#### Example
|
||||
```cpp
|
||||
#include "AM2320.h"
|
||||
AM2320 sensor;
|
||||
|
||||
void setup() {
|
||||
// initialize sensor library with SDA and SCL pins
|
||||
// https://github.com/esp8266/Arduino/blob/master/doc/libraries.md#i2c-wire-library
|
||||
sensor.begin(0, 2);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Measure temperature and humidity (%RH)
|
||||
Tell sensor to perform both temperature and humidity acquisitions.
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
bool measure();
|
||||
```
|
||||
|
||||
### Parameters
|
||||
None
|
||||
|
||||
### Return Value
|
||||
- `true` - the operation is successful
|
||||
- `false` - an error occurs. use `getErrorCode()` to get an error code. [More info](#error-codes)
|
||||
|
||||
### Usage Example
|
||||
You are encouraged to check a returned value of `measure()`.
|
||||
Chances are that the call to `measure()` hits the sensor when it is sleeping.
|
||||
This is normal behavior of AM2320 to prevent measurement errors from self-heating.
|
||||
|
||||
By checking the returned value, you can decide what to do.
|
||||
|
||||
See [**Example**](#example)
|
||||
|
||||
---
|
||||
|
||||
## Get the temperature
|
||||
Retrieve the latest temperature measurement from the call to `measure()`
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
float getTemperature();
|
||||
```
|
||||
|
||||
### Parameters
|
||||
None
|
||||
|
||||
### Return Value
|
||||
Floating point number representing the temperature in degree celcius.
|
||||
|
||||
### Usage Example
|
||||
See [**Example**](#example)
|
||||
|
||||
---
|
||||
|
||||
## Get the humidity
|
||||
Retrieve the latest humudity measurement from the call to `measure()`
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
float getHumidity();
|
||||
```
|
||||
|
||||
### Parameters
|
||||
None
|
||||
|
||||
### Return Value
|
||||
Floating point number representing the humudity in % RH (Relative Humidity).
|
||||
|
||||
### Usage Example
|
||||
See [**Example**](#example)
|
||||
|
||||
---
|
||||
|
||||
## Get error code
|
||||
Retrieve the error code of latest call to `measure()`. Error code provides more detail on the errors.
|
||||
|
||||
### Signature
|
||||
```cpp
|
||||
int getErrorCode();
|
||||
```
|
||||
#### Summary
|
||||
Return error code from latest operation.
|
||||
|
||||
#### Parameters
|
||||
None
|
||||
|
||||
#### Return Value
|
||||
Integer representing an error code. [More info](#error-codes)
|
||||
|
||||
### Usage Example
|
||||
See [**Example**](#example)
|
||||
|
||||
|
||||
|
||||
# Error Codes
|
||||
### Code 0
|
||||
**This indicates no error.** Everything works fine.
|
||||
### Code 1
|
||||
**Sensor is offline.** This happens when Arduino cannot connect to the sensor.
|
||||
|
||||
If this happens all the time from call to `measure()`. It is likely the the sensor is connected incorrectly or
|
||||
the sensor does not functional properly.
|
||||
|
||||
If this happens occasionally, It is possible that the call to `measure()` hits the sensor when it is sleeping.
|
||||
Try to slow down the call to `measure()`.
|
||||
### Code 2
|
||||
**CRC validation failed.** This happends when data transmitted from the sensor is received with errors.
|
||||
|
||||
Possible causes may be bad connections, lengthy cable, etc.
|
||||
|
||||
# Example
|
||||
```cpp
|
||||
// Include library into the sketch
|
||||
#include <AM2320.h>
|
||||
|
||||
// Create an instance of sensor
|
||||
AM2320 sensor;
|
||||
|
||||
void setup() {
|
||||
// enable serial communication
|
||||
// Include library into the sketch
|
||||
#include <AM2320.h>
|
||||
|
||||
// Create an instance of sensor
|
||||
AM2320 sensor;
|
||||
|
||||
void setup() {
|
||||
// enable serial communication
|
||||
Serial.begin(115200);
|
||||
// call sensor.begin() to initialize the library
|
||||
sensor.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// sensor.measure() returns boolean value
|
||||
// - true indicates measurement is completed and success
|
||||
// - false indicates that either sensor is not ready or crc validation failed
|
||||
// use getErrorCode() to check for cause of error.
|
||||
if (sensor.measure()) {
|
||||
Serial.print("Temperature: ");
|
||||
Serial.println(sensor.getTemperature());
|
||||
Serial.print("Humidity: ");
|
||||
Serial.println(sensor.getHumidity());
|
||||
}
|
||||
else { // error has occured
|
||||
int errorCode = sensor.getErrorCode();
|
||||
switch (errorCode) {
|
||||
case 1: Serial.println("ERR: Sensor is offline"); break;
|
||||
case 2: Serial.println("ERR: CRC validation failed."); break;
|
||||
}
|
||||
}
|
||||
|
||||
delay(500);
|
||||
}
|
||||
```
|
||||
# Contributions
|
||||
Open an issue if something doesn't work right. Submit pull request if you want to improve someting.
|
||||
|
||||
# The Story
|
||||
I wrote blog posts about the process of writing this library. You can find it here.
|
||||
|
||||
[Part 1](https://hibikiledo.xyz/2016/11/04/writing-am2320-arduino-library-1/)
|
||||
Part 2 .. working on it
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <Wire.h>
|
||||
#include <AM2320.h>
|
||||
|
||||
AM2320 th;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
switch(th.Read()) {
|
||||
case 2:
|
||||
Serial.println("CRC failed");
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("Sensor offline");
|
||||
break;
|
||||
case 0:
|
||||
Serial.print("humidity: ");
|
||||
Serial.print(th.h);
|
||||
Serial.print("%, temperature: ");
|
||||
Serial.print(th.t);
|
||||
Serial.println("*C");
|
||||
break;
|
||||
}
|
||||
|
||||
delay(200);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2016 Ratthanan Nalintasnai
|
||||
**/
|
||||
|
||||
// Include library into the sketch
|
||||
#include <AM2320.h>
|
||||
|
||||
// Create an instance of sensor
|
||||
AM2320 sensor;
|
||||
|
||||
void setup() {
|
||||
// enable serial communication
|
||||
Serial.begin(115200);
|
||||
// call sensor.begin() to initialize the library
|
||||
sensor.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// sensor.measure() returns boolean value
|
||||
// - true indicates measurement is completed and success
|
||||
// - false indicates that either sensor is not ready or crc validation failed
|
||||
// use getErrorCode() to check for cause of error.
|
||||
if (sensor.measure()) {
|
||||
Serial.print("Temperature: ");
|
||||
Serial.println(sensor.getTemperature());
|
||||
Serial.print("Humidity: ");
|
||||
Serial.println(sensor.getHumidity());
|
||||
}
|
||||
else { // error has occured
|
||||
int errorCode = sensor.getErrorCode();
|
||||
switch (errorCode) {
|
||||
case 1: Serial.println("ERR: Sensor is offline"); break;
|
||||
case 2: Serial.println("ERR: CRC validation failed."); break;
|
||||
}
|
||||
}
|
||||
|
||||
delay(500);
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
AM2320 KEYWORD1
|
||||
begin KEYWORD2
|
||||
measure KEYWORD2
|
||||
measureTemperature KEYWORD2
|
||||
measureHumidity KEYWORD2
|
||||
getTemperature KEYWORD2
|
||||
getHumidity KEYWORD2
|
||||
AM2320 KEYWORD1
|
||||
getTemperature KEYWORD2
|
||||
getHumidity KEYWORD2
|
||||
CRCCheck KEYWORD2
|
||||
|
||||
+24
-28
@@ -4,8 +4,9 @@
|
||||
//
|
||||
// Temperature and Humidity Sensor AM2320
|
||||
// written by https://github.com/krikk
|
||||
// based on this library: https://github.com/hibikiledo/AM2320
|
||||
// this code is based on version 1.0 of the above library
|
||||
// based on this library: https://github.com/thakshak/AM2320
|
||||
// this code is based on git-version https://github.com/thakshak/AM2320/commit/ddaabaf37952d4c74f3ea70af20e5a95cfdfcadb
|
||||
// of the above library
|
||||
//
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
@@ -70,35 +71,30 @@ boolean Plugin_051(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
AM2320 sensor;
|
||||
sensor.begin(Settings.Pin_i2c_sda, Settings.Pin_i2c_scl);
|
||||
AM2320 th;
|
||||
|
||||
// sensor.measure() returns boolean value
|
||||
// - true indicates measurement is completed and success
|
||||
// - false indicates that either sensor is not ready or crc validation failed
|
||||
// use getErrorCode() to check for cause of error.
|
||||
if (sensor.measure()) {
|
||||
UserVar[event->BaseVarIndex] = sensor.getTemperature();
|
||||
UserVar[event->BaseVarIndex + 1] = sensor.getHumidity();
|
||||
switch(th.Read()) {
|
||||
case 2:
|
||||
addLog(LOG_LEVEL_ERROR, F("AM2320: CRC failed"));
|
||||
break;
|
||||
case 1:
|
||||
addLog(LOG_LEVEL_ERROR, F("AM2320: Sensor offline"));
|
||||
break;
|
||||
case 0:
|
||||
UserVar[event->BaseVarIndex] = th.t;
|
||||
UserVar[event->BaseVarIndex + 1] = th.h;
|
||||
|
||||
String log = F("AM2320: Temperature: ");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
log = F("AM2320: Humidity: ");
|
||||
log += UserVar[event->BaseVarIndex + 1];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
else { // error has occured
|
||||
int errorCode = sensor.getErrorCode();
|
||||
switch (errorCode) {
|
||||
case 1: addLog(LOG_LEVEL_ERROR, F("AM2320: Sensor is offline")); break;
|
||||
case 2: addLog(LOG_LEVEL_ERROR, F("AM2320: CRC validation failed.")); break;
|
||||
}
|
||||
success = false;
|
||||
break;
|
||||
String log = F("AM2320: Temperature: ");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
log = F("AM2320: Humidity: ");
|
||||
log += UserVar[event->BaseVarIndex + 1];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
return success;
|
||||
|
||||
Reference in New Issue
Block a user