/*
=====================================================================
ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
=====================================================================
Version: 1.3.0
Author: [Your Name]
Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
DESCRIPTION
---------------------------------------------------------------------
This firmware enables external control and monitoring of Arduino
digital pins through a serial interface. It is designed for
integration with Python or similar host software.
The firmware supports dynamic pin-mode configuration, runtime
output control, input monitoring, duration measurement, and pin-map
query. All commands and responses use ASCII text terminated by '\n'.
=====================================================================
ASCII COMMAND REFERENCE
=====================================================================
+----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
+----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
| | | | |
| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
| | | | |
| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
| | | | |
| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
| | | | |
| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
| | | | Automatically reverts afterwards. |
| | | | |
| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
| | | | |
| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
| | | | |
| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
| | | | |
| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
| | | | |
| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
+----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
=====================================================================
OPERATIONAL NOTES
---------------------------------------------------------------------
- Baud rate: 115200
- Line termination: newline ('\n')
- States are HIGH or LOW
- Durations in milliseconds
- All commands and responses are ASCII
=====================================================================
*/
#include <Arduino.h>
// ------------------------------------------------------------------
// Board-specific pin range detection
// ------------------------------------------------------------------
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
const int FIRST_PIN = 2;
const int LAST_PIN = 13;
#elif defined(ARDUINO_AVR_MEGA2560)
const int FIRST_PIN = 2;
const int LAST_PIN = 53;
#elif defined(ARDUINO_AVR_LEONARDO)
const int FIRST_PIN = 2;
const int LAST_PIN = 13;
#else
const int FIRST_PIN = 2;
const int LAST_PIN = 13;
#endif
const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
// ------------------------------------------------------------------
// Dynamic role and state tracking
// ------------------------------------------------------------------
bool isInput[NUM_PINS];
bool isOutput[NUM_PINS];
bool watching[NUM_PINS];
unsigned long lastChangeTime[NUM_PINS];
int lastState[NUM_PINS];
// ------------------------------------------------------------------
// Setup
// ------------------------------------------------------------------
void setup() {
Serial.begin(115200);
// Default: all usable pins configured as OUTPUT and LOW
for (int i = 0; i < NUM_PINS; i++) {
int pin = FIRST_PIN + i;
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
isInput[i] = false;
isOutput[i] = true;
watching[i] = false;
lastState[i] = LOW;
lastChangeTime[i] = millis();
}
Serial.println("READY");
}
// ------------------------------------------------------------------
// Main loop
// ------------------------------------------------------------------
void loop() {
handleSerial();
monitorWatchedPins();
}
// ------------------------------------------------------------------
// Serial command processing
// ------------------------------------------------------------------
void handleSerial() {
static String inputString = "";
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
inputString.trim();
processCommand(inputString);
inputString = "";
} else {
inputString += c;
}
}
}
// ------------------------------------------------------------------
// Command dispatcher
// ------------------------------------------------------------------
void processCommand(String cmd) {
if (cmd == "GET_VERSION") {
Serial.println("VERSION:1.3.0");
} else if (cmd == "GET_PINMAP") {
handleGetPinmap();
} else if (cmd.startsWith("SET_MODE")) {
handleSetMode(cmd);
} else if (cmd.startsWith("SET_DEFAULT")) {
handleSetDefault(cmd);
} else if (cmd.startsWith("SET_FOR")) {
handleSetFor(cmd);
} else if (cmd.startsWith("WATCH")) {
handleWatch(cmd);
} else if (cmd.startsWith("GET_STATE")) {
handleGetState(cmd);
} else if (cmd.startsWith("GET_DURATION")) {
handleGetDuration(cmd);
} else if (cmd.startsWith("WAIT_FOR")) {
handleWaitFor(cmd);
} else {
Serial.println("ERROR:UNKNOWN_COMMAND");
}
}
// ------------------------------------------------------------------
// Command handlers
// ------------------------------------------------------------------
// --- SET_MODE:PIN:MODE ------------------------------------------------
void handleSetMode(String cmd) {
int first = cmd.indexOf(':');
int second = cmd.indexOf(':', first + 1);
if (first == -1 || second == -1) return;
int pin = cmd.substring(first + 1, second).toInt();
String mode = cmd.substring(second + 1);
if (pin < FIRST_PIN || pin > LAST_PIN) {
Serial.println("ERROR:INVALID_PIN");
return;
}
int index = pin - FIRST_PIN;
if (mode == "INPUT") {
pinMode(pin, INPUT);
isInput[index] = true;
isOutput[index] = false;
} else if (mode == "OUTPUT") {
pinMode(pin, OUTPUT);
isInput[index] = false;
isOutput[index] = true;
} else {
Serial.println("ERROR:INVALID_MODE");
return;
}
Serial.print("ACK:SET_MODE:");
Serial.print(pin);
Serial.print(":");
Serial.println(mode);
}
// --- GET_PINMAP -------------------------------------------------------
void handleGetPinmap() {
String response = "PINMAP:INPUT:";
bool firstInput = true;
for (int i = 0; i < NUM_PINS; i++) {
if (isInput[i]) {
if (!firstInput) response += ",";
response += String(FIRST_PIN + i);
firstInput = false;
}
}
response += ";OUTPUT:";
bool firstOutput = true;
for (int i = 0; i < NUM_PINS; i++) {
if (isOutput[i]) {
if (!firstOutput) response += ",";
response += String(FIRST_PIN + i);
firstOutput = false;
}
}
Serial.println(response);
}
// --- SET_DEFAULT:PIN:STATE --------------------------------------------
void handleSetDefault(String cmd) {
int first = cmd.indexOf(':');
int second = cmd.indexOf(':', first + 1);
if (first == -1 || second == -1) return;
int pin = cmd.substring(first + 1, second).toInt();
String stateStr = cmd.substring(second + 1);
bool state = (stateStr == "HIGH");
digitalWrite(pin, state ? HIGH : LOW);
Serial.print("ACK:SET_DEFAULT:");
Serial.print(pin);
Serial.print(":");
Serial.println(state ? "HIGH" : "LOW");
}
// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
void handleSetFor(String cmd) {
int first = cmd.indexOf(':');
int second = cmd.indexOf(':', first + 1);
int third = cmd.indexOf(':', second + 1);
if (first == -1 || second == -1 || third == -1) return;
int pin = cmd.substring(first + 1, second).toInt();
String stateStr = cmd.substring(second + 1, third);
unsigned long duration = cmd.substring(third + 1).toInt();
bool state = (stateStr == "HIGH");
digitalWrite(pin, state ? HIGH : LOW);
delay(duration);
digitalWrite(pin, state ? LOW : HIGH);
Serial.print("ACK:SET_FOR:");
Serial.print(pin);
Serial.print(":");
Serial.print(state ? "HIGH" : "LOW");
Serial.print(":");
Serial.println(duration);
}
// --- WATCH:PIN ---------------------------------------------------------
void handleWatch(String cmd) {
int first = cmd.indexOf(':');
if (first == -1) return;
int pin = cmd.substring(first + 1).toInt();
int index = pin - FIRST_PIN;
if (index < 0 || index >= NUM_PINS || !isInput[index]) {
Serial.println("ERROR:INVALID_PIN");
return;
}
watching[index] = true;
Serial.print("ACK:WATCH:");
Serial.println(pin);
}
// --- GET_STATE:PIN -----------------------------------------------------
void handleGetState(String cmd) {
int first = cmd.indexOf(':');
if (first == -1) return;
int pin = cmd.substring(first + 1).toInt();
int state = digitalRead(pin);
Serial.print("STATE:");
Serial.print(pin);
Serial.print(":");
Serial.println(state == HIGH ? "HIGH" : "LOW");
}
// --- GET_DURATION:PIN --------------------------------------------------
void handleGetDuration(String cmd) {
int first = cmd.indexOf(':');
if (first == -1) return;
int pin = cmd.substring(first + 1).toInt();
int index = pin - FIRST_PIN;
if (index < 0 || index >= NUM_PINS) return;
unsigned long duration = millis() - lastChangeTime[index];
Serial.print("DURATION:");
Serial.print(pin);
Serial.print(":");
Serial.println(duration);
}
// --- WAIT_FOR:PIN:STATE ------------------------------------------------
void handleWaitFor(String cmd) {
int first = cmd.indexOf(':');
int second = cmd.indexOf(':', first + 1);
if (first == -1 || second == -1) return;
int pin = cmd.substring(first + 1, second).toInt();
String targetStateStr = cmd.substring(second + 1);
bool targetState = (targetStateStr == "HIGH");
unsigned long startTime = millis();
while (digitalRead(pin) != targetState) {
delay(1);
}
unsigned long duration = millis() - startTime;
Serial.print("WAIT_RESULT:");
Serial.print(pin);
Serial.print(":");
Serial.print(targetState ? "HIGH" : "LOW");
Serial.print(":");
Serial.println(duration);
}
// ------------------------------------------------------------------
// Watch monitoring
// ------------------------------------------------------------------
void monitorWatchedPins() {
for (int i = 0; i < NUM_PINS; i++) {
if (watching[i] && isInput[i]) {
int pin = FIRST_PIN + i;
int currentState = digitalRead(pin);
if (currentState != lastState[i]) {
unsigned long now = millis();
unsigned long duration = now - lastChangeTime[i];
lastChangeTime[i] = now;
lastState[i] = currentState;
Serial.print("CHANGE:");
Serial.print(pin);
Serial.print(":");
Serial.print(currentState == HIGH ? "HIGH" : "LOW");
Serial.print(":");
Serial.println(duration);
}
}
}
}