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.
495 lines
14 KiB
C++
495 lines
14 KiB
C++
#include <ESP8266WiFi.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <Wire.h>
|
|
#include <INA226.h>
|
|
#include <ESP8266HTTPClient.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_;
|
|
|
|
// INA226 instances
|
|
INA226 redINA(0x45, &Wire_); // 1000101
|
|
INA226 blueINA(0x41, &Wire_); // 1000100
|
|
INA226 greenINA(0x44, &Wire_); // 1000001
|
|
|
|
const char *ssid = "dlink-AB48";
|
|
const char *password = "mjjbk74568";
|
|
|
|
// Array to store slave IP addresses
|
|
const char* slaveIPs[] = {
|
|
"192.168.129.119", // Slave 1
|
|
};
|
|
const int NUM_SLAVES = sizeof(slaveIPs) / sizeof(slaveIPs[0]);
|
|
|
|
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;
|
|
|
|
ESP8266WebServer server(80);
|
|
WiFiClient wifiClient;
|
|
|
|
|
|
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;
|
|
|
|
// Set master LED
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
|
|
// Propagate to slaves
|
|
String url = String("/set_rgb?r=") + redValue +
|
|
String("&g=") + greenValue +
|
|
String("&b=") + blueValue;
|
|
propagateToSlaves(url);
|
|
|
|
server.send(200, "text/plain", "RGB updated");
|
|
} else {
|
|
server.send(400, "text/plain", "Missing RGB arguments");
|
|
}
|
|
}
|
|
|
|
|
|
// Modified handleSetBrightness to propagate to slaves
|
|
void handleSetBrightness() {
|
|
if (server.hasArg("value")) {
|
|
brightness = server.arg("value").toFloat();
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
|
|
// Propagate to slaves
|
|
String url = String("/set_brightness?value=") + brightness;
|
|
propagateToSlaves(url);
|
|
|
|
server.send(200, "text/plain", "Brightness updated");
|
|
} else {
|
|
server.send(400, "text/plain", "Missing brightness argument");
|
|
}
|
|
}
|
|
|
|
// Modified handleRainbow to propagate to slaves
|
|
void handleRainbow() {
|
|
if (server.hasArg("state")) {
|
|
rainbowActive = server.arg("state") == "true";
|
|
if (!rainbowActive) {
|
|
setRGB(redValue, greenValue, blueValue, brightness);
|
|
}
|
|
|
|
// Propagate to slaves
|
|
String url = String("/rainbow?state=") + server.arg("state");
|
|
propagateToSlaves(url);
|
|
}
|
|
server.send(200, "text/plain", rainbowActive ? "Rainbow started" : "Rainbow stopped");
|
|
}
|
|
|
|
// New function to propagate commands to slaves
|
|
void propagateToSlaves(String endpoint) {
|
|
HTTPClient http;
|
|
for (int i = 0; i < NUM_SLAVES; i++) {
|
|
String url = String("http://") + slaveIPs[i] + endpoint;
|
|
http.begin(wifiClient, url);
|
|
int httpCode = http.GET();
|
|
|
|
if (httpCode != HTTP_CODE_OK) {
|
|
Serial.printf("Failed to propagate to slave %s: %d\n", slaveIPs[i], httpCode);
|
|
}
|
|
http.end();
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
delay(100);
|
|
Wire_.begin(5, 4);
|
|
delay(200);
|
|
scanI2CBus(Wire_);
|
|
delay(200); // Extended delay
|
|
initializeINA226(redINA, "RED");
|
|
delay(200);
|
|
initializeINA226(greenINA, "GREEN");
|
|
delay(200);
|
|
initializeINA226(blueINA, "BLUE");
|
|
|
|
WiFi.begin(ssid, password);
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(1000);
|
|
Serial.print(".");
|
|
}
|
|
Serial.println("Connected to Wi-Fi");
|
|
Serial.print("IP Address: ");
|
|
Serial.println(WiFi.localIP());
|
|
|
|
server.on("/", handleRoot);
|
|
server.on("/set_rgb", handleSetRGB);
|
|
server.on("/set_brightness", handleSetBrightness);
|
|
server.on("/rainbow", handleRainbow);
|
|
server.on("/get_currents", handleGetCurrents);
|
|
|
|
server.begin();
|
|
}
|
|
|
|
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>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-color/2.8.1/vue-color.min.js"></script>
|
|
<style>
|
|
@import url(https://fonts.googleapis.com/css?family=Barlow);
|
|
* { font-family: "Barlow"; box-sizing: border-box; }
|
|
body {
|
|
text-align: center;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #f0f0f0;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
.page-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 2rem;
|
|
justify-content: center;
|
|
align-items: stretch;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
.container {
|
|
background: white;
|
|
padding: 2rem;
|
|
border-radius: 15px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
width: 500px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
.current-monitor {
|
|
background: white;
|
|
padding: 2rem;
|
|
border-radius: 15px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
width: 500px;
|
|
}
|
|
.current-display {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
}
|
|
.current-item {
|
|
padding: 1rem;
|
|
border-radius: 10px;
|
|
color: white;
|
|
text-align: left;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.current-red { background: linear-gradient(to right, #ff0000, #ff4444); }
|
|
.current-green { background: linear-gradient(to right, #00ff00, #44ff44); }
|
|
.current-blue { background: linear-gradient(to right, #0000ff, #4444ff); }
|
|
.current-total {
|
|
background: linear-gradient(to right, #666, #999);
|
|
font-weight: bold;
|
|
}
|
|
.vc-chrome {
|
|
width: 100% !important;
|
|
margin: 1rem 0;
|
|
}
|
|
.controls {
|
|
width: 100%;
|
|
margin-top: 1rem;
|
|
}
|
|
.brightness-control {
|
|
margin: 1rem 0;
|
|
}
|
|
.brightness-control input {
|
|
width: 100%;
|
|
}
|
|
.color-preview {
|
|
width: 100%;
|
|
height: 60px;
|
|
border-radius: 10px;
|
|
margin: 1rem 0;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container,
|
|
.current-monitor {
|
|
width: 100%;
|
|
max-width: 500px;
|
|
}
|
|
.page-container {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app"></div>
|
|
<script>
|
|
new Vue({
|
|
el: "#app",
|
|
components: {
|
|
'chrome-picker': VueColor.Chrome
|
|
},
|
|
data: {
|
|
color: {
|
|
hex: "#000000",
|
|
rgba: { r: 0, g: 0, b: 0, a: 1 }
|
|
},
|
|
brightness: 0,
|
|
rainbow: false,
|
|
currents: {
|
|
red: 0,
|
|
green: 0,
|
|
blue: 0,
|
|
total: 0
|
|
}
|
|
},
|
|
methods: {
|
|
updateColor(color) {
|
|
this.color = color;
|
|
const { r, g, b } = color.rgba;
|
|
fetch(`/set_rgb?r=${r}&g=${g}&b=${b}`);
|
|
},
|
|
updateBrightness() {
|
|
fetch(`/set_brightness?value=${this.brightness}`);
|
|
},
|
|
toggleRainbow() {
|
|
fetch(`/rainbow?state=${this.rainbow}`);
|
|
},
|
|
async updateCurrents() {
|
|
try {
|
|
const response = await fetch('/get_currents');
|
|
this.currents = await response.json();
|
|
} catch (error) {
|
|
console.error('Error fetching currents:', error);
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
// Send initial state to the server
|
|
const { r, g, b } = this.color.rgba;
|
|
fetch(`/set_rgb?r=${r}&g=${g}&b=${b}`);
|
|
fetch(`/set_brightness?value=${this.brightness}`);
|
|
setInterval(this.updateCurrents, 200);
|
|
},
|
|
computed: {
|
|
previewStyle() {
|
|
const { r, g, b } = this.color.rgba;
|
|
const brightness = this.brightness / 100;
|
|
return {
|
|
backgroundColor: `rgba(${r}, ${g}, ${b}, ${brightness})`,
|
|
boxShadow: `0 0 15px rgba(${r}, ${g}, ${b}, ${brightness})`
|
|
};
|
|
}
|
|
},
|
|
template: `
|
|
<div class="page-container">
|
|
<div class="container">
|
|
<h1 class="title">RGB Control</h1>
|
|
|
|
<div class="color-preview" :style="previewStyle"></div>
|
|
|
|
<chrome-picker
|
|
:value="color"
|
|
@input="updateColor"
|
|
:disable-alpha="true"
|
|
></chrome-picker>
|
|
|
|
<div class="controls">
|
|
<div class="brightness-control">
|
|
<p>Brightness: {{ brightness }}%</p>
|
|
<input
|
|
type="range"
|
|
min="0"
|
|
max="100"
|
|
v-model="brightness"
|
|
@change="updateBrightness"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rainbow-toggle">
|
|
<label>
|
|
<input
|
|
type="checkbox"
|
|
v-model="rainbow"
|
|
@change="toggleRainbow"
|
|
/>
|
|
Rainbow Animation
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="current-monitor">
|
|
<h2>Current Monitor</h2>
|
|
<div class="current-display">
|
|
<div class="current-item current-red">
|
|
<span>Red LED:</span>
|
|
<span>{{ currents.red.toFixed(2) }} mA</span>
|
|
</div>
|
|
<div class="current-item current-green">
|
|
<span>Green LED:</span>
|
|
<span>{{ currents.green.toFixed(2) }} mA</span>
|
|
</div>
|
|
<div class="current-item current-blue">
|
|
<span>Blue LED:</span>
|
|
<span>{{ currents.blue.toFixed(2) }} mA</span>
|
|
</div>
|
|
<div class="current-item current-total">
|
|
<span>Total Current:</span>
|
|
<span>{{ currents.total.toFixed(2) }} mA</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
)rawliteral";
|
|
|
|
server.send(200, "text/html", html);
|
|
} |