Skip to main content

HTTP client library for testing hardware using ESP boards (NodeMCU, WeMos, ESP32C6)

Project description

METF Python Client

PyPI version Python Versions License: MIT Downloads

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

  1. Connect your MCU/device to an ESP board (NodeMCU, WeMos, or ESP32C6)
  2. Flash METF firmware to the ESP board
  3. 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:


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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Run tests and ensure code quality
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. 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


Links


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

This version

0.3

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metf_python_client-0.3.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

metf_python_client-0.3-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

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

Hashes for metf_python_client-0.3.tar.gz
Algorithm Hash digest
SHA256 8dfbe7f43682ee2fe878a5523862633eb75c3420a9511227b1bf669f94043b46
MD5 8ad18e23154ca7e10938132b6a191971
BLAKE2b-256 e86516b298cb30f7626925dd75da93adff8d0f5e69dfc2ce4adc0ce925a9597b

See more details on using hashes here.

File details

Details for the file metf_python_client-0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for metf_python_client-0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 6cc922397664ecf964a5e1526f7c84cdd640d575edb1a8d57bcf815195d9c779
MD5 ae6186137153b8a5b7ebd7839ec7ca18
BLAKE2b-256 0a9332794a89603d64f05ccffb5b8ba506c737d5b8fbfa7f047dfbd5dc61380a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page