You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
14 KiB
C++
454 lines
14 KiB
C++
#include <ESP8266WiFi.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <Wire.h>
|
|
#include <INA226.h>
|
|
|
|
|
|
const int redPin = 12; // Red
|
|
const int greenPin = 13; // Green
|
|
const int bluePin = 14; // Blue
|
|
|
|
const int warning = 16;
|
|
const int pwmResolution = 1024;
|
|
|
|
TwoWire Wire_;
|
|
|
|
// WiFi Configuration
|
|
const char *ap_ssid = "RGB_Controller"; // Name of the WiFi network to create
|
|
const char *ap_password = "12345678"; // Password for the WiFi network
|
|
IPAddress local_ip(192,168,4,1); // Static IP address for the access point
|
|
IPAddress gateway(192,168,4,1); // Gateway (same as static IP)
|
|
IPAddress subnet(255,255,255,0); // Subnet mask
|
|
|
|
// INA226 instances
|
|
INA226 redINA(0x45, &Wire_); // 1000101
|
|
INA226 blueINA(0x41, &Wire_); // 1000100
|
|
INA226 greenINA(0x44, &Wire_); // 1000001
|
|
|
|
|
|
ESP8266WebServer server(80);
|
|
|
|
int redValue = 0;
|
|
int greenValue = 0;
|
|
int blueValue = 0;
|
|
float brightness = 100.0;
|
|
bool rainbowActive = false;
|
|
unsigned long lastRainbowUpdate = 0;
|
|
int currentHue = 0;
|
|
|
|
// New variables for current monitoring
|
|
float redCurrent = 0;
|
|
float greenCurrent = 0;
|
|
float blueCurrent = 0;
|
|
unsigned long lastCurrentUpdate = 0;
|
|
|
|
void handleSetRGB() {
|
|
if (server.hasArg("r") && server.hasArg("g") && server.hasArg("b")) {
|
|
redValue = server.arg("r").toInt();
|
|
greenValue = server.arg("g").toInt();
|
|
blueValue = server.arg("b").toInt();
|
|
rainbowActive = false; // Stop rainbow when setting manual color
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
server.send(200, "text/plain", "RGB updated");
|
|
} else {
|
|
server.send(400, "text/plain", "Missing RGB arguments");
|
|
}
|
|
delay(10);
|
|
}
|
|
|
|
void handleSetBrightness() {
|
|
if (server.hasArg("value")) {
|
|
brightness = server.arg("value").toFloat();
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
server.send(200, "text/plain", "Brightness updated");
|
|
} else {
|
|
server.send(400, "text/plain", "Missing brightness argument");
|
|
}
|
|
}
|
|
|
|
void handleRainbow() {
|
|
if (server.hasArg("state")) {
|
|
rainbowActive = server.arg("state") == "true";
|
|
if (!rainbowActive) {
|
|
// Restore the last manual color when rainbow is turned off
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
}
|
|
}
|
|
server.send(200, "text/plain", rainbowActive ? "Rainbow started" : "Rainbow stopped");
|
|
}
|
|
|
|
|
|
void handleGetCurrents() {
|
|
String json = "{\"red\":" + String(redCurrent, 3) +
|
|
",\"green\":" + String(greenCurrent, 3) +
|
|
",\"blue\":" + String(blueCurrent, 3) +
|
|
",\"total\":" + String(redCurrent + greenCurrent + blueCurrent, 3) +
|
|
",\"redBusV\":" + String(redINA.getBusVoltage(), 3) +
|
|
",\"greenBusV\":" + String(greenINA.getBusVoltage(), 3) +
|
|
",\"blueBusV\":" + String(blueINA.getBusVoltage(), 3) +
|
|
"}";
|
|
server.send(200, "application/json", json);
|
|
}
|
|
|
|
void scanI2CBus(TwoWire &wire) {
|
|
Serial.println("Scanning I2C Bus...");
|
|
for (uint8_t address = 1; address < 127; ++address) {
|
|
wire.beginTransmission(address);
|
|
if (wire.endTransmission() == 0) {
|
|
Serial.print("Device found at address 0x");
|
|
Serial.println(address, HEX);
|
|
}
|
|
}
|
|
}
|
|
|
|
void initializeINA226(INA226 &sensor, const char* name) {
|
|
if (!sensor.begin()) {
|
|
Serial.print("INA226 initialization failed for ");
|
|
Serial.println(name);
|
|
} else {
|
|
Serial.print("INA226 initialized for ");
|
|
Serial.println(name);
|
|
}
|
|
|
|
if (strcmp(name, "RED") == 0) {
|
|
sensor.setMaxCurrentShunt(0.4, 0.09);
|
|
} else if (strcmp(name, "GREEN") == 0) {
|
|
sensor.setMaxCurrentShunt(0.35, 0.18);
|
|
} else if (strcmp(name, "BLUE") == 0) {
|
|
sensor.setMaxCurrentShunt(0.35, 0.18);
|
|
} else {
|
|
// Default case for unknown names
|
|
sensor.setMaxCurrentShunt(0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
analogWriteRange(pwmResolution);
|
|
Serial.println("Initializing RGB LED Controller with Current Monitoring");
|
|
|
|
pinMode(redPin, OUTPUT);
|
|
pinMode(greenPin, OUTPUT);
|
|
pinMode(bluePin, OUTPUT);
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
|
|
// Initialize I2C and INA226 sensors
|
|
delay(100);
|
|
Wire_.begin(5, 4);
|
|
delay(200);
|
|
scanI2CBus(Wire_);
|
|
delay(200);
|
|
initializeINA226(redINA, "RED");
|
|
delay(200);
|
|
initializeINA226(greenINA, "GREEN");
|
|
delay(200);
|
|
initializeINA226(blueINA, "BLUE");
|
|
|
|
// Configure ESP8266 as Access Point
|
|
WiFi.mode(WIFI_AP);
|
|
WiFi.softAPConfig(local_ip, gateway, subnet);
|
|
WiFi.softAP(ap_ssid, ap_password);
|
|
|
|
Serial.println("WiFi Access Point Created");
|
|
Serial.print("SSID: ");
|
|
Serial.println(ap_ssid);
|
|
Serial.print("Password: ");
|
|
Serial.println(ap_password);
|
|
Serial.print("IP Address: ");
|
|
Serial.println(local_ip);
|
|
|
|
server.on("/", handleRoot);
|
|
server.on("/set_rgb", handleSetRGB);
|
|
server.on("/set_brightness", handleSetBrightness);
|
|
server.on("/rainbow", handleRainbow);
|
|
server.on("/get_currents", handleGetCurrents);
|
|
|
|
server.begin();
|
|
Serial.println("HTTP server started");
|
|
}
|
|
|
|
void loop() {
|
|
server.handleClient();
|
|
|
|
unsigned long currentMillis = millis();
|
|
|
|
// Current measurement update (every 100ms)
|
|
if (currentMillis - lastCurrentUpdate >= 100) {
|
|
// Get current readings from all sensors
|
|
redCurrent = redINA.getCurrent_mA();
|
|
greenCurrent = greenINA.getCurrent_mA();
|
|
blueCurrent = blueINA.getCurrent_mA();
|
|
lastCurrentUpdate = currentMillis;
|
|
}
|
|
|
|
// Rainbow animation update (every 20ms)
|
|
if (rainbowActive && (currentMillis - lastRainbowUpdate >= 20)) {
|
|
float r, g, b;
|
|
hsvToRgb(currentHue, 1.0, brightness / 100.0, r, g, b);
|
|
setRGB((int)(r * 255), (int)(g * 255), (int)(b * 255), brightness);
|
|
currentHue = (currentHue + 1) % 360;
|
|
lastRainbowUpdate = currentMillis;
|
|
}
|
|
}
|
|
|
|
void hsvToRgb(int hue, float saturation, float value, float &r, float &g, float &b) {
|
|
float c = value * saturation;
|
|
float x = c * (1 - abs(fmod(hue / 60.0, 2) - 1));
|
|
float m = value - c;
|
|
|
|
if (hue >= 0 && hue < 60) {
|
|
r = c; g = x; b = 0;
|
|
} else if (hue >= 60 && hue < 120) {
|
|
r = x; g = c; b = 0;
|
|
} else if (hue >= 120 && hue < 180) {
|
|
r = 0; g = c; b = x;
|
|
} else if (hue >= 180 && hue < 240) {
|
|
r = 0; g = x; b = c;
|
|
} else if (hue >= 240 && hue < 300) {
|
|
r = x; g = 0; b = c;
|
|
} else {
|
|
r = c; g = 0; b = x;
|
|
}
|
|
|
|
r += m; g += m; b += m;
|
|
}
|
|
|
|
int mapDutyCycle(int value, float brightness) {
|
|
float adjusted = value * (brightness / 100.0);
|
|
return (int)(adjusted * pwmResolution / 255.0);
|
|
}
|
|
|
|
void setRGB(int red, int green, int blue, float brightness) {
|
|
analogWrite(redPin, mapDutyCycle(red, brightness));
|
|
analogWrite(greenPin, mapDutyCycle(green, brightness));
|
|
analogWrite(bluePin, mapDutyCycle(blue, brightness));
|
|
}
|
|
|
|
#include <ESP8266WiFi.h>
|
|
#include <ESP8266WebServer.h>
|
|
|
|
// [Previous ESP8266 setup code remains the same until handleRoot()]
|
|
|
|
void handleRoot() {
|
|
String html = R"rawliteral(
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>RGB Control</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
font-family: Arial, sans-serif;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #f0f0f0;
|
|
min-height: 100vh;
|
|
}
|
|
.page-container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
display: grid;
|
|
gap: 20px;
|
|
}
|
|
.card {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.color-preview {
|
|
width: 100%;
|
|
height: 60px;
|
|
border-radius: 8px;
|
|
margin: 10px 0;
|
|
border: 1px solid #ccc;
|
|
}
|
|
.color-inputs {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.color-input {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.color-input label {
|
|
margin-bottom: 5px;
|
|
color: #666;
|
|
}
|
|
input[type="range"] {
|
|
width: 100%;
|
|
margin: 10px 0;
|
|
}
|
|
input[type="number"] {
|
|
width: 100%;
|
|
padding: 8px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
.current-item {
|
|
padding: 10px;
|
|
margin: 5px 0;
|
|
border-radius: 5px;
|
|
color: white;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.red { background: linear-gradient(to right, #ff0000, #ff4444); }
|
|
.green { background: linear-gradient(to right, #00ff00, #44ff44); }
|
|
.blue { background: linear-gradient(to right, #0000ff, #4444ff); }
|
|
.total { background: linear-gradient(to right, #666, #999); }
|
|
.checkbox-wrapper {
|
|
margin: 10px 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="page-container">
|
|
<div class="card">
|
|
<h2>RGB Control</h2>
|
|
<div class="color-preview" id="colorPreview"></div>
|
|
|
|
<div class="color-inputs">
|
|
<div class="color-input">
|
|
<label>Red</label>
|
|
<input type="range" id="redSlider" min="0" max="255" value="0">
|
|
<input type="number" id="redValue" min="0" max="255" value="0">
|
|
</div>
|
|
<div class="color-input">
|
|
<label>Green</label>
|
|
<input type="range" id="greenSlider" min="0" max="255" value="0">
|
|
<input type="number" id="greenValue" min="0" max="255" value="0">
|
|
</div>
|
|
<div class="color-input">
|
|
<label>Blue</label>
|
|
<input type="range" id="blueSlider" min="0" max="255" value="0">
|
|
<input type="number" id="blueValue" min="0" max="255" value="0">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="brightness-control">
|
|
<label>Brightness</label>
|
|
<input type="range" id="brightnessSlider" min="0" max="100" value="100">
|
|
<span id="brightnessValue">100%</span>
|
|
</div>
|
|
|
|
<div class="checkbox-wrapper">
|
|
<label>
|
|
<input type="checkbox" id="rainbowToggle">
|
|
Rainbow Mode
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Current Monitor</h2>
|
|
<div id="currentDisplay">
|
|
<div class="current-item red">
|
|
<span>Red LED:</span>
|
|
<span id="redCurrent">0.00 mA</span>
|
|
</div>
|
|
<div class="current-item green">
|
|
<span>Green LED:</span>
|
|
<span id="greenCurrent">0.00 mA</span>
|
|
</div>
|
|
<div class="current-item blue">
|
|
<span>Blue LED:</span>
|
|
<span id="blueCurrent">0.00 mA</span>
|
|
</div>
|
|
<div class="current-item total">
|
|
<span>Total Current:</span>
|
|
<span id="totalCurrent">0.00 mA</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Color control elements
|
|
const redSlider = document.getElementById('redSlider');
|
|
const greenSlider = document.getElementById('greenSlider');
|
|
const blueSlider = document.getElementById('blueSlider');
|
|
const redValue = document.getElementById('redValue');
|
|
const greenValue = document.getElementById('greenValue');
|
|
const blueValue = document.getElementById('blueValue');
|
|
const brightnessSlider = document.getElementById('brightnessSlider');
|
|
const brightnessValue = document.getElementById('brightnessValue');
|
|
const colorPreview = document.getElementById('colorPreview');
|
|
const rainbowToggle = document.getElementById('rainbowToggle');
|
|
|
|
// Current monitoring elements
|
|
const redCurrent = document.getElementById('redCurrent');
|
|
const greenCurrent = document.getElementById('greenCurrent');
|
|
const blueCurrent = document.getElementById('blueCurrent');
|
|
const totalCurrent = document.getElementById('totalCurrent');
|
|
|
|
// Sync sliders with number inputs
|
|
function syncInputs(slider, value) {
|
|
slider.oninput = () => {
|
|
value.value = slider.value;
|
|
updateColor();
|
|
};
|
|
value.oninput = () => {
|
|
slider.value = value.value;
|
|
updateColor();
|
|
};
|
|
}
|
|
|
|
syncInputs(redSlider, redValue);
|
|
syncInputs(greenSlider, greenValue);
|
|
syncInputs(blueSlider, blueValue);
|
|
|
|
// Update color preview and send to ESP
|
|
function updateColor() {
|
|
const r = redSlider.value;
|
|
const g = greenSlider.value;
|
|
const b = blueSlider.value;
|
|
const brightness = brightnessSlider.value / 100;
|
|
|
|
colorPreview.style.backgroundColor =
|
|
`rgba(${r}, ${g}, ${b}, ${brightness})`;
|
|
|
|
fetch(`/set_rgb?r=${r}&g=${g}&b=${b}`);
|
|
}
|
|
|
|
// Brightness control
|
|
brightnessSlider.oninput = () => {
|
|
const value = brightnessSlider.value;
|
|
brightnessValue.textContent = value + '%';
|
|
fetch(`/set_brightness?value=${value}`);
|
|
updateColor();
|
|
};
|
|
|
|
// Rainbow mode
|
|
rainbowToggle.onchange = () => {
|
|
fetch(`/rainbow?state=${rainbowToggle.checked}`);
|
|
};
|
|
|
|
// Update current readings
|
|
function updateCurrents() {
|
|
fetch('/get_currents')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
redCurrent.textContent = data.red.toFixed(2) + ' mA';
|
|
greenCurrent.textContent = data.green.toFixed(2) + ' mA';
|
|
blueCurrent.textContent = data.blue.toFixed(2) + ' mA';
|
|
totalCurrent.textContent = data.total.toFixed(2) + ' mA';
|
|
});
|
|
}
|
|
|
|
// Update currents every 200ms
|
|
setInterval(updateCurrents, 200);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
)rawliteral";
|
|
|
|
server.send(200, "text/html", html);
|
|
}
|