/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved
 * You may use, distribute and modify this code under the
 * terms of the  AGPL-3.0 license.
 *
 * https://www.dennisgunia.de
 * https://github.com/dennis9819/splitflap_v1
 */

#include "rs485.h"

void rs485_init()
{
    // init I/O
    DDRD &= ~(1 << PD0);             // BUS_DIR & TX is OUTPUT
    DDRD |= (1 << PD2) | (1 << PD1); // BUS_DIR & TX is OUTPUT
    PORTD &= 0x07;                   // clear PD0-PD4
    // init UART
    UBRRH = (BAUDRATE >> 8);
    UBRRL = BAUDRATE;                                    // set baud rate
    UCSRB |= (1 << TXEN) | (1 << RXEN);                  // enable receiver and transmitter
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // 8bit data format
}

void dbg(char data)
{
    while (!(UCSRA & (1 << UDRE)))
        ;
    UDR = data;
}


void rs485_send_c(char data)
{
    PORTD |= (1 << PD2); // set transciever to transmitt
    while (!(UCSRA & (1 << UDRE)))
        ;               // wait until buffer is empty
    UCSRA = (1 << TXC); // clear transmit Complete bit
    UDR = data;
    while (!(UCSRA & (1 << TXC)))
    {
    }; // wait until transmitt complete
    PORTD &= ~(1 << PD2); // set transciever to transmitt
}

void rs485_send_str(char *data)
{
    for (unsigned int i = 0; i < sizeof(data); i++)
    {
        rs485_send_c(data[i]);
    }
}

char rs485_recv_c()
{
    while (!(UCSRA & (1 << RXC)))
        ; // wait while data is being received
    return UDR;
}

// SFBUS Functions
uint8_t sfbus_recv_frame(uint16_t address, char *payload)
{
    while (rs485_recv_c() != SFBUS_SOF_BYTE)
    {
    } // Wwait for start byte

    uint8_t frm_version = rs485_recv_c();
    if (frm_version != 0)
        return 0;
    uint8_t frm_length = rs485_recv_c();
    uint8_t frm_addrL = rs485_recv_c();
    uint8_t frm_addrH = rs485_recv_c();

    uint16_t frm_addr = frm_addrL | (frm_addrH << SHIFT_1B);
    if (frm_addr != address)
        return 0;
    char *_payload = payload;
    for (uint8_t i = 0; i < (frm_length - 3); i++)
    {
        *_payload = rs485_recv_c();
        _payload++;
    }

    if (rs485_recv_c() != SFBUS_EOF_BYTE)
        return -1;
    return frm_length;
}

void sfbus_send_frame(uint16_t address, char *payload, uint8_t length)
{
    uint8_t framelen = length;

    rs485_send_c(SFBUS_SOF_BYTE); // send startbyte 3 times
    rs485_send_c(0);              // send protocol version
    rs485_send_c((char)(framelen + 3));   // send lentgh of remaining frame

    rs485_send_c((char)(address & 0xFF)); // target address
    rs485_send_c((char)((address >> SHIFT_1B) & 0xFF));

    while (framelen > 0)
    { // send payload
        rs485_send_c(*payload);
        payload++;
        framelen--;
    }

    rs485_send_c(SFBUS_EOF_BYTE); // send end of frame byte
}
