Значения свойств теряются (после каждого цикла) при вложении библиотек

Я создал 2 библиотеки для использования в своем коде Arduino. Одна — это библиотека HwSwitch, другая — библиотека HwServo, которая использует библиотеку HwSwitch.

Библиотека HwSwitch:

HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{ 
    Name = switchName;
    SwitchPort = switchPort;
    _pressedState = pressedState;
    _lastCheckMillis = 0;

    pinMode(switchPort, inputType);
    _lastPinState = digitalRead(SwitchPort);
}

bool HwSwitch::IsPressed()
{
    int currentPinState = GetPinState();
    return currentPinState == _pressedState;
}

bool HwSwitch::SwitchStateChanged()
{
    int currentPinState = GetPinState();
    if (_lastPinState != currentPinState)
    {
        Serial.println("---");
        Serial.println("1. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));

        _lastPinState = currentPinState;
        Serial.println("2. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));

        return true;
    }

    return false;
}

int HwSwitch::GetPinState()
{
    unsigned long ms = millis();
    if ((ms - _lastCheckMillis) < 50)
    {
        return _lastPinState;
    }

    _lastCheckMillis = ms;
    return digitalRead(SwitchPort);
}

Библиотека HwServo:

HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch limitSwitch)
{
    _servo.attach(servoPort);
    _servo.write(zeroPoint);

    ServoPort = servoPort;
    ZeroPoint = zeroPoint;

    LimitSwitch = limitSwitch;
}

void HwServo::RotateUp()
{
    _servo.write(ZeroPoint + UP);
}

void HwServo::RotateDown()
{
    if (!LimitSwitch.IsPressed())
    {
        _servo.write(ZeroPoint + DOWN);
    }
}

void HwServo::Stop()
{
    _servo.write(ZeroPoint);
}

И вот как я инициализировал его в коде Arduino:

HwServo HwServos[] = {
    HwServo(9, 94, HwSwitch("S1", 14, INPUT_PULLUP, HIGH)),
    HwServo(5, 90, HwSwitch("S2", 8, INPUT_PULLUP, HIGH)),
};

void setup() { }

void loop() {

    for(int i = 0; i < 2; i++)
    {
        HwServo hwServo = HwServos[i];

        if (hwServo.LimitSwitch.SwitchStateChanged())
        {
            SendSwitchStateUpdate(hwServo.LimitSwitch);

            if (hwServo.LimitSwitch.IsPressed())
            {
                hwServo.Stop();
            }
        }
    }
}

Теперь, наконец, к проблеме! Как видите, в библиотеке HwSwitch я вывожу некоторые данные с помощью Serial.println. Здесь я вижу, что _lastPinState успешно обновляется, но сбрасывается после каждого цикла. Однако, когда я создаю HwSwitch напрямую и использую его, _lastPinState не сбрасывается. Другими словами, сброс значения происходит только тогда, когда библиотека HwSwitch используется внутри библиотеки HwServo.

Видимо, это как-то связано с указателями? Вероятно, я неправильно инициализирую свои классы, но я понятия не имею, как это исправить. Кто-нибудь, кто может помочь с (и желательно объяснить) эту проблему?


person Stitch10925    schedule 08.01.2017    source источник
comment
Попробуйте сделать переменную LimitSwitch указателем (т.е. в файле h напишите HwSwitch *LimitSwitch;). Затем передайте указатель на конструктор (поэтому конструктор становится ..., HwSwitch *limitSwitch), а затем вы вызываете его с помощью ...94, new HwSwitch("S1", 14...). Наконец, вы должны изменить прямые вызовы (например, .SwitchStateChanged()) на косвенные (так что ->SwitchStateChanged()). Это стандартный способ обработки объектов. И, если вы когда-нибудь планируете удалить объект HwServo, создайте деструктор и удалите и этот объект. Попробуйте это и проверьте, решает ли это ваши проблемы.   -  person frarugi87    schedule 08.01.2017
comment
Поскольку ключевое слово new не используется и не происходит динамического выделения памяти, простое использование деструктора по умолчанию и вывод указателя за пределы области видимости должно работать нормально :)   -  person NonCreature0714    schedule 09.01.2017
comment
@ frarugi87 Я дошел до использования указателей (*), но после этого мне не удалось найти, как получить доступ к методам или свойствам. Теперь я знаю, что должен был использовать -›, теперь код работает, спасибо!   -  person Stitch10925    schedule 10.01.2017
comment
@ NonCreature0714 Нет. Ключевое слово new используется вне класса, в вызове конструктора (в моих примерах). Поэтому необходимо явное уничтожение. Если вы поместите его в деструктор, вы можете забыть об уничтожении HwServo (но он в любом случае вызовет деструктор HwSwitch). В стежке, я рад, что это сработало ;)   -  person frarugi87    schedule 10.01.2017


