// ----------------------------------------------------------------------------------------
//
//  demos/can_mcp2515/main.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  Demonstrate support for the Microchip MCP2515
//
//  Copyright (C) 2007 Bob Cook
//
//      This program is free software; you can redistribute it and/or modify it
//      under the terms of the GNU General Public License as published by the
//      Free Software Foundation; either version 2 of the License, or (at your
//      option) any later version.
//    
//      This program is distributed in the hope that it will be useful, but 
//      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
//      or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
//      for more details.
//    
//      You should have received a copy of the GNU General Public License along
//      with this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#include <ctype.h>
#include <stdio.h>

#include "packages/avr/can/can_helpers.h"
#include "packages/avr/can/mcp2515.h"
#include "packages/avr/device/spinwait.h"
#include "packages/avr/redir/redir.h"

#include "packages/common/util/printutils.h"


// ----------------------------------------------------------------------------------------

bool demo_hw_init()
{
    //--    Turn off all interrupts.

    cli();
    TIMSK = 0;

    //--    The setup identifier is on PORTC.4 where the "master" end (the one with the
    //      serial connection) sets this input high; the other end sets it as "low".

    DDRC &= ~( 1 << PC4 );

    //--    Green LED is on PC5, the red LED is on PB0.

    DDRB |= ( 1 << PB0 );
    DDRC |= ( 1 << PC5 );

    PORTC |= ( 1 << PC5 );  // green off

    //--    Use UART0 for stdio input and output functions.

    redir_initialize();

    //--    Initialize the CAN hardware support package. We are using Timer1 as the clock
    //      source for the MCP2515. We are using INT1 (PORTD.3) as the INTERRUPT line.

    DDRB |= ( 1 << PB1 );

    TCCR1A = ( 1 << COM1A0 );
    TCCR1B = ( 1 << CS10   ) | ( 1 << WGM12 );

    TCNT1H = 0x00;      // clear the 16-bit timer value, high byte first
    TCNT1L = 0x00;

    OCR1AH = 0x00;      // clear the 16-bit timer match value, high byte first
    OCR1AL = 0x00;

    if ( ! mcp2515_init() )
    {
        printf_P( PSTR("failed to mcp2515_init()\n") );
        return false;
    }

    MCUCR &= ~( ( 1 << ISC11 ) | ( 1 << ISC10 ) );
    GICR  |= ( 1 << INT1 );

    //--    Enable interrupts.

    sei();

    return true;
}


// ----------------------------------------------------------------------------------------

inline void led_red_on()
{
    PORTB &= ~( 1 << PB0 );
}

inline void led_red_off()
{
    PORTB |= ( 1 << PB0 );
}

inline void led_green_on()
{
    PORTC &= ~( 1 << PC5 );
}

inline void led_green_off()
{
    PORTC |= ( 1 << PC5 );
}


// ----------------------------------------------------------------------------------------

ISR( INT1_vect )
{
    mcp2515_process_interrupt();
}


// ----------------------------------------------------------------------------------------

void read_string( const prog_char* prompt, char* str, uint8_t* strlen )
{
    *str = '\0';

    printf_P( prompt );

    uint8_t     curr = 0;

    for ( ;; )
    {
        char c = getchar();

        if ( c == EOF )
        {
            continue;
        }

        if ( ( c == 0x0a ) || ( c == 0x0d ) )
        {
            putchar( '\n' );
            *strlen = curr;
            return;
        }

        if ( isalnum( c ) )
        {
            putchar( c );
            *str = c;
            str++;
            curr++;
        }

        if ( curr == *strlen )
        {
            putchar( '\n' );
            *strlen = curr;
            return;
        }
    }
}


// ----------------------------------------------------------------------------------------

void demo_side_1()
{
    for ( ;; )
    {
        led_green_off();
        led_red_off();

        char    str[ mcp2515_max_message_data ];
        uint8_t len = mcp2515_max_message_data;

        read_string( PSTR("> "), str, &len );

        if ( len == 0 )
        {
            continue;
        }

        uint32_t msg_id;
        msg_id = can_build_message_id( 0x04, 0x06, 0x5CD );

        if ( ! mcp2515_send( msg_id, reinterpret_cast< uint8_t* >( str ), len ) )
        {
            led_red_on();
            printf_P( PSTR("1: send failed...\n") );
        }
       
        bool gotMessage = false;

        uint32_t message_id;
        bool     is_request;

        for ( uint16_t i = 0; i < 1000; i++ )
        {
            if ( mcp2515_read( &message_id,
                               &is_request,
                               reinterpret_cast< uint8_t* >( str ),
                               &len ) )
            {
                led_green_on();
                gotMessage = true;
                break;
            }

            spinwait_delay_ms( 5 );
        }
            
        if ( ! gotMessage )
        {
            led_red_on();
            printf_P( PSTR("1: recv failed...\n") );
            continue;
        }

        printf_P( PSTR("< ") );

        for ( uint8_t i = 0; i < len; i++ )
        {
            putchar( str[ i ] );
        }
        
        putchar( '\n' );

        spinwait_delay_ms( 500 );
    }
    // never reached
}


// ----------------------------------------------------------------------------------------

void demo_side_2()
{
    for ( ;; )
    {
        led_green_off();
        led_red_off();

        uint32_t message_id;
        bool     is_request;
        char     str[ mcp2515_max_message_data ];
        uint8_t  len;

        bool gotMessage = false;

        for ( uint16_t i = 0; i < 1000; i++ )
        {
            if ( mcp2515_read( &message_id,
                               &is_request,
                               reinterpret_cast< uint8_t* >( str ),
                               &len ) )
            {
                led_green_on();
                gotMessage = true;
                break;
            }

            spinwait_delay_ms( 5 );
        }

        if ( ! gotMessage )
        {
            led_red_on();
            printf_P( PSTR("2: recv failed...\n") );
            continue;
        }

        uint8_t  source;
        uint8_t  destination;
        uint16_t data_type;

        can_parse_message_id( message_id, &source, &destination, &data_type );

        data_type++;

        for ( uint8_t i = 0; i < len; i++ )
        {
            if ( islower( str[ i ] ) )
            {
                str[ i ] = toupper( str[ i ] );
            }
            else if ( isupper( str[ i ] ) )
            {
                str[ i ] = tolower( str[ i ] );
            }
        }

        message_id = can_build_message_id( destination, source, data_type );

        if ( ! mcp2515_send( message_id, reinterpret_cast< uint8_t* >( str ), len ) )
        {
            led_red_on();
            printf_P( PSTR("2: send failed...\n") );
        }

        spinwait_delay_ms( 500 );
    }
    // never reached
}


// ----------------------------------------------------------------------------------------

int main()
{
    if ( ! demo_hw_init() )
    {
        return 0;
    }

    bool    is_side_1 = ( PINC & ( 1 << PC4 ) ) != 0;

    printf_P( PSTR("\nAVR CAN demo; this is side ") );

    if ( is_side_1 )
    {
        printf_P( PSTR("1\n\n") );
        demo_side_1();
    }
    else
    {
        printf_P( PSTR("2\n\n") );
        demo_side_2();
    }

    return 0;   // keep the compiler happy
}


// ----------------------------------------------------------------------------------------
//  end of main.cpp
// ----------------------------------------------------------------------------------------

