Technical Reference - ESP32 RPC System
Architectuur Overzicht
┌─────────────────────────────────────────────────────────────┐
│ Python Host Machine │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ GUI Test │ │ Custom Scripts │ │
│ │ Application │ │ & Applications │ │
│ └────────┬────────┘ └────────┬─────────┘ │
│ │ │ │
│ └───────────┬───────────────┘ │
│ │ │
│ ┌───────────▼────────────┐ │
│ │ RPCClient Library │ │
│ │ ┌────────────────────┐ │ │
│ │ │ rpc_client.py │ │ │
│ │ │ - All RPC methods │ │ │
│ │ └────────────────────┘ │ │
│ │ ┌────────────────────┐ │ │
│ │ │ transport.py │ │ │
│ │ │ - USB/WiFi layer │ │ │
│ │ └────────────────────┘ │ │
│ └───────────┬────────────┘ │
│ │ │
│ ┌───────────▼────────────┐ │
│ │ JSON/Serial Data │ │
│ │ (USB or TCP Socket) │ │
│ └───────────┬────────────┘ │
│ │ │
└───────────────────────┼──────────────────────────────────────┘
│
┌─────────────▼─────────────┐
│ USB / WiFi Connection │
└─────────────┬─────────────┘
│
┌───────────────────────▼──────────────────────────────────────┐
│ ESP32 Device │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ RPC Server (main.cpp) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Serial/WiFi Input Buffer │ │ │
│ │ │ JSON Parser (ArduinoJSON) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────▼───────────────────────────────┐ │ │
│ │ │ Command Dispatcher │ │ │
│ │ │ (execute_command) │ │ │
│ │ └──────────────────┬───────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────▼───────────────────────────────┐ │ │
│ │ │ RPC Handler Methods │ │ │
│ │ │ ┌──────────────────────────────────────────┐ │ │ │
│ │ │ │ GPIO: pinMode, digitalWrite, digitalRead│ │ │ │
│ │ │ │ Analog: analogRead, analogWrite │ │ │ │
│ │ │ │ System: delay, millis, getFreeMem │ │ │ │
│ │ │ │ PWM: ledcSetup, ledcWrite │ │ │ │
│ │ │ └──────────────────────────────────────────┘ │ │ │
│ │ └──────────────────┬───────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────▼───────────────────────────────┐ │ │
│ │ │ Arduino Framework Functions │ │ │
│ │ │ (ESP32 Hardware APIs) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────▼───────────────────────────────┐ │ │
│ │ │ Response Builder & Serialization │ │ │
│ │ │ (JSON Output) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ ESP32 Hardware Resources │ │
│ │ ┌──────────────┬──────────────┬──────────────────┐ │ │
│ │ │ GPIO Pins │ ADC Pins │ PWM Channels │ │ │
│ │ │ (0-39) │ (36,37,etc) │ (0-15) │ │ │
│ │ └──────────────┴──────────────┴──────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
Communication Protocol Details
JSON Message Format
Request Structure:
{
"method": "methodName",
"params": {
"param1": "value1",
"param2": 123,
"param3": true
}
}
Response Structure:
{
"result": 0,
"message": "OK",
"data": {
"key1": "value1",
"key2": 456
}
}
Handshake & Communication Flow
1. Connect (USB/WiFi)
└─> Device opens serial port / socket connection
└─> ESP32 waits for JSON commands
2. Send Command
Client: {"method":"pinMode","params":{"pin":13,"mode":1}}
└─> Serialized as JSON string + newline
3. Parse on ESP32
└─> ArduinoJSON deserializes request
└─> Validates method exists
└─> Validates required parameters
4. Execute Handler
└─> Handler method called with params
└─> Result code set (0 = success, 1-5 = error)
└─> Optional data prepared
5. Send Response
Server: {"result":0,"message":"OK","data":{}}
└─> Serialized and sent back via serial/socket
6. Client Processes Response
└─> JSON deserialized
└─> Result code, message, data extracted
└─> Returned to Python caller
File Descriptions
For the complete file index, see <project_dir>/FILE_REFERENCE.md.
Key entry points:
<project_dir>/eps32_host/src/main.cpp - Firmware entry point and main loop.
<project_dir>/eps32_host/lib/rpc_server/src/rpc_server.cpp - RPC request dispatch and handlers.
<project_dir>/python_client/library/rpc_client.py - Python RPC client interface.
<project_dir>/python_client/library/transport.py - USB/WiFi transport implementations.
Boot-time mode selection and WiFi configure mode are documented in <project_dir>/QUICKSTART.md.
RPC Method Reference
API Quick Reference
GPIO:
pinMode,digitalWrite,digitalReadAnalog:
analogWrite,analogReadPWM:
ledcSetup,ledcWriteSystem:
delay,getMillis,getFreeMem,getChipIDPulse:
pulseBegin,pulse,pulseAsync,isPulsing,generatePulses,generatePulsesAsync,getRemainingPulses,stopPulseADC 3208:
adcReadRaw,adcReadVoltage,isButtonPressedDAC 4922:
dacSetVoltage,dacSetVoltageAllDIO:
dioGetInput,dioIsBitSet,dioSetOutput,dioSetBit,dioClearBit,dioToggleBitQC7366:
qcEnableCounter,qcDisableCounter,qcClearCountRegister,qcReadCountRegisterOLED:
oledClear,oledWriteLine
Optional APIs require matching firmware features enabled.
GPIO Methods
# Set pin mode (OUTPUT=1, INPUT=0, INPUT_PULLUP=2)
result, msg = client.pinMode(pin: int, mode: int)
# Write digital value (HIGH=1, LOW=0)
result, msg = client.digitalWrite(pin: int, value: int)
# Read digital value
result, msg, value = client.digitalRead(pin: int)
# Write PWM (0-255 for default 8-bit)
result, msg = client.analogWrite(pin: int, value: int)
# Read analog value (0-4095 on ESP32)
result, msg, value = client.analogRead(pin: int)
System Methods
# Sleep for milliseconds
result, msg = client.delay(ms: int)
# Get uptime in milliseconds
result, msg, millis = client.getMillis()
# Get free heap memory in bytes
result, msg, free_mem = client.getFreeMem()
# Get ESP32 chip ID
result, msg, chip_id = client.getChipID()
PWM Methods
# Setup PWM channel (channel 0-15, freq in Hz, bits 1-16)
result, msg = client.ledcSetup(channel: int, freq: int, bits: int)
# Write PWM duty cycle (0 to 2^bits - 1)
result, msg = client.ledcWrite(channel: int, duty: int)
Raw Method
# Call any RPC method with custom parameters
result, msg, data = client.call_raw(method: str, params: dict)
Data Types
Supported in Parameters
int / long: All integer values
float: Floating-point numbers
bool: true/false
string: Text values
Return Values
result: int (0-5, see result codes)
message: string (human-readable status)
data: dict (optional, contains method-specific data)
value: int/float (extracted from data dict)
Result Codes
Code |
Constant |
Meaning |
|---|---|---|
0 |
RPC_OK |
Operation successful |
1 |
RPC_ERROR_INVALID_COMMAND |
Method not found |
2 |
RPC_ERROR_INVALID_PARAMS |
Missing/invalid parameters |
3 |
RPC_ERROR_TIMEOUT |
No response from device |
4 |
RPC_ERROR_EXECUTION |
Error during execution |
5 |
RPC_ERROR_NOT_SUPPORTED |
Function not supported |
ESP32 Pin Configuration
GPIO Pins
GPIO 0-19, 21-23, 25-27, 32-39 (usable)
GPIO 6-11: Flash memory (don’t use)
GPIO 20, 24: Not available
ADC Pins (Analog Input)
ADC1: GPIO 32-39 (8 channels)
ADC2: GPIO 0, 2, 4, 12-15, 25-27 (10 channels)
Note: ADC2 shares pins with WiFi, use ADC1 preferably
PWM Channels
16 independent PWM channels (0-15)
Configurable frequency and resolution
Performance Characteristics
Operation |
Typical Time |
|---|---|
USB Command Round-trip |
5-20ms |
WiFi Command Round-trip |
10-50ms |
GPIO Digital I/O |
<1ms |
ADC Read |
10-20ms |
JSON Parsing (small message) |
<2ms |
Extending with New Functions
Step-by-Step Process
1. Define in Header (rpc_server.h)
private:
int rpc_myFunction(JsonObject params);
2. Implement Handler (rpc_server.cpp)
int RpcServer::rpc_myFunction(JsonObject params) {
// Validate parameters
if (!params.containsKey("requiredParam")) {
return RPC_ERROR_INVALID_PARAMS;
}
// Execute
int param = params["requiredParam"];
// ... implementation ...
// Return result
return RPC_OK; // or appropriate error code
}
3. Register in Dispatcher (in execute_command())
} else if (strcmp(method, "myFunction") == 0) {
return rpc_myFunction(params);
}
4. Add Python Wrapper (rpc_client.py)
def myFunction(self, param: int) -> Tuple[int, str]:
"""Description of myFunction"""
result, msg, _ = self._send_command("myFunction", {"requiredParam": param})
return result, msg
5. Test with GUI or Script
result, msg = client.myFunction(42)
assert result == RPC_OK
print(f"Result: {msg}")
Performance Tuning
Reduce Latency
Use USB instead of WiFi for low-latency
Batch multiple commands in sequence
Use PWM instead of repeated GPIO writes
Reduce Power
Use delay() to avoid busy-waiting
Disable WiFi when using USB only
Set appropriate PWM frequencies
Increase Reliability
Always check return codes
Implement timeout handling
Test on target hardware
For setup, troubleshooting, and debug steps, see <project_dir>/QUICKSTART.md.
For more details, see README.md and QUICKSTART.md