/* * mock_arduino.cpp -- Implementation of the Arduino API mock. * * This file provides function bodies for all Arduino core functions * and the test control API. Linked into the test executable via cmake. * * Generated by Anvil -- https://github.com/nexus-workshops/anvil */ #include "mock_arduino.h" #include #include #include #include #include // ============================================================================ // Global state // ============================================================================ MockArduinoState _mock_arduino; MockSerial Serial; // Wire mock (defined in Wire.h shim) #include "Wire.h" MockWire Wire; // SPI mock (defined in SPI.h shim) #include "SPI.h" MockSPI SPI; // ============================================================================ // Test control API // ============================================================================ void mock_arduino_reset() { memset(_mock_arduino.pin_modes, 0, sizeof(_mock_arduino.pin_modes)); memset(_mock_arduino.pin_digital, 0, sizeof(_mock_arduino.pin_digital)); memset(_mock_arduino.pin_analog, 0, sizeof(_mock_arduino.pin_analog)); memset(_mock_arduino.pin_pwm, 0, sizeof(_mock_arduino.pin_pwm)); _mock_arduino.millis_value = 0; _mock_arduino.micros_value = 0; _mock_arduino.serial_output.clear(); _mock_arduino.serial_input.clear(); _mock_arduino.serial_baud = 0; _mock_arduino.serial_begun = false; for (int i = 0; i < 8; ++i) { _mock_arduino.interrupts[i].callback = nullptr; _mock_arduino.interrupts[i].mode = 0; _mock_arduino.interrupts[i].attached = false; } _mock_arduino.interrupts_enabled = true; _mock_arduino.gpio_log.clear(); _mock_arduino.delay_log.clear(); } void mock_arduino_advance_millis(unsigned long ms) { _mock_arduino.millis_value += ms; _mock_arduino.micros_value += ms * 1000; } void mock_arduino_set_millis(unsigned long ms) { _mock_arduino.millis_value = ms; _mock_arduino.micros_value = ms * 1000; } void mock_arduino_set_digital(uint8_t pin, int value) { if (pin < MOCK_ARDUINO_MAX_PINS) { _mock_arduino.pin_digital[pin] = value; } } void mock_arduino_set_analog(uint8_t pin, int value) { if (pin < MOCK_ARDUINO_MAX_PINS) { _mock_arduino.pin_analog[pin] = value; } } int mock_arduino_get_digital(uint8_t pin) { if (pin < MOCK_ARDUINO_MAX_PINS) return _mock_arduino.pin_digital[pin]; return LOW; } int mock_arduino_get_pwm(uint8_t pin) { if (pin < MOCK_ARDUINO_MAX_PINS) return _mock_arduino.pin_pwm[pin]; return 0; } uint8_t mock_arduino_get_pin_mode(uint8_t pin) { if (pin < MOCK_ARDUINO_MAX_PINS) return _mock_arduino.pin_modes[pin]; return 0; } const std::string& mock_arduino_serial_output() { return _mock_arduino.serial_output; } void mock_arduino_inject_serial(const std::string& data) { for (char c : data) { _mock_arduino.serial_input.push_back(static_cast(c)); } } void mock_arduino_clear_serial() { _mock_arduino.serial_output.clear(); _mock_arduino.serial_input.clear(); } const std::vector& mock_arduino_gpio_log() { return _mock_arduino.gpio_log; } void mock_arduino_clear_gpio_log() { _mock_arduino.gpio_log.clear(); } int mock_arduino_count_writes(uint8_t pin, int value) { int count = 0; for (const auto& e : _mock_arduino.gpio_log) { if (e.pin == pin && e.value == value && !e.is_analog) ++count; } return count; } // ============================================================================ // Arduino digital I/O // ============================================================================ void pinMode(uint8_t pin, uint8_t mode) { if (pin < MOCK_ARDUINO_MAX_PINS) { _mock_arduino.pin_modes[pin] = mode; if (mode == INPUT_PULLUP) { _mock_arduino.pin_digital[pin] = HIGH; } } } void digitalWrite(uint8_t pin, uint8_t val) { if (pin < MOCK_ARDUINO_MAX_PINS) { _mock_arduino.pin_digital[pin] = val; _mock_arduino.gpio_log.push_back({ _mock_arduino.millis_value, pin, val, false }); } } int digitalRead(uint8_t pin) { if (pin < MOCK_ARDUINO_MAX_PINS) return _mock_arduino.pin_digital[pin]; return LOW; } // ============================================================================ // Arduino analog I/O // ============================================================================ int analogRead(uint8_t pin) { if (pin < MOCK_ARDUINO_MAX_PINS) return _mock_arduino.pin_analog[pin]; return 0; } void analogWrite(uint8_t pin, int val) { if (pin < MOCK_ARDUINO_MAX_PINS) { _mock_arduino.pin_pwm[pin] = val; _mock_arduino.pin_digital[pin] = (val > 0) ? HIGH : LOW; _mock_arduino.gpio_log.push_back({ _mock_arduino.millis_value, pin, val, true }); } } // ============================================================================ // Arduino timing // ============================================================================ unsigned long millis() { return _mock_arduino.millis_value; } unsigned long micros() { return _mock_arduino.micros_value; } void delay(unsigned long ms) { _mock_arduino.delay_log.push_back(ms); _mock_arduino.millis_value += ms; _mock_arduino.micros_value += ms * 1000; } void delayMicroseconds(unsigned long us) { _mock_arduino.micros_value += us; } // ============================================================================ // Arduino math helpers // ============================================================================ long map(long value, long fromLow, long fromHigh, long toLow, long toHigh) { if (fromHigh == fromLow) return toLow; return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } long constrain(long value, long low, long high) { if (value < low) return low; if (value > high) return high; return value; } static unsigned long _random_seed = 1; long random(long max) { if (max <= 0) return 0; _random_seed = _random_seed * 1103515245 + 12345; return (long)((_random_seed >> 16) % (unsigned long)max); } long random(long min, long max) { if (max <= min) return min; return random(max - min) + min; } void randomSeed(unsigned long seed) { _random_seed = seed; } // ============================================================================ // Interrupts // ============================================================================ void attachInterrupt(uint8_t interrupt, void (*callback)(), int mode) { if (interrupt < 8) { _mock_arduino.interrupts[interrupt].callback = callback; _mock_arduino.interrupts[interrupt].mode = mode; _mock_arduino.interrupts[interrupt].attached = true; } } void detachInterrupt(uint8_t interrupt) { if (interrupt < 8) { _mock_arduino.interrupts[interrupt].callback = nullptr; _mock_arduino.interrupts[interrupt].attached = false; } } void noInterrupts() { _mock_arduino.interrupts_enabled = false; } void interrupts() { _mock_arduino.interrupts_enabled = true; } uint8_t digitalPinToInterrupt(uint8_t pin) { // Uno/Nano: pin 2 = INT0, pin 3 = INT1 switch (pin) { case 2: return 0; case 3: return 1; default: return 255; // NOT_AN_INTERRUPT } } // ============================================================================ // MockSerial implementation // ============================================================================ void MockSerial::begin(unsigned long baud) { _mock_arduino.serial_baud = baud; _mock_arduino.serial_begun = true; } void MockSerial::end() { _mock_arduino.serial_begun = false; } size_t MockSerial::print(const char* str) { if (!str) return 0; _mock_arduino.serial_output += str; return strlen(str); } size_t MockSerial::print(char c) { _mock_arduino.serial_output += c; return 1; } static std::string long_to_string(long val, int base) { if (base == 16) { char buf[32]; snprintf(buf, sizeof(buf), "%lx", val); return buf; } if (base == 8) { char buf[32]; snprintf(buf, sizeof(buf), "%lo", val); return buf; } if (base == 2) { if (val == 0) return "0"; std::string result; unsigned long v = (unsigned long)val; while (v > 0) { result = (char)('0' + (v & 1)) + result; v >>= 1; } return result; } return std::to_string(val); } static std::string ulong_to_string(unsigned long val, int base) { return long_to_string((long)val, base); } size_t MockSerial::print(int val, int base) { std::string s = long_to_string(val, base); _mock_arduino.serial_output += s; return s.size(); } size_t MockSerial::print(unsigned int val, int base) { std::string s = ulong_to_string(val, base); _mock_arduino.serial_output += s; return s.size(); } size_t MockSerial::print(long val, int base) { std::string s = long_to_string(val, base); _mock_arduino.serial_output += s; return s.size(); } size_t MockSerial::print(unsigned long val, int base) { std::string s = ulong_to_string(val, base); _mock_arduino.serial_output += s; return s.size(); } size_t MockSerial::print(double val, int decimals) { char buf[64]; snprintf(buf, sizeof(buf), "%.*f", decimals, val); _mock_arduino.serial_output += buf; return strlen(buf); } size_t MockSerial::print(const std::string& str) { _mock_arduino.serial_output += str; return str.size(); } size_t MockSerial::println(const char* str) { size_t n = print(str); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(char c) { size_t n = print(c); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(int val, int base) { size_t n = print(val, base); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(unsigned int val, int base) { size_t n = print(val, base); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(long val, int base) { size_t n = print(val, base); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(unsigned long val, int base) { size_t n = print(val, base); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(double val, int decimals) { size_t n = print(val, decimals); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::println(const std::string& str) { size_t n = print(str); _mock_arduino.serial_output += "\n"; return n + 1; } size_t MockSerial::write(uint8_t b) { _mock_arduino.serial_output += static_cast(b); return 1; } size_t MockSerial::write(const uint8_t* buf, size_t len) { for (size_t i = 0; i < len; ++i) { _mock_arduino.serial_output += static_cast(buf[i]); } return len; } int MockSerial::available() { return static_cast(_mock_arduino.serial_input.size()); } int MockSerial::read() { if (_mock_arduino.serial_input.empty()) return -1; int c = _mock_arduino.serial_input.front(); _mock_arduino.serial_input.erase(_mock_arduino.serial_input.begin()); return c; } int MockSerial::peek() { if (_mock_arduino.serial_input.empty()) return -1; return _mock_arduino.serial_input.front(); } void MockSerial::flush() { // No-op in mock (real Arduino waits for TX buffer to drain) } // ============================================================================ // String class implementation // ============================================================================ String::String(int val, int base) { data_ = long_to_string(val, base); } String::String(unsigned int val, int base) { data_ = ulong_to_string(val, base); } String::String(long val, int base) { data_ = long_to_string(val, base); } String::String(unsigned long val, int base) { data_ = ulong_to_string(val, base); } String::String(double val, int decimals) { char buf[64]; snprintf(buf, sizeof(buf), "%.*f", decimals, val); data_ = buf; } char String::charAt(unsigned int index) const { if (index < data_.size()) return data_[index]; return 0; } void String::setCharAt(unsigned int index, char c) { if (index < data_.size()) data_[index] = c; } bool String::equalsIgnoreCase(const String& other) const { if (data_.size() != other.data_.size()) return false; for (size_t i = 0; i < data_.size(); ++i) { if (tolower((unsigned char)data_[i]) != tolower((unsigned char)other.data_[i])) return false; } return true; } int String::compareTo(const String& other) const { return data_.compare(other.data_); } int String::indexOf(char ch, unsigned int fromIndex) const { auto pos = data_.find(ch, fromIndex); return (pos == std::string::npos) ? -1 : (int)pos; } int String::indexOf(const String& str, unsigned int fromIndex) const { auto pos = data_.find(str.data_, fromIndex); return (pos == std::string::npos) ? -1 : (int)pos; } int String::lastIndexOf(char ch) const { auto pos = data_.rfind(ch); return (pos == std::string::npos) ? -1 : (int)pos; } String String::substring(unsigned int from, unsigned int to) const { if (from >= data_.size()) return String(); if (to > data_.size()) to = (unsigned int)data_.size(); return String(data_.substr(from, to - from)); } void String::toLowerCase() { for (char& c : data_) c = (char)tolower((unsigned char)c); } void String::toUpperCase() { for (char& c : data_) c = (char)toupper((unsigned char)c); } void String::trim() { size_t start = data_.find_first_not_of(" \t\r\n"); size_t end = data_.find_last_not_of(" \t\r\n"); if (start == std::string::npos) { data_.clear(); } else { data_ = data_.substr(start, end - start + 1); } } void String::replace(const String& from, const String& to) { size_t pos = 0; while ((pos = data_.find(from.data_, pos)) != std::string::npos) { data_.replace(pos, from.data_.size(), to.data_); pos += to.data_.size(); } } void String::remove(unsigned int index, unsigned int count) { if (index < data_.size()) { data_.erase(index, count); } } long String::toInt() const { return atol(data_.c_str()); } float String::toFloat() const { return (float)atof(data_.c_str()); } double String::toDouble() const { return atof(data_.c_str()); } String& String::concat(const String& other) { data_ += other.data_; return *this; } String& String::concat(const char* s) { if (s) data_ += s; return *this; } String& String::concat(char c) { data_ += c; return *this; } String& String::concat(int val) { data_ += std::to_string(val); return *this; } String& String::concat(unsigned long val) { data_ += std::to_string(val); return *this; } String String::operator+(const String& rhs) const { return String(data_ + rhs.data_); } char String::operator[](unsigned int index) const { if (index < data_.size()) return data_[index]; return 0; } char& String::operator[](unsigned int index) { return data_[index]; }