// // Rotator Control Unit for Arduino Nano, by OM2JU (2019, 2020) // // Uses incremental encoder (pulses on pin D2) to sense relative position // Uses 2x16 segment LCD display to show actual and desired values // Uses quadrature encoder with pushbutton to set desired values, navigate and execute actions // Uses internal Arduino EEPROM to store configuration data // Arduino controls H-bridge which drive the DC motor of rotator in clockwise or counter-clockwise direction // Simple control over serial console implemented, connection paramters are 9600bps, 8N1. Type 'h' to show help // // // // Connection of Arduino Nano pins: // // TXD - used for serial communication D1 Vin 5V supply voltage for Arduino-Nano (needed if not powered over USB) // RXD - used for serial communication D0 GND System Ground // Reset (keep it unconnected!) Reset Reset Reset (keep it unconnected!) // System Ground GND VCC_5V 5V supply voltage output // Pulses from incremental encoder D2 A7 Analog input (optional, not implemented yet) // A output from quadrature encoder D3 A6 Analog input (optional, not implemented yet) // B output from quadrature encoder D4 A5 User-out-D // Push-button from quadrature encoder D5 A4 User-out-C // User-out-B (via 1k resistor to base...) D6 A3 User-out-A (via 1k resistor to base of NPN open collector of MOSFET-N switch) // LCD-Display - RS D7 A2 Motor-enable (to H-bridge enable) // LCD-Display - E D8 A1 Motor-side-2 (to H-bridge high side) // LCD-Display - D4 D9 A0 Motor-side-1 (to H-bridge low side) // LCD-Display - D5 D10 AREF Analog voltage reference input/output (keep it unconnected) // LCD-Display - D6 D11 3V3 3V3 output (keep it unconnected) // LCD-Display - D7 D12 D13 via 470R to LCD-backlight (anode) // // // #include #include #include #include #include // Define LCD display pins RS,E,D4,D5,D6,D7 LiquidCrystal lcd (7,8,9,10,11,12); // #define VERSION_INFO "Rotator " #define VERSION_INFO2 "by OM2JU / v2.0 " // Version with 4 user outputs // Pin definitions // Note: D2 is connected to input from incremental encoder (interrupt) #define SW_A 3 // Rotary encoder contact A #define SW_B 4 // Rotary encoder contact B #define SW_PUSH 5 // Rotary encoder pushbutton #define USER_OUT_B 6 // User OUT_B // (pins 7-12 are used by LCD) #define DISPLAY_BG 13 // Display backlight #define MOTOR_SIDE_1 A0 // Motor H-Bridge side 1 #define MOTOR_SIDE_2 A1 // Motor H-Bridge side 2 #define MOTOR_ENA A2 // ENABLE for H-bridge driver #define USER_OUT_A A3 // User OUT_A #define USER_OUT_C A4 // User OUT_C #define USER_OUT_D A5 // User OUT_D #define ROTATION_RIGHT 1 #define ROTATION_LEFT 0 #define SW_PUSH_PRESSED digitalRead(SW_PUSH)==0 #define LOOP_DELAY_SLOW 150 #define TIMEOUT_REPEATS 50 #define DISP_BG_TIMEOUT_CNT_MAX 2048 //#define MOTOR_START_DELAY 200 // Delay in milisec to start motor; during this time no pulses should be checked //replaced by RotaryEnc_Rotator_motor_delay parameter // Rotator program state enum state_t { S_SHOW = 0, S_SHOW_SHOW, S_ROTATE_RIGHT, S_ROTATE_LEFT, S_ROTATE_ANGLE_REQUIRED, S_USER_OUT_A_TOGGLE, S_USER_OUT_B_TOGGLE, S_USER_OUT_C_TOGGLE, S_USER_OUT_D_TOGGLE, S_MENU, S_SET_MAX_L_ANGLE, S_SET_MAX_R_ANGLE, S_SET_MOTOR_STARTUP_DELAY, S_RESET_CNT, S_ERASE_EEPROM_CONFIRM, S_ERASE_EEPROM, S_WRITE_CNT_EE, S_PARAMETERS_STORE } rotator_state; enum rotator_motor_state_t { S_MOTOR_OFF = 0, S_MOTOR_LEFT, S_MOTOR_RIGHT } rotator_motor_state; //String command; uint32_t pulse_cnt = 0; uint32_t pulse_cnt_corrected = 0; uint32_t pulse_cnt_max = 0; uint32_t pulse_cnt_old = 0; uint8_t pulse_cnt_underflow = 0; uint8_t rotation_dir = ROTATION_RIGHT; uint8_t timeout_cnt = 0; uint16_t disp_bg_timeout_cnt = 0; uint32_t rotator_angle_deg = 0; uint32_t rotator_angle_deg_tmp = 0; uint32_t rotator_angle_deg_actual_full = 0; //uint32_t rotator_angle_deg_max_full = 0; uint32_t rotator_angle_deg_actual = 0; uint32_t rotator_angle_deg_required_360 = 180; uint32_t rotation_range_deg; float rotator_cnt_per_deg = 20; // initialized to some value in order to avoid division by zero after initial startup when there is no configurtion in EEPROM char str_report[80]; uint8_t loop_cnt = 0; uint8_t debug = 0; uint8_t rotator_data_valid = 0; uint8_t USER_OUT_A_val = 0; uint8_t USER_OUT_B_val = 0; uint8_t USER_OUT_C_val = 0; uint8_t USER_OUT_D_val = 0; int sw_push_pressed_val; byte eeDataB0 = 0; byte eeDataB1 = 0; byte eeDataB2 = 0; byte eeDataB3 = 0; uint8_t rotaryEnc_A = 0xFF; uint8_t rotaryEnc_B = 0xFF; struct RotaryEncounters { uint32_t cntVal; //uint32_t cntValOld; uint32_t cntMin; uint32_t cntMax; }; uint32_t RotaryEncISR_cntValOld; const byte numChars = 8; char str_data[numChars]; // an array to store the received data boolean newData = false; // // Rotary encoder Example using state table: // https://www.best-microcontroller-projects.com/rotary-encoder.html // // This project uses sampling of rotary encoder contacts in timer interrupt // RotaryEncounters RotaryEnc_MenuLevel; RotaryEncounters RotaryEnc_Rotator_angle_deg_min; RotaryEncounters RotaryEnc_Rotator_angle_deg_max; RotaryEncounters RotaryEnc_Rotator_angle_deg_desired; RotaryEncounters RotaryEnc_Rotator_motor_delay; RotaryEncounters RotaryEncISR; //RotaryEncounters RotaryEncISR_old; uint32_t RotaryEncCntEntryVal = 0xFFFFFFFF; //##### uint8_t cntValISR_Under_Over_Flag = 0; //uint32_t cntIncrISR = 1; uint8_t ISR_cnt = 0; // Rotator Motor Control using L298N stepper motor driver or Relays void Rotator_Motor_Control(rotator_motor_state_t r) { switch (r) { case S_MOTOR_OFF: digitalWrite(MOTOR_ENA, 0); digitalWrite(MOTOR_SIDE_1, 0); digitalWrite(MOTOR_SIDE_2, 0); break; case S_MOTOR_LEFT: digitalWrite(MOTOR_SIDE_1, 1); delay(1); digitalWrite(MOTOR_SIDE_2, 0); delay(1); digitalWrite(MOTOR_ENA, 1); rotation_dir = ROTATION_LEFT; break; case S_MOTOR_RIGHT: digitalWrite(MOTOR_SIDE_1, 0); delay(1); digitalWrite(MOTOR_SIDE_2, 1); delay(1); digitalWrite(MOTOR_ENA, 1); rotation_dir = ROTATION_RIGHT; break; default: digitalWrite(MOTOR_ENA, 0); //digitalWrite(MOTOR_SIDE_1, 0); //digitalWrite(MOTOR_SIDE_2, 0); break; } } // Copy values of variable pointed by r1 to RotaryEncISR variable void RotaryEncPush(RotaryEncounters *r1) { RotaryEncISR = *r1; } // Copy values of RotaryEncISR variable to variable pointed by r1 void RotaryEncPop(RotaryEncounters *r1) { *r1 = RotaryEncISR; } // function to display first line on LCD display void disp_line(uint8_t row, uint8_t clmn, const char s[16], uint32_t *val) { if (disp_bg_timeout_cnt!=DISP_BG_TIMEOUT_CNT_MAX) { lcd.setCursor(clmn,row); snprintf(str_report, 17, s, *val); lcd.print(str_report); } } // function to display first line on LCD display - constant string void disp_line(uint8_t row, const char s[16]) { // row = 0 - first line // row = 1 - second line if (disp_bg_timeout_cnt!=DISP_BG_TIMEOUT_CNT_MAX) { lcd.setCursor(0,row); lcd.print(s); } } // Calculate global variables (rotator_cnt_per_deg) used in main loop for calculation of act. azimuth void calc_rotator_data() { // Brute force calculation of angle difference rotator_angle_deg_tmp = RotaryEnc_Rotator_angle_deg_min.cntVal; rotation_range_deg = 0; while (rotator_angle_deg_tmp != RotaryEnc_Rotator_angle_deg_max.cntVal) { rotator_angle_deg_tmp = (rotator_angle_deg_tmp + 1) % 360; rotation_range_deg++; } // If difference is less than 180deg the it is most probably overlap if (rotation_range_deg <= 180) rotation_range_deg+=360; // //rotator_angle_deg_max_full = RotaryEnc_Rotator_angle_deg_min.cntVal + rotation_range_deg; // Counts per one degree rotator_cnt_per_deg = float(pulse_cnt_max) / float(rotation_range_deg); } // // Normalize azimuth value to range 0 - 359 void Az360 (uint32_t in1) { rotator_angle_deg_required_360 = in1; //if (rotator_angle_deg_required_360 > 359) rotator_angle_deg_required_360 = rotator_angle_deg_required_360 - 360; //if (rotator_angle_deg_required_360 > 359) rotator_angle_deg_required_360 = rotator_angle_deg_required_360 - 360; } // // Print No-pulses info to LCD and terminal void noPulsesInfo () { disp_line (1, "No pulses... "); Serial.println( "No pulses..."); delay(500); } // void timeoutInfo () { disp_line (1, "Timeout... "); //Serial.println( "No pulses... "); delay(500); } // // Print Azimuth to terminal void printAZ( uint8_t mask, uint8_t print_out_values) { // mask = 0x00 --> print always // mask = 0x03 --> print every 4-th frame if ( (loop_cnt & mask) == 0) { snprintf(str_report, 30, "Az = %-4d", rotator_angle_deg_actual_full); if (rotator_angle_deg_actual_full==0) { snprintf(str_report, 30, "Az = 0, Pulse-cnt = %-5d", pulse_cnt); } else { snprintf(str_report, 30, "Az = %-4d", rotator_angle_deg_actual_full); } Serial.println(str_report); } if (print_out_values != 0) { snprintf(str_report, 30, "Out A =%2d", USER_OUT_A_val); Serial.println(str_report); snprintf(str_report, 30, "Out B =%2d", USER_OUT_B_val); Serial.println(str_report); snprintf(str_report, 30, "Out C =%2d", USER_OUT_C_val); Serial.println(str_report); snprintf(str_report, 30, "Out D =%2d", USER_OUT_D_val); Serial.println(str_report); } } // // Write uint32_t at offset_addr into EEPROM void EE_wr_32b(int offset_addr, uint32_t inp) { noInterrupts(); EEPROM.write(offset_addr, inp & 0x000000FF); // B0 offset_addr++; EEPROM.write(offset_addr, inp>>8 & 0x000000FF); // B1 offset_addr++; EEPROM.write(offset_addr, inp>>16 & 0x000000FF); // B2 offset_addr++; EEPROM.write(offset_addr, inp>>24 & 0x000000FF); // B3 interrupts(); disp_line(0, "EEPROM Written "); //disp_line(1, "stored in EEPROM"); delay(500); } // Read uint32_t stored in EEPROM at offset_addr uint32_t EE_rd_32b (int offset_addr) { eeDataB0 = EEPROM.read(offset_addr); offset_addr++; eeDataB1 = EEPROM.read(offset_addr); offset_addr++; eeDataB2 = EEPROM.read(offset_addr); offset_addr++; eeDataB3 = EEPROM.read(offset_addr); return eeDataB3<<24 | eeDataB2<<16 | eeDataB1<<8 | eeDataB0; } // // Strings constants stored in FLASH memory const char help_0[] PROGMEM = "Rotator serial console"; const char help_0a[] PROGMEM = VERSION_INFO2; const char help_1[] PROGMEM = " "; const char help_2[] PROGMEM = " [nnn] - Any number - Rotate to desired angle"; const char help_3[] PROGMEM = " l - Rotate Left to left-stop azimuth"; const char help_4[] PROGMEM = " r - Rotate Right to right-stop azimuth"; const char help_5[] PROGMEM = " i - Init left-end azimuth (clears pulse counter)"; const char help_5a[] PROGMEM = " w - Write actual az. to EEPROM (= startup azimuth)"; const char help_6[] PROGMEM = " f - Print inFo"; const char help_6a[] PROGMEM = " D - Debug toggle"; const char help_7[] PROGMEM = " h - Print this help info"; const char help_8[] PROGMEM = " [Enter] - Stop rotation, print actual azimuth"; const char help_9[] PROGMEM = " "; const char help_10[] PROGMEM = " a - Toggle User Output A"; const char help_11[] PROGMEM = " b - Toggle User Output B"; const char help_12[] PROGMEM = " c - Toggle User Output C"; const char help_13[] PROGMEM = " d - Toggle User Output D"; // const char *const string_table[] PROGMEM = {help_0, help_0a, help_1, help_2, help_3, help_4, help_5, help_5a, help_6, help_6a, help_7, help_8, help_9, help_10, help_11, help_12, help_13}; //char buffer[70]; // make sure this is large enough for the largest string it must hold // const char no_ee_0[] PROGMEM = "No EEPROM data !"; const char no_ee_1[] PROGMEM = "Run SETUP first!"; // const char *const no_ee_table[] PROGMEM = {no_ee_0, no_ee_1}; // void Serial_Help () { for (int i = 0; i < 17; i++) { strcpy_P(str_report, (char *)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy. Serial.println(str_report); } } // // Arduino init void setup() { // Initialise variables which are changed by rotary encoder // { cntVal, cntMin, cntMax} RotaryEnc_MenuLevel = {0 , 0, 11}; RotaryEnc_Rotator_angle_deg_min = {0, 0, 359}; RotaryEnc_Rotator_angle_deg_max = {0, 0, 359}; RotaryEnc_Rotator_angle_deg_desired = {0, 0, 1080}; RotaryEnc_Rotator_motor_delay = {200, 100, 1000}; RotaryEncISR = {0, 0, 359}; // // I/O setup pinMode(2,INPUT_PULLUP); // D2 - interrupt from incremental encoder pinMode(SW_A,INPUT_PULLUP); pinMode(SW_B,INPUT_PULLUP); pinMode(SW_PUSH,INPUT_PULLUP); // pinMode(MOTOR_SIDE_1,OUTPUT); pinMode(MOTOR_SIDE_2,OUTPUT); pinMode(MOTOR_ENA,OUTPUT); pinMode(USER_OUT_A,OUTPUT); pinMode(USER_OUT_B,OUTPUT); pinMode(USER_OUT_C,OUTPUT); pinMode(USER_OUT_D,OUTPUT); pinMode(DISPLAY_BG,OUTPUT); // digitalWrite(USER_OUT_A, 0); digitalWrite(USER_OUT_B, 0); digitalWrite(USER_OUT_C, 0); digitalWrite(USER_OUT_D, 0); digitalWrite(DISPLAY_BG, 1); // We want to have display background ON Rotator_Motor_Control(S_MOTOR_OFF); // This will set outputs for H-bridge // // Initial state rotator_state = S_SHOW; // // LCD setup and welcome message lcd.begin(16,2); // setting LCD as 16x2 type disp_line(0, VERSION_INFO); disp_line(1, VERSION_INFO2); delay(1000); // // Read config data stored im EEPROM eeDataB0 = EEPROM.read(128); // EEbyte(128) = Data is valid or not ('R' = valid) if (eeDataB0 == 'R') { // found valid rotator init data pulse_cnt_max = EE_rd_32b(0); RotaryEnc_Rotator_angle_deg_min.cntVal = EE_rd_32b(4); RotaryEnc_Rotator_angle_deg_max.cntVal = EE_rd_32b(8); RotaryEnc_Rotator_motor_delay.cntVal = EE_rd_32b(12); calc_rotator_data(); rotator_data_valid++; RotaryEnc_Rotator_angle_deg_desired.cntMin = RotaryEnc_Rotator_angle_deg_min.cntVal; RotaryEnc_Rotator_angle_deg_desired.cntMax = RotaryEnc_Rotator_angle_deg_min.cntVal + rotation_range_deg; disp_line(1, "EEPROM data OK "); delay(1000); // RotaryEnc_Rotator_angle_deg_desired.cntVal = RotaryEnc_Rotator_angle_deg_min.cntVal; // Read pulse_cnt data stored in EEPROM pulse_cnt = EE_rd_32b(140); if (pulse_cnt != 0) { rotator_angle_deg_actual = uint32_t(RotaryEnc_Rotator_angle_deg_min.cntVal) + uint32_t(float(pulse_cnt) / rotator_cnt_per_deg); disp_line(0, 0, "Found init az. ", &rotator_angle_deg_actual); disp_line(1, 0, "in EEPROM: %4d ", &rotator_angle_deg_actual); RotaryEnc_Rotator_angle_deg_desired.cntVal = rotator_angle_deg_actual; delay(1000); } } else { disp_line(0, "No EEPROM data !"); disp_line(1, "Run SETUP first!"); delay(1000); } // RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); Wire.begin(); // Serial console init Serial.begin(9600); Serial_Help(); // // Periodic timer interrupt init and associate with ISR function Timer1.initialize(1500); // 1.5msec Timer1.attachInterrupt(ISR_rotaryEncoderSampleInputs); // // External interrupt on pin D2 init and associate with ISR function attachInterrupt(digitalPinToInterrupt(2),ISR_D2,RISING); // function for creating external interrupts at pin2 on Rising (LOW to HIGH) // } void loop() { loop_cnt++; // Underflow of unsigned number if (pulse_cnt > 0x1FFFFFFF) { pulse_cnt_corrected = 0; pulse_cnt_underflow = 1; } else { pulse_cnt_corrected = pulse_cnt; pulse_cnt_underflow = 0; } delay(LOOP_DELAY_SLOW); // rotator_angle_deg_actual = uint32_t(RotaryEnc_Rotator_angle_deg_min.cntVal) + uint32_t(float(pulse_cnt_corrected) / rotator_cnt_per_deg); rotator_angle_deg_actual_full = rotator_angle_deg_actual; if (rotator_angle_deg_actual>359) { rotator_angle_deg_actual=rotator_angle_deg_actual-360;} // wrap if more than 360 if (rotator_angle_deg_actual>359) { rotator_angle_deg_actual=rotator_angle_deg_actual-360;} // wrap if more than 720 // // // Main state-machine switch (rotator_state) { // case S_MENU: timeout_cnt++; switch (RotaryEncISR.cntVal) { case 0: disp_line(0, "Menu: "); disp_line(1, "1. INIT PULS-CNT"); break; case 1: disp_line(0, 0, "Menu: %-4d", &rotator_angle_deg_actual_full); disp_line(1, "2. STORE ACT. Az"); break; case 2: rotator_angle_deg_tmp=USER_OUT_A_val; disp_line(0, 0, "Menu: %-4d", &rotator_angle_deg_tmp); disp_line(1, "3. OUT-A TOGGLE "); break; case 3: rotator_angle_deg_tmp=USER_OUT_B_val; disp_line(0, 0, "Menu: %-4d", &rotator_angle_deg_tmp); disp_line(1, "4. OUT-B TOGGLE "); break; case 4: rotator_angle_deg_tmp=USER_OUT_C_val; disp_line(0, 0, "Menu: %-4d", &rotator_angle_deg_tmp); disp_line(1, "5. OUT-C TOGGLE "); break; case 5: rotator_angle_deg_tmp=USER_OUT_D_val; disp_line(0, 0, "Menu: %-4d", &rotator_angle_deg_tmp); disp_line(1, "6. OUT-D TOGGLE "); break; case 6: disp_line(0, 0, "Menu: %-4d", &RotaryEnc_Rotator_angle_deg_min.cntVal); disp_line(1, "7. LEFT-STOP AZ"); break; case 7: disp_line(0, 0, "Menu+EEWr %-4d", &RotaryEnc_Rotator_angle_deg_max.cntVal); disp_line(1, "8. RIGHT-STOP AZ"); break; case 8: disp_line(0, 0, "Menu+EEWr %-4d", &RotaryEnc_Rotator_motor_delay.cntVal); disp_line(1, "9. SENSE DELAY "); break; case 9: disp_line(0, "Menu: "); disp_line(1, "10. DEBUG 0/1/2 "); break; case 10: disp_line(0, "Menu: "); disp_line(1, "11. ERASE EEPROM"); break; case 11: disp_line(0, "Menu: "); disp_line(1, "12. EXIT Menu "); break; default: break; } // if (SW_PUSH_PRESSED) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release delay(500); timeout_cnt = 0; RotaryEncPop(&RotaryEnc_MenuLevel); switch (RotaryEncISR.cntVal) { case 0: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_RESET_CNT; break; case 1: rotator_state = S_WRITE_CNT_EE; break; case 2: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_USER_OUT_A_TOGGLE; break; case 3: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_USER_OUT_B_TOGGLE; break; case 4: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_USER_OUT_C_TOGGLE; break; case 5: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_USER_OUT_D_TOGGLE; break; case 6: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_min); rotator_state = S_SET_MAX_L_ANGLE; delay(300); break; case 7: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_max); rotator_state = S_SET_MAX_R_ANGLE; break; case 8: RotaryEncPush(&RotaryEnc_Rotator_motor_delay); rotator_state = S_SET_MOTOR_STARTUP_DELAY; break; case 9: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); debug = (debug + 1) % 3; rotator_angle_deg_tmp = debug; disp_line(1, 0, "DEBUG = %3u ", &rotator_angle_deg_tmp); delay(300); rotator_state = S_SHOW; break; case 10: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); disp_line(0, "Confirm to erase"); //disp_line(1, "to erase EEPROM "); delay(300); timeout_cnt=0; rotator_state = S_ERASE_EEPROM_CONFIRM; break; case 11: RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; break; default: break; } } // if (timeout_cnt>TIMEOUT_REPEATS) { timeoutInfo(); RotaryEncPop(&RotaryEnc_MenuLevel); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; }; break; // case S_MENU // case S_SHOW: disp_bg_timeout_cnt=0; disp_line(1, " "); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); RotaryEncCntEntryVal = RotaryEnc_Rotator_angle_deg_desired.cntVal; Rotator_Motor_Control(S_MOTOR_OFF); Serial.println(""); printAZ(0x00, 0x01); rotator_state = S_SHOW_SHOW; break; // case S_SHOW_SHOW: timeout_cnt = 0; sw_push_pressed_val = digitalRead(SW_PUSH); // if pressed value will be 0 // if ((disp_bg_timeout_cnt == DISP_BG_TIMEOUT_CNT_MAX) && (sw_push_pressed_val != 0)) { digitalWrite(DISPLAY_BG, 0); // Display Off } else { digitalWrite(DISPLAY_BG, 1); // Display On disp_bg_timeout_cnt++; // // Alternate display of debug info if ( ((debug!=0) || (rotator_data_valid==0)) && ( (loop_cnt & 0x07) <= 2) ) { disp_line(0, 0, "L: %4u ", &RotaryEnc_Rotator_angle_deg_min.cntVal); disp_line(0 ,8, "R: %4u ", &RotaryEnc_Rotator_angle_deg_max.cntVal); disp_line(1, 0, "CNT:%4u ", &pulse_cnt_corrected); disp_line(1, 8, "CMax%4u ", &pulse_cnt_max); } else { // Else display regular info + process inputs disp_line(0, VERSION_INFO); if ( (pulse_cnt_underflow != 0) && ( (loop_cnt>>2 & 0x01) == 1) ) { disp_line(1, 0, "A< %3u /", &rotator_angle_deg_actual); } else { disp_line(1, 0, "Az %3u/", &rotator_angle_deg_actual); } disp_line(1, 7, "%3u ", &rotator_angle_deg_actual_full); Az360(RotaryEncISR.cntVal); disp_line(1, 12, " %3u ", &rotator_angle_deg_required_360); // // if (((RotaryEncISR.cntVal - RotaryEncCntEntryVal) == 359) || ((RotaryEncCntEntryVal - RotaryEncISR.cntVal) == 1) || (cntValISR_Under_Over_Flag==1)) { disp_line(0, 12, "L<<", &RotaryEncISR.cntVal); if (sw_push_pressed_val==0) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release Rotator_Motor_Control(S_MOTOR_LEFT); pulse_cnt_old = pulse_cnt; delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_LEFT; } } else if(((RotaryEncISR.cntVal - RotaryEncCntEntryVal) == 1) || ((RotaryEncCntEntryVal - RotaryEncISR.cntVal) == 359) || (cntValISR_Under_Over_Flag==2)) { disp_line(0, 12, " >>R", &RotaryEncISR.cntVal); if (sw_push_pressed_val==0) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release Rotator_Motor_Control(S_MOTOR_RIGHT); pulse_cnt_old = pulse_cnt; delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_RIGHT; } } else if (RotaryEncISR.cntVal == RotaryEncCntEntryVal) { disp_line(0, 12, "Menu", &RotaryEncISR.cntVal); if (sw_push_pressed_val==0) { delay(50); while(SW_PUSH_PRESSED) // Busy wait for release RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); RotaryEncPush(&RotaryEnc_MenuLevel); rotator_state = S_MENU; } } else if (RotaryEncISR.cntVal > RotaryEncCntEntryVal) { disp_line(0, 12, " >Az", &RotaryEncISR.cntVal); if (sw_push_pressed_val==0) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release (rotator_angle_deg_actual_full < RotaryEncISR.cntVal) ? Rotator_Motor_Control(S_MOTOR_RIGHT) : Rotator_Motor_Control(S_MOTOR_LEFT); pulse_cnt_old = pulse_cnt; delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_ANGLE_REQUIRED; } } else if (RotaryEncISR.cntVal < RotaryEncCntEntryVal) { disp_line(0, 11, "Az<", &RotaryEncISR.cntVal); if (sw_push_pressed_val==0) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release (rotator_angle_deg_actual_full < RotaryEncISR.cntVal) ? Rotator_Motor_Control(S_MOTOR_RIGHT) : Rotator_Motor_Control(S_MOTOR_LEFT); pulse_cnt_old = pulse_cnt; delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_ANGLE_REQUIRED; } } else { disp_line(0, 13, " ", &RotaryEncISR.cntVal); } } pulse_cnt_old=pulse_cnt; if (debug == 2) { pulse_cnt_old++; } // This causes in dabug=2 mode that pulses are ignored } // else... // break; // case S_ROTATE_LEFT: disp_line(0, "Working... L<<<"); if ( (pulse_cnt_underflow != 0) && ( (loop_cnt>>2 & 0x01) == 1) ) { disp_line(1, 0, "A<%3u /", &rotator_angle_deg_actual); } else { disp_line(1, 0, "Az %3u/", &rotator_angle_deg_actual); } disp_line(1, 7, "%3u", &rotator_angle_deg_actual_full); Az360(RotaryEncISR.cntVal); disp_line(1, 12, " %3u ", &rotator_angle_deg_required_360); // printAZ(0x03, 0x00); // if (SW_PUSH_PRESSED || pulse_cnt == pulse_cnt_old ) { Rotator_Motor_Control(S_MOTOR_OFF); delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release if (pulse_cnt == pulse_cnt_old) noPulsesInfo(); //delay(500); // additional delay RotaryEnc_Rotator_angle_deg_desired.cntVal = rotator_angle_deg_actual_full; //RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } pulse_cnt_old=pulse_cnt; if (debug == 2) { pulse_cnt_old++; } // This causes in dabug=2 mode that pulses are ignored break; // case S_ROTATE_RIGHT: disp_line(0, "Working... >>>R"); disp_line(1, 0, "Az %3u/", &rotator_angle_deg_actual); disp_line(1, 7, "%3u", &rotator_angle_deg_actual_full); Az360(RotaryEncISR.cntVal); disp_line(1, 12, " %3u ", &rotator_angle_deg_required_360); // printAZ(0x03, 0x00); // if (SW_PUSH_PRESSED || pulse_cnt == pulse_cnt_old ) { Rotator_Motor_Control(S_MOTOR_OFF); delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release if (pulse_cnt == pulse_cnt_old) noPulsesInfo(); //delay(500); // additional delay RotaryEnc_Rotator_angle_deg_desired.cntVal = rotator_angle_deg_actual_full; //RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } pulse_cnt_old=pulse_cnt; if (debug == 2) { pulse_cnt_old++; } // This causes in dabug=2 mode that pulses are ignored break; // case S_USER_OUT_A_TOGGLE: USER_OUT_A_val = (USER_OUT_A_val + 1) % 2; digitalWrite(USER_OUT_A, USER_OUT_A_val); rotator_angle_deg_tmp = USER_OUT_A_val; // we need uint32_t for display function... disp_line(0, " "); disp_line(1, 0, "OUT-A = %3d ", &rotator_angle_deg_tmp); delay(1000); rotator_state = S_SHOW; break; // case S_USER_OUT_B_TOGGLE: USER_OUT_B_val = (USER_OUT_B_val + 1) % 2; digitalWrite(USER_OUT_B, USER_OUT_B_val); rotator_angle_deg_tmp = USER_OUT_B_val; // we need uint32_t for display function... disp_line(0, " "); disp_line(1, 0, "OUT-B = %3d ", &rotator_angle_deg_tmp); delay(1000); rotator_state = S_SHOW; break; // case S_USER_OUT_C_TOGGLE: USER_OUT_C_val = (USER_OUT_C_val + 1) % 2; digitalWrite(USER_OUT_C, USER_OUT_C_val); rotator_angle_deg_tmp = USER_OUT_C_val; // we need uint32_t for display function... disp_line(0, " "); disp_line(1, 0, "OUT-C = %3d ", &rotator_angle_deg_tmp); delay(1000); rotator_state = S_SHOW; break; // case S_USER_OUT_D_TOGGLE: USER_OUT_D_val = (USER_OUT_D_val + 1) % 2; digitalWrite(USER_OUT_D, USER_OUT_D_val); rotator_angle_deg_tmp = USER_OUT_D_val; // we need uint32_t for display function... disp_line(0, " "); disp_line(1, 0, "OUT-D = %3d ", &rotator_angle_deg_tmp); delay(1000); rotator_state = S_SHOW; break; // case S_SET_MOTOR_STARTUP_DELAY: disp_line(1, 0, "SenseDelay: %-4d", &RotaryEncISR.cntVal); timeout_cnt++; if (SW_PUSH_PRESSED) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release EE_wr_32b(12, RotaryEncISR.cntVal); //delay(500); RotaryEncPop(&RotaryEnc_Rotator_motor_delay); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state=S_SHOW; } if (timeout_cnt > TIMEOUT_REPEATS) { timeoutInfo(); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } break; // case S_SET_MAX_L_ANGLE: disp_line(1, 0, "LefStopAZ: %-4d", &RotaryEncISR.cntVal); pulse_cnt=0; // Reset pulse counter when turned to max-left position timeout_cnt++; if (SW_PUSH_PRESSED) { disp_line(0, "Info: Now turn "); disp_line(1, "to right stop "); delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release //delay(1000); RotaryEncPop(&RotaryEnc_Rotator_angle_deg_min); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } if (timeout_cnt > TIMEOUT_REPEATS) { timeoutInfo(); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } break; // case S_SET_MAX_R_ANGLE: disp_line(1, 0, "RightStopAZ:%-4d", &RotaryEncISR.cntVal); timeout_cnt++; if (SW_PUSH_PRESSED) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release pulse_cnt_max=pulse_cnt; //delay(400); RotaryEncPop(&RotaryEnc_Rotator_angle_deg_max); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_PARAMETERS_STORE; } if (timeout_cnt > TIMEOUT_REPEATS) { timeoutInfo(); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } break; // /* case S_SET_TURN_OFF_DEG_OVRSHOOT: disp_line(1, 0, "Off ovrshoot %3d", &RotaryEncISR.cntVal); timeout_cnt++; if (SW_PUSH_PRESSED) { delay(500); EE_wr_32b(12, RotaryEnc_Rotator_deg_ovrshoot.cntVal); RotaryEncPop(&RotaryEnc_Rotator_deg_ovrshoot); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } if (timeout_cnt > TIMEOUT_REPEATS) { timeoutInfo(); RotaryEncPush(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } break; */ // Reset counter / assuming the rotator is in lefrmost position case S_RESET_CNT: pulse_cnt=0; disp_line(0, "Counter reset..."); //disp_line(1, "was reset to 0 "); Serial.println("Pulse-cnt initialized..."); delay(1000); rotator_state=S_SHOW; break; // Confirm erase of EEPROM case S_ERASE_EEPROM_CONFIRM: timeout_cnt++; if (timeout_cnt > TIMEOUT_REPEATS) { timeoutInfo(); rotator_state = S_SHOW; } if (SW_PUSH_PRESSED) { delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release rotator_state = S_ERASE_EEPROM; //delay(500); } break; // Erase EEPROM data case S_ERASE_EEPROM: noInterrupts(); EEPROM.write(128, 'x'); // EEPROM Data invalid interrupts(); disp_line(0, "EEPROM Erased "); //disp_line(1, "data was erased "); rotator_data_valid=0; delay(500); rotator_state = S_SHOW; break; // case S_WRITE_CNT_EE: EE_wr_32b(140, pulse_cnt); disp_line(0, "EEPROM Write OK "); //disp_line(1, "turn-off power. "); Serial.println("Actual az. written to EE"); delay(500); rotator_state=S_SHOW; break; // Store parameters in EEPROM case S_PARAMETERS_STORE: calc_rotator_data(); RotaryEnc_Rotator_angle_deg_desired.cntMin = RotaryEnc_Rotator_angle_deg_min.cntVal; RotaryEnc_Rotator_angle_deg_desired.cntMax = RotaryEnc_Rotator_angle_deg_min.cntVal + rotation_range_deg; noInterrupts(); EEPROM.write(128, 'R'); EE_wr_32b(0, pulse_cnt); EE_wr_32b(4, RotaryEnc_Rotator_angle_deg_min.cntVal); EE_wr_32b(8, RotaryEnc_Rotator_angle_deg_max.cntVal); //interrupts(); -- already in EE_wr_32b rotator_data_valid++; delay(500); rotator_state = S_SHOW; break; // // Rotate to required angle case S_ROTATE_ANGLE_REQUIRED: disp_line(1, 0, "Az %3u/", &rotator_angle_deg_actual); disp_line(1, 7, "%3u", &rotator_angle_deg_actual_full); // if (rotation_dir == ROTATION_LEFT) { disp_line(0, "Working... Az<< "); Az360(RotaryEncISR.cntVal); disp_line(1, 12, " %3u ", &rotator_angle_deg_required_360); if (rotator_angle_deg_actual_full <= RotaryEncISR.cntVal){ Rotator_Motor_Control(S_MOTOR_OFF); RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } } else { disp_line(0, "Working... >>Az"); Az360(RotaryEncISR.cntVal); disp_line(1, 12, " %3u ", &rotator_angle_deg_required_360); if (rotator_angle_deg_actual_full >= RotaryEncISR.cntVal){ Rotator_Motor_Control(S_MOTOR_OFF); RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } } // printAZ(0x03, 0x00); // if (SW_PUSH_PRESSED || pulse_cnt == pulse_cnt_old ) { Rotator_Motor_Control(S_MOTOR_OFF); delay(50); while(SW_PUSH_PRESSED) {}; // Busy wait for release if (pulse_cnt == pulse_cnt_old) noPulsesInfo(); //delay(500); // additional delay RotaryEnc_Rotator_angle_deg_desired.cntVal = rotator_angle_deg_actual_full; //RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } pulse_cnt_old=pulse_cnt; if (debug == 2) { pulse_cnt_old++; } // This causes in dabug=2 mode that pulses are ignored break; // default: Rotator_Motor_Control(S_MOTOR_OFF); break; } SerialRxWithEOL(); if (newData == true) { //Serial.print("User entered: "); //Serial.println(str_data); newData = false; rotator_angle_deg_tmp = atoi(str_data); /* // Delta rotate to azimuth if((str_data[0]=='+') || (str_data[0]=='-')) { Serial.println("Rotating diff..."); rotator_angle_deg_tmp = atoi(str_data); rotator_angle_deg_tmp = rotator_angle_deg_tmp + rotator_angle_deg_actual_full; (rotator_angle_deg_actual_full < rotator_angle_deg_tmp) ? Rotator_Motor_Control(S_MOTOR_RIGHT) : Rotator_Motor_Control(S_MOTOR_LEFT); RotaryEncISR.cntVal = rotator_angle_deg_tmp; rotator_state = S_ROTATE_ANGLE_REQUIRED; delay(RotaryEnc_Rotator_motor_delay.cntVal); } else */ if ((rotator_angle_deg_tmp) || (str_data[0]=='0')) { Serial.print("Rotating to azimuth: "); Serial.println(str_data); (rotator_angle_deg_actual_full < rotator_angle_deg_tmp) ? Rotator_Motor_Control(S_MOTOR_RIGHT) : Rotator_Motor_Control(S_MOTOR_LEFT); RotaryEncISR.cntVal = rotator_angle_deg_tmp; rotator_state = S_ROTATE_ANGLE_REQUIRED; delay(RotaryEnc_Rotator_motor_delay.cntVal); } // Rotate Left if(str_data[0]=='l') { Serial.println("Rotating to Left-stop..."); Rotator_Motor_Control(S_MOTOR_LEFT); delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_LEFT; } // Rotate Right if(str_data[0]=='r') { Serial.println("Rotating to Right-stop..."); Rotator_Motor_Control(S_MOTOR_RIGHT); delay(RotaryEnc_Rotator_motor_delay.cntVal); rotator_state = S_ROTATE_RIGHT; } // if(str_data[0]=='a') { rotator_state = S_USER_OUT_A_TOGGLE; } if(str_data[0]=='b') { rotator_state = S_USER_OUT_B_TOGGLE; } if(str_data[0]=='c') { rotator_state = S_USER_OUT_C_TOGGLE; } if(str_data[0]=='d') { rotator_state = S_USER_OUT_D_TOGGLE; } if(str_data[0]=='h') { Serial_Help(); } if(str_data[0]=='w') { rotator_state = S_WRITE_CNT_EE; } if(str_data[0]=='i') { rotator_state = S_RESET_CNT; } // DEBUG if(str_data[0]=='D') { Serial.println("Debug toggle..."); debug = (debug + 1) % 3; } // Print inFo if(str_data[0]=='f') { Serial.println(""); Serial.println("Info:"); snprintf(str_report, 30, "Pulse-cnt-actual = %-5d", pulse_cnt); Serial.println(str_report); snprintf(str_report, 30, "Pulse-cnt-max = %-5d", pulse_cnt_max); Serial.println(str_report); snprintf(str_report, 30, "Az-L-stop = %-5d", RotaryEnc_Rotator_angle_deg_min.cntVal); Serial.println(str_report); snprintf(str_report, 30, "Az-R-stop = %-5d", RotaryEnc_Rotator_angle_deg_max.cntVal); Serial.println(str_report); snprintf(str_report, 30, "Sens-delay = %-5d", RotaryEnc_Rotator_motor_delay.cntVal); Serial.println(str_report); rotator_state = S_SHOW; } // Empty line if(str_data[0]==0) { Rotator_Motor_Control(S_MOTOR_OFF); RotaryEncPop(&RotaryEnc_Rotator_angle_deg_desired); rotator_state = S_SHOW; } } // newData == true } // void SerialRxWithEOL() { static byte ndx = 0; char endMarker = 13; // EOL //char endMarker = 'a'; // testing char rc; while (Serial.available() > 0 && newData == false) { rc = Serial.read(); if (rc != endMarker) { str_data[ndx] = rc; ndx++; if (ndx >= numChars) { ndx = numChars - 1; } } else { str_data[ndx] = '\0'; // terminate the string ndx = 0; newData = true; } } // while } // SerialRxWithEOL // ISR function excutes when push button at pin D2 is pressed void ISR_D2() { (rotation_dir==ROTATION_RIGHT) ? pulse_cnt++ : pulse_cnt--; } // Timer interrupt ISR void ISR_rotaryEncoderSampleInputs() { // uint32_t incr_val; // if (ISR_cnt != 255) ISR_cnt++; // SW debounce rotaryEnc_A = (rotaryEnc_A<<1 | (uint8_t)digitalRead(SW_A)) & 0x0F; // // Rising edge --> 0001 if (rotaryEnc_A == 0x01) { timeout_cnt = 0; disp_bg_timeout_cnt=0; rotaryEnc_B = digitalRead(SW_B); // Rotation speedup //(ISR_cnt < 50) ? cntIncrISR = 5 : cntIncrISR = 1; // if (rotaryEnc_B == 0) { (ISR_cnt < 50) ? RotaryEncISR.cntVal = RotaryEncISR.cntVal + 5 : RotaryEncISR.cntVal = RotaryEncISR.cntVal + 1; } else { (ISR_cnt < 50) ? RotaryEncISR.cntVal = RotaryEncISR.cntVal - 5 : RotaryEncISR.cntVal = RotaryEncISR.cntVal - 1; } ISR_cnt = 0; // This can be moved outside ISR too if (RotaryEncISR.cntVal > 0x1FFFFFFF) { // overflow of unsigned if (RotaryEncISR_cntValOld == RotaryEncISR.cntMin) cntValISR_Under_Over_Flag = 1; RotaryEncISR.cntVal = RotaryEncISR.cntMin; } else if (RotaryEncISR.cntVal > RotaryEncISR.cntMax) { if (RotaryEncISR_cntValOld == RotaryEncISR.cntMax) cntValISR_Under_Over_Flag = 2; RotaryEncISR.cntVal = RotaryEncISR.cntMax; } else { cntValISR_Under_Over_Flag = 0; } // RotaryEncISR_cntValOld = RotaryEncISR.cntVal; } }