Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/clang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:

- name: Summary
env:
clang-threshold: 2
clang-threshold: 1
run: |
echo "Status: ${{ steps.tidy.outputs.checks-failed }} checks failed, threshold for success is ${{ env.clang-threshold }}."
echo "Clang report:"
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,19 @@ Home Assistant with MQTT is recommended for the best experience, but the desk al

#### Diagnostics

| Name | Description | Requirement |
| -------------- | ---------------------------------------- | ----------- |
| Button down | Simulate a physical down-button press | `PIN_TPDN` |
| Button up | Simulate a physical up-button press | `PIN_TPUP` |
| Calibrate | Recalibrate the leg encoder sensors | |
| Firmware AVR | AVR firmware version | |
| Firmware ESP32 | ESP32 firmware version | |
| Offset | Current leg offset | |
| Position | Average encoder position | |
| Power supply | Input voltage of the desk’s power supply | `PIN_ADC` |
| Serial RX | Last UART message received | |
| Serial TX | Last UART message sent | |
| Temperature | Internal temperature of the ESP32 | |
| Wi-Fi signal | ESP32 Wi-Fi RSSI | |
| Name | Description | Requirement |
| ------------ | ---------------------------------------- | ----------- |
| Button down | Simulate a physical down-button press | `PIN_TPDN` |
| Button up | Simulate a physical up-button press | `PIN_TPUP` |
| Calibrate | Recalibrate the leg encoder sensors | |
| Errors | Currently detected communication errors | |
| Firmware | ESP32 firmware version | |
| Offset | Current leg offset | |
| Position | Average encoder position | |
| Power supply | Input voltage of the desk’s power supply | `PIN_ADC` |
| Serial RX | Last UART message received | |
| Serial TX | Last UART message sent | |
| Temperature | Internal temperature of the ESP32 | |
| Wi-Fi signal | ESP32 Wi-Fi RSSI | |

To avoid cluttering the Home Assistant interface, only a handful of entities are enabled by default.
8 changes: 3 additions & 5 deletions include/avr/ConsoleHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,14 @@ class ConsoleHandler
{
BUTTON_DOWN = 1U,
BUTTON_UP,
ENCODER8,
ENCODER9,
INITIALIZE,
CONSOLE,
INITIALIZATION,
LIN,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
NODE8,
NODE9,
POSITION,
PRESET_HIGH,
PRESET_LOW,
STATE8,
STATE9,
};

/**
Expand Down
4 changes: 3 additions & 1 deletion include/avr/ControllerService.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class ControllerService
static ControllerService &getInstance();

private:
bool error8{false};
bool error9{false};
bool pending{false};

unsigned char state8{0U};
Expand All @@ -66,7 +68,7 @@ class ControllerService
/**
* Handles leg movement commands and encoder communication.
*/
LegHandler lin{};
LegHandler leg{};

State state{State::IDLE};

Expand Down
140 changes: 83 additions & 57 deletions include/avr/LegHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,34 @@ class LegHandler
static constexpr unsigned char linDiagnosticResponseId{0x3DU};
static constexpr unsigned char linSyncByte{0x55U};

void serialBreak();
void requestDiscardResponse();

void sendResponse(unsigned char pid);

[[nodiscard]] unsigned char calcParity(unsigned char identifier);
void serialBreak();

[[nodiscard]] int readWithTimeout(unsigned int &remainingTime);
[[nodiscard]] int read(unsigned int &remainingTime);

public:
/**
* Calculates the complemented checksum for a sequence of bytes.
* @param data Bytes to include in the checksum.
* @param sum Initial checksum sum.
* @return The complemented checksum.
* Builds a LIN protected identifier from a frame identifier.
*
* @param identifier Frame identifier; only the lower six bits are used.
* @return The identifier with its parity bits in the upper two bits.
*/
template <unsigned int N> [[nodiscard]] unsigned char calcChecksum(const unsigned char (&data)[N], unsigned int sum)
{
for (const unsigned char byte : data)
{
sum += byte;
}
while ((sum >> 8U) != 0U)
{
sum = (sum & 0xFFU) + (sum >> 8U);
}
return static_cast<unsigned char>(~sum);
}

