Arduino CLI build system with HAL-based test architecture
Build and upload tool (arduino-build.sh): - Compile, upload, and monitor via arduino-cli - Device discovery with USB ID identification (--devices) - Persistent reconnecting serial monitor (--watch) - Split compile/upload workflow (--verify, --upload-only) - First-time setup wizard (--setup) - Comprehensive --help with troubleshooting and RedBoard specs Testable application architecture: - Hardware abstraction layer (lib/hal/) decouples logic from Arduino API - Google Mock HAL for unit tests (exact call verification) - Simulated HAL for system tests (GPIO state, virtual clock, I2C devices) - Example I2C temperature sensor simulator (TMP102) - Host-side test suite via CMake + Google Test (21 tests) Example sketch: - blink/ -- LED blink with button-controlled speed, wired through HAL
This commit is contained in:
140
test/test_i2c_example.cpp
Normal file
140
test/test_i2c_example.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "sim_hal.h"
|
||||
|
||||
// ============================================================================
|
||||
// Example: I2C Temperature Sensor Simulator
|
||||
//
|
||||
// Demonstrates how to mock an I2C device. This simulates a simple
|
||||
// temperature sensor at address 0x48 (like a TMP102 or LM75).
|
||||
//
|
||||
// Protocol (simplified):
|
||||
// Write register pointer (1 byte), then read 2 bytes back.
|
||||
// Register 0x00 = temperature (MSB:LSB, 12-bit, 0.0625 deg/LSB)
|
||||
// Register 0x01 = config register
|
||||
// ============================================================================
|
||||
|
||||
class TempSensorSim : public I2cDeviceSim {
|
||||
public:
|
||||
static const uint8_t ADDR = 0x48;
|
||||
static const uint8_t REG_TEMP = 0x00;
|
||||
static const uint8_t REG_CONFIG = 0x01;
|
||||
|
||||
TempSensorSim() : reg_pointer_(0), temp_raw_(0) {}
|
||||
|
||||
// Set temperature in degrees C (converted to 12-bit raw)
|
||||
void setTemperature(float deg_c) {
|
||||
temp_raw_ = static_cast<int16_t>(deg_c / 0.0625f);
|
||||
}
|
||||
|
||||
// I2cDeviceSim interface
|
||||
void onReceive(const uint8_t* data, size_t len) override {
|
||||
if (len >= 1) {
|
||||
reg_pointer_ = data[0];
|
||||
}
|
||||
}
|
||||
|
||||
size_t onRequest(uint8_t* buf, size_t max_len) override {
|
||||
if (max_len < 2) return 0;
|
||||
|
||||
if (reg_pointer_ == REG_TEMP) {
|
||||
// 12-bit left-aligned in 16 bits
|
||||
int16_t raw = temp_raw_ << 4;
|
||||
buf[0] = static_cast<uint8_t>((raw >> 8) & 0xFF);
|
||||
buf[1] = static_cast<uint8_t>(raw & 0xFF);
|
||||
return 2;
|
||||
}
|
||||
if (reg_pointer_ == REG_CONFIG) {
|
||||
buf[0] = 0x60; // default config
|
||||
buf[1] = 0xA0;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t reg_pointer_;
|
||||
int16_t temp_raw_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper: read temperature the way real Arduino code would
|
||||
// -----------------------------------------------------------------------
|
||||
static float readTemperature(Hal* hal, uint8_t addr) {
|
||||
// Set register pointer to 0x00 (temperature)
|
||||
hal->i2cBeginTransmission(addr);
|
||||
hal->i2cWrite(TempSensorSim::REG_TEMP);
|
||||
hal->i2cEndTransmission();
|
||||
|
||||
// Read 2 bytes
|
||||
uint8_t count = hal->i2cRequestFrom(addr, 2);
|
||||
if (count < 2) return -999.0f;
|
||||
|
||||
uint8_t msb = static_cast<uint8_t>(hal->i2cRead());
|
||||
uint8_t lsb = static_cast<uint8_t>(hal->i2cRead());
|
||||
|
||||
// Convert 12-bit left-aligned to temperature
|
||||
int16_t raw = (static_cast<int16_t>(msb) << 8) | lsb;
|
||||
raw >>= 4; // right-align the 12 bits
|
||||
return raw * 0.0625f;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tests
|
||||
// ============================================================================
|
||||
|
||||
class I2cTempSensorTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
sensor_.setTemperature(25.0f);
|
||||
sim_.attachI2cDevice(TempSensorSim::ADDR, &sensor_);
|
||||
sim_.i2cBegin();
|
||||
}
|
||||
|
||||
SimHal sim_;
|
||||
TempSensorSim sensor_;
|
||||
};
|
||||
|
||||
TEST_F(I2cTempSensorTest, ReadsRoomTemperature) {
|
||||
sensor_.setTemperature(25.0f);
|
||||
float temp = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
EXPECT_NEAR(temp, 25.0f, 0.1f);
|
||||
}
|
||||
|
||||
TEST_F(I2cTempSensorTest, ReadsFreezing) {
|
||||
sensor_.setTemperature(0.0f);
|
||||
float temp = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
EXPECT_NEAR(temp, 0.0f, 0.1f);
|
||||
}
|
||||
|
||||
TEST_F(I2cTempSensorTest, ReadsNegativeTemperature) {
|
||||
sensor_.setTemperature(-10.5f);
|
||||
float temp = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
EXPECT_NEAR(temp, -10.5f, 0.1f);
|
||||
}
|
||||
|
||||
TEST_F(I2cTempSensorTest, ReadsHighTemperature) {
|
||||
sensor_.setTemperature(85.0f);
|
||||
float temp = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
EXPECT_NEAR(temp, 85.0f, 0.1f);
|
||||
}
|
||||
|
||||
TEST_F(I2cTempSensorTest, UnregisteredDeviceReturnsNack) {
|
||||
// Try to talk to a device that does not exist
|
||||
sim_.i2cBeginTransmission(0x50);
|
||||
sim_.i2cWrite(0x00);
|
||||
uint8_t result = sim_.i2cEndTransmission();
|
||||
EXPECT_NE(result, 0); // non-zero = error
|
||||
}
|
||||
|
||||
TEST_F(I2cTempSensorTest, TemperatureUpdatesLive) {
|
||||
sensor_.setTemperature(20.0f);
|
||||
float t1 = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
|
||||
sensor_.setTemperature(30.0f);
|
||||
float t2 = readTemperature(&sim_, TempSensorSim::ADDR);
|
||||
|
||||
EXPECT_NEAR(t1, 20.0f, 0.1f);
|
||||
EXPECT_NEAR(t2, 30.0f, 0.1f);
|
||||
}
|
||||
Reference in New Issue
Block a user