/* Krokovy motor 28BYJ-48 s budicem ULN2003 */

// definice poctu kroku na jednu otacku
const int stepsPerRevolution = 4096/4; // plna otacka, bere v uvahu prevod ozubenym soukolim
byte curr = 0; // aktualni krok
byte dire = 0; // smer pohybu (0 - doprava, 1 - doleva)
byte steps[8]={B00000001,B00000011,B00000010,B00000110,B00000100,B00001100,B00001000,B00001001}; // jednotlive kroky na elektrodach
short pauza = 1000; // pauza mezi jednotlivymi kroky v mikrosekundach, nastaveno na 1 ms

#define SERIAL_SPEED 9600

// Propojeni:
// Pin 8 na IN1 na budici motoru
// Pin 9 na IN2 na budici motoru
// Pin 10 na IN3 na budici motoru
// Pin 11 na IN4 na budici motoru

enum Command {
  KO = 0, // odpoved neco je spatne
  OK = 1, // rozkaz prijaty
  STATUS = 2, // mame spojeni?
  MOTOR = 3, // prikaz na otoceni motoru x-krat
  READ_PHOTO = 4, // prikaz na precteni proudu z fotodiody
  CALIB_ANGLE = 5, // kalibrace otoceni na svislou polarizaci
  READ_SINUS = 6, // nacteni hodnot pro jednotliva otoceni polarizatoru
  CHANGE_SPEED = 7, // zmena rychlosti krokoveho motorku
};

bool is_connected = false;
enum Command received;
short parametr;
unsigned int pocet_kroku = stepsPerRevolution/8; // pocatecni hodnota kroku motoru

void setup() {
  for(int i=8;i<12;i++) { // nastaveni pinu pro rizeni motoru na vystup
    pinMode(i,OUTPUT);
  }
  // Begin Serial communication at a baud rate of 9600:
  Serial.begin(SERIAL_SPEED);
  while(!Serial);

  delay(500);

  while(!is_connected)
  {
    send_command(STATUS);
    wait_for_command(1000,1);
    get_message();
  } 

}

void loop() {
// tady se opakuje porad dokola cekani na prikaz
// a provedeni prikazu
  wait_for_command(1000,1);
  get_message();
}


void wait_for_command(unsigned long timeout, byte pocet) // cekani na prikaz
{
  unsigned long start_time = millis();
  while( (Serial.available() < pocet) && (millis()-start_time < timeout)){}
}

// nacteni prikazu a parametru
// a zpracovani prikazu
void get_message()
{
    received = read_command();
    short reply;
    
    switch(received)
    {
      case STATUS: // inicializace konverzace mezi pocitacem a polarimetrem
        is_connected = true;
        send_command(OK);
        break;

      case MOTOR: // priklad, jak se vola funkce s parametrem
        send_command(OK);
        wait_for_command(1000,2);
        parametr = read_parametr();
        send_command(OK);
        rotate_motor(parametr);
        break;

      case READ_PHOTO: // cteme z fotodiody
        reply = read_photo();
        send_command(OK);
        send_reply(reply);
        break;

      case CALIB_ANGLE: // kalibrace otoceni na svislou polarizaci
        send_command(OK);
        break;

      case READ_SINUS: // precteni dat z fotodiody pro ruzna natoceni a odeslani po seriovem portu
        send_command(OK);
        break;

      case CHANGE_SPEED: // zmena rychlosti motorku na parametr
        send_command(OK);
        wait_for_command(1000,2);
        pauza = read_parametr();
        send_command(OK);
        break;
        
      default:
//        send_command(KO);
      break;
    }
    
}

Command read_command() // precteni prikazu ze serioveho portu
{
  return (Command) Serial.read();
}

short read_parametr() // precteni parametru ze serioveho portu
{
  size_t i=0;
  int c;
  byte par[2];
  short navrat;
  while (i < 2)
  {
    c = Serial.read();
    if (c<0) break;
    par[i++]=(byte)c;
  }
  navrat = *(short *)par;
  return navrat;
}

void send_command(enum Command command) // poslani prikazu nebo statusu na seriovy port
{
  uint8_t* Command = (uint8_t*) &command;
  Serial.write(Command, sizeof(uint8_t));
}

void send_reply(short reply)
{
  uint8_t* Rep = (uint8_t*) &reply;
  Serial.write(Rep,2*sizeof(uint8_t));
}

void rotate_motor(int count) // procedura pro udelani count kroku motoru
{
  for(int i=0;i<(2*count);i++){
    if(dire==1)
      curr--;
    else
      curr++;
    curr %= 8;
    PORTB = ((PORTB & B11110000) | steps[curr]);
    delayMicroseconds(pauza);
  }
}


short read_photo()
{
  return(analogRead(A0));
}
