# LilyGo T-Embed Setup and Communication Protocol

## Overview

The LilyGo T-Embed is an ESP32-based device with an integrated CC1101 sub-GHz transceiver. This document covers hardware setup, firmware options, and the communication protocol between Android Termux and the T-Embed.

## Hardware Specifications

### LilyGo T-Embed CC1101
- **MCU**: ESP32-S3 (Dual-core 240MHz)
- **Display**: 1.9" ST7789 TFT (170x320 pixels)
- **RF Chip**: CC1101 Sub-GHz transceiver
- **Frequency Range**:
  - 300-348 MHz
  - 387-464 MHz
  - 779-928 MHz
- **Storage**: MicroSD card slot
- **Connectivity**: USB-C (serial + power)
- **Battery**: Built-in LiPo charger

### CC1101 Transceiver Capabilities
- **Modulation**: ASK/OOK, FSK, GFSK, MSK
- **Data Rate**: 0.6 - 500 kbps
- **RX Sensitivity**: -112 dBm @ 1.2 kbps
- **TX Power**: Configurable up to +12 dBm
- **FIFO Buffer**: 64 bytes RX/TX

## Firmware Options

### 1. Bruce Firmware (Recommended)
**Repository**: https://github.com/pr3y/Bruce

Bruce is a multi-tool firmware with excellent Sub-GHz support designed for T-Embed.

**Features**:
- Read/replay .sub files (Flipper compatible)
- Spectrum analyzer
- Frequency scanner
- Signal recorder
- SD card support for signature storage

**Installation**:
```bash
# Download from releases
wget https://github.com/pr3y/Bruce/releases/latest/download/Bruce_T-Embed.bin

# Flash using esptool (in Termux)
pip install esptool
esptool.py --chip esp32s3 --port /dev/ttyUSB0 write_flash 0x0 Bruce_T-Embed.bin
```

### 2. ESP32-Marauder
**Repository**: https://github.com/justcallmekoko/ESP32Marauder

Originally for WiFi, has been adapted for Sub-GHz.

### 3. Custom Firmware (Planned)
GigLez-specific firmware optimized for continuous scanning and GPS coordination.

## Communication Protocol

### Serial Connection

The T-Embed communicates via USB serial at 115200 baud.

**Termux Serial Setup**:
```bash
# Install required packages
pkg install libusb python

# Python serial library
pip install pyserial

# Find device
ls /dev/ttyUSB*
# Usually /dev/ttyUSB0 or /dev/ttyACM0
```

### Command Protocol

GigLez uses a JSON-based command protocol over serial.

#### Command Structure

All commands are JSON objects terminated by `\n`:

```json
{"cmd": "COMMAND_NAME", "params": {...}, "id": 12345}
```

- `cmd`: Command name (uppercase)
- `params`: Command-specific parameters
- `id`: Optional request ID for response matching

#### Response Structure

```json
{"status": "ok|error", "data": {...}, "id": 12345, "timestamp": 1704998400}
```

- `status`: "ok" or "error"
- `data`: Response data (command-specific)
- `id`: Request ID (if provided)
- `timestamp`: Unix timestamp from device

### Command Reference

#### 1. SCAN

Start scanning for signals on specified frequencies.

**Request**:
```json
{
  "cmd": "SCAN",
  "params": {
    "frequencies": [433920000, 315000000, 868000000],
    "duration": 60,
    "modulation": "OOK",
    "rssi_threshold": -90
  },
  "id": 1
}
```

**Parameters**:
- `frequencies`: Array of frequencies in Hz
- `duration`: Scan duration in seconds (0 = continuous)
- `modulation`: "OOK", "FSK", or "AUTO"
- `rssi_threshold`: Minimum signal strength in dBm

**Response**:
```json
{
  "status": "ok",
  "data": {
    "scan_id": "scan_20250111_1234",
    "started_at": 1704998400
  },
  "id": 1
}
```

#### 2. CAPTURE

Capture a detected signal.

**Request**:
```json
{
  "cmd": "CAPTURE",
  "params": {
    "frequency": 433920000,
    "duration": 5,
    "format": "RAW"
  },
  "id": 2
}
```

**Parameters**:
- `frequency`: Frequency in Hz
- `duration`: Capture duration in seconds
- `format`: "RAW", "DECODED", or "BOTH"

**Response**:
```json
{
  "status": "ok",
  "data": {
    "capture_id": "cap_20250111_1235",
    "frequency": 433920000,
    "rssi": -75,
    "protocol": "Princeton",
    "raw_data": [2980, -240, 520, -980, 520, -980, ...],
    "decoded": {
      "bit": 24,
      "key": "00 00 00 00 00 95 D5 D4",
      "te": 400
    }
  },
  "id": 2
}
```

