A hardware-based pick-to-light and sorting system designed for warehouse operations. The system uses RGB LEDs to highlight storage locations, helping workers efficiently collect orders or sort packages.
This ESP32-based device connects to RGB LED indicators and a Bluetooth barcode scanner to create an intelligent warehouse assistance system. When a barcode is scanned, the corresponding shelf location lights up, guiding workers to the correct pick or sort location.
- Barcode Scanner Integration: Connects to BLE barcode scanners
- RGB LED Control: Supports up to 48 individual LED zones via CH423 I/O expanders
- Additional NeoPixel Support: 60 addressable RGB LEDs for visual feedback
- WiFi Connectivity: Operates in both Station (client) and AP (access point) modes
- Web Interface: Browser-based configuration and monitoring
- REST API: Programmatic control via HTTP endpoints
- Real-time Updates: Server-sent events for live status monitoring
- Flexible Configuration: JSON-based configuration for easy customization
- Order Picking (Pick-to-Light): Workers scan order items, and LEDs highlight the shelf locations where items should be collected
- Package Sorting: Scan package barcodes to illuminate destination bins or shelves
- Inventory Management: Guide workers to specific storage locations
- Assembly Operations: Highlight component locations based on BOM (Bill of Materials)
- ESP32 Development Board (ESP32-DOIT-DevKit-V1 or compatible)
- CH423 I/O Expander (up to 2 units for 48 outputs)
- RGB LED strips or individual LEDs for shelf indicators
- WS2812B NeoPixel LED strip (60 LEDs, optional)
- Bluetooth LE Barcode Scanner
- Wire0 (I2C): SDA=GPIO21, SCL=GPIO22 (default ESP32 I2C)
- Wire1 (I2C): SDA=GPIO18, SCL=GPIO19
- Data Pin: GPIO16
- Pins 0-7: CH423 #1 GPIO pins
- Pins 8-23: CH423 #1 GPO pins
- Pins 24-31: CH423 #2 GPIO pins
- Pins 32-47: CH423 #2 GPO pins
- Pin 48: Special - controls all LEDs simultaneously
- PlatformIO (recommended) or Arduino IDE
- Git (for cloning dependencies)
- Clone the repository
git clone <repository-url>
cd ptl- Install dependencies
Dependencies are automatically managed by PlatformIO:
- ESPAsyncWebServer
- NimBLE-Arduino (v1.4.0+)
- ArduinoJson
- Adafruit NeoPixel (v1.11.0+)
- Build and upload
platformio run --target upload- Upload filesystem data (web interface and config files)
platformio run --target uploadfs-
First Boot: The device will create a WiFi access point
- SSID: From
data/config.json(default: "99") - Password: From
data/config.json
- SSID: From
-
Connect to the device
- Connect to the WiFi AP
- Open browser to
http://192.168.4.1(default AP IP)
-
Configure WiFi (if using station mode)
- Edit
data/config.jsonbefore uploading - Set
standalone: false - Configure your network SSID and password
- Edit
Located in data/config.json:
{
"standalone": false,
"ssid": "YourNetworkName",
"wifipass": "YourPassword",
"cidr": "192.168.88.201/24",
"gw": "192.168.88.1",
"addr": "aa:a8:a2:15:78:d9",
"pins": ["A", "B", "C"],
"service": "0000feea-0000-1000-8000-00805f9b34fb",
"charact": "00002aa1-0000-1000-8000-00805f9b34fb"
}Parameters:
standalone:truefor AP mode,falsefor station modessid: WiFi network name (or AP name if standalone)wifipass: WiFi passwordcidr: Static IP configuration (station mode)gw: Gateway IPaddr: Bluetooth MAC address of the barcode scannerpins: Array of pin names/labelsservice: BLE service UUID for barcode scannercharact: BLE characteristic UUID for barcode scanner notifications
Maps barcodes to shelf locations (pin names):
{
"A": ["1234567890", "0987654321"],
"B": ["1111111111", "2222222222"],
"C": ["3333333333"]
}Format: { "PinName": ["barcode1", "barcode2", ...] }
Returns WiFi connection information.
Response:
{
"status": "ok",
"ssid": "NetworkName",
"gw": "192.168.1.1",
"dns": "192.168.1.1",
"cidr": "192.168.1.100/24"
}Configure BLE scanner connection.
Request:
{
"address": "aa:a8:a2:15:78:d9",
"service": "0000feea-0000-1000-8000-00805f9b34fb",
"charact": "00002aa1-0000-1000-8000-00805f9b34fb"
}Manually trigger LED blinking for a specific pin.
Request:
{
"pin": 5
}Set LED based on barcode (legacy).
Request:
{
"code": "1234567890"
}Update device configuration (requires reboot).
Query Parameters:
reboot=true: Restart device after saving
Request Body: Complete config.json structure
Real-time event stream for monitoring.
Event Types:
- status - Device status updates
{
"status": 1,
"r": 1234567890,
"w": 1234567890,
"devices": [
{
"address": "aa:a8:a2:15:78:d9",
"service": "0000feea-0000-1000-8000-00805f9b34fb"
}
]
}- scan - Barcode scan events
{
"code": "1234567890",
"pin": "A",
"t": 1234567890
}- Power on the device
- Wait for BLE connection to the barcode scanner (check status LED/logs)
- Scan a barcode with the paired scanner
- LED illuminates at the corresponding shelf location
- LED blinks for 10 seconds then turns off
- Blink Duration: 10 seconds (configurable in code:
blinkDuration) - Blink Period: 1 second on/off cycle (configurable:
blinkPeriod) - Active State: LED is ON (LOW signal to CH423)
- Inactive State: LED is OFF (HIGH signal to CH423)
Access the web interface at the device's IP address:
- AP Mode:
http://192.168.4.1 - Station Mode: Check serial monitor for assigned IP or configure static IP
Features:
- View connected BLE devices
- Configure scanner pairing
- Test individual LEDs
- View scan history
- Update configuration
- Check scanner Bluetooth MAC address in config
- Verify scanner is in pairing/advertising mode
- Check serial monitor for connection logs
- Ensure correct service and characteristic UUIDs
- Verify I2C connections to CH423 chips
- Check I2C addresses are responding (serial monitor shows "Wire0/Wire1 not found!" if missing)
- Test with pin 48 to light all LEDs: POST to
/api/blinkwith{"pin": 48} - Verify LED wiring (LOW = ON, HIGH = OFF for CH423)
- Check SSID and password in config.json
- Try standalone (AP) mode first
- Monitor serial output (115200 baud) for connection status
- Ensure router supports 2.4GHz WiFi (ESP32 doesn't support 5GHz)
- Verify barcode exists in
table.json - Check pin name mapping in
config.json - Monitor
/eventsendpoint for scan events - Check serial monitor for "R: [barcode] = [pin]" messages
Baud rate: 115200
Key Log Messages:
Running ble on core 0- BLE task startedRunning blink on core 0- LED control task startedCONNECTED TO DEVICE- Scanner connectedSUBSCRIBED- Listening for scanner notificationsR: [code] = [pin]- Barcode processedStarting to blink [pin]- LED activated
src/
├── main.cpp # Main application, web server, WiFi
├── ble.cpp # Bluetooth LE scanner connection
├── blink.cpp # LED control via CH423
├── log.cpp # Logging functionality
├── ptl.hpp # Common definitions and structures
├── DFRobot_CH423.cpp # CH423 driver
└── DFRobot_CH423.h # CH423 driver header
data/
├── index.html # Web interface
├── config.json # Device configuration
├── table.json # Barcode to pin mapping
└── *.png # Web interface icons
Change LED Blink Duration:
Edit src/blink.cpp:
unsigned long blinkDuration = 10000UL; // millisecondsChange Blink Pattern:
Edit src/blink.cpp:
int blinkPeriod = 1000; // Full cycle duration (ms)
int blinkFill = 500; // ON duration (ms)Change BLE Scan Interval:
Edit src/ble.cpp:
#define SCAN_TIME 5 // secondsPre-designed PCB files are included in the pcb/ directory:
- Gerber files for manufacturing
- BOM (Bill of Materials)
- Pick-and-place files
Two PCB variants:
ptl-regi2: Main board with CH423 supportptl-mk_2: Alternative design
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2023 Serhii Nesterenko
Attribution to the original author must be maintained in any derivative works or distributions.
Contributions are welcome! Here's how you can help:
- Use the issue tracker to report bugs
- Provide detailed information: hardware setup, configuration, and steps to reproduce
- Include serial monitor output when relevant
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Follow the existing code style
- Test your changes thoroughly
- Update documentation as needed
- Commit your changes (
git commit -m 'Add amazing feature') - Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Test on actual hardware when possible
- Keep commits focused and atomic
- Update the README if adding new features or changing behavior
- Maintain backward compatibility with existing configurations
For questions, issues, or feature requests, please use the GitHub issue tracker.