template <unsigned int N> bool sendRequest(const unsigned char (&packet)[N])
[[nodiscard]] static constexpr unsigned char getPid(unsigned char identifier)
{
send(linDiagnosticRequestId, packet);
unsigned char response[N]{};
return request(linDiagnosticResponseId, response);
identifier &= 0x3FU;
const unsigned int parity0{
static_cast<unsigned int>(identifier & 1U) ^ (static_cast<unsigned int>(identifier >> 1U) & 1U) ^
(static_cast<unsigned int>(identifier >> 2U) & 1U) ^ (static_cast<unsigned int>(identifier >> 4U) & 1U)};
const unsigned int parity1{
~((static_cast<unsigned int>(identifier >> 1U) & 1U) ^ (static_cast<unsigned int>(identifier >> 3U) & 1U) ^
(static_cast<unsigned int>(identifier >> 4U) & 1U) ^ (static_cast<unsigned int>(identifier >> 5U) & 1U)) &
1U};
return static_cast<unsigned char>(identifier | ((parity0 | (parity1 << 1U)) << 6U));
}

public:
enum class Command : unsigned char
{
FINISH = 0x84U,
Expand All @@ -69,9 +64,9 @@ class LegHandler
IDLE = 0xFCU,
};

[[nodiscard]] bool begin();
[[nodiscard]] unsigned char begin();

void send(unsigned char identifier);
[[nodiscard]] unsigned char getLeg(unsigned char pid, unsigned char (&node)[3U]);

/**
* Sends a desk command with a target encoder position.
Expand All @@ -82,69 +77,100 @@ class LegHandler
void sendCommand(Command command, unsigned int position);

/**
* Sends a LIN frame containing the specified payload.
*
* @param identifier LIN frame identifier.
* @param data Payload bytes to transmit.
* Calculates the complemented checksum for a sequence of bytes.
* @param data Bytes to include in the checksum.
* @param sum Initial checksum sum.
* @return The complemented checksum.
*/
template <unsigned int N> void send(unsigned char identifier, const unsigned char (&data)[N])
template <unsigned int N>
[[nodiscard]] unsigned char getChecksum(const unsigned char (&data)[N], unsigned int sum = 0U)
{
static_assert(N <= 8U);
const unsigned char addressByte{static_cast<unsigned char>((identifier & 0x3FU) | calcParity(identifier))};
serialBreak();
Serial.write(linSyncByte);
Serial.write(addressByte);
Serial.write(data, N);
Serial.write(calcChecksum(data, identifier == linDiagnosticRequestId ? 0U : addressByte));
Serial.flush();
for (const unsigned char byte : data)
{
sum += byte;
}
while ((sum >> 8U) != 0U)
{
sum = (sum & 0xFFU) + (sum >> 8U);
}
return static_cast<unsigned char>(~sum);
}