#### 3. STOP_SCAN

Stop an active scan.

**Request**:
```json
{
  "cmd": "STOP_SCAN",
  "params": {
    "scan_id": "scan_20250111_1234"
  },
  "id": 3
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "total_signals": 127,
    "unique_protocols": 8
  },
  "id": 3
}
```

#### 4. REPLAY

Replay a stored .sub file.

**Request**:
```json
{
  "cmd": "REPLAY",
  "params": {
    "file": "/sd/captures/garage_door.sub",
    "repeat": 3,
    "delay_ms": 100
  },
  "id": 4
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "transmitted": 3,
    "frequency": 433920000
  },
  "id": 4
}
```

#### 5. STATUS

Get device status.

**Request**:
```json
{
  "cmd": "STATUS",
  "id": 5
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "firmware": "GigLez-T-Embed v0.1.0",
    "uptime": 3600,
    "battery_voltage": 3.85,
    "battery_percent": 78,
    "sd_card": {
      "present": true,
      "free_mb": 15234,
      "total_mb": 31456
    },
    "current_scan": "scan_20250111_1234",
    "cc1101": {
      "status": "idle",
      "frequency": 433920000,
      "rssi": -95
    }
  },
  "id": 5
}
```

#### 6. CONFIG

Configure device settings.

**Request**:
```json
{
  "cmd": "CONFIG",
  "params": {
    "auto_save": true,
    "save_path": "/sd/giglez/",
    "led_brightness": 50,
    "spectrum_display": false
  },
  "id": 6
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "config_updated": true
  },
  "id": 6
}
```

#### 7. LIST_FILES

List captured .sub files on SD card.

**Request**:
```json
{
  "cmd": "LIST_FILES",
  "params": {
    "path": "/sd/giglez/",
    "pattern": "*.sub"
  },
  "id": 7
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "files": [
      {
        "name": "capture_001.sub",
        "size": 245,
        "created": 1704998400
      },
      {
        "name": "capture_002.sub",
        "size": 312,
        "created": 1704998500
      }
    ],
    "total_count": 2,
    "total_bytes": 557
  },
  "id": 7
}
```

#### 8. GET_FILE

Retrieve a file from SD card.

**Request**:
```json
{
  "cmd": "GET_FILE",
  "params": {
    "path": "/sd/giglez/capture_001.sub"
  },
  "id": 8
}
```

**Response**:
```json
{
  "status": "ok",
  "data": {
    "filename": "capture_001.sub",
    "size": 245,
    "content": "Filetype: Flipper SubGhz Key File\nVersion: 1\n..."
  },
  "id": 8
}
```

### Event Notifications

The T-Embed can send unsolicited event notifications:

#### SIGNAL_DETECTED

```json
{
  "event": "SIGNAL_DETECTED",
  "data": {
    "frequency": 433920000,
    "rssi": -72,
    "timestamp": 1704998400,
    "protocol": "Unknown",
    "duration_ms": 45
  }
}
```

#### SCAN_COMPLETE

```json
{
  "event": "SCAN_COMPLETE",
  "data": {
    "scan_id": "scan_20250111_1234",
    "total_signals": 127,
    "duration": 60
  }
}
```

#### ERROR

```json
{
  "event": "ERROR",
  "data": {
    "code": "SD_CARD_FULL",
    "message": "SD card is full, cannot save capture"
  }
}
```

## Python Communication Library

### Basic Usage

