/*
Controladora de mouse implementada utilizando um AtTiny85 e um registrador de 
deslocamento (shift register) PISO (Parallel In Serial Out)

Para utilização de um protocolo de comunicação serial com fios diferente
do SPI, orientado a interrupts (interrupt-driven), utilizando apenas 2 linhas de 
comunicação, com o SCK e o MOSI compartilhando a mesma linha, através da utilização de
uma técnica de "clock aperiódico", e o MISO e a interrupt request compartilhando
a outra linha.

Esta controladora é o Slave, o Master seria o Arduino UNO no projeto 
principal em https://wokwi.com/projects/386223500534727681

Autor: Dante Meira
*/

// Como o Wokwi não tem diodo, a solução é utilizar duas portas NOT encadeadas 
// fazendo o papel de diodo, para evitar que a eletricidade flua em sentido indesejado
// Poderiam ser utilizadas portas OR, mas a solução mais barata é 
// usar diodos (o 1n4007 custa R$ 0,18 a unidade)


char buffer[5] = {0, 0, 0, 0, 0};
// o buffer de comunicação serial armazena as seguintes informaçôes:
// em 0 - o mouse enviou um sinal para mover o ponteiro uma unidade para cima
// em 1 - o mouse enviou um sinal para mover o ponteiro uma unidade para baixo
// em 2 - o mouse enviou um sinal para mover o ponteiro uma unidade para a direita
// em 3 - o mouse enviou um sinal para mover o ponteiro uma unidade para a esquerda
// em 4 - o mouse enviou um sinal de click

char position = 0;

void setup() {

  pinMode(PB0, INPUT);
  pinMode(PB1, OUTPUT);

  pinMode(PB3, OUTPUT); // clock do shift register
  pinMode(PB5, OUTPUT); // latch do shift register

 GIMSK = B00100000 ; // habilita interrupts de Pin Change 
 PCMSK = B00000001 ; // habilita PCINT0
 sei(); // habilita interrupts


      while(true){ // loop infinito fazendo nada, só aguardando interrupts
          
            asm volatile( "nop \n\t" );
            
      }
}


ISR(PCINT0_vect){   // ISR pin change 0

// checando o shift register para descobrir quem causou a interrupt

  if( (digitalRead(PB0)) == HIGH) {  // borda subindo

     digitalWrite(PB5, LOW);
     digitalWrite(PB5, HIGH);  // fazendo latch paralelo no shift register

     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo D7 no PB4 através de Q7
        // interrupt causada pelo Master enviando pulso na linha OUVE (azul)

            if(buffer[position] == 0){ 
                digitalWrite(PB1, LOW);                
            }else{
                digitalWrite(PB1, HIGH);                 
            } 
            
            buffer[position] = 0;

            if(position < 4) { position++; }

     } else{ 
    // só continua a checar os outros inputs se a interrupt não foi causada pelo Master

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D7 para D6 na saída Q7

     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo D6 no PB4 através de Q7
             buffer[1] = 1; // para baixo
             position = 0;
             // usando a linha "Fala" para avisar o Master que ocorreu um evento de mouse
             digitalWrite(PB1, HIGH); 
             digitalWrite(PB1, LOW); 
     }

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D6 para D5 na saída Q7

     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo D5 no PB4 através de Q7
             buffer[2] = 1; // para a direita
             position = 0;
             // usando a linha "Fala" para avisar o Master que ocorreu um evento de mouse
             digitalWrite(PB1, HIGH); 
             digitalWrite(PB1, LOW); 
     }
    }

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D5 para D4 na saída Q7

     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo D4 no PB4 através de Q7
             buffer[3] = 1; // para a esquerda
             position = 0;
             // usando a linha "Fala" para avisar o Master que ocorreu um evento de mouse
             digitalWrite(PB1, HIGH); 
             digitalWrite(PB1, LOW); 
     }
      
     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D4 para D3 na saída Q7

     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo D3 no PB4 através de Q7
             buffer[0] = 1; // para cima
             position = 0;
             // usando a linha "Fala" para avisar o Master que ocorreu um evento de mouse
             digitalWrite(PB1, HIGH); 
             digitalWrite(PB1, LOW); 
     }   

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D3 para D2 na saída Q7

     // em D2 poderia ser ligado o botão esquerdo do mouse

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D2 para D1 na saída Q7

     // em D1 poderia ser ligado o scroll up da scroll wheel

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D1 para D0 na saída Q7

     // em D0 poderia ser ligado o scroll down da scroll wheel

     digitalWrite(PB3, HIGH);
     digitalWrite(PB3, LOW); // clock para mudar de D0 para DS (nono input) na saída Q7
     
     asm volatile( "nop \n\t" );

     if( (digitalRead(PB4)) == HIGH) { // lendo DS no PB4 através de Q7
             buffer[4] = 1; // click
             position = 0;
             // usando a linha "Fala" para avisar o Master que ocorreu um evento de mouse
             digitalWrite(PB1, HIGH); 
             digitalWrite(PB1, LOW); 
     }  

  }else{ // borda descendo
     digitalWrite(PB5, LOW);
     digitalWrite(PB5, HIGH);  // fazendo latch paralelo no shift register
  }
}
ATTINY8520PU
74HC165
Arduino Uno (master)Breakout

ERC Warnings

not11:OUT: Multi-driven net on pin
not1:OUT: Multi-driven net on pin
not3:OUT: Multi-driven net on pin
not5:OUT: Multi-driven net on pin
not7:OUT: Multi-driven net on pin
not9:OUT: Multi-driven net on pin