Ответы (1)


У меня сейчас нет с собой Arduino, но я посмотрел и переписал ваш код, добавил пропущенные конструкторы, насколько я мог предположить, и получил его для компиляции. Были некоторые вещи, которые нужно было исправить. Я уверен, что есть и другие способы, но это то, что я сделал.

Чтобы получить полный код, перейдите здесь.

Во-первых, я создал несколько указателей на объекты, которые я хотел бы оставить, например так:

HwServo *HwServos[2];
HwSwitch *s1;
HwSwitch *s2; 
HwServo *sv1; 
HwServo *sv2;

Теперь каждый зарезервирован в памяти на Arduino.

Теперь создайте объекты в setup():

void setup() {
    s1 = new HwSwitch("S1", 14, INPUT_PULLUP, HIGH);
    s2 = new HwSwitch("S2", 8, INPUT_PULLUP, HIGH);
    sv1 = new HwServo(9, 94, *s1);
    sv2 = new HwServo(5, 90, *s2);


    //Now, since you're going through an array:
    HwServos[0] = sv1;
    HwServos[1] = sv2;
}

Используйте эту функцию настройки!!! Возможно, это не всегда необходимо, а в некоторых случаях даже рекомендуется, но приятно собирать вещи, которые нужно создать только один раз, особенно в этом случае.

Обратите внимание, что new не используется внутри области действия любого объекта, а скорее в области действия программы... Таким образом, не требуется никаких причудливых деструкторов в ваших объектах. Обычно вы беспокоитесь об удалении их всех перед завершением программы (или когда это лучше всего подходит), но в случае с Arduino он просто потеряет питание и все равно убьет все.

Вы должны изменить свои определения классов на это:

class HwSwitch {
public:
    String Name;
    int SwitchPort;
    int _pressedState;
    int _lastCheckMillis;
    int _lastPinState;
    HwSwitch(String, int, int, int);
    bool IsPressed();
    bool SwitchStateChanged();
    int GetPinState();
};

class HwServo {
public:
    HwServo();
    HwServo(int, int, HwSwitch &);
    int ServoPort;
    int ZeroPoint;
    HwSwitch & LimitSwitch;
    void RotateUp();
    void RotateDown();
    void Stop();
    Servo _servo;
};

Примечание. Я сделал все public, не стесняйтесь перемещать private обратно в личное, если хотите.

Я изменил конструкторы на:

HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{ 
    Name = switchName;
    SwitchPort = switchPort;
    _pressedState = pressedState;
    _lastCheckMillis = 0;

    pinMode(switchPort, inputType);
    _lastPinState = digitalRead(SwitchPort);
}

HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch &limitSwitch)
{
    _servo.attach(servoPort);
    _servo.write(zeroPoint);

    ServoPort = servoPort;
    ZeroPoint = zeroPoint;

    LimitSwitch = limitSwitch;
}

И я изменил loop() вот так:

void loop() {
// put your main code here, to run repeatedly:
    for(int i = 0; i < 2; i++)
    {
        if (HwServos[i]->LimitSwitch.SwitchStateChanged())
        {
            SendSwitchStateUpdate(HwServos[i]->LimitSwitch);
            if (HwServos[i]->LimitSwitch.IsPressed())
            {
                HwServos[i]->Stop();
            }
        }
    }
}
person NonCreature0714    schedule 09.01.2017
comment
Этот код, а также объяснение в предыдущих комментариях @frarugi87 помогли мне заставить код работать. Спасибо! - person Stitch10925; 10.01.2017