openstatic.org

openstatic.org

RGB Lights Controller

A Simple rgb light controller made with an ESP8266 and 3 MOSFETs. To use simply make a get request to it like so:

$ curl http://1.2.3.4/api.json?color=#ff0000

There are also two other interfaces:

  • a telnet interface (port 23). Simply send RGB hex values #ffffff followed by a newline character.
  • RTP/Apple Midi, Control Change Values 20, 21, 22 on channel 11 represent R, G, B

Demo Video

Code (download below)

/*
    RGB Light Controller For ESP8266
    Copyright (C) 2019  openstatic.org

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>
#include <IotWebConf.h>
#include <WiFiUdp.h>
#include "AppleMidi.h"
#include <ESP8266mDNS.h>

#undef IOTWEBCONF_DEBUG_TO_SERIAL
#define IOTWEBCONF_DEFAULT_AP_MODE_TIMEOUT_MS 120000

#define PWMRANGE
const int port = 23;

WiFiServer telnetServer(port);
WiFiClient telnetClient;
DNSServer dnsServer;
ESP8266WebServer httpServer(80);
wl_status_t wl_status;
IotWebConf iotWebConf("rgb_lights", &dnsServer, &httpServer, "initpass");

const int RED_PIN = 12;
const int GREEN_PIN = 14;
const int BLUE_PIN = 16;

const int MIDI_CHANNEL = 11;

const int RED_MIDI_CC = 20;
const int GREEN_MIDI_CC = 21;
const int BLUE_MIDI_CC = 22;

int red = 0;
int green = 0;
int blue = 0;

APPLEMIDI_CREATE_INSTANCE(WiFiUDP, AppleMIDI); // see definition in AppleMidi_Defs.h

// Forward declaration
void OnAppleMidiControlChange(byte channel, byte number, byte value);

String inputString = "";
boolean stringComplete = false;


extern "C" {
#include "user_interface.h"
}


void writeRGB()
{
  analogWrite(RED_PIN, red);
  analogWrite(GREEN_PIN, green);
  analogWrite(BLUE_PIN, blue);
}

int hex2int(char h)
{
  if (h >= '0' && h <= '9')
    return (int) h - 48;
  else if (h >= 'A' && h <= 'F')
    return (int) h - 55;
  else if (h >= 'a' && h <= 'f')
    return (int) h - 87;
  else
    return 0;
}

void processLine(String data)
{
  if (data.startsWith("#"))
  {
    //Serial.println(data);
    int r, g, b;
    //const char* hex = data.substring(1).c_str();
    //Serial.println(hex);
    red = (hex2int(data.charAt(1)) * 16) + hex2int(data.charAt(2));
    green = (hex2int(data.charAt(3)) * 16) + hex2int(data.charAt(4));
    blue = (hex2int(data.charAt(5)) * 16) + hex2int(data.charAt(6));
    writeRGB();
  }
}

void serialEvent()
{
  while (Serial.available())
  {
    char inChar = (char)Serial.read(); 
    if (inChar == '\n' || inChar == '\r') {
      if (inputString.length() > 0)
      {
        stringComplete = true;
      }
    } else {
      inputString += inChar; 
    }
  }
  if (stringComplete)
  {
    processLine(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

void readClient()
{
  while (telnetClient.available())
  {
    char inChar = (char) telnetClient.read(); 
    if (inChar == '\n' || inChar == '\r') {
      if (inputString.length() > 0)
      {
        stringComplete = true;
      }
    } else {
      inputString += inChar; 
    }
  }
  if (stringComplete)
  {
    processLine(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

void setup()
{
  //Setting the analogWriteRange ensures that the pin will output max at 255
  analogWriteRange(255);
  iotWebConf.init();
  Serial.begin(115200);
  
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  
  WiFi.hostname(iotWebConf.getThingName());
  AppleMIDI.begin(iotWebConf.getThingName());
  AppleMIDI.OnReceiveControlChange(OnAppleMidiControlChange);

  delay(200);
  Serial.println("INIT");
  writeRGB();
  telnetServer.begin();
  httpServer.on("/api.json", jsonRespond );
  httpServer.on("/config", []{ iotWebConf.handleConfig(); });
  httpServer.on("/", handleRoot );
  httpServer.onNotFound ( handleNotFound );
  httpServer.begin();
  tryMDNS();
}

void handleRoot()
{
  if (iotWebConf.handleCaptivePortal())
  {
    // -- Captive portal request were already served.
    return;
  }
}

// Broadcast MDNS services
void tryMDNS()
{
  if (MDNS.begin(iotWebConf.getThingName(), WiFi.localIP()))
  {
    MDNS.addService("http", "tcp", 80);
    MDNS.addService("apple-midi", "udp", 5004);
  }
}

void OnAppleMidiControlChange(byte channel, byte number, byte value)
{
  if (channel == MIDI_CHANNEL)
  {
    if (number == RED_MIDI_CC)
    {
      red = map(value, 0, 127, 0, 255);
    } else if (number == GREEN_MIDI_CC) {
      green = map(value, 0, 127, 0, 255);
    } else if (number == BLUE_MIDI_CC) {
      blue = map(value, 0, 127, 0, 255);
    }
    writeRGB();
  }
  
}

void loop() 
{
  iotWebConf.doLoop();  
  if (WiFi.status() == WL_CONNECTED)
  {
    MDNS.update();
    AppleMIDI.run();
    httpServer.handleClient();
    if (telnetServer.hasClient())
    {
      if (!telnetClient || !telnetClient.connected())
      {
        if (telnetClient) 
        {
          telnetClient.stop();
        }
        telnetClient = telnetServer.available();
      } else {
        telnetServer.available().stop();
      }
    }
    if (telnetClient && telnetClient.connected() && telnetClient.available())
    {
      readClient();
    }
  }
  // put your main code here, to run repeatedly:
  if (Serial.available())
  {
    serialEvent();
  }
  //delay(1);
}

// this is the only right way to turn storage data into json
void generateJSONObject(char* bfr, int s)
{
  DynamicJsonBuffer jsonBuffer;
  JsonObject& rootObject = jsonBuffer.createObject();
  rootObject["_freemem"] = system_get_free_heap_size();
  rootObject["red"] = red;
  rootObject["blue"] = blue;
  rootObject["green"] = green;
  rootObject.printTo(bfr, s);
}

// what we respond to /api.json with....
void jsonRespond( void )
{
    for ( uint8_t i = 0; i < httpServer.args(); i++ )
    {
      String argname = httpServer.argName(i);
      String argvalue = httpServer.arg(i);
      if (argname.equals("color"))
      {
        processLine(argvalue);
      }
    }
  char out[2048];
  generateJSONObject(out, 2048);
  httpServer.send(200, "text/javascript", out);
}

void handleNotFound()
{
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += httpServer.uri();
  message += "\nMethod: ";
  message += ( httpServer.method() == HTTP_GET ) ? "GET" : "POST";
  message += "\nArguments: ";
  message += httpServer.args();
  message += "\n";
  for ( uint8_t i = 0; i < httpServer.args(); i++ ) {
    message += " " + httpServer.argName ( i ) + ": " + httpServer.arg ( i ) + "\n";
  }
  httpServer.send ( 404, "text/plain", message );
}

Download

Latest Update: May 02 2020 04:48:00 PM EDT


rgb_controller_esp8266.ino
(Arduino Project)