Hello, welcome my friends! As
we know, we use keyboards for a variety of tasks, but there are some keys that
we use more frequently than others, particularly those for media control and
adjusting brightness. However, there are times when we’re deeply focused on
important work and don’t want to keep reaching for the keyboard to adjust the
volume or skip a track. That’s where a custom, hands-free solution comes in
handy!
I decided to create a media control
system using the Arduino Pro Micro. This compact microcontroller is perfect for
the job because it has built-in HID (Human Interface Device) capabilities,
meaning it can easily function as a keyboard, mouse, or even a media controller
without needing any extra software. Just plug it into your computer, and it’s
ready to go!
To make this project even more
intuitive and user-friendly, I integrated an ultrasonic sensor to detect hand
gestures. By simply moving your hand closer or further away from the sensor,
you can adjust the volume up or down, making it incredibly easy to control your
media without even touching the keyboard. The sensor has a range of up to 60 centimetres,
which allows for smooth and responsive control.
But I didn’t stop there! I also
added touch-sensitive TP223 sensors for additional controls like skipping to
the next track, going back to the previous one, and even muting or pausing the
music with just a tap. These capacitive sensors are highly sensitive and work
seamlessly with the Arduino Pro Micro.
To add a visual element, I
incorporated a 16-bit Neopixel ring that provides beautiful light animations
whenever you interact with the controls. For instance, when you skip to the
next track, the ring lights up with a colourful rotating animation, making the
experience even more engaging.
Finally, I included a 4-digit
7-segment TM1637 display to show the current volume level in real-time. This
ensures you always know exactly how loud your music is without having to guess.
Overall, this project combines
practicality with a touch of creativity, transforming a regular media control
setup into an interactive and hands-free experience. Whether you’re deep into
work or just lounging around, this Arduino-based media controller makes
managing your audio a breeze!
Component Needed
Arduino
Pro Micro: This is suitable for HID applications and its
key features.
Ultrasonic
Sensor: Describe how ultrasonic sensors works, their role in the
project, and why you chose this specific model.
TP223
Touch Sensor: Explain how capacitive touch sensors function
and their benefits in creating touch-based controls.
Neopixel
Ring: Discuss the visual feedback provided by the Neopixel ring
and how it enhances the user.
Other
Components: Mention any resistors, wires, breadboard, or
additional tools you used.
Circuit
Diagram:
Here is a Circuit Diagram Provided with detailed
instructions on how to connect each component, including wiring diagrams. You
can test it on your breadboard or directly solder it to plane PCB board.
Offer Advice to carefully solder and avoid common mistakes,
and ensuring stable connections.
Coding
and Project:
Here is the code you can save or Copy from here:-
#include <HID-Project.h> // Include HID_Project library #include <HID-Settings.h> #include <NewPing.h> #include <Adafruit_NeoPixel.h> // Include Adafruit Neopixel library // Define HID Consumer Control Codes for Brightness #define CONSUMER_BRIGHTNESS_UP 0x006F #define CONSUMER_BRIGHTNESS_DOWN 0x0070 #define TRIGGER_PIN 10 #define ECHO_PIN 9 #define MAX_DISTANCE 60 // Maximum distance we want to measure (in centimeters) // Touch sensor pins #define PREVIOUS_TOUCH_PIN 7 #define NEXT_TOUCH_PIN 14 #define PLAY_PAUSE_TOUCH_PIN 15 #define MUTE_TOUCH_PIN 16 // Define brightness control touch sensor pins #define BRIGHTNESS_UP_PIN 8 #define BRIGHTNESS_DOWN_PIN 6 // Neopixel settings #define NEOPIXEL_PIN 5 #define NUM_PIXELS 16 Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); // Define the 8 corner LEDs indices const int cornerLEDs[8] = {0, 2, 4, 6, 8, 10, 12, 14}; // Variables for continuous hue animation unsigned long lastHueUpdate = 0; const unsigned long hueUpdateInterval = 100; // Time between hue updates (in ms) int hueContinuous = 0; // Current hue value const uint8_t continuousBrightness = 100; // Brightness level (0-255) NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); int val = 0; int previousval = -1; int currentVolume = 0; int smoothVal = 0; // Variables for brightness control int currentBrightness = 50; // Current brightness level (0-100%) int targetBrightness = 50; // Target brightness level based on touch sensors unsigned long lastBrightnessUpdate = 0; const unsigned long brightnessCooldown = 100; // Cooldown time in ms between brightness adjustments unsigned long lastDebounceTime_brightnessUp = 0; unsigned long lastDebounceTime_brightnessDown = 0; const unsigned long debounceDelay = 200; // 200ms debounce delay void setup() { pinMode(PREVIOUS_TOUCH_PIN, INPUT); // Set touch sensor pins as input pinMode(NEXT_TOUCH_PIN, INPUT); pinMode(PLAY_PAUSE_TOUCH_PIN, INPUT); pinMode(MUTE_TOUCH_PIN, INPUT); pinMode(BRIGHTNESS_UP_PIN, INPUT); pinMode(BRIGHTNESS_DOWN_PIN, INPUT); Consumer.begin(); // Initialize computer connection ring.begin(); // Initialize Neopixel ring ring.show(); // Clear any previous data delay(1000); // Wait for computer to connect // Set initial volume to 0 for (int a = 0; a < 50; a++) { Consumer.write(MEDIA_VOLUME_DOWN); delay(2); } } void loop() { unsigned long currentMillis = millis(); unsigned long currentTime = millis(); int distance = sonar.ping_cm(); // Read distance from ultrasonic sensor // Simple smoothing filter for ultrasonic sensor if (distance > 0 && distance <= MAX_DISTANCE) { smoothVal = (smoothVal * 3 + distance) / 4; // Average with previous values for smoothing val = map(smoothVal, 0, MAX_DISTANCE, 0, 100); // Map smoothed distance to 0-100 range if (val != previousval) { // Check if volume level has changed if (val > previousval) { // Increase volume slowly while (currentVolume < val) { Consumer.write(MEDIA_VOLUME_UP); currentVolume++; delay(50); // Slower delay for smoother volume control } } else { // Decrease volume slowly while (currentVolume > val) { Consumer.write(MEDIA_VOLUME_DOWN); currentVolume--; delay(50); // Slower delay for smoother volume control } } previousval = val; // Update previous value } } // Adjust brightness smoothly towards targetBrightness if (currentBrightness < targetBrightness && (currentMillis - lastBrightnessUpdate > brightnessCooldown)) { Consumer.write(CONSUMER_BRIGHTNESS_UP); currentBrightness++; lastBrightnessUpdate = currentMillis; } if (currentBrightness > targetBrightness && (currentMillis - lastBrightnessUpdate > brightnessCooldown)) { Consumer.write(CONSUMER_BRIGHTNESS_DOWN); currentBrightness--; lastBrightnessUpdate = currentMillis; } // Handle brightness touch sensors with debouncing handleBrightnessControls(currentMillis); delay(10); // Short delay to prevent overwhelming the loop // Handle Previous Media touch sensor activation (counterclockwise animation) if (digitalRead(PREVIOUS_TOUCH_PIN) == HIGH) { Consumer.write(MEDIA_PREV); animateRing(false); // Trigger ring animation for previous (counterclockwise) delay(500); // Debounce delay } // Handle Next Media touch sensor activation (clockwise animation) if (digitalRead(NEXT_TOUCH_PIN) == HIGH) { Consumer.write(MEDIA_NEXT); animateRing(true); // Trigger ring animation for next (clockwise) delay(500); // Debounce delay } // Handle Play/Pause touch sensor activation if (digitalRead(PLAY_PAUSE_TOUCH_PIN) == HIGH) { Consumer.write(MEDIA_PLAY_PAUSE); playPauseNeopixelEffect(); delay(500); // Debounce delay } // Handle Mute touch sensor activation if (digitalRead(MUTE_TOUCH_PIN) == HIGH) { Consumer.write(MEDIA_VOLUME_MUTE); muteNeopixelEffect(); // Trigger violet effect for mute delay(500); // Debounce delay } startUp(); delay(100); // Adjust this delay for responsiveness } // Function to animate the Neopixel ring with hue colors void animateRing(bool clockwise) { uint16_t startHue = 0; for (int i = 0; i < NUM_PIXELS; i++) { startHue += 65536 / NUM_PIXELS; // Increment hue for each pixel (full rotation) int pixelIndex = clockwise ? i : (NUM_PIXELS - 1 - i); ring.setPixelColor(pixelIndex, ring.ColorHSV(startHue, 255, 255)); ring.show(); delay(50); // Delay between lighting each pixel } // Clear the ring after animation delay(300); ring.clear(); ring.show(); } // Function for Play/Pause Neopixel effect (Green for Play, Red for Pause) void playPauseNeopixelEffect() { // Green for Play and Red for Pause (alternate for effect) for (int i = 0; i < 2; i++) { ring.fill(ring.Color(0, 255, 0)); // Green for Play ring.show(); delay(200); ring.fill(ring.Color(255, 0, 0)); // Red for Pause ring.show(); delay(200); } ring.clear(); ring.show(); } // Function for Mute Neopixel effect (Violet color) void muteNeopixelEffect() { ring.fill(ring.Color(148, 0, 211)); // Violet color for mute ring.show(); delay(500); ring.clear(); ring.show(); } // Function to handle brightness touch controls void handleBrightnessControls(unsigned long currentMillis) { // Brightness Up Button if (digitalRead(BRIGHTNESS_UP_PIN) == HIGH && (currentMillis - lastDebounceTime_brightnessUp > debounceDelay)) { if (targetBrightness < 100) { targetBrightness += 1; // Increase brightness by 1% targetBrightness = constrain(targetBrightness, 0, 100); } lastDebounceTime_brightnessUp = currentMillis; } // Brightness Down Button if (digitalRead(BRIGHTNESS_DOWN_PIN) == HIGH && (currentMillis - lastDebounceTime_brightnessDown > debounceDelay)) { if (targetBrightness > 0) { targetBrightness -= 1; // Decrease brightness by 1% targetBrightness = constrain(targetBrightness, 0, 100); } lastDebounceTime_brightnessDown = currentMillis; } } /* // Function to set initial volume at startup void setInitialVolume(int initialVolume) { targetVolume = initialVolume; currentVolume = initialVolume; // Assume starting volume is already at initialVolume } */ // Function to set initial brightness at startup void setInitialBrightness(int initialBrightness) { targetBrightness = initialBrightness; currentBrightness = initialBrightness; // Assume starting brightness is already at initialBrightness } void startUp() { unsigned long currentMillis = millis(); // Check if it's time to update the hue if (currentMillis - lastHueUpdate >= hueUpdateInterval) { // Increment the hue value hueContinuous += 256; // Adjust this value to control hue change speed if (hueContinuous >= 65536) { // Hue range for ColorHSV is 0-65535 hueContinuous = 0; // Reset hue after a full rotation } // Update each corner LED with a different hue to create a rotating effect for (int i = 0; i < 8; i++) { // Calculate hue offset for each LED int hueOffset = (hueContinuous + (i * 65536 / 8)) % 65536; // Convert HSV to RGB and set the pixel color uint32_t color = ring.ColorHSV(hueOffset, 255, continuousBrightness); ring.setPixelColor(cornerLEDs[i], color); } // Display the updated colors on the NeoPixel ring ring.show(); // Update the last hue update time lastHueUpdate = currentMillis; // Optional: Debugging information // Serial.print("HueContinuous: "); // Serial.println(hueContinuous); } }
Testing and Debugging
Testing
your Arduino Pro Micro-based HID media control project is a crucial step to
ensure everything works as intended. Begin by verifying each component
individually. Start with the ultrasonic sensor by checking if it accurately
detects the distance of your hand. You can use a simple serial monitor output
to display the distance readings, ensuring they match your expectations within
the 0-60 cm range. If the sensor readings fluctuate wildly, consider adding a
slight delay or using a moving average filter to stabilize the output.
Next,
test the TP223 touch sensors by confirming that each sensor triggers the
intended media control action (e.g., Play/Pause, Next, Previous, Mute) without
delay or false triggers. If a sensor is unresponsive, double-check the wiring
and ensure the sensitivity is correctly adjusted.
Once the
sensors are working, move on to testing the HID functionality. Ensure that the
Arduino correctly sends media control commands to your computer. Use a simple
test code to increase and decrease the volume, and check if it responds
smoothly and accurately. If the volume control is too sensitive or not
responsive enough, you may need to adjust the mapping of sensor readings to
volume levels or introduce a small delay between commands to avoid overwhelming
the system.
After the
basic functions are working, test the Neopixel ring to ensure it provides the
correct visual feedback. If the colours are off or the animations are not
smooth, review your code for any timing issues or incorrect settings in the
Neopixel library.
Finally,
run the entire system together and observe its performance. Look for any
delays, incorrect outputs, or unexpected behaviour. Debugging may involve
tweaking sensor sensitivity, adjusting code logic, or improving power supply
stability to ensure a smooth and reliable user experience.
Conclusion
Creating
your own HID media control system with Arduino Pro Micro is more than just a
technical project—it's a step towards a more personalized, efficient workspace.
This project not only solves a common problem but also gives you the
satisfaction of crafting a tool tailored to your needs. The flexibility to
control your media with simple hand gestures or a tap adds a layer of
convenience that’s hard to match with standard devices. Plus, the vibrant
Neopixel feedback adds a bit of flair, making your setup as visually engaging
as it is functional. Dive into this project, and transform how you interact
with your media!
Comments
Post a Comment