diff --git a/examples/03.Full_measurement_test/03.Full_measurement_test.ino b/examples/03.Full_measurement_test/03.Full_measurement_test.ino index 7aae9d2..e1450cb 100644 --- a/examples/03.Full_measurement_test/03.Full_measurement_test.ino +++ b/examples/03.Full_measurement_test/03.Full_measurement_test.ino @@ -224,14 +224,14 @@ void showMeasurements(uint8_t group) // This will contain the amount of measurements in the current group, after calling the readGroup() function. uint8_t amount_of_measurements = 0; + // This flag keeps track if a [Header] was received for the current group, meaning it's of the "header+body" type. + bool received_group_header = false; + // In case of the "header+body" groups, the [Body] response is the one that actually contains live data. // In theory, the module should report the same number of measurements in the [Header] and in the [Body]. // Still, the number of measurements received in the [Header] will be stored separately, for correctness. uint8_t amount_of_measurements_in_header = 0; - // This flag keeps track if a [Header] was received for the current group, meaning it's of the "header+body" type. - bool received_group_header = false; - // For modules which report measuring groups in the "header+body" mode, it is important to do update() when requesting a new group, so the module sends the [Header]. diag.update(); @@ -327,18 +327,19 @@ void showMeasurements(uint8_t group) // Add an extra step to the loop, because this one shouldn't count, as it doesn't contain live data. attempt--; } - // We have nothing to display yet, the next readGroup() calls will get the actual data; skip this step of the loop. + // We have nothing to display yet, the next readGroup() will get the actual data; skip this step of the loop. continue; case KLineKWP1281Lib::GROUP_BODY: - // If we don't have a [Header], it doesn't make sense to receive a [Body]. - if (!received_group_header) { - Serial.println("Error reading header! (got body)"); - return; + // If we don't have a [Header], it doesn't make sense to receive a [Body]. + if (!received_group_header) + { + Serial.println("Error reading header! (got body)"); + return; + } } - - // Otherwise, execute the code after the switch(). + // If we have the [Header], now we also have the [Body]; execute the code after the switch(). break; // Execute the code after the switch(). diff --git a/examples/04.Continuous_measurement_test/04.Continuous_measurement_test.ino b/examples/04.Continuous_measurement_test/04.Continuous_measurement_test.ino new file mode 100644 index 0000000..b5d8366 --- /dev/null +++ b/examples/04.Continuous_measurement_test/04.Continuous_measurement_test.ino @@ -0,0 +1,341 @@ +/* + Title: + 04.Continuous_measurement_test.ino + + Description: + Demonstrates how to read a measurement group continuously. + + Notes: + *You can change which group to read by modifying the #define below. + *This group will be read continuously while the sketch is running. + *It is not necessary to maintain the connection with "diag.update();" in the loop, because any request will have the same effect (update() must + be used in periods of inactivity). + *This sketch will not fit on an Arduino UNO by default! + *To fix this, open the library's /src/KLineKWP1281Lib.h file in a text editor and comment out the line "#define KWP1281_TEXT_TABLE_SUPPORTED" + at the top, as explained in the comments. + *After manually disabling the text table, the value for some parameters will be displayed as "EN_f25". + *If you have the text table enabled, you can choose from a few different languages a bit further below in "KLineKWP1281Lib.h". Please only choose + one option. +*/ + +// Change which group to read. +#define GROUP_TO_READ 1 + +/* + *This sketch displays information in the Serial Monitor, so another serial port is required for the K-line. + *If another port is not available on the selected board, the regular port "Serial" can be used, although no information can be printed to it (good for + projects with external displays or for data logging). + + ***Uncomment the appropriate options in the "configuration.h" file! + + Arduino UNO + *no additional serial port, software serial is used + *library of choice: AltSoftSerial (better than SoftwareSerial because it can also receive while sending) + *pins: + *K-line TX -> RX pin 8 + *K-line RX -> TX pin 9 + + Arduino MEGA + *has three additional serial ports + *pins: + *K-line TX -> Serial1 - RX pin 19 / Serial2 - RX pin 17 / Serial3 - RX pin 15 + *K-line RX -> Serial1 - TX pin 18 / Serial2 - TX pin 16 / Serial3 - TX pin 14 + + ESP32 / ESP32-C6 + *has one additional serial port + *pins (they can be remapped, this is what they are configured to in these examples): + *K-line TX -> RX pin 16 + *K-line RX -> TX pin 17 + + ESP8266 + *has no additional serial ports + *library of choice: SoftwareSerial (the ESP version of SoftwareSerial can also receive while sending) + *pins: any unused + *chosen for this demo: + *K-line TX -> RX pin 4 (D2) + *K-line RX -> TX pin 5 (D1) + + Raspberry Pi Pico + *has two additional serial ports + *pins: interchangeable + *default: + *K-line TX -> Serial1 - RX pin 1 / Serial2 - RX pin 9 + *K-line RX -> Serial1 - TX pin 0 / Serial2 - TX pin 8 + + ***If using the first hardware serial port (Serial) (with other sketches), the interface must be disconnected during code upload, and no "Serial.print"s + should be used. +*/ + +// Include the library. +#include + +// Include the two files containing configuration options and the functions used for communication. +#include "configuration.h" +#include "communication.h" + +// Debugging can be enabled in "configuration.h" in order to print connection-related info on the Serial Monitor. +#if debug_info + KLineKWP1281Lib diag(beginFunction, endFunction, sendFunction, receiveFunction, TX_pin, is_full_duplex, &Serial); +#else + KLineKWP1281Lib diag(beginFunction, endFunction, sendFunction, receiveFunction, TX_pin, is_full_duplex); +#endif + +// Please find more information on the topic of KWP1281 measurement groups in the "03.Full_measurement_test" example. +uint8_t measurement_buffer[80]; +uint8_t measurement_body_buffer[4]; + +void setup() +{ + // Initialize the Serial Monitor. + Serial.begin(115200); + delay(500); + Serial.println("Sketch started."); + + // If debugging bus traffic was enabled, attach the debugging function. +#if debug_traffic + diag.KWP1281debugFunction(KWP1281debugFunction); +#endif + + // Connect to the module. + diag.connect(connect_to_module, module_baud_rate, false); + + Serial.print("Requesting group "); + Serial.print(GROUP_TO_READ); + Serial.println(" continuously."); +} + +void loop() +{ + showMeasurements(GROUP_TO_READ); +} + +/* + + -The variables used for "header+body" groups are stored globally. + -Normally, the flag `received_group_header` should be reset to false when requesting a new group. + -This example only requests one group, so it isn't necessary; if the group was "header+body" once, it will always be like that. + + -You could also keep `received_group_header` local to the showMeasurements() function, and do diag.update() before each request, + to ensure you receive the [Header]. + -Of course, this would negatively impact performance, since you need to receive more data from the module each time you read the group. + +*/ + +// This flag keeps track if a [Header] was received for the current group, meaning it's of the "header+body" type. +bool received_group_header = false; + +// This will contain the amount of measurements received in the [Header] of a "header+body" group. +uint8_t amount_of_measurements_in_header = 0; + +void showMeasurements(uint8_t group) +{ + // This will contain the amount of measurements in the current group, after calling the readGroup() function. + uint8_t amount_of_measurements = 0; + + /* + -For modules which report measuring groups in the "header+body" mode, it is important to do update() when requesting a new group, + so the module sends the [Header]. + -If it's the first request, it's not necessary; the module will always send the [Header] for the first time. + -Since this example only requests one group, it would decrease performance (measurement rate) to do update() every time, since it would cause the module + to send the [Header] over and over again, and it takes time to receive it, instead of only receiving it once, at the beginning. + */ + // diag.update(); + + /* + The readGroup() function can return: + KLineKWP1281Lib::FAIL - the requested group does not exist + KLineKWP1281Lib::ERROR - communication error + KLineKWP1281Lib::SUCCESS - received measurements + KLineKWP1281Lib::GROUP_BASIC_SETTING - received a [Basic settings] measurement; the buffer contains 10 raw values + KLineKWP1281Lib::GROUP_HEADER - received the [Header] for a "header+body" group; need to read again to get the [Body] + KLineKWP1281Lib::GROUP_BODY - received the [Body] for a "header+body" group + */ + + // Read the requested group and store the returneded value. + KLineKWP1281Lib::executionStatus readGroup_status; + // If the group is not of "header+body" type, or if it is and this is the first request, we don't have a [Header] (yet), so `received_group_header=false`. + // The response to this request will be stored in the larger array. + // If it is in fact of "header+body" type, the [Header] will be stored in this array. + if (!received_group_header) + { + readGroup_status = diag.readGroup(amount_of_measurements, group, measurement_buffer, sizeof(measurement_buffer)); + } + // If the group is of "header+body" type, and this is not the first request, it means we have a header, so `received_group_header=true`. + // The response to this request will be stored in the smaller array, because it should be the [Body]. + else + { + readGroup_status = diag.readGroup(amount_of_measurements, group, measurement_body_buffer, sizeof(measurement_body_buffer)); + } + + // Check the returned value. + switch (readGroup_status) + { + case KLineKWP1281Lib::ERROR: + { + Serial.println("Error reading group!"); + } + // There is no reason to continue, exit the function. + return; + + case KLineKWP1281Lib::FAIL: + { + Serial.print("Group "); + Serial.print(group); + Serial.println(" does not exist!"); + } + // There is no reason to continue, exit the function. + return; + + case KLineKWP1281Lib::GROUP_BASIC_SETTINGS: + { + // If we have a [Header], it means this group sends responses of "header+body" type. + // So, at this point, it doesn't make sense to receive something other than a [Body]. + if (received_group_header) + { + Serial.println("Error reading body! (got basic settings)"); + return; + } + + // We have 10 raw values in the `measurement_buffer` array. + Serial.print("Basic settings in group "); + Serial.print(group); + Serial.print(": "); + for (uint8_t i = 0; i < 10; i++) + { + Serial.print(measurement_buffer[i]); + Serial.print(" "); + } + Serial.println(); + } + // We have nothing else to display, exit the function. + return; + + case KLineKWP1281Lib::GROUP_HEADER: + { + // If we have a [Header], it means this group sends responses of "header+body" type. + // So, at this point, it doesn't make sense to receive something other than a [Body]. + if (received_group_header) + { + Serial.println("Error reading body! (got header)"); + return; + } + + // Set the flag to indicate that a header was received, making this a "header+body" group response. + received_group_header = true; + + // Store the number of measurements received in the header. + amount_of_measurements_in_header = amount_of_measurements; + } + // We have nothing to display yet, the next readGroup() will get the actual data; exit the function. + return; + + case KLineKWP1281Lib::GROUP_BODY: + { + // If we don't have a [Header], it doesn't make sense to receive a [Body]. + if (!received_group_header) + { + Serial.println("Error reading header! (got body)"); + return; + } + } + // If we have the [Header], now we also have the [Body]; execute the code after the switch(). + break; + + // Execute the code after the switch(). + case KLineKWP1281Lib::SUCCESS: + break; + } + + // If the group was read successfully, display its measurements. + Serial.print("Group "); + Serial.print(group); + Serial.println(':'); + + // Display each measurement. + for (uint8_t i = 0; i < 4; i++) + { + // Format the values with a leading tab. + Serial.print(" "); + + /* + The getMeasurementType() function can return: + KLineKWP1281Lib::UNKNOWN - index out of range (the measurement doesn't exist in the group) or the formula is invalid/not-applicable + KLineKWP1281Lib::VALUE - regular measurement, with a value and units + KLineKWP1281Lib::TEXT - text measurement + */ + + // Get the current measurement's type. + KLineKWP1281Lib::measurementType measurement_type; + if (!received_group_header) + { + measurement_type = KLineKWP1281Lib::getMeasurementType(i, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer)); + } + // For "header+body" measurements, you need to use this other function that specifically parses headers instead of regular responses. + else + { + measurement_type = KLineKWP1281Lib::getMeasurementTypeFromHeader(i, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer)); + } + + // Check the returned value. + switch (measurement_type) + { + // "Value and units" type + case KLineKWP1281Lib::VALUE: + { + // The measurement's units will be copied into this array, so they can be displayed. + char units_string[16]; + + // Regular mode: + if (!received_group_header) + { + // Display the calculated value, with the recommended amount of decimals. + Serial.print(KLineKWP1281Lib::getMeasurementValue(i, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer)), + KLineKWP1281Lib::getMeasurementDecimals(i, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer))); + + // The function getMeasurementUnits() returns the same string that it's given. It's the same as units_string. + Serial.print(' '); + Serial.println(KLineKWP1281Lib::getMeasurementUnits(i, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer), units_string, sizeof(units_string))); + } + // "Header+body" mode: + else + { + // Display the calculated value, with the recommended amount of decimals. + Serial.print(KLineKWP1281Lib::getMeasurementValueFromHeaderBody(i, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer)), + KLineKWP1281Lib::getMeasurementDecimalsFromHeader(i, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer))); + + // The function getMeasurementUnitsFromHeaderBody() returns the same string that it's given. It's the same as units_string. + Serial.print(' '); + Serial.println(KLineKWP1281Lib::getMeasurementUnitsFromHeaderBody(i, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer), units_string, sizeof(units_string))); + } + } + break; + + // "Text" type + case KLineKWP1281Lib::TEXT: + { + // The measurement's text will be copied into this array, so it can be displayed. + char text_string[16]; + + if (!received_group_header) + { + // The function getMeasurementText() returns the same string that it's given. It's the same as text_string. + Serial.println(KLineKWP1281Lib::getMeasurementText(i, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer), text_string, sizeof(text_string))); + } + else + { + // The function getMeasurementTextFromHeaderBody() returns the same string that it's given. It's the same as text_string. + Serial.println(KLineKWP1281Lib::getMeasurementTextFromHeaderBody(i, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer), text_string, sizeof(text_string))); + } + } + break; + + // Invalid measurement index + case KLineKWP1281Lib::UNKNOWN: + Serial.println("N/A"); + break; + } + } + + // Leave an empty line. + Serial.println(); +} diff --git a/examples/04.Continuous_measurement_test/communication.h b/examples/04.Continuous_measurement_test/communication.h new file mode 100644 index 0000000..cf243d5 --- /dev/null +++ b/examples/04.Continuous_measurement_test/communication.h @@ -0,0 +1,67 @@ +// Initialize the serial port +void beginFunction(unsigned long baud) +{ +#ifdef RX_pin + // The configuration for the ESP32 has both the RX and TX pins defined (for consistency), since they can be mapped to any pins. + K_line.begin(baud, SERIAL_8N1, RX_pin, TX_pin); +#else + // For other boards (if RX_pin is not defined), use the standard function. + K_line.begin(baud); +#endif +} + +// Stop communication on the serial port +void endFunction() +{ + K_line.end(); +} + +// Send a byte +void sendFunction(uint8_t data) +{ + K_line.write(data); +} + +// Receive a byte +bool receiveFunction(uint8_t *data) +{ + if (K_line.available()) + { + *data = K_line.read(); + return true; + } + return false; +} + +// Debugging can be enabled in configuration.h in order to print bus traffic on the Serial Monitor. +#if debug_traffic +void KWP1281debugFunction(bool direction, uint8_t message_sequence, uint8_t message_type, uint8_t *data, size_t length) +{ + Serial.print(direction ? "\tRECEIVE" : "\tSEND"); + + Serial.print(" S:"); + if (message_sequence < 0x10) Serial.print(0); + Serial.print(message_sequence, HEX); + + Serial.print(" T:"); + if (message_type < 0x10) Serial.print(0); + Serial.print(message_type, HEX); + + Serial.print(" L:"); + Serial.print(length); + + if (length) + { + Serial.print(" D:"); + + // Iterate through the bytes of the message. + for (size_t i = 0; i < length; i++) + { + if (data[i] < 0x10) Serial.print(0); + Serial.print(data[i], HEX); + Serial.print(' '); + } + } + Serial.println(); +} +#endif diff --git a/examples/04.Continuous_measurement_test/configuration.h b/examples/04.Continuous_measurement_test/configuration.h new file mode 100644 index 0000000..7f6054c --- /dev/null +++ b/examples/04.Continuous_measurement_test/configuration.h @@ -0,0 +1,76 @@ +// Select your target module address: +#define connect_to_module 0x01 + +// Select your target module speed: +#define module_baud_rate 10400 + +// Enable/disable printing library debug information on the Serial Monitor. +// You may change the debug level in "KLineKWP1281Lib.h". +#define debug_info false +// Enable/disable printing bus traffic on the Serial Monitor. +#define debug_traffic false + +// Select whether or not your serial interface can receive at the same time as sending (or whether or not your K-line interface has echo as it should). +// Most software serial libraries for AVR microcontrollers are half-duplex (can't receive while sending), so if using such library this should be false. +#define is_full_duplex true + +// Uncomment one of the following options: + +// Arduino UNO (no additional hardware serial ports, must use software serial; AltSoftSerial was chosen as it is full-duplex) +/* + #include + AltSoftSerial software_serial_port; //pins cannot be configured + #define K_line software_serial_port + #define TX_pin 9 +*/ + +// Arduino Mega (can use Serial1-Serial3) +/* + #define K_line Serial1 + #define TX_pin 18 +*/ +/* + #define K_line Serial2 + #define TX_pin 16 +*/ +/* + #define K_line Serial3 + #define TX_pin 14 +*/ + +// ESP32 (can use Serial1/Serial2) +/* + #define K_line Serial2 + #define TX_pin 17 + #define RX_pin 16 +*/ + +// ESP32-C6 (can use Serial1) +/* + #define K_line Serial1 + #define TX_pin 17 + #define RX_pin 16 +*/ + +// ESP8266 (no additional hardware serial ports, must use software serial) +// You can change the RX and TX pins to any unused pin, don't forget to also change the TX_pin define below. +/* + #include "SoftwareSerial.h" + SoftwareSerial software_serial_port(4, 5); //D2, D1 + #define K_line software_serial_port + #define TX_pin 5 //D1 +*/ + +// Raspberry Pi Pico (can use Serial1/Serial2) +/* + #define K_line Serial1 + #define TX_pin 0 +*/ +/* + #define K_line Serial2 + #define TX_pin 8 +*/ + +#ifndef K_line + #error Please select an option in configuration.h! +#endif diff --git a/examples/05.Single_measurement_test/05.Single_measurement_test.ino b/examples/05.Single_measurement_test/05.Single_measurement_test.ino new file mode 100644 index 0000000..4211611 --- /dev/null +++ b/examples/05.Single_measurement_test/05.Single_measurement_test.ino @@ -0,0 +1,330 @@ +/* + Title: + 05.Single_measurement_test.ino + + Description: + Demonstrates how to read a single measurement continuously. + + Notes: + *You can change which group and which measurement index to read by modifying the #defines below. + *This measurement will be read continuously while the sketch is running. + *It is not necessary to maintain the connection with "diag.update();" in the loop, because any request will have the same effect (update() must + be used in periods of inactivity). + *This sketch will not fit on an Arduino UNO by default! + *To fix this, open the library's /src/KLineKWP1281Lib.h file in a text editor and comment out the line "#define KWP1281_TEXT_TABLE_SUPPORTED" + at the top, as explained in the comments. + *After manually disabling the text table, the value for some parameters will be displayed as "EN_f25". + *If you have the text table enabled, you can choose from a few different languages a bit further below in "KLineKWP1281Lib.h". Please only choose + one option. +*/ + +// Change which measurement to read, from which group. +#define GROUP_TO_READ 1 // valid range: 0-255 +#define INDEX_TO_READ 0 // valid range: 0-3 + +/* + *This sketch displays information in the Serial Monitor, so another serial port is required for the K-line. + *If another port is not available on the selected board, the regular port "Serial" can be used, although no information can be printed to it (good for + projects with external displays or for data logging). + + ***Uncomment the appropriate options in the "configuration.h" file! + + Arduino UNO + *no additional serial port, software serial is used + *library of choice: AltSoftSerial (better than SoftwareSerial because it can also receive while sending) + *pins: + *K-line TX -> RX pin 8 + *K-line RX -> TX pin 9 + + Arduino MEGA + *has three additional serial ports + *pins: + *K-line TX -> Serial1 - RX pin 19 / Serial2 - RX pin 17 / Serial3 - RX pin 15 + *K-line RX -> Serial1 - TX pin 18 / Serial2 - TX pin 16 / Serial3 - TX pin 14 + + ESP32 / ESP32-C6 + *has one additional serial port + *pins (they can be remapped, this is what they are configured to in these examples): + *K-line TX -> RX pin 16 + *K-line RX -> TX pin 17 + + ESP8266 + *has no additional serial ports + *library of choice: SoftwareSerial (the ESP version of SoftwareSerial can also receive while sending) + *pins: any unused + *chosen for this demo: + *K-line TX -> RX pin 4 (D2) + *K-line RX -> TX pin 5 (D1) + + Raspberry Pi Pico + *has two additional serial ports + *pins: interchangeable + *default: + *K-line TX -> Serial1 - RX pin 1 / Serial2 - RX pin 9 + *K-line RX -> Serial1 - TX pin 0 / Serial2 - TX pin 8 + + ***If using the first hardware serial port (Serial) (with other sketches), the interface must be disconnected during code upload, and no "Serial.print"s + should be used. +*/ + +// Include the library. +#include + +// Include the two files containing configuration options and the functions used for communication. +#include "configuration.h" +#include "communication.h" + +// Debugging can be enabled in "configuration.h" in order to print connection-related info on the Serial Monitor. +#if debug_info + KLineKWP1281Lib diag(beginFunction, endFunction, sendFunction, receiveFunction, TX_pin, is_full_duplex, &Serial); +#else + KLineKWP1281Lib diag(beginFunction, endFunction, sendFunction, receiveFunction, TX_pin, is_full_duplex); +#endif + +// Please find more information on the topic of KWP1281 measurement groups in the "03.Full_measurement_test" example. +uint8_t measurement_buffer[80]; +uint8_t measurement_body_buffer[4]; + +void setup() +{ + // Initialize the Serial Monitor. + Serial.begin(115200); + delay(500); + Serial.println("Sketch started."); + + // If debugging bus traffic was enabled, attach the debugging function. +#if debug_traffic + diag.KWP1281debugFunction(KWP1281debugFunction); +#endif + + // Connect to the module. + diag.connect(connect_to_module, module_baud_rate, false); + + Serial.print("Requesting group "); + Serial.print(GROUP_TO_READ); + Serial.print(", measurement index "); + Serial.print(INDEX_TO_READ); + Serial.println(" continuously."); +} + +void loop() +{ + showSingleMeasurement(GROUP_TO_READ, INDEX_TO_READ); +} + +/* + + -The variables used for "header+body" groups are stored globally. + -Normally, the flag `received_group_header` should be reset to false when requesting a new group. + -This example only requests one group, so it isn't necessary; if the group was "header+body" once, it will always be like that. + + -You could also keep `received_group_header` local to the showSingleMeasurement() function, and do diag.update() before each request, + to ensure you receive the [Header]. + -Of course, this would negatively impact performance, since you need to receive more data from the module each time you read the group. + +*/ + +// This flag keeps track if a [Header] was received for the current group, meaning it's of the "header+body" type. +bool received_group_header = false; + +// This will contain the amount of measurements received in the [Header] of a "header+body" group. +uint8_t amount_of_measurements_in_header = 0; + +void showSingleMeasurement(uint8_t group, uint8_t measurement_index) +{ + // This will contain the amount of measurements in the current group, after calling the readGroup() function. + uint8_t amount_of_measurements = 0; + + /* + -For modules which report measuring groups in the "header+body" mode, it is important to do update() when requesting a new group, + so the module sends the [Header]. + -If it's the first request, it's not necessary; the module will always send the [Header] for the first time. + -Since this example only requests one group, it would decrease performance (measurement rate) to do update() every time, since it would cause the module + to send the [Header] over and over again, and it takes time to receive it, instead of only receiving it once, at the beginning. + */ + // diag.update(); + + /* + The readGroup() function can return: + KLineKWP1281Lib::FAIL - the requested group does not exist + KLineKWP1281Lib::ERROR - communication error + KLineKWP1281Lib::SUCCESS - received measurements + KLineKWP1281Lib::GROUP_BASIC_SETTING - received a [Basic settings] measurement; the buffer contains 10 raw values + KLineKWP1281Lib::GROUP_HEADER - received the [Header] for a "header+body" group; need to read again to get the [Body] + KLineKWP1281Lib::GROUP_BODY - received the [Body] for a "header+body" group + */ + + // Read the requested group and store the returned value. + KLineKWP1281Lib::executionStatus readGroup_status; + // If the group is not of "header+body" type, or if it is and this is the first request, we don't have a [Header] (yet), so `received_group_header=false`. + // The response to this request will be stored in the larger array. + // If it is in fact of "header+body" type, the [Header] will be stored in this array. + if (!received_group_header) + { + readGroup_status = diag.readGroup(amount_of_measurements, group, measurement_buffer, sizeof(measurement_buffer)); + } + // If the group is of "header+body" type, and this is not the first request, it means we have a header, so `received_group_header=true`. + // The response to this request will be stored in the smaller array, because it should be the [Body]. + else + { + readGroup_status = diag.readGroup(amount_of_measurements, group, measurement_body_buffer, sizeof(measurement_body_buffer)); + } + + // Check the returned value. + switch (readGroup_status) + { + case KLineKWP1281Lib::ERROR: + { + Serial.println("Error reading group!"); + } + // There is no reason to continue, exit the function. + return; + + case KLineKWP1281Lib::FAIL: + { + Serial.print("Group "); + Serial.print(group); + Serial.println(" does not exist!"); + } + // There is no reason to continue, exit the function. + return; + + case KLineKWP1281Lib::GROUP_BASIC_SETTINGS: + { + // If we have a [Header], it means this group sends responses of "header+body" type. + // So, at this point, it doesn't make sense to receive something other than a [Body]. + if (received_group_header) + { + Serial.println("Error reading body! (got basic settings)"); + return; + } + + // We have 10 raw values in the `measurement_buffer` array. + Serial.print("Basic settings in group "); + Serial.print(group); + Serial.print(": "); + for (uint8_t i = 0; i < 10; i++) + { + Serial.print(measurement_buffer[i]); + Serial.print(" "); + } + Serial.println(); + } + // We have nothing else to display, exit the function. + return; + + case KLineKWP1281Lib::GROUP_HEADER: + { + // If we have a [Header], it means this group sends responses of "header+body" type. + // So, at this point, it doesn't make sense to receive something other than a [Body]. + if (received_group_header) + { + Serial.println("Error reading body! (got header)"); + return; + } + + // Set the flag to indicate that a header was received, making this a "header+body" group response. + received_group_header = true; + + // Store the number of measurements received in the header. + amount_of_measurements_in_header = amount_of_measurements; + } + // We have nothing to display yet, the next readGroup() will get the actual data; exit the function. + return; + + case KLineKWP1281Lib::GROUP_BODY: + { + // If we don't have a [Header], it doesn't make sense to receive a [Body]. + if (!received_group_header) + { + Serial.println("Error reading header! (got body)"); + return; + } + } + // If we have the [Header], now we also have the [Body]; execute the code after the switch(). + break; + + // Execute the code after the switch(). + case KLineKWP1281Lib::SUCCESS: + break; + } + + /* + The getMeasurementType() function can return: + *KLineKWP1281Lib::UNKNOWN - index out of range (measurement doesn't exist in group) + *KLineKWP1281Lib::VALUE - regular measurement, with a value and units + *KLineKWP1281Lib::TEXT - text measurement + */ + + // Get the selected measurement's type. + KLineKWP1281Lib::measurementType measurement_type; + if (!received_group_header) + { + measurement_type = KLineKWP1281Lib::getMeasurementType(measurement_index, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer)); + } + // For "header+body" measurements, you need to use this other function that specifically parses headers instead of regular responses. + else + { + measurement_type = KLineKWP1281Lib::getMeasurementTypeFromHeader(measurement_index, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer)); + } + + // Check the returned value. + switch (measurement_type) + { + // "Value and units" type + case KLineKWP1281Lib::VALUE: + { + // The measurement's units will be copied into this array, so they can be displayed. + char units_string[16]; + + // Regular mode: + if (!received_group_header) + { + // Determine how many decimal places are best suited to this measurement. + uint8_t decimals = KLineKWP1281Lib::getMeasurementDecimals(measurement_index, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer)); + + // Display the calculated value, with the recommended amount of decimals. + Serial.print(KLineKWP1281Lib::getMeasurementValue(measurement_index, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer)), decimals); + Serial.print(' '); + Serial.println(KLineKWP1281Lib::getMeasurementUnits(measurement_index, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer), units_string, sizeof(units_string))); + } + // "Header+body" mode: + else + { + // Determine how many decimal places are best suited to this measurement. + uint8_t decimals = KLineKWP1281Lib::getMeasurementDecimalsFromHeader(measurement_index, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer)); + + // Display the calculated value, with the recommended amount of decimals. + Serial.print(KLineKWP1281Lib::getMeasurementValueFromHeaderBody(measurement_index, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer)), decimals); + Serial.print(' '); + Serial.println(KLineKWP1281Lib::getMeasurementUnitsFromHeaderBody(measurement_index, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer), units_string, sizeof(units_string))); + } + } + break; + + // "Text" type + case KLineKWP1281Lib::TEXT: + { + // The measurement's text will be copied into this array, so it can be displayed. + char text_string[16]; + + // Display the text. + if (!received_group_header) + { + // The function getMeasurementText() returns the same string that it's given. It's the same as text_string. + Serial.println(KLineKWP1281Lib::getMeasurementText(measurement_index, amount_of_measurements, measurement_buffer, sizeof(measurement_buffer), text_string, sizeof(text_string))); + } + else + { + // The function getMeasurementTextFromHeaderBody() returns the same string that it's given. It's the same as text_string. + Serial.println(KLineKWP1281Lib::getMeasurementTextFromHeaderBody(measurement_index, amount_of_measurements_in_header, measurement_buffer, sizeof(measurement_buffer), amount_of_measurements, measurement_body_buffer, sizeof(measurement_body_buffer), text_string, sizeof(text_string))); + } + } + break; + + // Invalid measurement index + case KLineKWP1281Lib::UNKNOWN: + Serial.println("N/A"); + break; + } +} diff --git a/examples/05.Single_measurement_test/communication.h b/examples/05.Single_measurement_test/communication.h new file mode 100644 index 0000000..cf243d5 --- /dev/null +++ b/examples/05.Single_measurement_test/communication.h @@ -0,0 +1,67 @@ +// Initialize the serial port +void beginFunction(unsigned long baud) +{ +#ifdef RX_pin + // The configuration for the ESP32 has both the RX and TX pins defined (for consistency), since they can be mapped to any pins. + K_line.begin(baud, SERIAL_8N1, RX_pin, TX_pin); +#else + // For other boards (if RX_pin is not defined), use the standard function. + K_line.begin(baud); +#endif +} + +// Stop communication on the serial port +void endFunction() +{ + K_line.end(); +} + +// Send a byte +void sendFunction(uint8_t data) +{ + K_line.write(data); +} + +// Receive a byte +bool receiveFunction(uint8_t *data) +{ + if (K_line.available()) + { + *data = K_line.read(); + return true; + } + return false; +} + +// Debugging can be enabled in configuration.h in order to print bus traffic on the Serial Monitor. +#if debug_traffic +void KWP1281debugFunction(bool direction, uint8_t message_sequence, uint8_t message_type, uint8_t *data, size_t length) +{ + Serial.print(direction ? "\tRECEIVE" : "\tSEND"); + + Serial.print(" S:"); + if (message_sequence < 0x10) Serial.print(0); + Serial.print(message_sequence, HEX); + + Serial.print(" T:"); + if (message_type < 0x10) Serial.print(0); + Serial.print(message_type, HEX); + + Serial.print(" L:"); + Serial.print(length); + + if (length) + { + Serial.print(" D:"); + + // Iterate through the bytes of the message. + for (size_t i = 0; i < length; i++) + { + if (data[i] < 0x10) Serial.print(0); + Serial.print(data[i], HEX); + Serial.print(' '); + } + } + Serial.println(); +} +#endif diff --git a/examples/05.Single_measurement_test/configuration.h b/examples/05.Single_measurement_test/configuration.h new file mode 100644 index 0000000..7f6054c --- /dev/null +++ b/examples/05.Single_measurement_test/configuration.h @@ -0,0 +1,76 @@ +// Select your target module address: +#define connect_to_module 0x01 + +// Select your target module speed: +#define module_baud_rate 10400 + +// Enable/disable printing library debug information on the Serial Monitor. +// You may change the debug level in "KLineKWP1281Lib.h". +#define debug_info false +// Enable/disable printing bus traffic on the Serial Monitor. +#define debug_traffic false + +// Select whether or not your serial interface can receive at the same time as sending (or whether or not your K-line interface has echo as it should). +// Most software serial libraries for AVR microcontrollers are half-duplex (can't receive while sending), so if using such library this should be false. +#define is_full_duplex true + +// Uncomment one of the following options: + +// Arduino UNO (no additional hardware serial ports, must use software serial; AltSoftSerial was chosen as it is full-duplex) +/* + #include + AltSoftSerial software_serial_port; //pins cannot be configured + #define K_line software_serial_port + #define TX_pin 9 +*/ + +// Arduino Mega (can use Serial1-Serial3) +/* + #define K_line Serial1 + #define TX_pin 18 +*/ +/* + #define K_line Serial2 + #define TX_pin 16 +*/ +/* + #define K_line Serial3 + #define TX_pin 14 +*/ + +// ESP32 (can use Serial1/Serial2) +/* + #define K_line Serial2 + #define TX_pin 17 + #define RX_pin 16 +*/ + +// ESP32-C6 (can use Serial1) +/* + #define K_line Serial1 + #define TX_pin 17 + #define RX_pin 16 +*/ + +// ESP8266 (no additional hardware serial ports, must use software serial) +// You can change the RX and TX pins to any unused pin, don't forget to also change the TX_pin define below. +/* + #include "SoftwareSerial.h" + SoftwareSerial software_serial_port(4, 5); //D2, D1 + #define K_line software_serial_port + #define TX_pin 5 //D1 +*/ + +// Raspberry Pi Pico (can use Serial1/Serial2) +/* + #define K_line Serial1 + #define TX_pin 0 +*/ +/* + #define K_line Serial2 + #define TX_pin 8 +*/ + +#ifndef K_line + #error Please select an option in configuration.h! +#endif