Files
GruntStand/grunt_stand.ino
2026-01-30 18:36:20 +03:00

883 lines
27 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <LiquidCrystal.h>
#include <HX711.h>
#include <EEPROM.h>
// LCD Keypad Shield 1602 pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// HX711 pins
const int LOADCELL_DOUT_PIN = A3;
const int LOADCELL_SCK_PIN = A2;
// Button pin
const int BUTTON_PIN = 13;
// Heat control pin
const int HEAT_PIN = 3; // D3 для управления нагревом
// LCD Backlight pin
const int BACKLIGHT_PIN = 10; // D10 для подсветки LCD
// Voltage measurement pin
const int VOLTAGE_PIN = A1; // Пин для измерения напряжения
const float VOLTAGE_DIVIDER = 5.0; // Делитель 1:10 (16V -> 1.6V на пине)
const float VREF = 5.0; // Опорное напряжение Arduino (5V)
// Calibration
float calibration_factor = 1.0;
const float CALIBRATION_WEIGHT = 100.0;
// EEPROM
const int EEPROM_CALIBRATION_ADDR = 0;
// Весовые переменные
float current_weight = 0.0;
float displayed_weight = 0.0;
float m = 0.0; // Масса пустой кюветы
float m1 = 0.0; // Масса заполненной кюветы
float m0 = 0.0; // Текущая масса во время нагрева
// Режимы работы
enum SystemState {
STATE_READY,
STATE_EMPTY_CUVETTE,
STATE_SAVING_EMPTY,
STATE_FILLED_CUVETTE,
STATE_SAVING_FILLED,
STATE_HEATING,
STATE_HEATING_COMPLETE
};
SystemState current_state = STATE_READY;
bool need_display_clear = true;
// Таймеры для нагрева
unsigned long heat_start_time = 0;
const unsigned long HEAT_DURATION = 900000; // 15 минут = 900000 мс
bool heating_active = false;
// Циклический нагрев
const unsigned long HEAT_ON_TIME = 20000; // 15 секунд нагрева
const unsigned long HEAT_OFF_TIME = 30000; // 30 секунд паузы
unsigned long last_cycle_change = 0; // Время последнего переключения
bool heat_cycle_on = false; // Текущее состояние цикла (вкл/выкл)
// Для расчета W (без фильтрации)
float current_W = 0.0; // Текущее значение влажности
float max_W = 0.0; // Максимальное значение W за время нагрева
unsigned long last_W_update = 0;
const unsigned long W_UPDATE_INTERVAL = 500; // Обновлять W каждые 500 мс
// Button timing
bool button_pressed = false;
unsigned long button_press_time = 0;
const unsigned long MIN_TARE_TIME = 4000; // 4 секунды для тарирования
const unsigned long MAX_TARE_TIME = 6000; // 6 секунд максимум для тарирования
const unsigned long MIN_CAL_TIME = 8000; // 8 секунд для калибровки
const unsigned long MAX_CAL_TIME = 10000; // 10 секунд максимум для калибровки
bool showing_countdown = false; // Флаг отображения отсчета
// Scale
HX711 scale;
// === ФИЛЬТРЫ ===
// МЕДИАННЫЙ ФИЛЬТР
class MedianFilter {
private:
float buffer[7];
int index = 0;
bool filled = false;
public:
float update(float new_val) {
buffer[index] = new_val;
index = (index + 1) % 7;
if (index == 0) filled = true;
float temp[7];
int count = filled ? 7 : index;
for (int i = 0; i < count; i++) temp[i] = buffer[i];
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (temp[i] > temp[j]) {
float t = temp[i];
temp[i] = temp[j];
temp[j] = t;
}
}
}
return temp[count / 2];
}
void reset() {
index = 0;
filled = false;
}
};
MedianFilter median_filter;
// ФИЛЬТР ВЫВОДА W (с дедбэндом для уменьшения дрейфа)
class WFilter {
private:
float filtered_value = 0.0;
float displayed_value = 0.0;
const float ALPHA = 0.15; // Коэффициент EMA (меньше = больше сглаживание)
const float DEADBAND = 0.01; // Игнорировать изменения меньше 0.3%
bool initialized = false;
public:
float update(float raw_value) {
if (!initialized) {
filtered_value = raw_value;
displayed_value = raw_value;
initialized = true;
return displayed_value;
}
// Экспоненциальное сглаживание (EMA)
filtered_value = ALPHA * raw_value + (1.0 - ALPHA) * filtered_value;
// Дедбэнд: обновляем отображаемое значение только если изменение значительное
if (abs(filtered_value - displayed_value) >= DEADBAND) {
displayed_value = filtered_value;
}
return displayed_value;
}
float getValue() {
return displayed_value;
}
void reset() {
filtered_value = 0.0;
displayed_value = 0.0;
initialized = false;
}
void setImmediate(float value) {
filtered_value = value;
displayed_value = value;
initialized = true;
}
};
WFilter w_filter;
// ФИЛЬТР ВЫВОДА веса
class DisplayFilter {
private:
float target_value = 0.0;
float current_display = 0.0;
const float SMOOTH_SPEED = 0.5;
public:
float update(float new_value) {
target_value = new_value;
float diff = target_value - current_display;
if (abs(diff) < 0.01) {
current_display = target_value;
} else {
current_display += diff * SMOOTH_SPEED;
}
return current_display;
}
void reset() {
current_display = 0.0;
target_value = 0.0;
}
void setImmediate(float value) {
current_display = value;
target_value = value;
}
};
DisplayFilter display_filter;
// EEPROM функции
void saveCalibrationToEEPROM(float factor) {
EEPROM.put(EEPROM_CALIBRATION_ADDR, factor);
}
float loadCalibrationFromEEPROM() {
float factor = 1.0;
EEPROM.get(EEPROM_CALIBRATION_ADDR, factor);
if (isnan(factor) || factor < 0.001 || factor > 10000.0) {
factor = 1.0;
}
return factor;
}
// Функция для измерения среднего значения
float measureAverageWeight(int samples = 10) {
float sum = 0;
for (int i = 0; i < samples; i++) {
float raw = scale.get_units(1);
float filtered = median_filter.update(raw);
sum += filtered;
delay(100);
}
return sum / samples;
}
// Функция центрированного вывода текста
void printCentered(int row, String text) {
int spaces = (16 - text.length()) / 2;
lcd.setCursor(spaces, row);
lcd.print(text);
}
// Функция центрированного вывода веса
void printWeightCentered(int row, float weight) {
char buffer[16];
if (weight >= 100.0) {
strcpy(buffer, "cuvette:>99.99g");
} else if (weight <= -100.0) {
strcpy(buffer, "cuvette:<-99.99g");
} else {
// Форматируем число с 2 знаками после запятой
char num_buf[8];
dtostrf(fabs(weight), 0, 2, num_buf); // Без пробелов, 2 знака после запятой
// Формируем полную строку
if (weight < 0) {
snprintf(buffer, sizeof(buffer), "cuvette:-%sg", num_buf);
} else {
snprintf(buffer, sizeof(buffer), "cuvette:%sg", num_buf);
}
}
// Центрируем
int text_len = strlen(buffer);
int spaces = (16 - text_len) / 2;
lcd.setCursor(spaces, row);
lcd.print(buffer);
}
// Функция расчета W
float calculateW() {
if (m0 > m && m1 > m) {
return 100.0 * (m1 - m0) / (m0 - m);
}
return 0.0;
}
// Функция измерения напряжения (сырое значение)
float readVoltageRaw() {
int raw = analogRead(VOLTAGE_PIN);
float voltage_on_pin = (raw / 1023.0) * VREF; // Напряжение на пине (0-5V)
float actual_voltage = voltage_on_pin * VOLTAGE_DIVIDER; // Реальное напряжение с учетом делителя
return actual_voltage;
}
// Фильтрованное напряжение (EMA)
float filtered_voltage = 0.0;
bool voltage_initialized = false;
const float VOLTAGE_ALPHA = 0.1; // Коэффициент EMA для напряжения (меньше = больше сглаживание)
// Функция обновления и получения отфильтрованного напряжения
float updateVoltage() {
float raw_voltage = readVoltageRaw();
if (!voltage_initialized) {
filtered_voltage = raw_voltage;
voltage_initialized = true;
} else {
filtered_voltage = VOLTAGE_ALPHA * raw_voltage + (1.0 - VOLTAGE_ALPHA) * filtered_voltage;
}
return filtered_voltage;
}
void setup() {
Serial.begin(9600);
delay(500);
// LCD
lcd.begin(16, 2);
// Настройка пина нагрева
pinMode(HEAT_PIN, OUTPUT);
digitalWrite(HEAT_PIN, LOW);
// Яркость подсветки
pinMode(BACKLIGHT_PIN, OUTPUT);
analogWrite(BACKLIGHT_PIN, 180);
// Button
pinMode(BUTTON_PIN, INPUT);
// HX711
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
scale.set_gain(128);
// Быстрое тарирование
scale.tare();
// Загрузка калибровки
calibration_factor = loadCalibrationFromEEPROM();
scale.set_scale(calibration_factor);
// Стартовое сообщение
displayReadyScreen();
delay(1000);
}
void loop() {
// Обработка кнопки
handleButton();
// Обновление веса и напряжения
static unsigned long last_weight_update = 0;
if (millis() - last_weight_update > 100) {
float raw = scale.get_units(1);
float median = median_filter.update(raw);
current_weight = median;
displayed_weight = display_filter.update(current_weight);
// Обновляем отфильтрованное напряжение
updateVoltage();
// В режиме нагрева обновляем W только когда индуктор ВЫКЛЮЧЕН
// (чтобы избежать влияния электромагнитных помех на измерения)
if (current_state == STATE_HEATING && !heat_cycle_on) {
m0 = current_weight; // Обновляем текущую массу только при выключенном индукторе
// Обновляем W каждые W_UPDATE_INTERVAL мс
if (millis() - last_W_update > W_UPDATE_INTERVAL) {
current_W = calculateW(); // Вычисляем W напрямую (без фильтра)
// DEBUG: выводим все значения для диагностики
Serial.print("m=");
Serial.print(m, 4);
Serial.print(" m1=");
Serial.print(m1, 4);
Serial.print(" m0=");
Serial.print(m0, 4);
Serial.print(" W=");
Serial.print(current_W, 4);
Serial.print(" max_W=");
Serial.println(max_W, 4);
// Обновляем максимальное значение W
if (current_W > max_W) {
max_W = current_W;
}
last_W_update = millis();
}
}
// Обновление дисплея в зависимости от состояния
updateDisplay();
last_weight_update = millis();
}
// Управление нагревом
if (current_state == STATE_HEATING) {
manageHeating();
}
delay(10);
}
void displayReadyScreen() {
lcd.clear();
printCentered(0, "Gruntometriya");
printCentered(1, "ready");
}
void updateDisplay() {
if (need_display_clear) {
lcd.clear();
need_display_clear = false;
}
switch (current_state) {
case STATE_READY:
// Показываем центрированный текст
printCentered(0, "Gruntometriya");
// Вторая строка: "ready" и напряжение
{
char ready_line[17];
char v_buf[5];
dtostrf(filtered_voltage, 4, 1, v_buf);
snprintf(ready_line, sizeof(ready_line), "ready %sV", v_buf);
lcd.setCursor(0, 1);
lcd.print(ready_line);
}
// Показываем отсчет в правом нижнем углу если нужно
if (showing_countdown) {
unsigned long hold_time = millis() - button_press_time;
int seconds = hold_time / 1000;
// Отображаем отсчет в правом нижнем углу (позиция 13-15)
lcd.setCursor(13, 1);
if (seconds < 10) {
lcd.print(" ");
lcd.setCursor(15, 1);
lcd.print(seconds);
} else {
lcd.setCursor(14, 1);
lcd.print(seconds);
}
}
break;
case STATE_EMPTY_CUVETTE:
printCentered(0, "hang an empty");
// Во второй строке показываем текущий вес кюветы
printWeightCentered(1, displayed_weight);
break;
case STATE_SAVING_EMPTY:
printCentered(0, "saving value");
printCentered(1, "measuring...");
// Измерение и сохранение массы пустой кюветы
m = measureAverageWeight(10);
delay(500);
current_state = STATE_FILLED_CUVETTE;
need_display_clear = true;
break;
case STATE_FILLED_CUVETTE:
printCentered(0, "Hang the filled");
// Во второй строке показываем текущий вес заполненной кюветы
printWeightCentered(1, displayed_weight);
break;
case STATE_SAVING_FILLED:
printCentered(0, "saving value");
printCentered(1, "measuring...");
// Измерение и сохранение массы заполненной кюветы
m1 = measureAverageWeight(10);
delay(500);
current_state = STATE_HEATING;
need_display_clear = true;
heat_start_time = millis();
heating_active = true;
heat_cycle_on = true;
last_cycle_change = millis();
last_W_update = millis();
// Фильтр W удалён - используем прямое значение
max_W = 0.0; // Сбрасываем максимальное W
digitalWrite(HEAT_PIN, HIGH);
break;
case STATE_HEATING:
displayHeating();
break;
case STATE_HEATING_COMPLETE:
displayHeatingComplete();
break;
}
}
void handleButton() {
int button_state = digitalRead(BUTTON_PIN);
if (button_state == 0) { // Кнопка нажата
if (!button_pressed) {
button_pressed = true;
button_press_time = millis();
showing_countdown = false;
}
unsigned long hold_time = millis() - button_press_time;
// Показываем отсчет начиная с 1 секунды (только в режиме READY)
if (current_state == STATE_READY && hold_time >= 1000 && !showing_countdown) {
showing_countdown = true;
need_display_clear = true; // Обновляем дисплей для показа отсчета
}
// Обновляем отсчет каждую секунду (только в режиме READY)
if (current_state == STATE_READY && showing_countdown) {
// Обновляем отсчет когда меняется секунда
static int last_seconds = -1;
int seconds = hold_time / 1000;
if (seconds != last_seconds && seconds <= 10) {
last_seconds = seconds;
need_display_clear = true; // Запрашиваем обновление дисплея
}
}
}
else { // Кнопка отпущена
if (button_pressed) {
unsigned long press_duration = millis() - button_press_time;
button_pressed = false;
// Скрываем отсчет
if (showing_countdown) {
showing_countdown = false;
need_display_clear = true; // Запрашиваем обновление экрана
}
// Определяем действие по времени нажатия (только в режиме READY)
if (current_state == STATE_READY) {
if (press_duration >= MIN_TARE_TIME && press_duration <= MAX_TARE_TIME) {
// Тарирование 4-6 секунд
performTaring();
}
else if (press_duration >= MIN_CAL_TIME) {
// Калибровка 8+ секунд
performCalibration();
}
else if (press_duration < 1000) {
// Короткое нажатие
processShortPress();
}
} else {
// Короткое нажатие в других состояниях
if (press_duration < 1000) {
processShortPress();
}
}
}
}
}
void processShortPress() {
switch (current_state) {
case STATE_READY:
current_state = STATE_EMPTY_CUVETTE;
need_display_clear = true;
break;
case STATE_EMPTY_CUVETTE:
current_state = STATE_SAVING_EMPTY;
need_display_clear = true;
break;
case STATE_SAVING_EMPTY:
// Сохранение уже произошло, состояние изменится автоматически
break;
case STATE_FILLED_CUVETTE:
current_state = STATE_SAVING_FILLED;
need_display_clear = true;
break;
case STATE_SAVING_FILLED:
// Сохранение уже произошло, состояние изменится автоматически
break;
case STATE_HEATING:
// Прерывание нагрева - max_W уже содержит максимальное значение
digitalWrite(HEAT_PIN, LOW);
heating_active = false;
heat_cycle_on = false;
current_state = STATE_HEATING_COMPLETE;
need_display_clear = true;
break;
case STATE_HEATING_COMPLETE:
// Возврат к началу
current_state = STATE_READY;
need_display_clear = true;
break;
}
}
void displayHeating() {
// Первая строка: время, напряжение
unsigned long elapsed = millis() - heat_start_time;
unsigned long remaining = HEAT_DURATION - elapsed;
int minutes_remaining = (remaining / 60000) + 1; // +1 чтобы показывать целые минуты
if (minutes_remaining < 0) minutes_remaining = 0;
char line1[17];
char v_buf[5];
dtostrf(filtered_voltage, 4, 1, v_buf);
// Формат: "Heat 15m 16.0V"
snprintf(line1, sizeof(line1), "Heat %2dm %sV", minutes_remaining, v_buf);
lcd.setCursor(0, 0);
lcd.print(line1);
// Вторая строка: масса (слева) и W (справа)
char line2[17];
char w_buf[6];
char mass_buf[7];
// Форматируем максимальный W
if (max_W >= 100) {
strcpy(w_buf, ">99");
} else {
dtostrf(max_W, 4, 1, w_buf);
}
// Форматируем массу m0 (показываем когда индуктор выключен)
if (!heat_cycle_on) {
// Индуктор выключен - показываем текущую массу
if (m0 >= 100.0) {
strcpy(mass_buf, ">99g");
} else if (m0 <= -10.0) {
strcpy(mass_buf, "<-9g");
} else {
dtostrf(m0, 5, 2, mass_buf);
strcat(mass_buf, "g");
}
// Формат: "XX.XXg W:XX.X%"
snprintf(line2, sizeof(line2), "%-8s %s%%", mass_buf, w_buf);
} else {
// Индуктор включен - показываем только W по центру
snprintf(line2, sizeof(line2), " * %s%%", w_buf);
}
lcd.setCursor(0, 1);
lcd.print(line2);
}
void displayHeatingComplete() {
// Первая строка
printCentered(0, "end of heating");
// Вторая строка с максимальным W (это и есть результат измерения влажности)
String w_text = "W: ";
if (max_W >= 100) {
w_text += ">99%";
} else if (max_W >= 0) {
w_text += String(max_W, 1);
w_text += "%";
} else {
w_text += "0.0%"; // Если max_W отрицательный, показываем 0
}
printCentered(1, w_text);
}
void manageHeating() {
if (!heating_active) return;
unsigned long elapsed = millis() - heat_start_time;
unsigned long cycle_elapsed = millis() - last_cycle_change;
// Управление циклическим нагревом (30 сек ON / 20 сек OFF)
if (heat_cycle_on) {
// Если прошло 30 секунд нагрева - выключаем
if (cycle_elapsed >= HEAT_ON_TIME) {
heat_cycle_on = false;
last_cycle_change = millis();
digitalWrite(HEAT_PIN, LOW);
}
} else {
// Если прошло 20 секунд паузы - включаем
if (cycle_elapsed >= HEAT_OFF_TIME) {
heat_cycle_on = true;
last_cycle_change = millis();
digitalWrite(HEAT_PIN, HIGH);
}
}
// Проверка завершения общего времени нагрева (15 минут)
if (elapsed >= HEAT_DURATION) {
// max_W уже содержит максимальное значение W за время нагрева
// Гарантированно выключаем нагрев
digitalWrite(HEAT_PIN, LOW);
heat_cycle_on = false;
heating_active = false;
current_state = STATE_HEATING_COMPLETE;
need_display_clear = true;
}
}
void performTaring() {
lcd.clear();
printCentered(0, "Auto-Taring...");
scale.tare();
median_filter.reset();
display_filter.setImmediate(0.0);
// Если был режим нагрева - выходим из него
if (heating_active) {
digitalWrite(HEAT_PIN, LOW);
heating_active = false;
heat_cycle_on = false;
}
lcd.setCursor(0, 1);
lcd.print("Done!");
delay(500);
lcd.clear();
need_display_clear = true;
// Возвращаемся в состояние READY
current_state = STATE_READY;
}
void performCalibration() {
calibrationProcedure();
}
void calibrationProcedure() {
Serial.println("=== CALIBRATION START ===");
Serial.print("Old calibration_factor: ");
Serial.println(calibration_factor, 6);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibration ");
// Если был режим нагрева - выходим из него
if (heating_active) {
digitalWrite(HEAT_PIN, LOW);
heating_active = false;
heat_cycle_on = false;
Serial.println("Heating was active, turned off");
}
// Шаг 1: Clear platform
Serial.println("Step 1: Waiting for empty platform...");
lcd.setCursor(0, 1);
lcd.print("Clear platform ");
// Ждем нажатия кнопки
while (digitalRead(BUTTON_PIN) == 1) {
delay(10);
}
while (digitalRead(BUTTON_PIN) == 0) {
delay(10);
}
delay(100);
// Сообщение о тарировании
Serial.println("Step 2: Taring...");
lcd.setCursor(0, 1);
lcd.print("Taring... ");
// ВАЖНО: Устанавливаем scale=1.0 ПЕРЕД тарированием,
// чтобы получать сырые значения и избежать зацикливания калибровки
scale.set_scale(1.0);
Serial.println("Scale set to 1.0 for raw measurements");
// Тарируем весы (устанавливаем ноль)
delay(500); // Даем время на стабилизацию
scale.tare();
Serial.println("Tare complete");
// Шаг 2: Set 100g
Serial.println("Step 3: Waiting for 100g weight...");
lcd.setCursor(0, 1);
lcd.print("Set 100g ");
// Ждем нажатия кнопки
while (digitalRead(BUTTON_PIN) == 1) {
delay(10);
}
while (digitalRead(BUTTON_PIN) == 0) {
delay(10);
}
delay(100);
// Сообщение о сохранении результатов 100г
Serial.println("Step 4: Measuring 100g raw values (15 samples)...");
lcd.setCursor(0, 1);
lcd.print("saving results ");
// Измеряем среднее СЫРОЕ значение 100г (15 измерений)
// scale=1.0, поэтому get_units() возвращает сырые значения
delay(1000); // Даем время на стабилизацию
float weight_sum = 0;
for (int i = 0; i < 15; i++) {
float reading = scale.get_units(1); // При scale=1.0 это сырое значение
weight_sum += reading;
Serial.print(" Raw sample ");
Serial.print(i + 1);
Serial.print(": ");
Serial.println(reading, 2);
delay(100);
}
float weight_average = weight_sum / 15;
Serial.print("Raw weight sum: ");
Serial.println(weight_sum, 2);
Serial.print("Raw weight average: ");
Serial.println(weight_average, 2);
Serial.print("CALIBRATION_WEIGHT: ");
Serial.println(CALIBRATION_WEIGHT, 2);
// Вычисляем коэффициент калибровки
// scale=1.0 и tare() выполнен, поэтому weight_average - это СЫРОЕ значение АЦП
// new_factor = raw_value / known_weight
// Проверяем, что груз поставлен (сырое значение больше 1000 единиц АЦП)
if (abs(weight_average) > 1000) {
calibration_factor = weight_average / CALIBRATION_WEIGHT;
Serial.print("New calibration_factor: ");
Serial.println(calibration_factor, 6);
// Проверяем, что коэффициент положительный
if (calibration_factor <= 0) {
// Ошибка: отрицательный или нулевой коэффициент - неправильная калибровка
// (датчик подключен неправильно или вес убрали вместо добавления)
Serial.println("ERROR: calibration_factor <= 0!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibration ");
lcd.setCursor(0, 1);
lcd.print("ERROR: coef<0! ");
delay(2000);
} else {
// Сохраняем коэффициент в EEPROM
saveCalibrationToEEPROM(calibration_factor);
Serial.println("Calibration factor saved to EEPROM");
// Устанавливаем новый коэффициент
scale.set_scale(calibration_factor);
Serial.println("Scale updated with new factor");
// Сообщение об успешной калибровке
Serial.println("=== CALIBRATION OK ===");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibration ");
lcd.setCursor(0, 1);
lcd.print("OK! ");
delay(1000);
}
} else {
// Сообщение о неудачной калибровке (груз не поставлен или слишком легкий)
Serial.print("ERROR: abs(raw_weight_average) = ");
Serial.print(abs(weight_average), 2);
Serial.println(" <= 1000, raw ADC value too low!");
Serial.println("Check: is 100g weight placed? Is HX711 connected?");
Serial.println("=== CALIBRATION FAILED ===");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibration ");
lcd.setCursor(0, 1);
lcd.print("Failed! ");
delay(1000);
}
lcd.clear();
// Возврат в режим READY
current_state = STATE_READY;
need_display_clear = true;
Serial.println("Returned to READY state");
}