Setting values not working as expected - noob needs help

This is how the web page looks after a quick redesign


(current state is #2 about 70% time remaining)

Here is the full code with the reworked web page

#include <WebServer.h>
#include <Adafruit_PCA9685.h>
SYSTEM_MODE(SEMI_AUTOMATIC)

const char htmlPAGE[] = 
"<html><head><title>IAB Lightbox Control-Panel</title><style>"
"table, th, td {border:1px solid black;border-collapse:collapse}"
"th, td {width:15%%;text-align:center;padding:5px}"
"tr {background-color:#F8F8F0}"
"button {width:100%%;height:30px}"
"progress {direction:rtl}"
"</style></head><body><h1>IAB Lightbox 5 Control-Panel</h1>"
"<form action='/box' method='POST'>"
"<table><tr>"
"<th style='background-color:#FFAAAA'>red</th>"
"<th style='background-color:#AAFFAA'>green</th>"
"<th style='background-color:#AAAAFF'>blue</th>"
"<th style='background-color:#FFFFFF'>white</th>"
"<th style='color:#FF0000'>infrared</th>"
"<th>duration</th></tr>"
"<tr><th colspan='5'>µm Photons / m² * s</th><th>minutes</th></tr>"
"<tr style='background-color:%s'>"
"<td><input type='number' name='red_1' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='green_1' min='0' max='2000'value='%lu'></td>"
"<td><input type='number' name='blue_1' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='white_1' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='infrared_1' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='timer_1' min='0' max='600000' value='%lu'></td>"
"<tr style='background-color:%s'>"
"<td><input type='number' name='red_2' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='green_2' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='blue_2' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='white_2' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='infrared_2' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='timer_2' min='0' max='600000' value='%lu'></td>"
"<tr style='background-color:%s'>"
"<td><input type='number' name='red_3' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='green_3' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='blue_3' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='white_3' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='infrared_3' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='timer_3' min='0' max='600000' value='%lu'></td>"
"<tr style='background-color:%s'>"
"<td><input type='number' name='red_4' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='green_4' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='blue_4' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='white_4' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='infrared_4' min='0' max='2000' value='%lu'></td>"
"<td><input type='number' name='timer_4' min='0' max='600000' value='%lu'>"
"</tr><tr>"
"<td><button name='enable' value='1' onclick='window.location.reload();'>Start</button></td>"
"<td style='text-align:left'><input type='checkbox' name='repeat' value='1' %s>repeat</td>"
"<td><button name='enable' value='0' onclick='window.location.reload();'>Reset</button></td>"
"<td colspan='3' style='text-align:right'>Christian Streng & Christian Karle &copy;2019</td>"
"</tr><tr><td colspan='6'><progress value='%.2f' max='1' style='width:100%%'/></td></tr>"
"</body></html>"
;

IPAddress  ipAddress( 192, 168,   5, 204 );
IPAddress  ipGateway( 192, 168,   5, 100 );
IPAddress  ipNetmask( 255, 255, 255,   0 );
IPAddress  ipDNS    (   8,   8,   8,   8 );

const char PREFIX[]    = "/box";
const int  BUFFER_SIZE = sizeof(htmlPAGE)+512;

void wifiSetup() {
  uint8_t ip[4];
  EEPROM.get(0, ip);
  WiFi.on();
  if (!(ipAddress == ip)) {
    WiFi.setStaticIP(ipAddress, ipNetmask, ipGateway, ipDNS);
    WiFi.useStaticIP();
    EEPROM.put(0, ipAddress.raw());
  }
  WiFi.connect();
}
STARTUP(wifiSetup());

const int STATE_COUNT = 5;
enum LEDS {
  cGREEN = 0,
  cIR,
  cWHITE,
  cRED,
  cBLUE,
  LED_COUNT 
};

const char itemNames[][12] = 
{ "green"
, "infrared"
, "white"
, "red"
, "blue"
, "timer"
};

struct LED_PARA {
  float    offset;
  float    factor;
} ledParameters[LED_COUNT] = 
{ { -0.2930, 0.0099 }                                               // originally { +0.239,  1.130 }
, { -0.6229, 0.0264 }                                               //            { -0.617,  2.609 }
, { -4.7511, 0.1155 }                                               //            { +6.701, 13.771 }
, { -1.1121, 0.0086 }                                               //            { +0.979,  1.087 }
, { -0.1621, 0.0197 }                                               //            { +1.308,  2.713 }
};

struct LED_STATE {
  uint32_t base[LED_COUNT];
  uint32_t duration; 
} ledStates[STATE_COUNT];

int      curState = 0;                                              // number of current state (0..off)
uint32_t msState  = 0;                                              // time when the current state was initiated
bool     repeat   = false;

WebServer webServer(PREFIX, 80);
Adafruit_PCA9685 ledDriver(0x40, true);

void boxCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete);

char ip[16];
void setup() {
  Particle.variable("localIP", ip);
  RGB.control(true);
  ledDriver.begin();
  ledDriver.setPWMFreq(300);

  webServer.setDefaultCommand(&boxCmd);
  webServer.begin();
  strcpy(ip, WiFi.localIP().toString());
}

void loop() {
  static int      prevState = -1;
  
  webServer.processConnection();

  if (!curState) {                                                  // in idle state
    if (curState != prevState) {
      for(int i=0; i < LED_COUNT; i++)                              // switch off LEDs 
        ledDriver.setVal(i, 0);
      RGB.color(0, 0, 0);
      prevState = 0;
    }
    return;
  }

  if (curState != prevState) {                                      // when transition to new state is required
    for(int i=0; i < LED_COUNT; i++)                                //   set LEDs according to state parameters
      ledDriver.setVal(i, curState ? (ledStates[curState].base[i] + ledParameters[i].offset) / ledParameters[i].factor : 0 );
    RGB.color(ledStates[curState].base[cRED], ledStates[curState].base[cGREEN], ledStates[curState].base[cBLUE]);
    msState = millis();                                             //   record when this state was started
    prevState = curState;
  }
  
  if (millis() - msState >= ledStates[curState].duration * 60000) { // when state duration has elapsed
    curState++;                                                     //   push forward the current state
    if (curState == STATE_COUNT)                                    //   when last state done
      curState = repeat;                                            //   wrap round and stop or restart (depending on repeat flag)
  }
}

void boxCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) {
  if (type == WebServer::POST) {
    char     name[16];
    char     value[16];
    char     item[12];
    int      state = 0;
    uint32_t val = 0;
      
    while(server.readPOSTparam (name, sizeof(name)-1, value, sizeof(value)-1 )) {
      val = strtoul(value, NULL, 10);
      if (2 == sscanf(name, "%[^_]_%d", item, &state)) {            // if name is of format XXXX_#
        for(int i=0; i <= LED_COUNT; i++) {                         // travers itemNames list to find the respective item
          if (!strcmp(item, itemNames[i])) {                        // when found
            if (i < LED_COUNT)                                      // and index indicates a LED 
              ledStates[state].base[i] = val;                       // store base value
            else                                                    // must be a timer value
              ledStates[state].duration = val;                      // store timer duration
            break;
          }
        }
      }
      else if (!strcmp(name, "enable")) {
        if (!(curState = atoi(value)))                              // if "enable" is false
          memset(ledStates, 0, sizeof(ledStates));                  //   reset the letStates to 0 
        repeat = false;                                             // reset repeat flag before it gets parsed next
      }
      else if (!strcmp(name, "repeat"))                             // checkbox will only be transmitted when checked 
        repeat = true;
    } 
    server.httpSeeOther(PREFIX);
  }
  else if (type == WebServer::GET) {
    char buffer[BUFFER_SIZE];
    server.httpSuccess();
    snprintf(buffer, sizeof(buffer)
            , htmlPAGE
            , 1==curState ? "#FFFFA0" : "", ledStates[1].base[cRED], ledStates[1].base[cGREEN], ledStates[1].base[cBLUE], ledStates[1].base[cWHITE], ledStates[1].base[cIR], ledStates[1].duration
            , 2==curState ? "#FFFFA0" : "", ledStates[2].base[cRED], ledStates[2].base[cGREEN], ledStates[2].base[cBLUE], ledStates[2].base[cWHITE], ledStates[2].base[cIR], ledStates[2].duration
            , 3==curState ? "#FFFFA0" : "", ledStates[3].base[cRED], ledStates[3].base[cGREEN], ledStates[3].base[cBLUE], ledStates[3].base[cWHITE], ledStates[3].base[cIR], ledStates[3].duration
            , 4==curState ? "#FFFFA0" : "", ledStates[4].base[cRED], ledStates[4].base[cGREEN], ledStates[4].base[cBLUE], ledStates[4].base[cWHITE], ledStates[4].base[cIR], ledStates[4].duration
            , repeat ? "checked" : "unchecked", curState ? (ledStates[curState].duration - (millis()-msState)/60000.0) : 0
            );
    server.write((uint8_t *)buffer, strlen(buffer));
  }
}
2 Likes

Thank you! The page looks much more sophisticated and beautiful.

I will test it tomorrow! I wasn’t in the lab the last days.

1 Like

That’s incredible!

The page looks really amazing and the the bar at the bottom that indicates the time is superb! Thank you so much! Totally love it!

1 Like

Glad you like it :blush:

Liebe Grüße aus Salzburg nach Karlsruhe!

Ebenfalls liebe Grüße :slight_smile: Ohne deine Hilfe wäre die Software nie so zu Stande gekommen.

1 Like

Würdest du mir kurz eine Mail schreiben, dass ich mit Vorname und Nachname zitieren kann? Sonst würde ich mit ScruffR (Particle, San Francisco [USA]) zitieren. Wenn das paper raus ist, kann ich dich natürlich auch informieren.

We found one minor bug. Could you help me fix it?

If we submit the value for white, the white LED lights up as intended. However the value, which was set in white is printed to infrared. For Infrared the opposite effect is true.

Thanks!

That's an easy fix.
When I redesigned the web page I swapped the "IR" and "white" columns but forgot to also swap them in the snprintf() statement which fills the page :blush:

should rather be

    snprintf(buffer, sizeof(buffer)
            , htmlPAGE
            , 1==curState ? "#FFFFA0" : "", ledStates[1].base[cRED], ledStates[1].base[cGREEN], ledStates[1].base[cBLUE], ledStates[1].base[cWHITE], ledStates[1].base[cIR], ledStates[1].duration
            , 2==curState ? "#FFFFA0" : "", ledStates[2].base[cRED], ledStates[2].base[cGREEN], ledStates[2].base[cBLUE], ledStates[2].base[cWHITE], ledStates[2].base[cIR], ledStates[2].duration
            , 3==curState ? "#FFFFA0" : "", ledStates[3].base[cRED], ledStates[3].base[cGREEN], ledStates[3].base[cBLUE], ledStates[3].base[cWHITE], ledStates[3].base[cIR], ledStates[3].duration
            , 4==curState ? "#FFFFA0" : "", ledStates[4].base[cRED], ledStates[4].base[cGREEN], ledStates[4].base[cBLUE], ledStates[4].base[cWHITE], ledStates[4].base[cIR], ledStates[4].duration
            , repeat ? "checked" : "unchecked", curState ? (ledStates[curState].duration - (millis()-msState)/60000.0) : 0
            );

(corrected above)

1 Like

Thank you! That’s perfect :slight_smile:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.