/**
* Receives a LIN response for the specified identifier.
* Requests a LIN response and reads its payload and checksum.
*
* @param identifier LIN frame identifier to request.
* @param pid Protected identifier to transmit in the response header.
* @param data Buffer to populate with the received payload.
* @return `true` if a complete response with a valid checksum is received, `false` on timeout or checksum
* failure.
* @return The received checksum byte, or `-1` if the response is incomplete.
*/
template <unsigned int N> [[nodiscard]] bool request(unsigned char identifier, unsigned char (&data)[N])
template <unsigned int N> int receiveResponse(unsigned char pid, unsigned char (&data)[N])
{
static_assert(N <= 8U);
const unsigned char idByte{static_cast<unsigned char>((identifier & 0x3FU) | calcParity(identifier))};
serialBreak();
Serial.write(linSyncByte);
Serial.write(idByte);
Serial.write(pid);
Serial.flush();
int receivedByte{};
unsigned int remainingTime{static_cast<unsigned int>(LinFrame::frameBits * 1'000'000UL / baudRate)};
do // NOLINT(cppcoreguidelines-avoid-do-while)
{
receivedByte = readWithTimeout(remainingTime);
receivedByte = read(remainingTime);
} while (receivedByte != -1 && receivedByte != static_cast<int>(linSyncByte));
if (receivedByte == -1)
{
return false;
return -1;
}
do // NOLINT(cppcoreguidelines-avoid-do-while)
{
receivedByte = readWithTimeout(remainingTime);
} while (receivedByte != -1 && receivedByte != idByte);
receivedByte = read(remainingTime);
} while (receivedByte != -1 && receivedByte != pid);
if (receivedByte == -1)
{
return false;
return -1;
}
for (unsigned char &dataByte : data)
{
receivedByte = readWithTimeout(remainingTime);
receivedByte = read(remainingTime);
if (receivedByte == -1)
{
return false;
return -1;
}
dataByte = static_cast<unsigned char>(receivedByte);
}
receivedByte = readWithTimeout(remainingTime);
return receivedByte != -1 &&
calcChecksum(data, identifier == linDiagnosticResponseId ? 0U : idByte) == receivedByte;
return read(remainingTime);
}

/**
* Sends a LIN diagnostic request with a classic checksum.
*
* @param data Diagnostic payload to transmit.
*/
template <unsigned int N> void sendDiagnosticRequest(const unsigned char (&data)[N])
{
static_assert(N <= 8U);
serialBreak();
Serial.write(linSyncByte);
Serial.write(getPid(linDiagnosticRequestId));
Serial.write(data, N);
Serial.write(getChecksum(data));
Serial.flush();
}

/**
* Sends a LIN response with an enhanced checksum.
*
* @param pid Protected identifier to transmit.
* @param data Response payload to transmit.
*/
template <unsigned int N> void sendResponse(unsigned char pid, const unsigned char (&data)[N])
{
static_assert(N <= 8U);
serialBreak();
Serial.write(linSyncByte);
Serial.write(pid);
Serial.write(data, N);
Serial.write(getChecksum(data, pid));
Serial.flush();
}
};

Expand Down
24 changes: 17 additions & 7 deletions include/esp/ConsoleHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#ifdef ARDUINO_ARCH_ESP32

#include <ArduinoJson.h> // NOLINT(misc-include-cleaner)
#include <HardwareSerial.h>
#include <span>
#include <string>
Expand All @@ -22,23 +23,23 @@ class ConsoleHandler
{
BUTTON_DOWN = 1U,
BUTTON_UP,
ENCODER8,
ENCODER9,
INITIALIZE,
CONSOLE,
INITIALIZATION,
LIN,
NODE8,
NODE9,
POSITION,
PRESET_HIGH,
PRESET_LOW,
STATE8,
STATE9,
};

/**
* Initializes console handling.
*/
void begin();

void getErrors(JsonArray &errors);

/**
* Processes available console input.
*/
Expand All @@ -49,11 +50,16 @@ class ConsoleHandler
*/
void forward();

void reset();

void send(Command command);

void send(Command command, uint16_t value);

private:
uint8_t errorLin{0U};
uint8_t errorTx{0U};

size_t bytesRx{0U};
size_t bytesTx{0U};
size_t lengthRx{0U};
Expand All @@ -72,12 +78,16 @@ class ConsoleHandler
*/
State stateRx{};

static inline hardwareSerial_error_t lastError{hardwareSerial_error_t::UART_NO_ERROR};
static inline hardwareSerial_error_t errorRx{hardwareSerial_error_t::UART_NO_ERROR};

/**
* Parses a received console payload.
*/
void parse() const;
void parse();

void setErrorLin(uint8_t flags);

void setErrorTx(uint8_t flags);

void write(std::span<const uint8_t> payload);

Expand Down
Loading