```python
import serial
import json
import time

class TEmbedController:
    def __init__(self, port='/dev/ttyUSB0', baudrate=115200):
        self.serial = serial.Serial(port, baudrate, timeout=1)
        self.request_id = 0
        time.sleep(2)  # Wait for device reset

    def send_command(self, cmd, params=None):
        """Send command and return response"""
        self.request_id += 1
        message = {
            'cmd': cmd,
            'params': params or {},
            'id': self.request_id
        }

        # Send
        self.serial.write(json.dumps(message).encode() + b'\n')

        # Wait for response
        line = self.serial.readline().decode().strip()
        if line:
            return json.loads(line)
        return None

    def start_scan(self, frequencies, duration=60):
        """Start scanning on specified frequencies"""
        return self.send_command('SCAN', {
            'frequencies': frequencies,
            'duration': duration,
            'modulation': 'AUTO',
            'rssi_threshold': -90
        })

    def capture_signal(self, frequency, duration=5):
        """Capture signal at frequency"""
        return self.send_command('CAPTURE', {
            'frequency': frequency,
            'duration': duration,
            'format': 'BOTH'
        })

    def get_status(self):
        """Get device status"""
        return self.send_command('STATUS')

    def listen_events(self, callback):
        """Listen for event notifications"""
        while True:
            line = self.serial.readline().decode().strip()
            if line:
                try:
                    data = json.loads(line)
                    if 'event' in data:
                        callback(data)
                except json.JSONDecodeError:
                    pass

# Example usage
device = TEmbedController('/dev/ttyUSB0')

# Check status
status = device.get_status()
print(f"Battery: {status['data']['battery_percent']}%")

# Start scan
scan = device.start_scan([433920000, 315000000, 868000000], duration=60)
print(f"Scan started: {scan['data']['scan_id']}")

# Listen for signals
def on_event(event):
    if event['event'] == 'SIGNAL_DETECTED':
        print(f"Signal detected: {event['data']['frequency']} Hz, RSSI: {event['data']['rssi']} dBm")

device.listen_events(on_event)
```

### Async Implementation

For better performance in production:

```python
import asyncio
import aioserial

class AsyncTEmbedController:
    def __init__(self, port='/dev/ttyUSB0', baudrate=115200):
        self.port = port
        self.baudrate = baudrate
        self.request_id = 0
        self.pending_requests = {}

    async def connect(self):
        self.serial = aioserial.AioSerial(port=self.port, baudrate=self.baudrate)
        await asyncio.sleep(2)

    async def send_command(self, cmd, params=None):
        self.request_id += 1
        message = {
            'cmd': cmd,
            'params': params or {},
            'id': self.request_id
        }

        # Send
        await self.serial.write_async(json.dumps(message).encode() + b'\n')

        # Create future for response
        future = asyncio.Future()
        self.pending_requests[self.request_id] = future

        return await future

    async def listen(self, event_callback):
        """Background task to listen for responses and events"""
        while True:
            line = await self.serial.readline_async()
            if line:
                try:
                    data = json.loads(line.decode().strip())

                    # Handle response
                    if 'id' in data and data['id'] in self.pending_requests:
                        self.pending_requests[data['id']].set_result(data)
                        del self.pending_requests[data['id']]

                    # Handle event
                    elif 'event' in data:
                        await event_callback(data)

                except json.JSONDecodeError:
                    pass
```

## GPS Coordination

### Timestamp Synchronization

The T-Embed doesn't have GPS, so timestamps come from Android:

```python
import time

# Send timestamp to device
device.send_command('CONFIG', {
    'system_time': int(time.time())
})

# Device will use this to calibrate its internal clock
```

### Capture with GPS

When a signal is captured, immediately tag it with GPS:

```python
import android
from tembed import TEmbedController

# Initialize Android SL4A for GPS
droid = android.Android()
droid.startLocating()

# T-Embed controller
device = TEmbedController()

# Event handler
async def on_signal(event):
    if event['event'] == 'SIGNAL_DETECTED':
        # Get current GPS location
        location = droid.getLastKnownLocation().result
        gps = location.get('gps') or location.get('network')

        if gps:
            # Store in database with GPS
            store_capture({
                'frequency': event['data']['frequency'],
                'rssi': event['data']['rssi'],
                'latitude': gps['latitude'],
                'longitude': gps['longitude'],
                'accuracy': gps.get('accuracy', 0),
                'timestamp': event['data']['timestamp']
            })

# Start listening
await device.listen(on_signal)
```

## Troubleshooting

### Device Not Detected

```bash
# Check USB devices
lsusb

# Check serial ports
ls -l /dev/ttyUSB* /dev/ttyACM*

# Grant permissions (Termux)
termux-usb -l  # List devices
termux-usb -r /dev/bus/usb/XXX/YYY  # Request permission
```

### Serial Communication Errors

```python
# Add error handling
try:
    response = device.send_command('STATUS')
except serial.SerialException as e:
    print(f"Serial error: {e}")
    # Attempt reconnection
    device.serial.close()
    device.serial.open()
```

### Firmware Update

```bash
# Backup SD card first
adb pull /sdcard/giglez /path/to/backup

# Flash new firmware
esptool.py --chip esp32s3 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32s3 --port /dev/ttyUSB0 write_flash 0x0 firmware.bin
```

## Next Steps

1. Flash Bruce firmware to T-Embed
2. Test serial communication from Termux
3. Implement capture coordination with GPS
4. Build async event handling system
5. Develop custom firmware optimized for GigLez
