mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +01:00
commit
b91a1aa027
27 changed files with 560 additions and 745 deletions
|
@ -19,7 +19,7 @@ from esphome.const import (
|
|||
CONF_PLATFORMIO_OPTIONS,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||
from esphome.helpers import color, indent
|
||||
from esphome.helpers import indent
|
||||
from esphome.util import (
|
||||
run_external_command,
|
||||
run_external_process,
|
||||
|
@ -27,6 +27,7 @@ from esphome.util import (
|
|||
list_yaml_files,
|
||||
get_serial_ports,
|
||||
)
|
||||
from esphome.log import color, setup_log, Fore
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -57,7 +58,7 @@ def choose_prompt(options):
|
|||
raise ValueError
|
||||
break
|
||||
except ValueError:
|
||||
safe_print(color("red", f"Invalid option: '{opt}'"))
|
||||
safe_print(color(Fore.RED, f"Invalid option: '{opt}'"))
|
||||
return options[opt - 1][1]
|
||||
|
||||
|
||||
|
@ -263,46 +264,6 @@ def clean_mqtt(config, args):
|
|||
)
|
||||
|
||||
|
||||
def setup_log(debug=False, quiet=False):
|
||||
if debug:
|
||||
log_level = logging.DEBUG
|
||||
CORE.verbose = True
|
||||
elif quiet:
|
||||
log_level = logging.CRITICAL
|
||||
else:
|
||||
log_level = logging.INFO
|
||||
logging.basicConfig(level=log_level)
|
||||
fmt = "%(levelname)s %(message)s"
|
||||
colorfmt = f"%(log_color)s{fmt}%(reset)s"
|
||||
datefmt = "%H:%M:%S"
|
||||
|
||||
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||
|
||||
try:
|
||||
import colorama
|
||||
|
||||
colorama.init(strip=True)
|
||||
|
||||
from colorlog import ColoredFormatter
|
||||
|
||||
logging.getLogger().handlers[0].setFormatter(
|
||||
ColoredFormatter(
|
||||
colorfmt,
|
||||
datefmt=datefmt,
|
||||
reset=True,
|
||||
log_colors={
|
||||
"DEBUG": "cyan",
|
||||
"INFO": "green",
|
||||
"WARNING": "yellow",
|
||||
"ERROR": "red",
|
||||
"CRITICAL": "red",
|
||||
},
|
||||
)
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def command_wizard(args):
|
||||
from esphome import wizard
|
||||
|
||||
|
@ -442,30 +403,30 @@ def command_update_all(args):
|
|||
click.echo(f"{half_line}{middle_text}{half_line}")
|
||||
|
||||
for f in files:
|
||||
print("Updating {}".format(color("cyan", f)))
|
||||
print("Updating {}".format(color(Fore.CYAN, f)))
|
||||
print("-" * twidth)
|
||||
print()
|
||||
rc = run_external_process(
|
||||
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
|
||||
)
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f))
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||
success[f] = True
|
||||
else:
|
||||
print_bar("[{}] {}".format(color("bold_red", "ERROR"), f))
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
|
||||
success[f] = False
|
||||
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
|
||||
print_bar("[{}]".format(color("bold_white", "SUMMARY")))
|
||||
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
|
||||
failed = 0
|
||||
for f in files:
|
||||
if success[f]:
|
||||
print(" - {}: {}".format(f, color("green", "SUCCESS")))
|
||||
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
|
||||
else:
|
||||
print(" - {}: {}".format(f, color("bold_red", "FAILED")))
|
||||
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
|
||||
failed += 1
|
||||
return failed
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ from esphome import const
|
|||
import esphome.api.api_pb2 as pb
|
||||
from esphome.const import CONF_PASSWORD, CONF_PORT
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.helpers import resolve_ip_address, indent, color
|
||||
from esphome.helpers import resolve_ip_address, indent
|
||||
from esphome.log import color, Fore
|
||||
from esphome.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -488,7 +489,7 @@ def run_logs(config, address):
|
|||
text = msg.message
|
||||
if msg.send_failed:
|
||||
text = color(
|
||||
"white",
|
||||
Fore.WHITE,
|
||||
"(Message skipped because it was too big to fit in "
|
||||
"TCP buffer - This is only cosmetic)",
|
||||
)
|
||||
|
|
|
@ -241,7 +241,7 @@ ESPBTUUID ESPBTUUID::as_128bit() const {
|
|||
}
|
||||
bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
|
||||
if (this->uuid_.len == ESP_UUID_LEN_16) {
|
||||
return (this->uuid_.uuid.uuid16 >> 8) == data2 || (this->uuid_.uuid.uuid16 & 0xFF) == data1;
|
||||
return (this->uuid_.uuid.uuid16 >> 8) == data2 && (this->uuid_.uuid.uuid16 & 0xFF) == data1;
|
||||
} else if (this->uuid_.len == ESP_UUID_LEN_32) {
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
bool a = ((this->uuid_.uuid.uuid32 >> i * 8) & 0xFF) == data1;
|
||||
|
|
|
@ -37,6 +37,11 @@ void HOT ESP8266PWM::write_state(float state) {
|
|||
uint32_t duty_off = total_time_us - duty_on;
|
||||
|
||||
if (duty_on == 0) {
|
||||
// This is a hacky fix for servos: Servo PWM high time is maximum 2.4ms by default
|
||||
// The frequency check is to affect this fix for servos mostly as the frequency is usually 50-300 hz
|
||||
if (this->pin_->digital_read() && 50 <= this->frequency_ && this->frequency_ <= 300) {
|
||||
delay(3);
|
||||
}
|
||||
stopWaveform(this->pin_->get_pin());
|
||||
this->pin_->digital_write(this->pin_->is_inverted());
|
||||
} else if (duty_off == 0) {
|
||||
|
|
|
@ -72,7 +72,7 @@ def validate_truetype_file(value):
|
|||
|
||||
|
||||
DEFAULT_GLYPHS = (
|
||||
' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
|
||||
' !"%()+,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
|
||||
)
|
||||
CONF_RAW_DATA_ID = "raw_data_id"
|
||||
|
||||
|
|
|
@ -7,22 +7,38 @@
|
|||
namespace esphome {
|
||||
namespace rc522 {
|
||||
|
||||
static const uint8_t WAIT_I_RQ = 0x30; // RxIRq and IdleIRq
|
||||
|
||||
static const char *TAG = "rc522";
|
||||
|
||||
static const uint8_t RESET_COUNT = 5;
|
||||
|
||||
void format_uid(char *buf, const uint8_t *uid, uint8_t uid_length) {
|
||||
std::string format_buffer(uint8_t *b, uint8_t len) {
|
||||
char buf[32];
|
||||
int offset = 0;
|
||||
for (uint8_t i = 0; i < uid_length; i++) {
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
const char *format = "%02X";
|
||||
if (i + 1 < uid_length)
|
||||
if (i + 1 < len)
|
||||
format = "%02X-";
|
||||
offset += sprintf(buf + offset, format, b[i]);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string format_uid(std::vector<uint8_t> &uid) {
|
||||
char buf[32];
|
||||
int offset = 0;
|
||||
for (uint8_t i = 0; i < uid.size(); i++) {
|
||||
const char *format = "%02X";
|
||||
if (i + 1 < uid.size())
|
||||
format = "%02X-";
|
||||
offset += sprintf(buf + offset, format, uid[i]);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
void RC522::setup() {
|
||||
initialize_pending_ = true;
|
||||
state_ = STATE_SETUP;
|
||||
// Pull device out of power down / reset state.
|
||||
|
||||
// First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode.
|
||||
|
@ -48,7 +64,7 @@ void RC522::setup() {
|
|||
}
|
||||
|
||||
void RC522::initialize_() {
|
||||
// Per originall code, wait 50 ms
|
||||
// Per original code, wait 50 ms
|
||||
if (millis() - reset_timeout_ < 50)
|
||||
return;
|
||||
|
||||
|
@ -75,9 +91,8 @@ void RC522::initialize_() {
|
|||
pcd_write_register(TX_ASK_REG, 0x40);
|
||||
pcd_write_register(MODE_REG, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC
|
||||
// command to 0x6363 (ISO 14443-3 part 6.2.4)
|
||||
pcd_antenna_on_(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
|
||||
|
||||
initialize_pending_ = false;
|
||||
state_ = STATE_INIT;
|
||||
}
|
||||
|
||||
void RC522::dump_config() {
|
||||
|
@ -99,76 +114,163 @@ void RC522::dump_config() {
|
|||
}
|
||||
}
|
||||
|
||||
void RC522::update() {
|
||||
if (state_ == STATE_INIT) {
|
||||
pcd_antenna_on_();
|
||||
pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
|
||||
buffer_[0] = PICC_CMD_REQA;
|
||||
pcd_transceive_data_(1);
|
||||
state_ = STATE_PICC_REQUEST_A;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Communication takes longer than update interval: %d", state_);
|
||||
}
|
||||
}
|
||||
|
||||
void RC522::loop() {
|
||||
// First check reset is needed
|
||||
if (reset_count_ > 0) {
|
||||
pcd_reset_();
|
||||
return;
|
||||
}
|
||||
if (initialize_pending_) {
|
||||
if (state_ == STATE_SETUP) {
|
||||
initialize_();
|
||||
return;
|
||||
}
|
||||
|
||||
if (millis() - update_wait_ < this->update_interval_)
|
||||
return;
|
||||
StatusCode status = STATUS_ERROR; // For lint passing. TODO: refactor this
|
||||
if (awaiting_comm_) {
|
||||
if (state_ == STATE_SELECT_SERIAL_DONE)
|
||||
status = await_crc_();
|
||||
else
|
||||
status = await_transceive_();
|
||||
|
||||
auto status = picc_is_new_card_present_();
|
||||
|
||||
static StatusCode LAST_STATUS = StatusCode::STATUS_OK;
|
||||
|
||||
if (status != LAST_STATUS) {
|
||||
ESP_LOGD(TAG, "Status is now: %d", status);
|
||||
LAST_STATUS = status;
|
||||
}
|
||||
|
||||
if (status == STATUS_ERROR) // No card
|
||||
{
|
||||
// ESP_LOGE(TAG, "Error");
|
||||
// mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != STATUS_OK) // We can receive STATUS_TIMEOUT when no card, or unexpected status.
|
||||
return;
|
||||
|
||||
// Try process card
|
||||
if (!picc_read_card_serial_()) {
|
||||
ESP_LOGW(TAG, "Requesting tag read failed!");
|
||||
return;
|
||||
};
|
||||
|
||||
if (uid_.size < 4) {
|
||||
return;
|
||||
ESP_LOGW(TAG, "Read serial size: %d", uid_.size);
|
||||
}
|
||||
|
||||
update_wait_ = millis();
|
||||
|
||||
bool report = true;
|
||||
// 1. Go through all triggers
|
||||
for (auto *trigger : this->triggers_)
|
||||
trigger->process(uid_.uiduint8_t, uid_.size);
|
||||
|
||||
// 2. Find a binary sensor
|
||||
for (auto *tag : this->binary_sensors_) {
|
||||
if (tag->process(uid_.uiduint8_t, uid_.size)) {
|
||||
// 2.1 if found, do not dump
|
||||
report = false;
|
||||
if (status == STATUS_WAITING) {
|
||||
return;
|
||||
}
|
||||
awaiting_comm_ = false;
|
||||
ESP_LOGV(TAG, "finished communication status: %d, state: %d", status, state_);
|
||||
}
|
||||
|
||||
if (report) {
|
||||
char buf[32];
|
||||
format_uid(buf, uid_.uiduint8_t, uid_.size);
|
||||
ESP_LOGD(TAG, "Found new tag '%s'", buf);
|
||||
}
|
||||
}
|
||||
switch (state_) {
|
||||
case STATE_PICC_REQUEST_A: {
|
||||
if (status == STATUS_TIMEOUT) { // no tag present
|
||||
for (auto *obj : this->binary_sensors_)
|
||||
obj->on_scan_end(); // reset the binary sensors
|
||||
ESP_LOGV(TAG, "CMD_REQA -> TIMEOUT (no tag present) %d", status);
|
||||
state_ = STATE_DONE;
|
||||
} else if (status != STATUS_OK) {
|
||||
ESP_LOGW(TAG, "CMD_REQA -> Not OK %d", status);
|
||||
state_ = STATE_DONE;
|
||||
} else if (back_length_ != 2) { // || *valid_bits_ != 0) { // ATQA must be exactly 16 bits.
|
||||
ESP_LOGW(TAG, "CMD_REQA -> OK, but unexpacted back_length_ of %d", back_length_);
|
||||
state_ = STATE_DONE;
|
||||
} else {
|
||||
state_ = STATE_READ_SERIAL;
|
||||
}
|
||||
if (state_ == STATE_DONE) {
|
||||
// Don't wait another loop cycle
|
||||
pcd_antenna_off_();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_READ_SERIAL: {
|
||||
ESP_LOGV(TAG, "STATE_READ_SERIAL (%d)", status);
|
||||
switch (uid_idx_) {
|
||||
case 0:
|
||||
buffer_[0] = PICC_CMD_SEL_CL1;
|
||||
break;
|
||||
case 3:
|
||||
buffer_[0] = PICC_CMD_SEL_CL2;
|
||||
break;
|
||||
case 6:
|
||||
buffer_[0] = PICC_CMD_SEL_CL3;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "uid_idx_ invalid, uid_idx_ = %d", uid_idx_);
|
||||
state_ = STATE_DONE;
|
||||
}
|
||||
buffer_[1] = 32;
|
||||
pcd_transceive_data_(2);
|
||||
state_ = STATE_SELECT_SERIAL;
|
||||
break;
|
||||
}
|
||||
case STATE_SELECT_SERIAL: {
|
||||
buffer_[1] = 0x70; // select
|
||||
// todo: set CRC
|
||||
buffer_[6] = buffer_[2] ^ buffer_[3] ^ buffer_[4] ^ buffer_[5];
|
||||
pcd_calculate_crc_(buffer_, 7);
|
||||
state_ = STATE_SELECT_SERIAL_DONE;
|
||||
break;
|
||||
}
|
||||
case STATE_SELECT_SERIAL_DONE: {
|
||||
send_len_ = 6;
|
||||
pcd_transceive_data_(9);
|
||||
state_ = STATE_READ_SERIAL_DONE;
|
||||
break;
|
||||
}
|
||||
case STATE_READ_SERIAL_DONE: {
|
||||
if (status != STATUS_OK || back_length_ != 3) {
|
||||
if (status == STATUS_TIMEOUT)
|
||||
ESP_LOGV(TAG, "STATE_READ_SERIAL_DONE -> TIMEOUT (no tag present) %d", status);
|
||||
else
|
||||
ESP_LOGW(TAG, "Unexpected response. Read status is %d. Read bytes: %d (%s)", status, back_length_,
|
||||
format_buffer(buffer_, 9).c_str());
|
||||
|
||||
void RC522::update() {
|
||||
for (auto *obj : this->binary_sensors_)
|
||||
obj->on_scan_end();
|
||||
}
|
||||
state_ = STATE_DONE;
|
||||
uid_idx_ = 0;
|
||||
|
||||
pcd_antenna_off_();
|
||||
return;
|
||||
}
|
||||
|
||||
// copy the uid
|
||||
bool cascade = buffer_[2] == PICC_CMD_CT; // todo: should be determined based on select response (buffer[6])
|
||||
for (uint8_t i = 2 + cascade; i < 6; i++)
|
||||
uid_buffer_[uid_idx_++] = buffer_[i];
|
||||
ESP_LOGVV(TAG, "copied uid to idx %d last byte is 0x%x, cascade is %d", uid_idx_, uid_buffer_[uid_idx_ - 1],
|
||||
cascade);
|
||||
|
||||
if (cascade) { // there is more bytes in the UID
|
||||
state_ = STATE_READ_SERIAL;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rfid_uid(std::begin(uid_buffer_), std::begin(uid_buffer_) + uid_idx_);
|
||||
uid_idx_ = 0;
|
||||
// ESP_LOGD(TAG, "Processing '%s'", format_uid(rfid_uid).c_str());
|
||||
pcd_antenna_off_();
|
||||
state_ = STATE_INIT; // scan again on next update
|
||||
bool report = true;
|
||||
|
||||
for (auto *tag : this->binary_sensors_) {
|
||||
if (tag->process(rfid_uid)) {
|
||||
report = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->current_uid_ == rfid_uid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->current_uid_ = rfid_uid;
|
||||
|
||||
for (auto *trigger : this->triggers_)
|
||||
trigger->process(rfid_uid);
|
||||
|
||||
if (report) {
|
||||
ESP_LOGD(TAG, "Found new tag '%s'", format_uid(rfid_uid).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_DONE: {
|
||||
this->current_uid_ = {};
|
||||
state_ = STATE_INIT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} // namespace rc522
|
||||
|
||||
/**
|
||||
* Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
|
||||
|
@ -176,14 +278,14 @@ void RC522::update() {
|
|||
void RC522::pcd_reset_() {
|
||||
// The datasheet does not mention how long the SoftRest command takes to complete.
|
||||
// But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg)
|
||||
// Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let
|
||||
// us be generous: 50ms.
|
||||
// Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs.
|
||||
// Let us be generous: 50ms.
|
||||
|
||||
if (millis() - reset_timeout_ < 50)
|
||||
return;
|
||||
|
||||
if (reset_count_ == RESET_COUNT) {
|
||||
ESP_LOGV(TAG, "Soft reset...");
|
||||
ESP_LOGI(TAG, "Soft reset...");
|
||||
// Issue the SoftReset command.
|
||||
pcd_write_register(COMMAND_REG, PCD_SOFT_RESET);
|
||||
}
|
||||
|
@ -199,6 +301,7 @@ void RC522::pcd_reset_() {
|
|||
|
||||
if (--reset_count_ == 0) {
|
||||
ESP_LOGE(TAG, "Unable to reset RC522.");
|
||||
this->error_code_ = RESET_FAILED;
|
||||
mark_failed();
|
||||
}
|
||||
}
|
||||
|
@ -215,49 +318,13 @@ void RC522::pcd_antenna_on_() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or
|
||||
* selection. 7 bit frame. Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT -
|
||||
* probably due do bad antenna design.
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
* Turns the antenna off by disabling pins TX1 and TX2.
|
||||
*/
|
||||
RC522::StatusCode RC522::picc_request_a_(
|
||||
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
|
||||
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
|
||||
) {
|
||||
return picc_reqa_or_wupa_(PICC_CMD_REQA, buffer_atqa, buffer_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits REQA or WUPA commands.
|
||||
* Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna
|
||||
* design.
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
RC522::StatusCode RC522::picc_reqa_or_wupa_(
|
||||
uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
|
||||
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
|
||||
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
|
||||
) {
|
||||
uint8_t valid_bits;
|
||||
RC522::StatusCode status;
|
||||
|
||||
if (buffer_atqa == nullptr || *buffer_size < 2) { // The ATQA response is 2 uint8_ts long.
|
||||
return STATUS_NO_ROOM;
|
||||
void RC522::pcd_antenna_off_() {
|
||||
uint8_t value = pcd_read_register(TX_CONTROL_REG);
|
||||
if ((value & 0x03) != 0x00) {
|
||||
pcd_write_register(TX_CONTROL_REG, value & ~0x03);
|
||||
}
|
||||
pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
|
||||
valid_bits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only)
|
||||
// uint8_t. TxLastBits = BitFramingReg[2..0]
|
||||
status = pcd_transceive_data_(&command, 1, buffer_atqa, buffer_size, &valid_bits);
|
||||
if (status != STATUS_OK)
|
||||
return status;
|
||||
if (*buffer_size != 2 || valid_bits != 0) { // ATQA must be exactly 16 bits.
|
||||
ESP_LOGVV(TAG, "picc_reqa_or_wupa_() -> STATUS_ERROR");
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -280,140 +347,86 @@ void RC522::pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to
|
|||
pcd_write_register(reg, tmp & (~mask)); // clear bit mask
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the Transceive command.
|
||||
* CRC validation can only be done if backData and backLen are specified.
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
RC522::StatusCode RC522::pcd_transceive_data_(
|
||||
uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO.
|
||||
uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO.
|
||||
uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command.
|
||||
uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
|
||||
uint8_t
|
||||
*valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. Default nullptr.
|
||||
uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
|
||||
bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be
|
||||
///< validated.
|
||||
) {
|
||||
uint8_t wait_i_rq = 0x30; // RxIRq and IdleIRq
|
||||
auto ret = pcd_communicate_with_picc_(PCD_TRANSCEIVE, wait_i_rq, send_data, send_len, back_data, back_len, valid_bits,
|
||||
rx_align, check_crc);
|
||||
|
||||
if (ret == STATUS_OK && *back_len == 5)
|
||||
ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ) -> %d [%x, %x, %x, %x, %x]", send_len, ret, back_data[0],
|
||||
back_data[1], back_data[2], back_data[3], back_data[4]);
|
||||
else
|
||||
ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ... ) -> %d", send_len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO.
|
||||
* CRC validation can only be done if backData and backLen are specified.
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
RC522::StatusCode RC522::pcd_communicate_with_picc_(
|
||||
uint8_t command, ///< The command to execute. One of the PCD_Command enums.
|
||||
uint8_t wait_i_rq, ///< The bits in the ComIrqReg register that signals successful completion of the command.
|
||||
uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO.
|
||||
uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO.
|
||||
uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command.
|
||||
uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
|
||||
uint8_t *valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits.
|
||||
uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
|
||||
bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be
|
||||
///< validated.
|
||||
) {
|
||||
ESP_LOGVV(TAG, "pcd_communicate_with_picc_(%d, %d,... %d)", command, wait_i_rq, check_crc);
|
||||
|
||||
void RC522::pcd_transceive_data_(uint8_t send_len) {
|
||||
ESP_LOGV(TAG, "PCD TRANSCEIVE: RX: %s", format_buffer(buffer_, send_len).c_str());
|
||||
delayMicroseconds(1000); // we need 1 ms delay between antenna on and those communication commands
|
||||
send_len_ = send_len;
|
||||
// Prepare values for BitFramingReg
|
||||
uint8_t tx_last_bits = valid_bits ? *valid_bits : 0;
|
||||
uint8_t bit_framing =
|
||||
(rx_align << 4) + tx_last_bits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
|
||||
// For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only)
|
||||
// uint8_t. TxLastBits = BitFramingReg[2..0]
|
||||
uint8_t bit_framing = (buffer_[0] == PICC_CMD_REQA) ? 7 : 0;
|
||||
|
||||
pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
|
||||
pcd_write_register(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits
|
||||
pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
|
||||
pcd_write_register(FIFO_DATA_REG, send_len, send_data); // Write sendData to the FIFO
|
||||
pcd_write_register(BIT_FRAMING_REG, bit_framing); // Bit adjustments
|
||||
pcd_write_register(COMMAND_REG, command); // Execute the command
|
||||
if (command == PCD_TRANSCEIVE) {
|
||||
pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts
|
||||
}
|
||||
pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
|
||||
pcd_write_register(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits
|
||||
pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization
|
||||
pcd_write_register(FIFO_DATA_REG, send_len_, buffer_); // Write sendData to the FIFO
|
||||
pcd_write_register(BIT_FRAMING_REG, bit_framing); // Bit adjustments
|
||||
pcd_write_register(COMMAND_REG, PCD_TRANSCEIVE); // Execute the command
|
||||
pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts
|
||||
awaiting_comm_ = true;
|
||||
awaiting_comm_time_ = millis();
|
||||
}
|
||||
|
||||
// Wait for the command to complete.
|
||||
// In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops
|
||||
// transmitting. Each iteration of the do-while-loop takes 17.86μs.
|
||||
// TODO check/modify for other architectures than Arduino Uno 16bit
|
||||
uint16_t i;
|
||||
for (i = 2000; i > 0; i--) {
|
||||
uint8_t n = pcd_read_register(
|
||||
COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
|
||||
if (n & wait_i_rq) { // One of the interrupts that signal success has been set.
|
||||
break;
|
||||
}
|
||||
if (n & 0x01) { // Timer interrupt - nothing received in 25ms
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
// 35.7ms and nothing happend. Communication with the MFRC522 might be down.
|
||||
if (i == 0) {
|
||||
RC522::StatusCode RC522::await_transceive_() {
|
||||
if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
|
||||
return STATUS_WAITING;
|
||||
uint8_t n = pcd_read_register(
|
||||
COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
|
||||
if (n & 0x01) { // Timer interrupt - nothing received in 25ms
|
||||
back_length_ = 0;
|
||||
error_counter_ = 0; // reset the error counter
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
if (!(n & WAIT_I_RQ)) { // None of the interrupts that signal success has been set.
|
||||
// Wait for the command to complete.
|
||||
if (millis() - awaiting_comm_time_ < 40)
|
||||
return STATUS_WAITING;
|
||||
back_length_ = 0;
|
||||
ESP_LOGW(TAG, "Communication with the MFRC522 might be down, reset in %d",
|
||||
10 - error_counter_); // todo: trigger reset?
|
||||
if (error_counter_++ > 10)
|
||||
setup();
|
||||
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
// Stop now if any errors except collisions were detected.
|
||||
uint8_t error_reg_value = pcd_read_register(
|
||||
ERROR_REG); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
|
||||
if (error_reg_value & 0x13) { // BufferOvfl ParityErr ProtocolErr
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
error_counter_ = 0; // reset the error counter
|
||||
|
||||
uint8_t valid_bits_local = 0;
|
||||
|
||||
// If the caller wants data back, get it from the MFRC522.
|
||||
if (back_data && back_len) {
|
||||
uint8_t n = pcd_read_register(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO
|
||||
if (n > *back_len) {
|
||||
return STATUS_NO_ROOM;
|
||||
}
|
||||
*back_len = n; // Number of uint8_ts returned
|
||||
pcd_read_register(FIFO_DATA_REG, n, back_data, rx_align); // Get received data from FIFO
|
||||
valid_bits_local =
|
||||
pcd_read_register(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last
|
||||
// received uint8_t. If this value is 000b, the whole uint8_t is valid.
|
||||
if (valid_bits) {
|
||||
*valid_bits = valid_bits_local;
|
||||
}
|
||||
}
|
||||
n = pcd_read_register(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO
|
||||
if (n > sizeof(buffer_))
|
||||
return STATUS_NO_ROOM;
|
||||
if (n > sizeof(buffer_) - send_len_)
|
||||
send_len_ = sizeof(buffer_) - n; // simply overwrite the sent values
|
||||
back_length_ = n; // Number of uint8_ts returned
|
||||
pcd_read_register(FIFO_DATA_REG, n, buffer_ + send_len_, rx_align_); // Get received data from FIFO
|
||||
uint8_t valid_bits_local =
|
||||
pcd_read_register(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last
|
||||
// received uint8_t. If this value is 000b, the whole uint8_t is valid.
|
||||
|
||||
// Tell about collisions
|
||||
if (error_reg_value & 0x08) { // CollErr
|
||||
ESP_LOGW(TAG, "collision error, received %d bytes + %d bits (but anticollision not implemented)",
|
||||
back_length_ - (valid_bits_local > 0), valid_bits_local);
|
||||
return STATUS_COLLISION;
|
||||
}
|
||||
|
||||
// Perform CRC_A validation if requested.
|
||||
if (back_data && back_len && check_crc) {
|
||||
// In this case a MIFARE Classic NAK is not OK.
|
||||
if (*back_len == 1 && valid_bits_local == 4) {
|
||||
return STATUS_MIFARE_NACK;
|
||||
}
|
||||
// We need at least the CRC_A value and all 8 bits of the last uint8_t must be received.
|
||||
if (*back_len < 2 || valid_bits_local != 0) {
|
||||
return STATUS_CRC_WRONG;
|
||||
}
|
||||
// Verify CRC_A - do our own calculation and store the control in controlBuffer.
|
||||
uint8_t control_buffer[2];
|
||||
RC522::StatusCode status = pcd_calculate_crc_(&back_data[0], *back_len - 2, &control_buffer[0]);
|
||||
if (status != STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
if ((back_data[*back_len - 2] != control_buffer[0]) || (back_data[*back_len - 1] != control_buffer[1])) {
|
||||
return STATUS_CRC_WRONG;
|
||||
}
|
||||
// Tell about collisions
|
||||
if (valid_bits_local) {
|
||||
ESP_LOGW(TAG, "only %d valid bits received, tag distance to high? Error code is 0x%x", valid_bits_local,
|
||||
error_reg_value); // TODO: is this always due to collissions?
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
ESP_LOGV(TAG, "received %d bytes: %s", back_length_, format_buffer(buffer_ + send_len_, back_length_).c_str());
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
@ -424,10 +437,8 @@ RC522::StatusCode RC522::pcd_communicate_with_picc_(
|
|||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
|
||||
RC522::StatusCode RC522::pcd_calculate_crc_(
|
||||
uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
|
||||
uint8_t length, ///< In: The number of uint8_ts to transfer.
|
||||
uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first.
|
||||
void RC522::pcd_calculate_crc_(uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
|
||||
uint8_t length ///< In: The number of uint8_ts to transfer.
|
||||
) {
|
||||
ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length);
|
||||
pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command.
|
||||
|
@ -436,323 +447,50 @@ RC522::StatusCode RC522::pcd_calculate_crc_(
|
|||
pcd_write_register(FIFO_DATA_REG, length, data); // Write data to the FIFO
|
||||
pcd_write_register(COMMAND_REG, PCD_CALC_CRC); // Start the calculation
|
||||
|
||||
// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs.
|
||||
// TODO check/modify for other architectures than Arduino Uno 16bit
|
||||
awaiting_comm_ = true;
|
||||
awaiting_comm_time_ = millis();
|
||||
}
|
||||
|
||||
// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us.
|
||||
for (uint16_t i = 5000; i > 0; i--) {
|
||||
// DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
|
||||
uint8_t n = pcd_read_register(DIV_IRQ_REG);
|
||||
if (n & 0x04) { // CRCIRq bit set - calculation done
|
||||
pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO.
|
||||
// Transfer the result from the registers to the result buffer
|
||||
result[0] = pcd_read_register(CRC_RESULT_REG_L);
|
||||
result[1] = pcd_read_register(CRC_RESULT_REG_H);
|
||||
RC522::StatusCode RC522::await_crc_() {
|
||||
if (millis() - awaiting_comm_time_ < 2) // wait at least 2 ms
|
||||
return STATUS_WAITING;
|
||||
|
||||
ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK");
|
||||
return STATUS_OK;
|
||||
}
|
||||
// DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
|
||||
uint8_t n = pcd_read_register(DIV_IRQ_REG);
|
||||
if (n & 0x04) { // CRCIRq bit set - calculation done
|
||||
pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO.
|
||||
// Transfer the result from the registers to the result buffer
|
||||
buffer_[7] = pcd_read_register(CRC_RESULT_REG_L);
|
||||
buffer_[8] = pcd_read_register(CRC_RESULT_REG_H);
|
||||
|
||||
ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK");
|
||||
return STATUS_OK;
|
||||
}
|
||||
ESP_LOGVV(TAG, "pcd_calculate_crc_() TIMEOUT");
|
||||
if (millis() - awaiting_comm_time_ < 89)
|
||||
return STATUS_WAITING;
|
||||
|
||||
ESP_LOGD(TAG, "pcd_calculate_crc_() TIMEOUT");
|
||||
// 89ms passed and nothing happend. Communication with the MFRC522 might be down.
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
/**
|
||||
* Returns STATUS_OK if a PICC responds to PICC_CMD_REQA.
|
||||
* Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored.
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
|
||||
RC522::StatusCode RC522::picc_is_new_card_present_() {
|
||||
uint8_t buffer_atqa[2];
|
||||
uint8_t buffer_size = sizeof(buffer_atqa);
|
||||
|
||||
// Reset baud rates
|
||||
pcd_write_register(TX_MODE_REG, 0x00);
|
||||
pcd_write_register(RX_MODE_REG, 0x00);
|
||||
// Reset ModWidthReg
|
||||
pcd_write_register(MOD_WIDTH_REG, 0x26);
|
||||
|
||||
auto result = picc_request_a_(buffer_atqa, &buffer_size);
|
||||
|
||||
ESP_LOGV(TAG, "picc_is_new_card_present_() -> %d", result);
|
||||
bool RC522BinarySensor::process(std::vector<uint8_t> &data) {
|
||||
bool result = true;
|
||||
if (data.size() != this->uid_.size())
|
||||
result = false;
|
||||
else {
|
||||
for (uint8_t i = 0; i < data.size(); i++) {
|
||||
if (data[i] != this->uid_[i]) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->publish_state(result);
|
||||
this->found_ = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper around PICC_Select.
|
||||
* Returns true if a UID could be read.
|
||||
* Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first.
|
||||
* The read UID is available in the class variable uid.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool RC522::picc_read_card_serial_() {
|
||||
RC522::StatusCode result = picc_select_(&this->uid_);
|
||||
ESP_LOGVV(TAG, "picc_select_(...) -> %d", result);
|
||||
return (result == STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits SELECT/ANTICOLLISION commands to select a single PICC.
|
||||
* Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or
|
||||
* PICC_WakeupA(). On success:
|
||||
* - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the
|
||||
* ISO/IEC 14443-3 draft.)
|
||||
* - The UID size and value of the chosen PICC is returned in *uid along with the SAK.
|
||||
*
|
||||
* A PICC UID consists of 4, 7 or 10 uint8_ts.
|
||||
* Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs two or three iterations are used:
|
||||
* UID size Number of UID uint8_ts Cascade levels Example of PICC
|
||||
* ======== =================== ============== ===============
|
||||
* single 4 1 MIFARE Classic
|
||||
* double 7 2 MIFARE Ultralight
|
||||
* triple 10 3 Not currently in use?
|
||||
*
|
||||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||||
*/
|
||||
RC522::StatusCode RC522::picc_select_(
|
||||
Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
|
||||
uint8_t valid_bits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply
|
||||
///< uid->size.
|
||||
) {
|
||||
bool uid_complete;
|
||||
bool select_done;
|
||||
bool use_cascade_tag;
|
||||
uint8_t cascade_level = 1;
|
||||
RC522::StatusCode result;
|
||||
uint8_t count;
|
||||
uint8_t check_bit;
|
||||
uint8_t index;
|
||||
uint8_t uid_index; // The first index in uid->uiduint8_t[] that is used in the current Cascade Level.
|
||||
int8_t current_level_known_bits; // The number of known UID bits in the current Cascade Level.
|
||||
uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A
|
||||
uint8_t buffer_used; // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO.
|
||||
uint8_t rx_align; // Used in BitFramingReg. Defines the bit position for the first bit received.
|
||||
uint8_t tx_last_bits; // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t.
|
||||
uint8_t *response_buffer;
|
||||
uint8_t response_length;
|
||||
|
||||
// Description of buffer structure:
|
||||
// uint8_t 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
|
||||
// uint8_t 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete
|
||||
// uint8_ts,
|
||||
// Low nibble: Extra bits. uint8_t 2: UID-data or CT See explanation below. CT means Cascade Tag. uint8_t
|
||||
// 3: UID-data uint8_t 4: UID-data uint8_t 5: UID-data uint8_t 6: BCC Block Check Character - XOR of
|
||||
// uint8_ts 2-5 uint8_t 7: CRC_A uint8_t 8: CRC_A The BCC and CRC_A are only transmitted if we know all the UID bits
|
||||
// of the current Cascade Level.
|
||||
//
|
||||
// Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
|
||||
// UID size Cascade level uint8_t2 uint8_t3 uint8_t4 uint8_t5
|
||||
// ======== ============= ===== ===== ===== =====
|
||||
// 4 uint8_ts 1 uid0 uid1 uid2 uid3
|
||||
// 7 uint8_ts 1 CT uid0 uid1 uid2
|
||||
// 2 uid3 uid4 uid5 uid6
|
||||
// 10 uint8_ts 1 CT uid0 uid1 uid2
|
||||
// 2 CT uid3 uid4 uid5
|
||||
// 3 uid6 uid7 uid8 uid9
|
||||
|
||||
// Sanity checks
|
||||
if (valid_bits > 80) {
|
||||
return STATUS_INVALID;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "picc_select_(&, %d)", valid_bits);
|
||||
|
||||
// Prepare MFRC522
|
||||
pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
|
||||
|
||||
// Repeat Cascade Level loop until we have a complete UID.
|
||||
uid_complete = false;
|
||||
while (!uid_complete) {
|
||||
// Set the Cascade Level in the SEL uint8_t, find out if we need to use the Cascade Tag in uint8_t 2.
|
||||
switch (cascade_level) {
|
||||
case 1:
|
||||
buffer[0] = PICC_CMD_SEL_CL1;
|
||||
uid_index = 0;
|
||||
use_cascade_tag = valid_bits && uid->size > 4; // When we know that the UID has more than 4 uint8_ts
|
||||
break;
|
||||
|
||||
case 2:
|
||||
buffer[0] = PICC_CMD_SEL_CL2;
|
||||
uid_index = 3;
|
||||
use_cascade_tag = valid_bits && uid->size > 7; // When we know that the UID has more than 7 uint8_ts
|
||||
break;
|
||||
|
||||
case 3:
|
||||
buffer[0] = PICC_CMD_SEL_CL3;
|
||||
uid_index = 6;
|
||||
use_cascade_tag = false; // Never used in CL3.
|
||||
break;
|
||||
|
||||
default:
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// How many UID bits are known in this Cascade Level?
|
||||
current_level_known_bits = valid_bits - (8 * uid_index);
|
||||
if (current_level_known_bits < 0) {
|
||||
current_level_known_bits = 0;
|
||||
}
|
||||
// Copy the known bits from uid->uiduint8_t[] to buffer[]
|
||||
index = 2; // destination index in buffer[]
|
||||
if (use_cascade_tag) {
|
||||
buffer[index++] = PICC_CMD_CT;
|
||||
}
|
||||
uint8_t uint8_ts_to_copy = current_level_known_bits / 8 +
|
||||
(current_level_known_bits % 8
|
||||
? 1
|
||||
: 0); // The number of uint8_ts needed to represent the known bits for this level.
|
||||
if (uint8_ts_to_copy) {
|
||||
uint8_t maxuint8_ts =
|
||||
use_cascade_tag ? 3 : 4; // Max 4 uint8_ts in each Cascade Level. Only 3 left if we use the Cascade Tag
|
||||
if (uint8_ts_to_copy > maxuint8_ts) {
|
||||
uint8_ts_to_copy = maxuint8_ts;
|
||||
}
|
||||
for (count = 0; count < uint8_ts_to_copy; count++) {
|
||||
buffer[index++] = uid->uiduint8_t[uid_index + count];
|
||||
}
|
||||
}
|
||||
// Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
|
||||
if (use_cascade_tag) {
|
||||
current_level_known_bits += 8;
|
||||
}
|
||||
|
||||
// Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
|
||||
select_done = false;
|
||||
while (!select_done) {
|
||||
// Find out how many bits and uint8_ts to send and receive.
|
||||
if (current_level_known_bits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT.
|
||||
|
||||
if (response_length < 4) {
|
||||
ESP_LOGW(TAG, "Not enough data received.");
|
||||
return STATUS_INVALID;
|
||||
}
|
||||
|
||||
// Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
|
||||
buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole uint8_ts
|
||||
// Calculate BCC - Block Check Character
|
||||
buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
|
||||
// Calculate CRC_A
|
||||
result = pcd_calculate_crc_(buffer, 7, &buffer[7]);
|
||||
if (result != STATUS_OK) {
|
||||
return result;
|
||||
}
|
||||
tx_last_bits = 0; // 0 => All 8 bits are valid.
|
||||
buffer_used = 9;
|
||||
// Store response in the last 3 uint8_ts of buffer (BCC and CRC_A - not needed after tx)
|
||||
response_buffer = &buffer[6];
|
||||
response_length = 3;
|
||||
} else { // This is an ANTICOLLISION.
|
||||
// Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
|
||||
tx_last_bits = current_level_known_bits % 8;
|
||||
count = current_level_known_bits / 8; // Number of whole uint8_ts in the UID part.
|
||||
index = 2 + count; // Number of whole uint8_ts: SEL + NVB + UIDs
|
||||
buffer[1] = (index << 4) + tx_last_bits; // NVB - Number of Valid Bits
|
||||
buffer_used = index + (tx_last_bits ? 1 : 0);
|
||||
// Store response in the unused part of buffer
|
||||
response_buffer = &buffer[index];
|
||||
response_length = sizeof(buffer) - index;
|
||||
}
|
||||
|
||||
// Set bit adjustments
|
||||
rx_align = tx_last_bits; // Having a separate variable is overkill. But it makes the next line easier to read.
|
||||
pcd_write_register(
|
||||
BIT_FRAMING_REG,
|
||||
(rx_align << 4) + tx_last_bits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
|
||||
|
||||
// Transmit the buffer and receive the response.
|
||||
result = pcd_transceive_data_(buffer, buffer_used, response_buffer, &response_length, &tx_last_bits, rx_align);
|
||||
if (result == STATUS_COLLISION) { // More than one PICC in the field => collision.
|
||||
uint8_t value_of_coll_reg = pcd_read_register(
|
||||
COLL_REG); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
|
||||
if (value_of_coll_reg & 0x20) { // CollPosNotValid
|
||||
return STATUS_COLLISION; // Without a valid collision position we cannot continue
|
||||
}
|
||||
uint8_t collision_pos = value_of_coll_reg & 0x1F; // Values 0-31, 0 means bit 32.
|
||||
if (collision_pos == 0) {
|
||||
collision_pos = 32;
|
||||
}
|
||||
if (collision_pos <= current_level_known_bits) { // No progress - should not happen
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
// Choose the PICC with the bit set.
|
||||
current_level_known_bits = collision_pos;
|
||||
count = current_level_known_bits % 8; // The bit to modify
|
||||
check_bit = (current_level_known_bits - 1) % 8;
|
||||
index = 1 + (current_level_known_bits / 8) + (count ? 1 : 0); // First uint8_t is index 0.
|
||||
if (response_length > 2) // Note: Otherwise buffer[index] might be not initialized
|
||||
buffer[index] |= (1 << check_bit);
|
||||
} else if (result != STATUS_OK) {
|
||||
return result;
|
||||
} else { // STATUS_OK
|
||||
if (current_level_known_bits >= 32) { // This was a SELECT.
|
||||
select_done = true; // No more anticollision
|
||||
// We continue below outside the while.
|
||||
} else { // This was an ANTICOLLISION.
|
||||
// We now have all 32 bits of the UID in this Cascade Level
|
||||
current_level_known_bits = 32;
|
||||
// Run loop again to do the SELECT.
|
||||
}
|
||||
}
|
||||
} // End of while (!selectDone)
|
||||
|
||||
// We do not check the CBB - it was constructed by us above.
|
||||
|
||||
// Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[]
|
||||
index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[]
|
||||
uint8_ts_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
|
||||
for (count = 0; count < uint8_ts_to_copy; count++) {
|
||||
uid->uiduint8_t[uid_index + count] = buffer[index++];
|
||||
}
|
||||
|
||||
// Check response SAK (Select Acknowledge)
|
||||
if (response_length != 3 || tx_last_bits != 0) { // SAK must be exactly 24 bits (1 uint8_t + CRC_A).
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
// Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those uint8_ts are not needed
|
||||
// anymore.
|
||||
result = pcd_calculate_crc_(response_buffer, 1, &buffer[2]);
|
||||
if (result != STATUS_OK) {
|
||||
return result;
|
||||
}
|
||||
if ((buffer[2] != response_buffer[1]) || (buffer[3] != response_buffer[2])) {
|
||||
return STATUS_CRC_WRONG;
|
||||
}
|
||||
if (response_buffer[0] & 0x04) { // Cascade bit set - UID not complete yes
|
||||
cascade_level++;
|
||||
} else {
|
||||
uid_complete = true;
|
||||
uid->sak = response_buffer[0];
|
||||
}
|
||||
} // End of while (!uidComplete)
|
||||
|
||||
// Set correct uid->size
|
||||
uid->size = 3 * cascade_level + 1;
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool RC522BinarySensor::process(const uint8_t *data, uint8_t len) {
|
||||
if (len != this->uid_.size())
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
if (data[i] != this->uid_[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
void RC522Trigger::process(const uint8_t *uid, uint8_t uid_length) {
|
||||
char buf[32];
|
||||
format_uid(buf, uid, uid_length);
|
||||
this->trigger(std::string(buf));
|
||||
}
|
||||
void RC522Trigger::process(std::vector<uint8_t> &data) { this->trigger(format_uid(data)); }
|
||||
|
||||
} // namespace rc522
|
||||
} // namespace esphome
|
||||
|
|
|
@ -26,6 +26,33 @@ class RC522 : public PollingComponent {
|
|||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
|
||||
protected:
|
||||
// Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more.
|
||||
// last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered
|
||||
enum StatusCode : uint8_t {
|
||||
STATUS_OK, // Success
|
||||
STATUS_WAITING, // Waiting result from RC522 chip
|
||||
STATUS_ERROR, // Error in communication
|
||||
STATUS_COLLISION, // Collission detected
|
||||
STATUS_TIMEOUT, // Timeout in communication.
|
||||
STATUS_NO_ROOM, // A buffer is not big enough.
|
||||
STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-)
|
||||
STATUS_INVALID, // Invalid argument.
|
||||
STATUS_CRC_WRONG, // The CRC_A does not match
|
||||
STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK.
|
||||
};
|
||||
|
||||
enum State {
|
||||
STATE_NONE = 0,
|
||||
STATE_SETUP,
|
||||
STATE_INIT,
|
||||
STATE_PICC_REQUEST_A,
|
||||
STATE_READ_SERIAL,
|
||||
STATE_SELECT_SERIAL,
|
||||
STATE_SELECT_SERIAL_DONE,
|
||||
STATE_READ_SERIAL_DONE,
|
||||
STATE_DONE,
|
||||
} state_{STATE_NONE};
|
||||
|
||||
enum PcdRegister : uint8_t {
|
||||
// Page 0: Command and status
|
||||
// 0x00 // reserved for future use
|
||||
|
@ -150,33 +177,11 @@ class RC522 : public PollingComponent {
|
|||
PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 uint8_t page to the PICC.
|
||||
};
|
||||
|
||||
// Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more.
|
||||
// last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered
|
||||
enum StatusCode : uint8_t {
|
||||
STATUS_OK, // Success
|
||||
STATUS_ERROR, // Error in communication
|
||||
STATUS_COLLISION, // Collission detected
|
||||
STATUS_TIMEOUT, // Timeout in communication.
|
||||
STATUS_NO_ROOM, // A buffer is not big enough.
|
||||
STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-)
|
||||
STATUS_INVALID, // Invalid argument.
|
||||
STATUS_CRC_WRONG, // The CRC_A does not match
|
||||
STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK.
|
||||
};
|
||||
|
||||
// A struct used for passing the UID of a PICC.
|
||||
using Uid = struct {
|
||||
uint8_t size; // Number of uint8_ts in the UID. 4, 7 or 10.
|
||||
uint8_t uiduint8_t[10];
|
||||
uint8_t sak; // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection.
|
||||
};
|
||||
|
||||
Uid uid_;
|
||||
uint32_t update_wait_{0};
|
||||
|
||||
void pcd_reset_();
|
||||
void initialize_();
|
||||
void pcd_antenna_on_();
|
||||
void pcd_antenna_off_();
|
||||
|
||||
virtual uint8_t pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums.
|
||||
) = 0;
|
||||
|
||||
|
@ -202,15 +207,6 @@ class RC522 : public PollingComponent {
|
|||
uint8_t *values ///< The values to write. uint8_t array.
|
||||
) = 0;
|
||||
|
||||
StatusCode picc_request_a_(
|
||||
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
|
||||
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
|
||||
);
|
||||
StatusCode picc_reqa_or_wupa_(
|
||||
uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
|
||||
uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in
|
||||
uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK.
|
||||
);
|
||||
void pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums.
|
||||
uint8_t mask ///< The bits to set.
|
||||
);
|
||||
|
@ -218,38 +214,33 @@ class RC522 : public PollingComponent {
|
|||
uint8_t mask ///< The bits to clear.
|
||||
);
|
||||
|
||||
StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len,
|
||||
uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
|
||||
StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len,
|
||||
uint8_t *back_data = nullptr, uint8_t *back_len = nullptr,
|
||||
uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false);
|
||||
StatusCode pcd_calculate_crc_(
|
||||
uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
|
||||
uint8_t length, ///< In: The number of uint8_ts to transfer.
|
||||
uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first.
|
||||
);
|
||||
RC522::StatusCode picc_is_new_card_present_();
|
||||
bool picc_read_card_serial_();
|
||||
StatusCode picc_select_(
|
||||
Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
|
||||
uint8_t valid_bits = 0 ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also
|
||||
///< supply uid->size.
|
||||
void pcd_transceive_data_(uint8_t send_len);
|
||||
|
||||
void pcd_calculate_crc_(uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
|
||||
uint8_t length ///< In: The number of uint8_ts to transfer.
|
||||
);
|
||||
|
||||
/** Read a data frame from the RC522 and return the result as a vector.
|
||||
*
|
||||
* Note that is_ready needs to be checked first before requesting this method.
|
||||
*
|
||||
* On failure, an empty vector is returned.
|
||||
*/
|
||||
std::vector<uint8_t> r_c522_read_data_();
|
||||
bool awaiting_comm_;
|
||||
uint32_t awaiting_comm_time_;
|
||||
StatusCode await_transceive_();
|
||||
StatusCode await_crc_();
|
||||
|
||||
uint8_t buffer_[9]; ///< buffer for communication, the first bits [0..back_idx-1] are for tx ,
|
||||
///< [back_idx..back_idx+back_len] for rx
|
||||
uint8_t send_len_; // index of first byte for RX
|
||||
uint8_t back_length_; ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned.
|
||||
uint8_t uid_buffer_[10]; // buffer to construct the uid (for 7 and 10 bit uids)
|
||||
uint8_t uid_idx_ = 0; // number of read uid bytes e.g. index of the next available position in uid_buffer
|
||||
uint8_t error_counter_ = 0; // to reset if unresponsive
|
||||
uint8_t rx_align_;
|
||||
uint8_t *valid_bits_;
|
||||
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
uint8_t reset_count_{0};
|
||||
uint32_t reset_timeout_{0};
|
||||
bool initialize_pending_{false};
|
||||
std::vector<RC522BinarySensor *> binary_sensors_;
|
||||
std::vector<RC522Trigger *> triggers_;
|
||||
std::vector<uint8_t> current_uid_;
|
||||
|
||||
enum RC522Error {
|
||||
NONE = 0,
|
||||
|
@ -261,7 +252,7 @@ class RC522BinarySensor : public binary_sensor::BinarySensor {
|
|||
public:
|
||||
void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; }
|
||||
|
||||
bool process(const uint8_t *data, uint8_t len);
|
||||
bool process(std::vector<uint8_t> &data);
|
||||
|
||||
void on_scan_end() {
|
||||
if (!this->found_) {
|
||||
|
@ -277,7 +268,7 @@ class RC522BinarySensor : public binary_sensor::BinarySensor {
|
|||
|
||||
class RC522Trigger : public Trigger<std::string> {
|
||||
public:
|
||||
void process(const uint8_t *uid, uint8_t uid_length);
|
||||
void process(std::vector<uint8_t> &data);
|
||||
};
|
||||
|
||||
} // namespace rc522
|
||||
|
|
|
@ -36,10 +36,6 @@ void RC522I2C::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
|||
return;
|
||||
}
|
||||
|
||||
std::string buf;
|
||||
buf = "Rx";
|
||||
char cstrb[20];
|
||||
|
||||
uint8_t b = values[0];
|
||||
read_bytes(reg >> 1, values, count);
|
||||
|
||||
|
@ -69,31 +65,5 @@ void RC522I2C::pcd_write_register(PcdRegister reg, ///< The register to write t
|
|||
write_bytes(reg >> 1, values, count);
|
||||
}
|
||||
|
||||
// bool RC522I2C::write_data(const std::vector<uint8_t> &data) {
|
||||
// return this->write_bytes_raw(data.data(), data.size()); }
|
||||
|
||||
// bool RC522I2C::read_data(std::vector<uint8_t> &data, uint8_t len) {
|
||||
// delay(5);
|
||||
|
||||
// std::vector<uint8_t> ready;
|
||||
// ready.resize(1);
|
||||
// uint32_t start_time = millis();
|
||||
// while (true) {
|
||||
// if (this->read_bytes_raw(ready.data(), 1)) {
|
||||
// if (ready[0] == 0x01)
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (millis() - start_time > 100) {
|
||||
// ESP_LOGV(TAG, "Timed out waiting for readiness from RC522!");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// data.resize(len + 1);
|
||||
// this->read_bytes_raw(data.data(), len + 1);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
} // namespace rc522_i2c
|
||||
} // namespace esphome
|
||||
|
|
|
@ -32,7 +32,7 @@ uint8_t RC522Spi::pcd_read_register(PcdRegister reg ///< The register to read f
|
|||
transfer_byte(0x80 | reg);
|
||||
value = read_byte();
|
||||
disable();
|
||||
ESP_LOGV(TAG, "read_register_(%x) -> %x", reg, value);
|
||||
ESP_LOGVV(TAG, "read_register_(%d) -> %d", reg, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,11 @@ void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
|||
uint8_t *values, ///< uint8_t array to store the values in.
|
||||
uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated.
|
||||
) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
std::string buf;
|
||||
buf = "Rx";
|
||||
char cstrb[20];
|
||||
#endif
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -68,25 +70,30 @@ void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
|||
values[0] = (values[0] & ~mask) | (value & mask);
|
||||
index++;
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
sprintf(cstrb, " %x", values[0]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
}
|
||||
while (index < count) {
|
||||
values[index] = transfer_byte(address); // Read value and tell that we want to read the same address again.
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
sprintf(cstrb, " %x", values[index]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
|
||||
index++;
|
||||
}
|
||||
values[index] = transfer_byte(0); // Read the final uint8_t. Send 0 to stop reading.
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
buf = buf + " ";
|
||||
sprintf(cstrb, "%x", values[index]);
|
||||
buf.append(cstrb);
|
||||
|
||||
ESP_LOGVV(TAG, "read_register_array_(%x, %d, , %d) -> %s", reg, count, rx_align, buf.c_str());
|
||||
|
||||
#endif
|
||||
disable();
|
||||
}
|
||||
|
||||
|
@ -108,21 +115,25 @@ void RC522Spi::pcd_write_register(PcdRegister reg, ///< The register to write t
|
|||
uint8_t count, ///< The number of uint8_ts to write to the register
|
||||
uint8_t *values ///< The values to write. uint8_t array.
|
||||
) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
std::string buf;
|
||||
buf = "Tx";
|
||||
char cstrb[20];
|
||||
#endif
|
||||
|
||||
enable();
|
||||
transfer_byte(reg);
|
||||
char cstrb[20];
|
||||
|
||||
for (uint8_t index = 0; index < count; index++) {
|
||||
transfer_byte(values[index]);
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
sprintf(cstrb, " %x", values[index]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
}
|
||||
disable();
|
||||
ESP_LOGVV(TAG, "write_register_(%x, %d) -> %s", reg, count, buf.c_str());
|
||||
ESP_LOGVV(TAG, "write_register_(%d, %d) -> %s", reg, count, buf.c_str());
|
||||
}
|
||||
|
||||
} // namespace rc522_spi
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import math
|
||||
from typing import Optional
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
|
@ -180,8 +181,12 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_, device_class_):
|
||||
# type: (str, str, int, str) -> cv.Schema
|
||||
def sensor_schema(
|
||||
unit_of_measurement_: str,
|
||||
icon_: str,
|
||||
accuracy_decimals_: int,
|
||||
device_class_: Optional[str] = DEVICE_CLASS_EMPTY,
|
||||
) -> cv.Schema:
|
||||
schema = SENSOR_SCHEMA
|
||||
if unit_of_measurement_ != UNIT_EMPTY:
|
||||
schema = schema.extend(
|
||||
|
|
|
@ -148,10 +148,10 @@ void SlidingWindowMovingAverageFilter::set_window_size(size_t window_size) { thi
|
|||
optional<float> SlidingWindowMovingAverageFilter::new_value(float value) {
|
||||
if (!isnan(value)) {
|
||||
if (this->queue_.size() == this->window_size_) {
|
||||
this->sum_ -= this->queue_.front();
|
||||
this->queue_.pop();
|
||||
this->sum_ -= this->queue_[0];
|
||||
this->queue_.pop_front();
|
||||
}
|
||||
this->queue_.push(value);
|
||||
this->queue_.push_back(value);
|
||||
this->sum_ += value;
|
||||
}
|
||||
float average;
|
||||
|
@ -161,8 +161,16 @@ optional<float> SlidingWindowMovingAverageFilter::new_value(float value) {
|
|||
average = this->sum_ / this->queue_.size();
|
||||
ESP_LOGVV(TAG, "SlidingWindowMovingAverageFilter(%p)::new_value(%f) -> %f", this, value, average);
|
||||
|
||||
if (++this->send_at_ >= this->send_every_) {
|
||||
this->send_at_ = 0;
|
||||
if (++this->send_at_ % this->send_every_ == 0) {
|
||||
if (this->send_at_ >= 10000) {
|
||||
// Recalculate to prevent floating point error accumulating
|
||||
this->sum_ = 0;
|
||||
for (auto v : this->queue_)
|
||||
this->sum_ += v;
|
||||
average = this->sum_ / this->queue_.size();
|
||||
this->send_at_ = 0;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "SlidingWindowMovingAverageFilter(%p)::new_value(%f) SENDING", this, value);
|
||||
return average;
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ class SlidingWindowMovingAverageFilter : public Filter {
|
|||
|
||||
protected:
|
||||
float sum_{0.0};
|
||||
std::queue<float> queue_;
|
||||
std::deque<float> queue_;
|
||||
size_t send_every_;
|
||||
size_t send_at_;
|
||||
size_t window_size_;
|
||||
|
|
|
@ -52,6 +52,8 @@ void Servo::loop() {
|
|||
|
||||
void Servo::write(float value) {
|
||||
value = clamp(value, -1.0f, 1.0f);
|
||||
if (this->target_value_ == value)
|
||||
this->internal_write(value);
|
||||
this->target_value_ = value;
|
||||
this->source_value_ = this->current_value_;
|
||||
this->state_ = STATE_ATTACHED;
|
||||
|
|
|
@ -103,7 +103,7 @@ bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult
|
|||
return false;
|
||||
}
|
||||
|
||||
while (payload_length > 0) {
|
||||
while (payload_length > 3) {
|
||||
if (payload[payload_offset + 1] != 0x10) {
|
||||
ESP_LOGVV(TAG, "parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
|
||||
break;
|
||||
|
@ -171,7 +171,10 @@ optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::Service
|
|||
result.type = XiaomiParseResult::TYPE_MUE4094RT;
|
||||
result.name = "MUE4094RT";
|
||||
result.raw_offset -= 6;
|
||||
} else if ((raw[2] == 0x47) && (raw[3] == 0x03)) { // round body, e-ink display
|
||||
} else if ((raw[2] == 0x47) && (raw[3] == 0x03)) { // ClearGrass-branded, round body, e-ink display
|
||||
result.type = XiaomiParseResult::TYPE_CGG1;
|
||||
result.name = "CGG1";
|
||||
} else if ((raw[2] == 0x48) && (raw[3] == 0x0B)) { // Qingping-branded, round body, e-ink display — with bindkeys
|
||||
result.type = XiaomiParseResult::TYPE_CGG1;
|
||||
result.name = "CGG1";
|
||||
} else if ((raw[2] == 0xbc) && (raw[3] == 0x03)) { // VegTrug Grow Care Garden
|
||||
|
|
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
|||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_LEVEL,
|
||||
CONF_BINDKEY,
|
||||
CONF_HUMIDITY,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_TEMPERATURE,
|
||||
|
@ -27,6 +28,7 @@ CONFIG_SCHEMA = (
|
|||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(XiaomiCGG1),
|
||||
cv.Optional(CONF_BINDKEY): cv.bind_key,
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
|
@ -50,6 +52,8 @@ def to_code(config):
|
|||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
if CONF_BINDKEY in config:
|
||||
cg.add(var.set_bindkey(config[CONF_BINDKEY]))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
|
|
|
@ -10,6 +10,7 @@ static const char *TAG = "xiaomi_cgg1";
|
|||
|
||||
void XiaomiCGG1::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi CGG1");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
@ -31,8 +32,9 @@ bool XiaomiCGG1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
|||
if (res->is_duplicate) {
|
||||
continue;
|
||||
}
|
||||
if (res->has_encryption) {
|
||||
ESP_LOGVV(TAG, "parse_device(): payload decryption is currently not supported on this device.");
|
||||
if (res->has_encryption &&
|
||||
(!(xiaomi_ble::decrypt_xiaomi_payload(const_cast<std::vector<uint8_t> &>(service_data.data), this->bindkey_,
|
||||
this->address_)))) {
|
||||
continue;
|
||||
}
|
||||
if (!(xiaomi_ble::parse_xiaomi_message(service_data.data, *res))) {
|
||||
|
@ -57,6 +59,18 @@ bool XiaomiCGG1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void XiaomiCGG1::set_bindkey(const std::string &bindkey) {
|
||||
memset(bindkey_, 0, 16);
|
||||
if (bindkey.size() != 32) {
|
||||
return;
|
||||
}
|
||||
char temp[3] = {0};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
strncpy(temp, &(bindkey.c_str()[i * 2]), 2);
|
||||
bindkey_[i] = std::strtoul(temp, NULL, 16);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xiaomi_cgg1
|
||||
} // namespace esphome
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace xiaomi_cgg1 {
|
|||
class XiaomiCGG1 : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; }
|
||||
void set_bindkey(const std::string &bindkey);
|
||||
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
|
||||
|
@ -24,6 +25,7 @@ class XiaomiCGG1 : public Component, public esp32_ble_tracker::ESPBTDeviceListen
|
|||
|
||||
protected:
|
||||
uint64_t address_;
|
||||
uint8_t bindkey_[16];
|
||||
sensor::Sensor *temperature_{nullptr};
|
||||
sensor::Sensor *humidity_{nullptr};
|
||||
sensor::Sensor *battery_level_{nullptr};
|
||||
|
|
|
@ -19,13 +19,14 @@ from esphome.const import (
|
|||
CONF_SUBSTITUTIONS,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError # noqa
|
||||
from esphome.helpers import color, indent
|
||||
from esphome.helpers import indent
|
||||
from esphome.util import safe_print, OrderedDict
|
||||
|
||||
from typing import List, Optional, Tuple, Union # noqa
|
||||
from esphome.core import ConfigType # noqa
|
||||
from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
|
||||
from esphome.voluptuous_schema import ExtraKeysInvalid
|
||||
from esphome.log import color, Fore
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -790,7 +791,7 @@ def line_info(config, path, highlight=True):
|
|||
if obj:
|
||||
mark = obj.start_mark
|
||||
source = "[source {}:{}]".format(mark.document, mark.line + 1)
|
||||
return color("cyan", source)
|
||||
return color(Fore.CYAN, source)
|
||||
return "None"
|
||||
|
||||
|
||||
|
@ -813,7 +814,9 @@ def dump_dict(config, path, at_root=True):
|
|||
if at_root:
|
||||
error = config.get_error_for_path(path)
|
||||
if error is not None:
|
||||
ret += "\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n"
|
||||
ret += (
|
||||
"\n" + color(Fore.BOLD_RED, _format_vol_invalid(error, config)) + "\n"
|
||||
)
|
||||
|
||||
if isinstance(conf, (list, tuple)):
|
||||
multiline = True
|
||||
|
@ -826,12 +829,14 @@ def dump_dict(config, path, at_root=True):
|
|||
error = config.get_error_for_path(path_)
|
||||
if error is not None:
|
||||
ret += (
|
||||
"\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n"
|
||||
"\n"
|
||||
+ color(Fore.BOLD_RED, _format_vol_invalid(error, config))
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
sep = "- "
|
||||
if config.is_in_error_path(path_):
|
||||
sep = color("red", sep)
|
||||
sep = color(Fore.RED, sep)
|
||||
msg, _ = dump_dict(config, path_, at_root=False)
|
||||
msg = indent(msg)
|
||||
inf = line_info(config, path_, highlight=config.is_in_error_path(path_))
|
||||
|
@ -851,12 +856,14 @@ def dump_dict(config, path, at_root=True):
|
|||
error = config.get_error_for_path(path_)
|
||||
if error is not None:
|
||||
ret += (
|
||||
"\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n"
|
||||
"\n"
|
||||
+ color(Fore.BOLD_RED, _format_vol_invalid(error, config))
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
st = f"{k}: "
|
||||
if config.is_in_error_path(path_):
|
||||
st = color("red", st)
|
||||
st = color(Fore.RED, st)
|
||||
msg, m = dump_dict(config, path_, at_root=False)
|
||||
|
||||
inf = line_info(config, path_, highlight=config.is_in_error_path(path_))
|
||||
|
@ -878,7 +885,7 @@ def dump_dict(config, path, at_root=True):
|
|||
if len(conf) > 80:
|
||||
conf = "|-\n" + indent(conf)
|
||||
error = config.get_error_for_path(path)
|
||||
col = "bold_red" if error else "white"
|
||||
col = Fore.BOLD_RED if error else Fore.KEEP
|
||||
ret += color(col, str(conf))
|
||||
elif isinstance(conf, core.Lambda):
|
||||
if is_secret(conf):
|
||||
|
@ -886,13 +893,13 @@ def dump_dict(config, path, at_root=True):
|
|||
|
||||
conf = "!lambda |-\n" + indent(str(conf.value))
|
||||
error = config.get_error_for_path(path)
|
||||
col = "bold_red" if error else "white"
|
||||
col = Fore.BOLD_RED if error else Fore.KEEP
|
||||
ret += color(col, conf)
|
||||
elif conf is None:
|
||||
pass
|
||||
else:
|
||||
error = config.get_error_for_path(path)
|
||||
col = "bold_red" if error else "white"
|
||||
col = Fore.BOLD_RED if error else Fore.KEEP
|
||||
ret += color(col, str(conf))
|
||||
multiline = "\n" in ret
|
||||
|
||||
|
@ -934,13 +941,13 @@ def read_config(command_line_substitutions):
|
|||
if not CORE.verbose:
|
||||
res = strip_default_ids(res)
|
||||
|
||||
safe_print(color("bold_red", "Failed config"))
|
||||
safe_print(color(Fore.BOLD_RED, "Failed config"))
|
||||
safe_print("")
|
||||
for path, domain in res.output_paths:
|
||||
if not res.is_in_error_path(path):
|
||||
continue
|
||||
|
||||
errstr = color("bold_red", f"{domain}:")
|
||||
errstr = color(Fore.BOLD_RED, f"{domain}:")
|
||||
errline = line_info(res, path)
|
||||
if errline:
|
||||
errstr += " " + errline
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
MAJOR_VERSION = 1
|
||||
MINOR_VERSION = 17
|
||||
PATCH_VERSION = "0"
|
||||
PATCH_VERSION = "1"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
|
||||
|
|
|
@ -266,7 +266,11 @@ template<typename... Ts> class UpdateComponentAction : public Action<Ts...> {
|
|||
public:
|
||||
UpdateComponentAction(PollingComponent *component) : component_(component) {}
|
||||
|
||||
void play(Ts... x) override { this->component_->update(); }
|
||||
void play(Ts... x) override {
|
||||
if (this->component_->is_failed())
|
||||
return;
|
||||
this->component_->update();
|
||||
}
|
||||
|
||||
protected:
|
||||
PollingComponent *component_;
|
||||
|
|
|
@ -319,3 +319,18 @@ template<typename T> class Parented {
|
|||
uint32_t fnv1_hash(const std::string &str);
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
template<typename T> T *new_buffer(size_t length) {
|
||||
T *buffer;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (psramFound()) {
|
||||
buffer = (T *) ps_malloc(length);
|
||||
} else {
|
||||
buffer = new T[length];
|
||||
}
|
||||
#else
|
||||
buffer = new T[length];
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
} // namespace esphome
|
||||
|
|
|
@ -160,5 +160,6 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
|||
((byte) &0x08 ? '1' : '0'), ((byte) &0x04 ? '1' : '0'), ((byte) &0x02 ? '1' : '0'), ((byte) &0x01 ? '1' : '0')
|
||||
#define YESNO(b) ((b) ? "YES" : "NO")
|
||||
#define ONOFF(b) ((b) ? "ON" : "OFF")
|
||||
#define TRUEFALSE(b) ((b) ? "TRUE" : "FALSE")
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -57,17 +57,6 @@ def cpp_string_escape(string, encoding="utf-8"):
|
|||
return '"' + result + '"'
|
||||
|
||||
|
||||
def color(the_color, message=""):
|
||||
from colorlog.escape_codes import escape_codes, parse_colors
|
||||
|
||||
if not message:
|
||||
res = parse_colors(the_color)
|
||||
else:
|
||||
res = parse_colors(the_color) + message + escape_codes["reset"]
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def run_system_command(*args):
|
||||
import subprocess
|
||||
|
||||
|
|
82
esphome/log.py
Normal file
82
esphome/log.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
import logging
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
class AnsiFore:
|
||||
KEEP = ""
|
||||
BLACK = "\033[30m"
|
||||
RED = "\033[31m"
|
||||
GREEN = "\033[32m"
|
||||
YELLOW = "\033[33m"
|
||||
BLUE = "\033[34m"
|
||||
MAGENTA = "\033[35m"
|
||||
CYAN = "\033[36m"
|
||||
WHITE = "\033[37m"
|
||||
RESET = "\033[39m"
|
||||
|
||||
BOLD_BLACK = "\033[1;30m"
|
||||
BOLD_RED = "\033[1;31m"
|
||||
BOLD_GREEN = "\033[1;32m"
|
||||
BOLD_YELLOW = "\033[1;33m"
|
||||
BOLD_BLUE = "\033[1;34m"
|
||||
BOLD_MAGENTA = "\033[1;35m"
|
||||
BOLD_CYAN = "\033[1;36m"
|
||||
BOLD_WHITE = "\033[1;37m"
|
||||
BOLD_RESET = "\033[1;39m"
|
||||
|
||||
|
||||
class AnsiStyle:
|
||||
BRIGHT = "\033[1m"
|
||||
BOLD = "\033[1m"
|
||||
DIM = "\033[2m"
|
||||
THIN = "\033[2m"
|
||||
NORMAL = "\033[22m"
|
||||
RESET_ALL = "\033[0m"
|
||||
|
||||
|
||||
Fore = AnsiFore()
|
||||
Style = AnsiStyle()
|
||||
|
||||
|
||||
def color(col: str, msg: str, reset: bool = True) -> bool:
|
||||
if col and not col.startswith("\033["):
|
||||
raise ValueError("Color must be value from esphome.log.Fore")
|
||||
s = str(col) + msg
|
||||
if reset and col:
|
||||
s += str(Style.RESET_ALL)
|
||||
return s
|
||||
|
||||
|
||||
class ESPHomeLogFormatter(logging.Formatter):
|
||||
def __init__(self):
|
||||
super().__init__(fmt="%(levelname)s %(message)s", datefmt="%H:%M:%S", style="%")
|
||||
|
||||
def format(self, record):
|
||||
formatted = super().format(record)
|
||||
prefix = {
|
||||
"DEBUG": Fore.CYAN,
|
||||
"INFO": Fore.GREEN,
|
||||
"WARNING": Fore.YELLOW,
|
||||
"ERROR": Fore.RED,
|
||||
"CRITICAL": Fore.RED,
|
||||
}.get(record.levelname, "")
|
||||
return f"{prefix}{formatted}{Style.RESET_ALL}"
|
||||
|
||||
|
||||
def setup_log(debug=False, quiet=False):
|
||||
import colorama
|
||||
|
||||
if debug:
|
||||
log_level = logging.DEBUG
|
||||
CORE.verbose = True
|
||||
elif quiet:
|
||||
log_level = logging.CRITICAL
|
||||
else:
|
||||
log_level = logging.INFO
|
||||
logging.basicConfig(level=log_level)
|
||||
|
||||
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||
|
||||
colorama.init()
|
||||
logging.getLogger().handlers[0].setFormatter(ESPHomeLogFormatter())
|
|
@ -22,7 +22,7 @@ from esphome.const import (
|
|||
CONF_USERNAME,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.helpers import color
|
||||
from esphome.log import color, Fore
|
||||
from esphome.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -158,7 +158,7 @@ def get_fingerprint(config):
|
|||
|
||||
sha1 = hashlib.sha1(cert_der).hexdigest()
|
||||
|
||||
safe_print("SHA1 Fingerprint: " + color("cyan", sha1))
|
||||
safe_print("SHA1 Fingerprint: " + color(Fore.CYAN, sha1))
|
||||
safe_print(
|
||||
"Copy the string above into mqtt.ssl_fingerprints section of {}"
|
||||
"".format(CORE.config_path)
|
||||
|
|
|
@ -6,7 +6,8 @@ import unicodedata
|
|||
import voluptuous as vol
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.helpers import color, get_bool_env, write_file
|
||||
from esphome.helpers import get_bool_env, write_file
|
||||
from esphome.log import color, Fore
|
||||
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
from esphome.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
|
||||
|
@ -148,13 +149,13 @@ def wizard(path):
|
|||
if not path.endswith(".yaml") and not path.endswith(".yml"):
|
||||
safe_print(
|
||||
"Please make your configuration file {} have the extension .yaml or .yml"
|
||||
"".format(color("cyan", path))
|
||||
"".format(color(Fore.CYAN, path))
|
||||
)
|
||||
return 1
|
||||
if os.path.exists(path):
|
||||
safe_print(
|
||||
"Uh oh, it seems like {} already exists, please delete that file first "
|
||||
"or chose another configuration file.".format(color("cyan", path))
|
||||
"or chose another configuration file.".format(color(Fore.CYAN, path))
|
||||
)
|
||||
return 2
|
||||
safe_print("Hi there!")
|
||||
|
@ -171,7 +172,7 @@ def wizard(path):
|
|||
safe_print()
|
||||
safe_print_step(1, CORE_BIG)
|
||||
safe_print(
|
||||
"First up, please choose a " + color("green", "name") + " for your node."
|
||||
"First up, please choose a " + color(Fore.GREEN, "name") + " for your node."
|
||||
)
|
||||
safe_print(
|
||||
"It should be a unique name that can be used to identify the device later."
|
||||
|
@ -179,12 +180,12 @@ def wizard(path):
|
|||
sleep(1)
|
||||
safe_print(
|
||||
"For example, I like calling the node in my living room {}.".format(
|
||||
color("bold_white", "livingroom")
|
||||
color(Fore.BOLD_WHITE, "livingroom")
|
||||
)
|
||||
)
|
||||
safe_print()
|
||||
sleep(1)
|
||||
name = input(color("bold_white", "(name): "))
|
||||
name = input(color(Fore.BOLD_WHITE, "(name): "))
|
||||
|
||||
while True:
|
||||
try:
|
||||
|
@ -193,7 +194,7 @@ def wizard(path):
|
|||
except vol.Invalid:
|
||||
safe_print(
|
||||
color(
|
||||
"red",
|
||||
Fore.RED,
|
||||
f'Oh noes, "{name}" isn\'t a valid name. Names can only '
|
||||
f"include numbers, lower-case letters, underscores and "
|
||||
f"hyphens.",
|
||||
|
@ -202,12 +203,12 @@ def wizard(path):
|
|||
name = strip_accents(name).lower().replace(" ", "_")
|
||||
name = "".join(c for c in name if c in ALLOWED_NAME_CHARS)
|
||||
safe_print(
|
||||
'Shall I use "{}" as the name instead?'.format(color("cyan", name))
|
||||
'Shall I use "{}" as the name instead?'.format(color(Fore.CYAN, name))
|
||||
)
|
||||
sleep(0.5)
|
||||
name = default_input("(name [{}]): ", name)
|
||||
|
||||
safe_print('Great! Your node is now called "{}".'.format(color("cyan", name)))
|
||||
safe_print('Great! Your node is now called "{}".'.format(color(Fore.CYAN, name)))
|
||||
sleep(1)
|
||||
safe_print_step(2, ESP_BIG)
|
||||
safe_print(
|
||||
|
@ -216,16 +217,16 @@ def wizard(path):
|
|||
)
|
||||
safe_print(
|
||||
"Are you using an "
|
||||
+ color("green", "ESP32")
|
||||
+ color(Fore.GREEN, "ESP32")
|
||||
+ " or "
|
||||
+ color("green", "ESP8266")
|
||||
+ color(Fore.GREEN, "ESP8266")
|
||||
+ " platform? (Choose ESP8266 for Sonoff devices)"
|
||||
)
|
||||
while True:
|
||||
sleep(0.5)
|
||||
safe_print()
|
||||
safe_print("Please enter either ESP32 or ESP8266.")
|
||||
platform = input(color("bold_white", "(ESP32/ESP8266): "))
|
||||
platform = input(color(Fore.BOLD_WHITE, "(ESP32/ESP8266): "))
|
||||
try:
|
||||
platform = vol.All(vol.Upper, vol.Any("ESP32", "ESP8266"))(platform)
|
||||
break
|
||||
|
@ -235,7 +236,7 @@ def wizard(path):
|
|||
'"{}". Please try again.'.format(platform)
|
||||
)
|
||||
safe_print(
|
||||
"Thanks! You've chosen {} as your platform.".format(color("cyan", platform))
|
||||
"Thanks! You've chosen {} as your platform.".format(color(Fore.CYAN, platform))
|
||||
)
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
@ -250,37 +251,39 @@ def wizard(path):
|
|||
)
|
||||
|
||||
safe_print(
|
||||
"Next, I need to know what " + color("green", "board") + " you're using."
|
||||
"Next, I need to know what " + color(Fore.GREEN, "board") + " you're using."
|
||||
)
|
||||
sleep(0.5)
|
||||
safe_print("Please go to {} and choose a board.".format(color("green", board_link)))
|
||||
safe_print(
|
||||
"Please go to {} and choose a board.".format(color(Fore.GREEN, board_link))
|
||||
)
|
||||
if platform == "ESP32":
|
||||
safe_print("(Type " + color("green", "esp01_1m") + " for Sonoff devices)")
|
||||
safe_print("(Type " + color(Fore.GREEN, "esp01_1m") + " for Sonoff devices)")
|
||||
safe_print()
|
||||
# Don't sleep because user needs to copy link
|
||||
if platform == "ESP32":
|
||||
safe_print('For example "{}".'.format(color("bold_white", "nodemcu-32s")))
|
||||
safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "nodemcu-32s")))
|
||||
boards = list(ESP32_BOARD_PINS.keys())
|
||||
else:
|
||||
safe_print('For example "{}".'.format(color("bold_white", "nodemcuv2")))
|
||||
safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "nodemcuv2")))
|
||||
boards = list(ESP8266_BOARD_PINS.keys())
|
||||
safe_print("Options: {}".format(", ".join(sorted(boards))))
|
||||
|
||||
while True:
|
||||
board = input(color("bold_white", "(board): "))
|
||||
board = input(color(Fore.BOLD_WHITE, "(board): "))
|
||||
try:
|
||||
board = vol.All(vol.Lower, vol.Any(*boards))(board)
|
||||
break
|
||||
except vol.Invalid:
|
||||
safe_print(
|
||||
color("red", f'Sorry, I don\'t think the board "{board}" exists.')
|
||||
color(Fore.RED, f'Sorry, I don\'t think the board "{board}" exists.')
|
||||
)
|
||||
safe_print()
|
||||
sleep(0.25)
|
||||
safe_print()
|
||||
|
||||
safe_print(
|
||||
"Way to go! You've chosen {} as your board.".format(color("cyan", board))
|
||||
"Way to go! You've chosen {} as your board.".format(color(Fore.CYAN, board))
|
||||
)
|
||||
safe_print()
|
||||
sleep(1)
|
||||
|
@ -291,20 +294,20 @@ def wizard(path):
|
|||
sleep(1)
|
||||
safe_print(
|
||||
"First, what's the "
|
||||
+ color("green", "SSID")
|
||||
+ color(Fore.GREEN, "SSID")
|
||||
+ f" (the name) of the WiFi network {name} I should connect to?"
|
||||
)
|
||||
sleep(1.5)
|
||||
safe_print('For example "{}".'.format(color("bold_white", "Abraham Linksys")))
|
||||
safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "Abraham Linksys")))
|
||||
while True:
|
||||
ssid = input(color("bold_white", "(ssid): "))
|
||||
ssid = input(color(Fore.BOLD_WHITE, "(ssid): "))
|
||||
try:
|
||||
ssid = cv.ssid(ssid)
|
||||
break
|
||||
except vol.Invalid:
|
||||
safe_print(
|
||||
color(
|
||||
"red",
|
||||
Fore.RED,
|
||||
'Unfortunately, "{}" doesn\'t seem to be a valid SSID. '
|
||||
"Please try again.".format(ssid),
|
||||
)
|
||||
|
@ -314,20 +317,20 @@ def wizard(path):
|
|||
|
||||
safe_print(
|
||||
'Thank you very much! You\'ve just chosen "{}" as your SSID.'
|
||||
"".format(color("cyan", ssid))
|
||||
"".format(color(Fore.CYAN, ssid))
|
||||
)
|
||||
safe_print()
|
||||
sleep(0.75)
|
||||
|
||||
safe_print(
|
||||
"Now please state the "
|
||||
+ color("green", "password")
|
||||
+ color(Fore.GREEN, "password")
|
||||
+ " of the WiFi network so that I can connect to it (Leave empty for no password)"
|
||||
)
|
||||
safe_print()
|
||||
safe_print('For example "{}"'.format(color("bold_white", "PASSWORD42")))
|
||||
safe_print('For example "{}"'.format(color(Fore.BOLD_WHITE, "PASSWORD42")))
|
||||
sleep(0.5)
|
||||
psk = input(color("bold_white", "(PSK): "))
|
||||
psk = input(color(Fore.BOLD_WHITE, "(PSK): "))
|
||||
safe_print(
|
||||
"Perfect! WiFi is now set up (you can create static IPs and so on later)."
|
||||
)
|
||||
|
@ -340,12 +343,12 @@ def wizard(path):
|
|||
)
|
||||
safe_print(
|
||||
"This can be insecure if you do not trust the WiFi network. Do you want to set "
|
||||
"a " + color("green", "password") + " for connecting to this ESP?"
|
||||
"a " + color(Fore.GREEN, "password") + " for connecting to this ESP?"
|
||||
)
|
||||
safe_print()
|
||||
sleep(0.25)
|
||||
safe_print("Press ENTER for no password")
|
||||
password = input(color("bold_white", "(password): "))
|
||||
password = input(color(Fore.BOLD_WHITE, "(password): "))
|
||||
|
||||
wizard_write(
|
||||
path=path,
|
||||
|
@ -359,8 +362,8 @@ def wizard(path):
|
|||
|
||||
safe_print()
|
||||
safe_print(
|
||||
color("cyan", "DONE! I've now written a new configuration file to ")
|
||||
+ color("bold_cyan", path)
|
||||
color(Fore.CYAN, "DONE! I've now written a new configuration file to ")
|
||||
+ color(Fore.BOLD_CYAN, path)
|
||||
)
|
||||
safe_print()
|
||||
safe_print("Next steps:")
|
||||
|
|
|
@ -2,7 +2,6 @@ voluptuous==0.12.1
|
|||
PyYAML==5.4.1
|
||||
paho-mqtt==1.5.1
|
||||
colorama==0.4.4
|
||||
colorlog==4.7.2
|
||||
tornado==6.1
|
||||
protobuf==3.15.6
|
||||
tzlocal==2.1
|
||||
|
|
Loading…
Reference in a new issue