// ----------------------------------------------------------------------------------------
//
//  demos/mcp2515_sniff/main.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  Spy on CAN communications using the Microchip MCP2515 at 125 kHz.
//
//  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 sniffer_hw_init()
{
    //--    Turn off all interrupts.

    cli();
    TIMSK = 0;

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

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

    PORTB |= ( 1 << PB0 );  // red off
    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;
    TCNT1L = 0x00;

    OCR1AH = 0x00;
    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 )
{
    led_red_on();

    mcp2515_process_interrupt();
    
    led_red_off();
}


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

void sniffer()
{
    for ( ;; )
    {
        led_green_off();

        uint8_t data[ mcp2515_max_message_data ];
        uint8_t len = mcp2515_max_message_data;

        uint32_t message_id;
        bool     is_request;

        if ( ! mcp2515_read( &message_id, &is_request, &data[ 0 ], &len ) )
        {
            continue;
        }

        led_green_on();
            
        uint8_t  src_node;
        uint8_t  dst_node;
        uint16_t data_type;

        can_parse_message_id( message_id, &src_node, &dst_node, &data_type);

        if ( is_request )
        {
            putchar( 'R' );
        }
        else
        {
            putchar( 'M' );
        }

        putchar( ' ' );
        putchar( '<' );
        print_hex( src_node );
        
        putchar( ' ' );
        putchar( '>' );
        print_hex( dst_node );

        putchar( ' ' );
        print_hex( data_type );

        if ( is_request )
        {
            continue;
        }

        putchar( ':' );
        putchar( ' ' );

        for ( uint8_t i = 0; i < len; i++ )
        {
            print_hex( data[ i ] );
            putchar( ' ' );
        }

        for ( uint8_t i = len; i < 8; i++ )
        {
            putchar( '.' );
            putchar( '.' );
            putchar( ' ' );
        }

        putchar( '|' );
        putchar( ' ' );

        for ( uint8_t i = 0; i < len; i++ )
        {
            if ( isgraph( static_cast< char >( data[ i ] ) ) )
            {
                putchar( static_cast< char >( data[ i ] ) );
            }
            else
            {
                putchar( '.' );
            }
        }

        putchar( '\n' );
    }
    // never reached
}


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

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

    sniffer();

    return 0;   // keep the compiler happy
}


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

