bertmelis / VitoWiFi

Communicate with Viessmann boilers using the optolink for ESP8266 and ESP32
MIT License
117 stars 39 forks source link

Cannot write (rawOptolink) #55

Closed ondras12345 closed 5 years ago

ondras12345 commented 5 years ago

Installation specifics

Hi, I am trying to write using the OptolinkP300 library and I am unable to do so. If I only try to read everything works ok. But when I try to write It seems to get stuck in busy state (the lcd prints quickly changing numbers + "busy" as it tries to do something and doesn't pass the !myOptolink.isBusy() condition.)

I'm working on another code and it does read ok until it tries to write, then it gets stuck in the busy state.

I couldn't find any example of how to write using the Optolink library.

BTW I had to modify few lines in the library to make it work with Arduino Pro Mini (I am using VisualStudio with Arduino plugin - maybe it's specific just to VisualStudio):

// begin serial @ 4800 baud, 8 bits, even parity, 2 stop bits
//#ifdef ARDUINO_ARCH_ESP32
//void OptolinkP300::begin(HardwareSerial* serial, int8_t rxPin, int8_t txPin) {
//  serial->begin(4800, SERIAL_8E2, rxPin, txPin);
//  _stream = serial;
//  // serial->flush();
//}
//#endif
//#ifdef ESP8266
void OptolinkP300::begin(HardwareSerial* serial) {
  serial->begin(4800, SERIAL_8E2);
  _stream = serial;
  // serial->flush();
}
//#endif

Example: (Tries to write 27 °C to the set temperature register and then reads it).

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <OptolinkP300.hpp>
#include <Constants.hpp>

OptolinkP300 myOptolink;
#define lcd_width 16
LiquidCrystal_I2C lcd(0x27, lcd_width, 2);
#define room_temperature_day_address 0x2306 //(3..37) °C
#define room_temperature_day_length 1

#define pin_LED_alarm 5
#define pin_LED_communication 7
#define pin_LED_DEBUG 8
#define pin_LED_write LED_BUILTIN

uint32_t lastMillis = 0;
bool getValues = false;
boolean writeValues = false;
boolean readRequested = false;

void setup() {
    pinMode(pin_LED_alarm, OUTPUT);
    pinMode(pin_LED_communication, OUTPUT);
    pinMode(pin_LED_DEBUG, OUTPUT);
    pinMode(pin_LED_write, OUTPUT);

    myOptolink.begin(&Serial);
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("boot");

    //blink all LEDs top indicate the boot
    digitalWrite(pin_LED_alarm, HIGH);
    digitalWrite(pin_LED_communication, HIGH);
    digitalWrite(pin_LED_DEBUG, HIGH);
    digitalWrite(pin_LED_write, HIGH);
    delay(500);
    digitalWrite(pin_LED_alarm, LOW);
    digitalWrite(pin_LED_communication, LOW);
    digitalWrite(pin_LED_DEBUG, LOW);
    digitalWrite(pin_LED_write, LOW);

}

// the loop function runs over and over again until power down or reset
/*
//The loop has two states - getValues and writeValues
void loop() {
    myOptolink.loop();

    if (millis() - lastMillis > 10 * 1000UL) {
        lastMillis = millis();
        //RESET the LEDs
        digitalWrite(pin_LED_alarm, LOW);
        digitalWrite(pin_LED_communication, LOW);
        digitalWrite(pin_LED_DEBUG, LOW);
        lcd.clear();

        if (writeValues) { //sets state to get
            getValues = true;
            readRequested = false;
            writeValues = false;
            lcd.setCursor(0, 1);
            lcd.print("get");
        }
        else { //sets state to write
            getValues = false;
            writeValues = true;
            lcd.setCursor(0, 1);
            lcd.print("write");
        }

        digitalWrite(pin_LED_write, writeValues); //write LED is on when writing
    }

    if (getValues) {
        if (!myOptolink.isBusy()) {
            if (!readRequested) {
                digitalWrite(pin_LED_communication, HIGH);
                myOptolink.readFromDP(room_temperature_day_address, room_temperature_day_length);
                readRequested = true;
            }
        }
        else {
            lcd.setCursor(0, 0);
            lcd.print(millis());
            lcd.print("Busy");
        }
        if (readRequested) {
            if (myOptolink.available() > 0) {
                uint8_t value[4] = { 0 };
                myOptolink.read(value);

                if (value[0] == 27) {
                    digitalWrite(pin_LED_DEBUG, HIGH); //read data matches the written data
                }
                getValues = false;
                digitalWrite(pin_LED_communication, LOW);

            }
        }
    }

    if (writeValues) {
        if (!myOptolink.isBusy()) {
            digitalWrite(pin_LED_communication, HIGH);
            byte value[4] = { 27,0,0,0 };
            boolean err = !myOptolink.writeToDP(room_temperature_day_address, room_temperature_day_length, value);
            if (err) {
                digitalWrite(pin_LED_alarm, HIGH);
                lcd.setCursor(0, 0);
                lcd.print(millis());
                lcd.print("Write error");
            }
            digitalWrite(pin_LED_communication, LOW);
        }
        else {
            lcd.setCursor(0, 0);
            lcd.print(millis());
            lcd.print("Busy");
        }
    }

    if (myOptolink.available() < 0) { //error
        digitalWrite(pin_LED_alarm, HIGH);
        lcd.setCursor(0, 0);
        lcd.print(millis());
        lcd.print("error");
        lcd.print(myOptolink.readError());
    }
}

Edit: I tried to compile the same code with Arduino IDE and it does exactly the same thing.

bertmelis commented 5 years ago

Is it possible to have debug outputs? --> set printer in Optolink, uncomment the setState and setAction methods in OptolinkP300.hpp

I must admit it has been a while since I looked at the current code. I'm working on a brand new version, much easier to work with.

ondras12345 commented 5 years ago

I'd have to use SoftwareSerial to send the debug data as I'm using Arduino Pro Mini that only has one hardware UART. I'll try it tomorrow.

ondras12345 commented 5 years ago

Here is what I got from the debug printer (going trought the loop for about a minute):

Optolink state: 1
Optolink state: 2
Optolink state: 3
Optolink state: 0
Optolink state: 1
Optolink state: 2
Optolink state: 3
Optolink state: 4
Optolink action: 1
Optolink state: 5
Optolink state: 6
WRITE 410600022306011b4d
ack
Optolink state: 7
RCV 4105010223060132
Optolink state: 8
ack
Optolink state: 4
Optolink action: 2
Optolink state: 2
Optolink state: 3
Optolink state: 4
Optolink state: 2
Optolink state: 3
Optolink state: 4
Optolink state: 2
Optolink state: 3
Optolink state: 4
Optolink state: 2
Optolink state: 3
Optolink state: 4
Optolink state: 2
Optolink state: 3
Optolink state: 4
ondras12345 commented 5 years ago

I restarted the boiler because I was using KW that could affect it before. I does exactly the same thing.

18:58:11.261 -> Optolink state: 1
18:58:11.261 -> Optolink state: 2
18:58:11.261 -> Optolink state: 3
18:58:12.282 -> Optolink state: 0
18:58:12.282 -> Optolink state: 1
18:58:12.353 -> Optolink state: 2
18:58:12.353 -> Optolink state: 3
18:58:12.353 -> Optolink state: 4
18:58:19.670 -> Optolink action: 1
18:58:19.670 -> Optolink state: 5
18:58:19.707 -> Optolink state: 6
18:58:19.707 -> WRITE 410600022306011b4d
18:58:19.741 -> ack
18:58:19.741 -> Optolink state: 7
18:58:19.741 -> RCV 4105010223060132
18:58:19.778 -> Optolink state: 8
18:58:19.778 -> ack
18:58:19.778 -> Optolink state: 4
18:58:19.778 -> Optolink action: 2
18:58:34.819 -> Optolink state: 2
18:58:34.819 -> Optolink state: 3
18:58:34.819 -> Optolink state: 4
18:58:49.806 -> Optolink state: 2
18:58:49.842 -> Optolink state: 3
18:58:49.842 -> Optolink state: 4
18:59:04.832 -> Optolink state: 2
18:59:04.866 -> Optolink state: 3
18:59:04.866 -> Optolink state: 4
18:59:19.881 -> Optolink state: 2
18:59:19.881 -> Optolink state: 3
18:59:19.919 -> Optolink state: 4
18:59:34.928 -> Optolink state: 2
18:59:34.928 -> Optolink state: 3
18:59:34.928 -> Optolink state: 4
18:59:49.928 -> Optolink state: 2
18:59:49.962 -> Optolink state: 3
18:59:49.962 -> Optolink state: 4
bertmelis commented 5 years ago

After writing you also have to "reset" by reading the optolink like you do on if (readRequested) { ... Otherwise you'll be stuck in a "returning value" state.

v2.0.0 will be a lot more easy to use. So if you're adventurous, you can take a look at the v2.0.0 branch I don't know if it is working on a AVR though. I don't know if you're able to use a std::queue. Now there something C++11 in the code. If you really want AVR Arduino compatability, now is the time to ask!

ondras12345 commented 5 years ago

Thank you. I'll try it and let you know if it works. Should I do it exactly the same way as reading? if (myOptolink.available() > 0) myOptolink.read(value); What is it supposed to read?

It would be nice to include this into the rawOptolink example.

bertmelis commented 5 years ago

It just returns the written value or the error, in case.

if (myOptolink.available() > 0) {
  uint8_t value[4] = {0};
  myOptolink.read(value);
} else if (myOptolink.available() < 0) {
  myOptolink.readError();
}
ondras12345 commented 5 years ago

Thank you. I don't have enough time to try it today so I'll try it tomorrow.

ondras12345 commented 5 years ago

Hi, I modified my code and it works:

#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <OptolinkP300.hpp>
#include <Constants.hpp>

OptolinkP300 myOptolink;
#define lcd_width 16
LiquidCrystal_I2C lcd(0x27, lcd_width, 2);
SoftwareSerial swSerial(9,10); //TX:10

#define room_temperature_day_address 0x2306 //(3..37) °C
#define room_temperature_day_length 1

#define pin_LED_alarm 5
#define pin_LED_communication 7
#define pin_LED_DEBUG 8
#define pin_LED_write LED_BUILTIN

uint32_t lastMillis = 0;
bool getValues = false;
boolean writeValues = false;
boolean readRequested = false;
boolean state = false; //0: read 1:write

// the setup function runs once when you press reset or power the board
void setup() {
    pinMode(pin_LED_alarm, OUTPUT);
    pinMode(pin_LED_communication, OUTPUT);
    pinMode(pin_LED_DEBUG, OUTPUT);
    pinMode(pin_LED_write, OUTPUT);

    myOptolink.begin(&Serial);
    swSerial.begin(57600);
    myOptolink.setLogger(&swSerial);
    lcd.init();
    lcd.backlight();

    lcd.setCursor(0, 0);
    lcd.print("boot");

    //blink all LEDs top indicate the boot
    digitalWrite(pin_LED_alarm, HIGH);
    digitalWrite(pin_LED_communication, HIGH);
    digitalWrite(pin_LED_DEBUG, HIGH);
    digitalWrite(pin_LED_write, HIGH);
    delay(500);
    digitalWrite(pin_LED_alarm, LOW);
    digitalWrite(pin_LED_communication, LOW);
    digitalWrite(pin_LED_DEBUG, LOW);
    digitalWrite(pin_LED_write, LOW);

}

// the loop function runs over and over again until power down or reset
/*
The loop has two states - getValues and writeValues

*/

void loop() {
    myOptolink.loop();

    if (millis() - lastMillis > 10 * 1000UL) {
        lastMillis = millis();
        //RESET the LEDs
        digitalWrite(pin_LED_alarm, LOW);
        digitalWrite(pin_LED_communication, LOW);
        digitalWrite(pin_LED_DEBUG, LOW);
        lcd.clear();

        if (state) { //sets state to get
            getValues = true;
            readRequested = false;
            writeValues = false;
            state = false;
            lcd.setCursor(0, 1);
            lcd.print("get");
        }
        else { //sets state to write
            getValues = false;
            writeValues = true;
            state = true;
            lcd.setCursor(0, 1);
            lcd.print("write");
        }

        digitalWrite(pin_LED_write, writeValues); //write LED is on when writing
    }

    if (getValues) {
        if (!myOptolink.isBusy()) {
            if (!readRequested) {
                digitalWrite(pin_LED_communication, HIGH);
                myOptolink.readFromDP(room_temperature_day_address, room_temperature_day_length);
                readRequested = true;
            }
        }
        else {
            lcd.setCursor(0, 0);
            lcd.print(millis());
            lcd.print("Busy");
        }
        if (readRequested) {
            if (myOptolink.available() > 0) {
                uint8_t value[4] = { 0 };
                myOptolink.read(value);

                if (value[0] == 27) {
                    digitalWrite(pin_LED_DEBUG, HIGH); //read data matches the written data
                }
                getValues = false;
                digitalWrite(pin_LED_communication, LOW);

            }
        }
    }

    if (writeValues) {
        if (!myOptolink.isBusy()) {
            digitalWrite(pin_LED_communication, HIGH);
            byte value[4] = { 27,0,0,0 };
            boolean err = !myOptolink.writeToDP(room_temperature_day_address, room_temperature_day_length, value);
            writeValues = false; //to prevent multiple writing
            while (myOptolink.available() == 0) {
                myOptolink.loop();
            }
            if (myOptolink.available() > 0) {
                uint8_t value[4] = { 0 };
                myOptolink.read(value);
                if (value[0] != 27) {
                    err = true;
                }
            }
            if (myOptolink.available() < 0) {
                myOptolink.readError();
                err = true;
            }

            if (err) {
                digitalWrite(pin_LED_alarm, HIGH);
                lcd.setCursor(0, 0);
                lcd.print(millis());
                lcd.print("Write error");
            }
            digitalWrite(pin_LED_communication, LOW);
        }
        else {
            lcd.setCursor(0, 0);
            lcd.print(millis());
            lcd.print("Busy");
        }
    }

    if (myOptolink.available() < 0) { //error
        digitalWrite(pin_LED_alarm, HIGH);
        lcd.setCursor(0, 0);
        lcd.print(millis());
        lcd.print("error");
        lcd.print(myOptolink.readError());
    }
}

Here is the debug output I got:

13:51:23.315 -> Optolink state: 1
13:51:23.820 -> Optolink state: 0
13:51:23.820 -> Optolink state: 1
13:51:24.300 -> Optolink state: 0
13:51:24.300 -> Optolink state: 1
13:51:24.806 -> Optolink state: 0
13:51:24.806 -> Optolink state: 1
13:51:25.302 -> Optolink state: 2
13:51:25.302 -> Optolink state: 3
13:51:25.302 -> Optolink state: 4
13:51:31.765 -> Optolink action: 1
13:51:31.765 -> Optolink state: 5
13:51:31.765 -> Optolink state: 6
13:51:31.765 -> WRITE 410600022306011b4d
13:51:31.765 -> ack
13:51:31.765 -> Optolink state: 7
13:51:31.765 -> RCV 4105010223060132
13:51:31.810 -> Optolink state: 8
13:51:31.810 -> ack
13:51:31.810 -> Optolink state: 4
13:51:31.810 -> Optolink action: 2
13:51:31.810 -> Optolink action: 0
13:51:41.725 -> Optolink action: 1
13:51:41.725 -> Optolink state: 5
13:51:41.778 -> Optolink state: 6
13:51:41.778 -> READ 4105000123060130
13:51:41.828 -> ack
13:51:41.828 -> Optolink state: 7
13:51:41.828 -> RCV 410601012306011b4d
13:51:41.828 -> Optolink state: 8
13:51:41.828 -> ack
13:51:41.828 -> Optolink state: 4
13:51:41.828 -> Optolink action: 2
13:51:41.862 -> Optolink action: 0
13:51:51.736 -> Optolink action: 1
13:51:51.736 -> Optolink state: 5
13:51:51.736 -> Optolink state: 6
13:51:51.736 -> WRITE 410600022306011b4d
13:51:51.774 -> ack
13:51:51.774 -> Optolink state: 7
13:51:51.774 -> RCV 4105010223060132
13:51:51.835 -> Optolink state: 8
13:51:51.835 -> ack
13:51:51.835 -> Optolink state: 4
13:51:51.835 -> Optolink action: 2
13:51:51.835 -> Optolink action: 0
13:52:01.762 -> Optolink action: 1
13:52:01.762 -> Optolink state: 5
13:52:01.762 -> Optolink state: 6
13:52:01.762 -> READ 4105000123060130
13:52:01.801 -> ack
13:52:01.801 -> Optolink state: 7
13:52:01.801 -> RCV 410601012306011b4d
13:52:01.801 -> Optolink state: 8
13:52:01.801 -> ack
13:52:01.850 -> Optolink state: 4
13:52:01.850 -> Optolink action: 2
13:52:01.850 -> Optolink action: 0
13:52:11.758 -> Optolink action: 1
13:52:11.758 -> Optolink state: 5
13:52:11.758 -> Optolink state: 6
13:52:11.758 -> WRITE 410600022306011b4d
13:52:11.758 -> ack
13:52:11.758 -> Optolink state: 7
13:52:11.803 -> RCV 4105010223060132
13:52:11.803 -> Optolink state: 8
13:52:11.803 -> ack
13:52:11.803 -> Optolink state: 4
13:52:11.803 -> Optolink action: 2
13:52:11.803 -> Optolink action: 0
13:52:21.738 -> Optolink action: 1
13:52:21.738 -> Optolink state: 5
13:52:21.738 -> Optolink state: 6
13:52:21.773 -> READ 4105000123060130
13:52:21.808 -> ack
13:52:21.808 -> Optolink state: 7
13:52:21.808 -> RCV 410601012306011b4d
13:52:21.808 -> Optolink state: 8
13:52:21.808 -> ack
13:52:21.857 -> Optolink state: 4
13:52:21.857 -> Optolink action: 2
13:52:21.857 -> Optolink action: 0
13:52:31.762 -> Optolink action: 1
13:52:31.762 -> Optolink state: 5
13:52:31.762 -> Optolink state: 6
13:52:31.762 -> WRITE 410600022306011b4d
13:52:31.762 -> ack
13:52:31.762 -> Optolink state: 7
13:52:31.808 -> RCV 4105010223060132
13:52:31.808 -> Optolink state: 8
13:52:31.808 -> ack
13:52:31.808 -> Optolink state: 4
13:52:31.808 -> Optolink action: 2
13:52:31.808 -> Optolink action: 0

I don't need to do anything else while writing so I solved it with a while loop - it waits for some data to be received and then continues by reading it and comparing it to the data it wrote.

I'll modify the code I'm working on and close this issue if everything works as expected. Thank you.

bertmelis commented 5 years ago

You're welcome! Succes!

ondras12345 commented 5 years ago

Everything works just fine. Thank you for your time.

After writing you also have to "reset" by reading the optolink like you do on if (readRequested) { ... Otherwise you'll be stuck in a "returning value" state.

It just returns the written value or the error, in case.

if (myOptolink.available() > 0) {
  uint8_t value[4] = {0};
  myOptolink.read(value);
} else if (myOptolink.available() < 0) {
  myOptolink.readError();
}

It would be nice to include this as a comment in the rawOptolink example, or somewhere else in the documentation.