HTTP client library for testing hardware using ESP boards (NodeMCU, WeMos, ESP32C6)
Project description
METF Python Client
HTTP client library for testing hardware using ESP boards (NodeMCU, WeMos, ESP32C6)
Test your embedded hardware through HTTP requests. Perfect for Continuous Integration in hardware development.
Features
- Digital I/O Control: pinMode, digitalRead, digitalWrite operations
- I2C Communication: Full I2C master support (begin, ask, setClock, setClockStretchLimit)
- Serial Port Sniffing: Read and monitor serial output from connected devices
- RGB LED Control: Control RGB LEDs connected to ESP boards
- Multiple Board Support: NodeMCU, WeMos D1 Mini, ESP32C6 Super Mini
- Simple HTTP API: Test hardware from any language/platform that supports HTTP
- Hardware CI/CD: Integrate hardware tests into your continuous integration pipeline
Installation
pip install metf-python-client
Quick Start
1. Setup Hardware
- Connect your MCU/device to an ESP board (NodeMCU, WeMos, or ESP32C6)
- Flash METF firmware to the ESP board
- Connect ESP to your network (note its IP address)
2. Write Test Script
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import LED_BUILTIN_AUX, HIGH, LOW, OUTPUT
# Connect to ESP board
api = METFClient('192.168.1.100')
# Test connectivity
api.ping() # Returns 'pong'
# Control GPIO
pin = LED_BUILTIN_AUX
api.pinMode(pin, OUTPUT)
api.digitalWrite(pin, LOW)
assert api.digitalRead(pin) == LOW
api.delay(1000)
api.digitalWrite(pin, HIGH)
assert api.digitalRead(pin) == HIGH
API Reference
Connection
from metf_python_client import METFClient
# Connect to ESP board
api = METFClient(host='192.168.1.100', port=80, timeout=3.0)
# Test connection
api.ping() # Returns 'pong' if successful
Digital I/O
from metf_python_client.boards.nodemcu import D1, D2, D3, INPUT, OUTPUT, INPUT_PULLUP, HIGH, LOW
from metf_python_client import METFClient
api = METFClient("192.168.1.2") # nodemcu active point with METF firmware
# Set pin mode
api.pinMode(D1, OUTPUT)
api.pinMode(D2, INPUT)
api.pinMode(D3, INPUT_PULLUP)
# Write digital value
api.digitalWrite(D1, HIGH)
api.digitalWrite(D1, LOW)
# Read digital value
value = api.digitalRead(D2) # Returns 0 or 1
# Wait for specific value (with timeout)
button_pressed = api.wait_digital(D2, LOW, timeout=5.0)
if button_pressed:
print("Button was pressed within 5 seconds")
I2C Communication
from metf_python_client.boards.nodemcu import D3, D4
# Initialize I2C (default pins)
api.i2c_begin() # Use default SDA/SCL for your board
# OR initialize with custom pins
api.i2c_begin(sda=D3, scl=D4)
# Set clock speed
api.i2c_setClock(400000) # 400kHz
# Set clock stretch limit (ESP8266 specific)
api.i2c_setClockStretchLimit(1500)
# Send message and receive response
slave_address = 0x48
message = 'M' # Send byte 0x4D
response_length = 1
response = api.i2c_ask(slave_address, message, response_length)
print(f"Received: {ord(response[0])}")
# Flush I2C buffer
api.i2c_flush()
Working with Binary Data
from metf_python_client.utils import DataStruct
# Define structure format
fields = [
('version', 'B'), # unsigned char
('value_uint16', 'H'), # unsigned short
('value_uint32', 'L'), # unsigned long
]
# Calculate structure size
header_len = DataStruct.calcsize(fields)
# Read from I2C device
response = api.i2c_ask(slave_address, 'A', header_len)
# Parse binary data
header = DataStruct(fields, response)
print(f"Version: {header.version}")
print(f"Value 16: {header.value_uint16}")
print(f"Value 32: {header.value_uint32}")
Serial Port Sniffing
# Initialize serial port
api.serial_begin(baudrate=115200)
# Read available data
data = api.serial_read() # Returns string or empty string
if data:
print(f"Received: {data}")
# Read lines with timeout and parsing
lines = api.serial_readlines(
wait=5000, # Wait up to 5000ms
delimiter='\n', # Split by newline
prefix='00:' # Merge lines starting with this prefix
)
for line in lines:
print(f"Line: {line}")
# Flush serial buffer
api.serial_flush()
RGB LED Control
# Initialize RGB LED
api.rgb_begin()
# Set brightness (0-255)
api.rgb_brightness(128)
# Set color (hex string)
api.rgb_color('FF0000') # Red
api.rgb_color('00FF00') # Green
api.rgb_color('0000FF') # Blue
api.rgb_color('FFFFFF') # White
Utility Functions
# Blink LED
api.blynk(
pin=LED_BUILTIN,
duration=1000, # Duration in milliseconds
invert=False, # Invert logic (for active-low LEDs)
low=0, # Low value
high=1 # High value
)
# Delay
api.delay(1000) # Delay 1000ms (1 second)
Board Pin Definitions
Import board-specific pin mappings and constants:
NodeMCU
from metf_python_client.boards.nodemcu import (
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10,
LED_BUILTIN, LED_BUILTIN_AUX,
HIGH, LOW,
INPUT, OUTPUT, INPUT_PULLUP
)
WeMos D1 Mini
from metf_python_client.boards.wemos import (
D0, D1, D2, D3, D4, D5, D6, D7, D8,
LED_BUILTIN,
HIGH, LOW,
INPUT, OUTPUT, INPUT_PULLUP
)
ESP32C6 Super Mini
from metf_python_client.boards.esp32c6_super_mini import (
IO0, IO1, IO2, IO3, IO4, IO5, IO6, IO7, IO8, IO9, IO10,
IO11, IO12, IO13, IO15, IO18, IO19, IO20, IO21, IO22, IO23,
LED_BUILTIN,
HIGH, LOW,
INPUT, OUTPUT, INPUT_PULLUP
)
Examples
Complete examples are available in the examples directory:
Example 1: Blink LED
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import LED_BUILTIN_AUX, OUTPUT
api = METFClient('192.168.1.100')
api.pinMode(LED_BUILTIN_AUX, OUTPUT)
# Blink LED
api.blynk(LED_BUILTIN_AUX, duration=1000)
Example 2: Check Button Press
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import D5, INPUT_PULLUP, LOW
api = METFClient('192.168.1.100')
api.pinMode(D5, INPUT_PULLUP)
# Wait for button press (3 second timeout)
if api.wait_digital(D5, LOW, 3.0):
print("Button pressed!")
else:
print("Timeout - button not pressed")
Example 3: I2C Communication
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import D3, D4
api = METFClient('192.168.1.100')
api.i2c_begin(D3, D4)
# Send command and receive response
slave_address = 0x48
message = '\x00' # Read register 0x00
response = api.i2c_ask(slave_address, message, 2)
print(f"Received {len(response)} bytes")
Example 4: RGB LED
from metf_python_client import METFClient
api = METFClient('192.168.1.100')
api.rgb_begin()
api.rgb_brightness(100)
# Cycle through colors
colors = ['FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF']
for color in colors:
api.rgb_color(color)
api.delay(1000)
More examples:
- 01-Blynk.py - Blink onboard LED
- 02-CheckButton.py - Button press detection
- 03-BlynkUnitTest.py - Unit test for LED
- 04-ButtonUnitTest.py - Unit test for button
- 05-i2c.py - I2C communication
- 06-RGBLed.py - RGB LED control
Use Cases
Hardware CI/CD Pipeline
Integrate hardware tests into your continuous integration workflow:
import unittest
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import D1, D2, INPUT, OUTPUT, HIGH, LOW
class HardwareTests(unittest.TestCase):
def setUp(self):
self.api = METFClient('192.168.1.100')
def test_power_led(self):
"""Test that power LED is on"""
self.api.pinMode(D1, INPUT)
self.assertEqual(self.api.digitalRead(D1), HIGH)
def test_button_press(self):
"""Test button press detection"""
self.api.pinMode(D2, INPUT_PULLUP)
pressed = self.api.wait_digital(D2, LOW, timeout=5.0)
self.assertTrue(pressed, "Button was not pressed")
def test_i2c_sensor(self):
"""Test I2C sensor communication"""
self.api.i2c_begin()
response = self.api.i2c_ask(0x48, '\x00', 2)
self.assertEqual(len(response), 2, "Sensor not responding")
if __name__ == '__main__':
unittest.main()
Interactive Hardware Testing
Test hardware configurations interactively:
from metf_python_client import METFClient
def test_voltage_levels():
"""Test device at different voltage levels"""
voltages = [3.0, 3.3, 5.0]
api = METFClient('192.168.1.100')
for voltage in voltages:
input(f"Set power supply to {voltage}V and press Enter...")
api.i2c_begin()
response = api.i2c_ask(0x48, '\x00', 2)
if len(response) == 2:
print(f"✓ {voltage}V: PASS")
else:
print(f"✗ {voltage}V: FAIL")
test_voltage_levels()
Production Testing
Automated testing during manufacturing:
from metf_python_client import METFClient
from metf_python_client.boards.nodemcu import *
def production_test(board_id):
"""Run complete production test suite"""
api = METFClient('192.168.1.100')
results = {}
# Test 1: Power-on self-test
api.pinMode(LED_BUILTIN, OUTPUT)
api.digitalWrite(LED_BUILTIN, HIGH)
results['led'] = api.digitalRead(LED_BUILTIN) == HIGH
# Test 2: I2C sensors
api.i2c_begin()
try:
response = api.i2c_ask(0x48, '\x00', 2)
results['sensor'] = len(response) == 2
except:
results['sensor'] = False
# Test 3: Serial communication
api.serial_begin(115200)
api.serial_flush()
lines = api.serial_readlines(wait=2000, delimiter='\n')
results['serial'] = len(lines) > 0
# Generate report
print(f"\n=== Board {board_id} Test Results ===")
for test, passed in results.items():
status = "PASS" if passed else "FAIL"
print(f"{test}: {status}")
return all(results.values())
# Test multiple boards
for board_num in range(1, 11):
if production_test(board_num):
print(f"Board {board_num}: ✓ PASSED\n")
else:
print(f"Board {board_num}: ✗ FAILED - CHECK HARDWARE\n")
Requirements
- Python: 3.7 or higher
- Dependencies: requests>=2.22.0
- Hardware: ESP8266/ESP32 board with METF firmware
Supported ESP Boards
- NodeMCU (ESP8266)
- WeMos D1 Mini (ESP8266)
- ESP32C6 Super Mini
ESP Firmware
The ESP board runs METF firmware, which provides an HTTP API for hardware control. The firmware is based on ESPAsyncWebServer.
Get the firmware: https://github.com/dontsovcmc/metf
Firmware Features
- HTTP REST API for GPIO, I2C, Serial control
- Supports multiple ESP8266 and ESP32 boards
- Async web server for fast response times
- Easy configuration via web interface
Development
Install Development Dependencies
# Option 1: Install package with dev dependencies
pip install metf-python-client[dev]
# Option 2: Install from source with requirements_dev.txt (recommended for contributors)
git clone https://github.com/dontsovcmc/metf-python-client
cd metf-python-client
pip install -r requirements_dev.txt
pip install -e .
# Option 3: Install from source with dev dependencies
git clone https://github.com/dontsovcmc/metf-python-client
cd metf-python-client
pip install -e .[dev]
Note: requirements_dev.txt includes all tools needed for development, testing, code quality checks, and publishing to PyPI.
Run Tests
pytest tests/
Code Style
# Format code
black metf_python_client/
# Check style
flake8 metf_python_client/
# Type checking
mypy metf_python_client/
Build and Publish
# Build package
python3 -m build
# Check build
twine check dist/*
# Publish to PyPI (maintainers only)
twine upload dist/*
For detailed publishing instructions, see CONTRIBUTING.md.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
How to Contribute
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and ensure code quality
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Guidelines
- Follow PEP 8 style guide
- Add tests for new features
- Update documentation as needed
- Keep commits atomic and well-described
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Evgeny Dontsov
- Email: don-and-home@mail.ru
- GitHub: @dontsovcmc
Links
- PyPI: https://pypi.org/project/metf-python-client/
- GitHub Repository: https://github.com/dontsovcmc/metf-python-client
- ESP Firmware: https://github.com/dontsovcmc/metf
- Issue Tracker: https://github.com/dontsovcmc/metf-python-client/issues
- Changelog: CHANGELOG.md
Acknowledgments
- Based on ESPAsyncWebServer
- Inspired by the need for hardware CI/CD in embedded development
FAQ
Q: Can I use this with other ESP boards?
A: Yes! As long as you flash the METF firmware, any ESP8266 or ESP32 board should work. You may need to create custom pin definitions for your specific board.
Q: How do I find the IP address of my ESP board?
A: Check your router's DHCP client list, use a network scanner, or configure the ESP with a static IP in the firmware.
Q: Can I control multiple ESP boards from one script?
A: Yes! Just create multiple METFClient instances with different IP addresses.
esp1 = METFClient('192.168.1.100')
esp2 = METFClient('192.168.1.101')
Q: What's the difference between METF and direct ESP programming?
A: METF allows you to control hardware remotely via HTTP, making it perfect for automated testing and CI/CD. No need to flash new firmware for each test - just write Python scripts!
Q: Can I use this in production devices?
A: METF is designed for testing and development. For production, consider direct firmware implementation for better security and performance.
Made with ❤️ for hardware testing automation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file metf_python_client-0.3.tar.gz.
File metadata
- Download URL: metf_python_client-0.3.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8dfbe7f43682ee2fe878a5523862633eb75c3420a9511227b1bf669f94043b46
|
|
| MD5 |
8ad18e23154ca7e10938132b6a191971
|
|
| BLAKE2b-256 |
e86516b298cb30f7626925dd75da93adff8d0f5e69dfc2ce4adc0ce925a9597b
|
File details
Details for the file metf_python_client-0.3-py3-none-any.whl.
File metadata
- Download URL: metf_python_client-0.3-py3-none-any.whl
- Upload date:
- Size: 17.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cc922397664ecf964a5e1526f7c84cdd640d575edb1a8d57bcf815195d9c779
|
|
| MD5 |
ae6186137153b8a5b7ebd7839ec7ca18
|
|
| BLAKE2b-256 |
0a9332794a89603d64f05ccffb5b8ba506c737d5b8fbfa7f047dfbd5dc61380a
|