2 Commits

Author SHA1 Message Date
kenji 3014bc0c3b add schematic 2025-09-11 12:27:29 -04:00
kenji a1cb13a2f9 Add Pico 2 support 2025-09-06 12:01:00 -04:00
11 changed files with 14848 additions and 56 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
/build/**/* /*/**/*
/external/**/* !/html/**/*
+5 -1
View File
@@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.13)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk) set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB) set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB)
set(TUSB_NETWORKING_PATH ${PICO_SDK_PATH}/lib/tinyusb/lib/networking) set(TUSB_NETWORKING_PATH ${PICO_SDK_PATH}/lib/tinyusb/lib/networking)
if (NOT DEFINED PICO_BOARD)
set(PICO_BOARD pico)
endif()
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake) include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM) project(${PROJECT} C CXX ASM)
@@ -67,5 +70,6 @@ target_link_libraries(${PROJECT} PRIVATE
hardware_adc hardware_adc
) )
pico_add_extra_outputs(${PROJECT}) set_target_properties(${PROJECT} PROPERTIES OUTPUT_NAME "${PROJECT}-${PICO_BOARD}")
pico_add_extra_outputs(${PROJECT})
+19 -17
View File
@@ -1,9 +1,9 @@
# HyperX Alloy Elite 2 RGB Controller # HyperX Alloy Elite 2 RGB Controller
This project provides individual controls of the RGB LEDs of a HyperX Alloy This project provides individual controls of the RGB LEDs of a HyperX Alloy
Elite 2 keyboard using a webpage served over a USB connection. ADC readings Elite 2 keyboard using a webpage served over a USB connection from a Raspberry
from an attached light dependent resistor can also be used to make the Pi Pico (2). ADC readings from an attached light dependent resistor can also be
lighting adaptive to the environment. used to make the lighting adaptive to the environment.
![Web interface for setting individual LED RGB values](ui.png) ![Web interface for setting individual LED RGB values](ui.png)
@@ -12,23 +12,25 @@ lighting adaptive to the environment.
### Hardware ### Hardware
You will need the following hardware to make the device: You will need the following hardware to make the device:
- Raspberry Pi Pico - Raspberry Pi Pico or Raspberry Pi Pico 2
- USB extension cable - USB extension cable
- light dependent resistor such as GL5528 (specific part number may vary) - light dependent resistor such as GL5528 (specific part number may vary)
- 10k ohm resistor (resistance value may vary) - 10k ohm resistor (resistance value may vary)
![Wiring schematic](schematic.svg)
You will need to cut the USB extension in half and connect the wires from the You will need to cut the USB extension in half and connect the wires from the
female end to the Raspberry Pi Pico. The default configuration is to attach the female end to the Raspberry Pi Pico (2). The default configuration is to attach
USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will also the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will
need to connect the red to 5V VBUS/VSYS (since you'll be powering from the also need to connect the red to 5V VBUS/VSYS (since you'll be powering from the
USB host device, VBUS should be fine) and the black to any ground pin. Pin 38 USB host device, VBUS should be fine) and the black to any ground pin. Pin 38
is the closest and most convenient. While you can connect the Raspberry Pi Pico is the closest and most convenient. While you can connect the Raspberry Pi Pico
to the host device using the micro USB port and a micro USB cable, since (2) to the host device using the micro USB port and a micro USB cable, since
you have already sacrificed half of the USB extension cable, you might as well you have already sacrificed half of the USB extension cable, you might as well
use the male half to create a standard USB-A connection. If you wish to do so, use the male half to create a standard USB-A connection. If you wish to do so,
then simply connect the red and black wires of the male connector end to then simply connect the red and black wires of the male connector end to
VBUS (5V) and GND, respectively. For the data wires, you can solder them to the VBUS (5V) and GND, respectively. For the data wires, you can solder them to the
two test points TP2 and TP3 on the back of the Raspberry Pi Pico. The white two test points TP2 and TP3 on the back of the Raspberry Pi Pico (2). The white
cable goes to TP2 and the green cable to TP3. cable goes to TP2 and the green cable to TP3.
![Back of Raspberry Pi Pico with USB connections to TP2 and TP3](back.jpg) ![Back of Raspberry Pi Pico with USB connections to TP2 and TP3](back.jpg)
@@ -49,23 +51,23 @@ hole for the LDR to detect ambient light.
## Software ## Software
Flash the elite2_rgb.uf2 file from the latest Flash the elite2_rgb-pico.uf2 or elite2_rgb-pico2.uf2 file from the latest
[release](https://git.kkozai.com/kenji/alloy_elite2_rgb/releases) to the [release](https://git.kkozai.com/kenji/alloy_elite2_rgb/releases) to the
Raspberry Pi Pico, and connect the keyboard to the female USB port and insert Raspberry Pi Pico (2), and connect the keyboard to the female USB port and
the male USB connector of the device into your host device such as PC. insert the male USB connector of the device into your host device such as PC.
To load the UI for configuring the RGB lighting, open a browser to the page at To load the UI for configuring the RGB lighting, open a browser to the page at
http://alloyelite2.usb, or if that doesn't load, to http://192.168.226.1 http://alloyelite2.usb, or if that doesn't load, to http://192.168.226.1
(226 is E2 for "Elite 2" in hexademical). From the webpage, you can click on (226 is E2 for "Elite 2" in hexadecimal). From the webpage, you can click on
any individual key that you want to configure and change the color using the any individual key that you want to configure and change the color using the
color selector or by manually inputting the RGB color value into the text color selector or by manually inputting the RGB color value into the text
boxes. To finalize setting the color for the selected key(s), click on the boxes. To finalize setting the color for the selected key(s), click on the
"Set Color" button. "Set Color" button.
To save the lighting configuration to the Pico 2 so that it loads the next time To save the lighting configuration to the Pico (2) so that it loads the next
it is powered on, click on the "Save" button under the "Flash Memory" section. time it is powered on, click on the "Save" button under the "Flash Memory"
If you make unsaved changes and want to reload the configuration from memory, section. If you make unsaved changes and want to reload the configuration from
you can also click the "Load" button to reset to the last saved setting. memory, you can also click the "Load" button to reset to the last saved setting.
If the checkbox next to "Adaptive" is selected when setting the color, the key's If the checkbox next to "Adaptive" is selected when setting the color, the key's
brightness will automatically adjust with the ambient lighting, as determined by brightness will automatically adjust with the ambient lighting, as determined by
+7 -25
View File
@@ -1,4 +1,5 @@
#include <stdlib.h> #include <stdlib.h>
#include <math.h>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "pico/multicore.h" #include "pico/multicore.h"
@@ -13,7 +14,8 @@
static absolute_time_t lastSend; static absolute_time_t lastSend;
static absolute_time_t lastRead; static absolute_time_t lastRead;
static uint16_t adc_value = 0; //static uint16_t adc_value = 0;
static uint8_t adc_value = 0;
static bool mute = false; static bool mute = false;
static unsigned char buf[BUF_SIZE]; static unsigned char buf[BUF_SIZE];
@@ -165,7 +167,7 @@ static struct key key_list[NUM_KEYS] =
void get_light() { void get_light() {
// get ADC reading from LDR every 500ms // get ADC reading from LDR every 500ms
if ( absolute_time_diff_us(lastRead, get_absolute_time()) >= 500000) { if ( absolute_time_diff_us(lastRead, get_absolute_time()) >= 500000) {
adc_value = adc_read(); adc_value = log2(adc_read());
} }
} }
@@ -176,24 +178,6 @@ void rgb_task(uint8_t dev_addr) {
// updated color info (packets_sent>0) and continue to send the next // updated color info (packets_sent>0) and continue to send the next
// packet if so at a rate of one packet every 20ms // packet if so at a rate of one packet every 20ms
// otherwise, wait 0.5s before sending the next set of color packets // otherwise, wait 0.5s before sending the next set of color packets
/*if (packets_sent>0) {
if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 20000) {
if (backlight) {
send_color(dev_addr, 0x20, 0x20, 0x20);
// send a dim white color (#202020) for all keys
} else {
send_color(dev_addr, 0x00, 0x00, 0x00);
// turn off all lighting by sending (#000000) for all keys
}
lastSend = get_absolute_time();
}
} else {
if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 500000) {
send_initial(dev_addr);
lastSend = get_absolute_time();
}
}*/
if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= delay) { if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= delay) {
if ( packets_sent == 0) { if ( packets_sent == 0) {
// first packet is initialization packet // first packet is initialization packet
@@ -227,9 +211,9 @@ static void send_color(uint8_t dev_addr) {
buf[buf_idx] = 0x81; buf[buf_idx] = 0x81;
switch (key_list[key_idx].mode) { switch (key_list[key_idx].mode) {
case RGB_MODE_ADAPTIVE: // adjust brightness based on LDR ADC reading case RGB_MODE_ADAPTIVE: // adjust brightness based on LDR ADC reading
buf[buf_idx+1] = (ADC_MAX-adc_value)*key_list[key_idx].red/ADC_MAX; buf[buf_idx+1] = (ADC_MAX_LOG2-adc_value)*key_list[key_idx].red/ADC_MAX_LOG2;
buf[buf_idx+2] = (ADC_MAX-adc_value)*key_list[key_idx].green/ADC_MAX; buf[buf_idx+2] = (ADC_MAX_LOG2-adc_value)*key_list[key_idx].green/ADC_MAX_LOG2;
buf[buf_idx+3] = (ADC_MAX-adc_value)*key_list[key_idx].blue/ADC_MAX; buf[buf_idx+3] = (ADC_MAX_LOG2-adc_value)*key_list[key_idx].blue/ADC_MAX_LOG2;
break; break;
case RGB_MODE_MUTE: case RGB_MODE_MUTE:
if (mute) { if (mute) {
@@ -328,8 +312,6 @@ void parse_colors(char * data, uint16_t len) {
if (token != NULL) { if (token != NULL) {
// first string is the RGB color code // first string is the RGB color code
uint8_t red, green, blue; uint8_t red, green, blue;
// sscanf(token, "%2x%2x%2x", &red, &green, &blue);
// sscanf causes Pico 1 to crash
red = hexbyte(token); red = hexbyte(token);
green = hexbyte(token+2); green = hexbyte(token+2);
blue = hexbyte(token+4); blue = hexbyte(token+4);
+8 -1
View File
@@ -11,6 +11,7 @@
#define NUM_KEYS 126 #define NUM_KEYS 126
#define BUF_SIZE 64 #define BUF_SIZE 64
#define ADC_MAX 4096 #define ADC_MAX 4096
#define ADC_MAX_LOG2 log2(ADC_MAX)
enum { enum {
RGG_MODE_INVALID=0, RGG_MODE_INVALID=0,
@@ -187,6 +188,12 @@ bool load_rgb_config(void);
#define CFG_SIGNATURE 0x9e4c #define CFG_SIGNATURE 0x9e4c
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) #if PICO_RP2040
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
#elif PICO_RP2350
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_BLOCK_SIZE - FLASH_SECTOR_SIZE)
#else
#error "Unsupported device. Expected RP2040 or RP2350."
#endif
#endif #endif
+25 -1
View File
@@ -1034,7 +1034,31 @@ static const unsigned char data_index_html[] = {
0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3c, 0x20, 0x30, 0x29, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3c, 0x20, 0x30, 0x29,
0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x63, 0x68, 0x61, 0x6e, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
0x3d, 0x20, 0x30, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x3d, 0x20, 0x30, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0x20, 0x65,
0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x20, 0x69,
0x73, 0x4e, 0x61, 0x4e, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65,
0x49, 0x6e, 0x74, 0x28, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x29, 0x20,
0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x2f, 0x2f, 0x20,
0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69,
0x63, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
0x65, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x2c, 0x20, 0x73,
0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0xa, 0x9, 0x9, 0x9, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x20, 0x3d, 0x20, 0x31, 0x32, 0x38, 0x3b, 0xa, 0x9, 0x9,
0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9,
0x9, 0x9, 0x2f, 0x2f, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20,
0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x62,
0x6f, 0x78, 0x20, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74,
0x73, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x70, 0x61, 0x72,
0x73, 0x65, 0x72, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x73,
0xa, 0x9, 0x9, 0x9, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20,
0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74, 0x28, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9,
0x7d, 0xa, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x7d, 0xa, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x72,
+14772
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 268 KiB

+2 -2
View File
@@ -13,9 +13,9 @@
#include "usb_device.h" #include "usb_device.h"
char cdc_buf[64]; char cdc_buf[128];
uint16_t cdc_len; uint16_t cdc_len;
size_t cdc_count; uint32_t cdc_count;
device_state_t device_state; device_state_t device_state;
static uint8_t desc_configuration[DESC_CFG_MAX]; static uint8_t desc_configuration[DESC_CFG_MAX];
+2 -2
View File
@@ -30,9 +30,9 @@ typedef enum {
DEVICE_RESTART, DEVICE_RESTART,
} device_state_t; } device_state_t;
extern char cdc_buf[64]; extern char cdc_buf[128];
extern uint16_t cdc_len; extern uint16_t cdc_len;
extern size_t cdc_count; extern uint32_t cdc_count;
extern device_state_t device_state; extern device_state_t device_state;
void usb_device_main(void); void usb_device_main(void);
+4 -3
View File
@@ -354,12 +354,14 @@ static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p
uint8_t masked = data[1] & 0x80; uint8_t masked = data[1] & 0x80;
uint16_t msg_len = data[1] & 0x7F; uint16_t msg_len = data[1] & 0x7F;
uint8_t *msg; uint8_t *msg;
uint8_t *mask;
switch (msg_len) { switch (msg_len) {
case 126: // next two bytes are length case 126: // next two bytes are length
memcpy(&msg_len, &data[2], 2); msg_len = ( (uint16_t)data[2] << 8) | data[3];
if (len >= 8) { if (len >= 8) {
msg = &data[8]; msg = &data[8];
mask = &data[4];
} }
break; break;
case 127: // next four bytes are length case 127: // next four bytes are length
@@ -369,6 +371,7 @@ static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p
default: default:
if (len >= 6) { if (len >= 6) {
msg = &data[6]; msg = &data[6];
mask = &data[2];
} }
break; break;
} }
@@ -382,8 +385,6 @@ static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p
if (msg && ws_receive_cb != NULL) { if (msg && ws_receive_cb != NULL) {
// unmask the data if mask bit is received // unmask the data if mask bit is received
if (masked) { if (masked) {
uint8_t *mask = &data[2];
for (int i=0; i<msg_len; i++) { for (int i=0; i<msg_len; i++) {
msg[i] ^= mask[i % 4]; msg[i] ^= mask[i % 4];
} }
+1 -1
View File
@@ -7,7 +7,7 @@
#define WS_MAX_RETRIES 10 #define WS_MAX_RETRIES 10
#define WS_POLL_INTERVAL 60 // WS_POLL_INTERVAL/2 seconds #define WS_POLL_INTERVAL 60 // WS_POLL_INTERVAL/2 seconds
#define WS_MAX_CONN 4 #define WS_MAX_CONN 4
#define WS_BUFFER_SIZE 512 #define WS_BUFFER_SIZE 1024
#define OP_CONT 0x00 #define OP_CONT 0x00
#define OP_TEXT 0x01 #define OP_TEXT 0x01