// ATtiny85 Servo Control with UART Communication // This code runs on ATtiny85 and communicates with Arduino Uno // It receives delay values via UART and controls a servo for clip release #include // Smaller servo library for ATtiny85 #include // For storing delay values persistently #include "ATtinyRxUART.h" // Custom UART library for ATtiny85 // #include //commenting-to-check-for-interference // TinyDebugSerial debugSerial;//yDebugSerial uses PB3 (Arduino pin 3) as its TX (transmit) pin internally. // Pin definitions for ATtiny85 const int connection_pin = 2; // PB2 - Connection status from Arduino const int servoPin = 1; // PB1 - Servo control pin const int statusPin = 4; // PB4 - Status LED/indicator const int rxPin = 0; // PB0 - UART RX pin (receives from Arduino) // EEPROM storage const int eepromDelayAddr = 0; // Address to store delay value const int eepromValidAddr = 4; // Address to store validation flag // Communication protocol constants (my own)(must match other MCU code!!!!!) const byte CMD_SET_DELAY = 0xAA; const byte CMD_TRIGGER = 0xBB; const byte CMD_STATUS = 0xCC; const byte ACK_RECEIVED = 0xDD; const byte EEPROM_VALID_FLAG = 0x42; // rand number to validate EEPROM data // Global variables Servo8Bit clipServo; float delaySeconds = 2.0; // Default delay in seconds float lastStoredDelay = -1.0; // Last delay stored in EEPROM bool hasTriggered = false; // Prevents multiple triggers bool isConnected = false; // Connection status with Arduino // Function to validate and read delay from EEPROM void loadDelayFromEEPROM() { byte validFlag = EEPROM.read(eepromValidAddr); if (validFlag == EEPROM_VALID_FLAG) { // EEPROM contains valid data, read the stored delay EEPROM.get(eepromDelayAddr, delaySeconds); // Validate the delay value is reasonable (between 0.1 and 50 seconds) if (delaySeconds < 0.1 || delaySeconds > 50.0) { delaySeconds = 2.0; // Reset to default if invalid } } else { // EEPROM is empty or invalid, use default delaySeconds = 2.0; } } // Function to save delay to EEPROM with validation void saveDelayToEEPROM(float newDelay) { // Only write to EEPROM if the value has changed (EEPROM has limited write cycles) if (newDelay != lastStoredDelay) { EEPROM.put(eepromDelayAddr, newDelay); EEPROM.write(eepromValidAddr, EEPROM_VALID_FLAG); lastStoredDelay = newDelay; } } // Function to process incoming UART data bool processUARTData() { if (RxUART_available() > 0) { byte command = RxUART_read(); // Indicate we're receiving data digitalWrite(statusPin, HIGH); if (command == CMD_SET_DELAY) { // Arduino is sending a new delay value // Wait for the complete float value (4 bytes) plus checksum (1 byte) unsigned long timeout = millis() + 500; // 500ms timeout while (RxUART_available() < 5 && millis() < timeout) { // Wait for all bytes to arrive } if (RxUART_available() >= 5) { // Read the float value byte by byte byte floatBytes[4]; for (int i = 0; i < 4; i++) { floatBytes[i] = RxUART_read(); } // Read the checksum byte receivedChecksum = RxUART_read(); // Calculate expected checksum byte calculatedChecksum = 0; for (int i = 0; i < 4; i++) { calculatedChecksum ^= floatBytes[i]; } // Verify checksum if (receivedChecksum == calculatedChecksum) { // Checksum is valid, extract the float value float newDelay; memcpy(&newDelay, floatBytes, sizeof(float)); // Validate the delay value if (newDelay >= 0.1 && newDelay <= 50.0) { delaySeconds = newDelay; saveDelayToEEPROM(delaySeconds); // Send acknowledgment back to Arduino // debugSerial.write(ACK_RECEIVED); // Flash status LED to indicate successful programming for (int i = 0; i < 3; i++) { digitalWrite(statusPin, HIGH); delay(50); digitalWrite(statusPin, LOW); delay(50); } return true; } } } } else if (command == CMD_TRIGGER) { // Arduino is requesting immediate trigger if (!hasTriggered) { triggerServo(); return true; } } else if (command == CMD_STATUS) { // Arduino is requesting status - send back current delay byte* delayBytes = (byte*)&delaySeconds; for (int i = 0; i < sizeof(float); i++) { // debugSerial.write(delayBytes[i]); } return true; } digitalWrite(statusPin, LOW); } return false; } // Function to trigger the servo void triggerServo() { if (!hasTriggered) { // Move servo to release position clipServo.attach(servoPin); clipServo.write(180); hasTriggered = true; // Keep status LED on to indicate triggered state digitalWrite(statusPin, HIGH); } } // Function to check connection status and handle disconnection void checkConnectionStatus() { bool currentConnectionState = digitalRead(connection_pin); if (currentConnectionState != isConnected) { // Connection state has changed if (currentConnectionState == LOW && isConnected == true) { // Arduino has disconnected (pulled pin LOW) // Wait for the calculated delay then trigger servo // Add a small debounce delay to ensure it's a real disconnection delay(50); if (digitalRead(connection_pin) == LOW) { // Confirmed disconnection - start the countdown unsigned long delayMillis = (unsigned long)(delaySeconds * 1000); delay(delayMillis); // Trigger the servo after the delay triggerServo(); } } isConnected = currentConnectionState; } } void setup() { // Initialize UART communication RxUART_Begin(); // Initialize RX UART on pin 0 // Configure pins pinMode(connection_pin, INPUT_PULLUP); // Connection status from Arduino pinMode(statusPin, OUTPUT); // Status indicator pinMode(servoPin, OUTPUT); // Servo control // Initialize servo //clipServo.attach(servoPin); //clipServo.write(0); // Start at closed position // Load stored delay from EEPROM loadDelayFromEEPROM(); // Initialize connection status isConnected = digitalRead(connection_pin); // Brief startup indication digitalWrite(statusPin, HIGH); delay(200); digitalWrite(statusPin, LOW); delay(100); digitalWrite(statusPin, HIGH); delay(200); digitalWrite(statusPin, LOW); // System is ready // debugSerial.begin(9600); // debugSerial.println("Ready to send data"); } void loop() { // Process any incoming UART commands from Arduino processUARTData(); // Check connection status and handle disconnection trigger checkConnectionStatus(); // Small delay to prevent overwhelming the CPU delay(10); }