gbfans / afterlife-lightkit

This is the code repository for the Afterlife Light Kit, coming soon!
GNU General Public License v3.0
1 stars 0 forks source link

Configuration and default patterns #10

Open ajquick opened 2 years ago

ajquick commented 2 years ago

My thoughts for configuration is as follows.

A JSON file is created that contains a standard set of configuration options. This file can be stored in the file system and will be able to be uploaded through the web app. This JSON file can be manually edited, but it can also be updated via GUI in the web app if need be. When config settings are changed, the JSON file is updated and a restart of processes (but not webserver) is initiated.

Alternatively, we could also be exporting the JSON settings to Eeprom for (potentially) easier access to the variables. Perhaps once the config is updated or a new JSON file is uploaded it would save those settings to Eeprom and not touch the JSON file. Ultimately that may come down to what is fastest and what makes the most sense. Perhaps the pin settings for example cannot be used from JSON, but could be used from Eeprom?

I have created an example of a JSON that I think could possibly work here: https://github.com/gbfans/afterlife-lightkit/blob/configuration-testing/SOFTWARE/data/config.json-example

{
  "setup": {
    "powercell": {
      "pin": "GPIO0",
      "num_of_leds": 15,
      "brightness": 255,
      "speed": 10
    },
    "cycltron": {
      "pin": "GPIO5",
      "num_of_leds": 40,
      "brightness": 255,
      "speed": 50
    },
    "vent": {
      "pin": "GPIO4",
      "num_of_leds": 10,
      "brightness": 255,
      "speed": 10
    }
  },
  "state": {
    "startup": {
      "powercell_pattern": "standard",
      "duration": 5,
      "vent": "off"
    },
    "idle": {
      "cyclotron_speed": "random",
      "vent": "off"
    },
    "firing": {
      "duration": 15,
      "automatic_venting": true,
      "venting_time": 5,
      "vent": "off"
    },
    "venting": {
      "powercell_pattern": "static",
      "cyclotron_pattern": "fadeout",
      "duration": 5
    },
    "restart": {
      "repeat_startup": false,
      "duration": 0,
      "vent": "off"
    },
    "shutdown": {
      "powercell_pattern": "countdown",
      "cyclotron": "slowdown",
      "duration": 5,
      "vent": "off"
    }
  },
  "modes": {
    "standard": {
      "powercell_color": "blue",
      "powercell_pattern": "scroll",
      "cyclotron_color": "red",
      "cyclotron_pattern": "smooth",
      "vent_color": "white",
      "vent_pattern": "strobe"
    },
    "slime": {
      "powercell_color": "green",
      "powercell_pattern": "level",
      "cyclotron_color": "green",
      "cyclotron_pattern": "fluid",
      "vent_color": "green",
      "vent_pattern": "spin"
    },
    "meson": {
      "powercell_color": "yellow",
      "powercell_pattern": "level",
      "cyclotron_color": "yellow",
      "cyclotron_pattern": "fade",
      "vent_color": "red",
      "vent_pattern": "glow"
    },
    "stasis": {
      "powercell_color": "blue",
      "powercell_pattern": "level",
      "cyclotron_color": "blue",
      "cyclotron_pattern": "electric",
      "vent_color": "white",
      "vent_pattern": "glow"
    },
    "party":{
      "powercell_color": "rainbow",
      "powercell_pattern": "strobe",
      "cyclotron_color": "rainbow",
      "pattern": "smooth",
      "vent_color": "rainbow",
      "vent_pattern": "spin"
    }
  }
}

Lots of stuff in there is of course not linked to anything in code. So I figured I would outline what I envision the pattern names are and what they would do. Feel free to chime in and provide feedback on any of these.

Cyclotron Specific Patterns

Powercell Specific Patterns

Generic Patterns

There can also be patterns for multi-color things like rainbow.

Those are just things off the top of my head. Obviously only a few of those are an initial must have.

ajquick commented 2 years ago

I uploaded a very basic test script, though currently it is outputting 'null' to the serial port.

https://github.com/gbfans/afterlife-lightkit/blob/configuration-testing/SOFTWARE/config_test.ino

prodestrian commented 2 years ago

Finally finished work for the week so I can get back into all this. Awesome work on the test script, I was planning to use ArduinoJSON as well, it looks great. I'll see if I can pick up where you left off.

prodestrian commented 2 years ago

I've been struggling with the code all day (and part of yesterday), got it this far: #11

It's a start, but there's quite a few roadblocks. I'm hitting up against my C coding knowledge, I know how I want to structure things but I struggle to know how to write it in C. The ConfigManager is a perfect example, it's only loading a handful of settings but it's already taking a lot of manual effort to do it.

prodestrian commented 1 year ago

Progress Update

After some additional proof of concept work, I have scoped out a potential way to organise the Configuration.

Preset

This is the highest level of configuration, which determines which combination of settings should be used. Examples could include:

Additionally users will be able to create their own Presets and import/export/share them. We can also lock our presets, users will then need to clone them to make changes.

A preset should be contained within a single JSON file. We can include multiple JSON files on the filesystem (instead of trying to squash everything into one config.json file).

Light / Element

Also known as the Light Strips, these are the actual hardware connected to the board. There are only 3 (currently):

Configuration involves mapping settings to each of the 3 Light Strips inside each Mode, and for each State. We could also call these "Elements" if that makes the configuration easier to follow.

State

Simply put, the current State of the Lights:

INACTIVE, START, IDLE, OVERHEATING, FIRING, VENTING, SHUTDOWN

NOTE: This is unrelated to the Control State of the Pack (as sent from the Soundboard). Our main application loop (in main.cpp) translates the Control State into a Light State. Configuration will only work with the Light State.

NOTE: For simplicity, Colors will be excluded from States and will only apply to the Modes (for now). So it will not be possible to assign different Colors for different States on the Pack, eg "When firing in Proton Mode, make the Cyclotron purple, but switch back to Red when idle".

Mode

These are different options within each preset, for example the Video Game (TVG) modes. As documented here, the GBFans Soundboard returns a specific Mode when this is triggered via the wand. So we are told specifically "RED", "GREEN", "BLUE", or "ORANGE".

NOTE: The Wand cycles through 8 different modes:

Proton Stream, Boson Dart, Slime Blower, Slime Tether, Stasis Stream, Shock Blast, Overload Pulse and Meson Collider

However the Soundboard doesn't send this to the lightkit, only the color. So both Slime Tether and Slime Blower are "GREEN".

We can re-map the colors to our own naming convention, for example: Mode Name
RED Proton
GREEN Slime
BLUE Stasis
ORANGE Meson

And then allow choosing/assigning the Color as part of the Preset. So if a user wanted their Stasis Mode Purple instead of Blue, they could configure this easily. The Color is assigned individually to each lightstrip/Element.

Potential JSON Schema

NOTE: This requires a little more planning.

JSON Files could/should be given the name of their Preset, eg afterlife-phoebe.json. We will need to limit the length of preset names as per LittleFS requirements:

The LittleFS implementation for the ESP8266 supports filenames of up to 31 characters + terminating zero (i.e. char filename[32]), and as many subdirectories as space permits.

We can differentiate between default/locked presets and user custom ones by adding a gbfans- prefix to the filename. gbfans-afterlife-phoebe.json is still only 28 characters total.

If possible, a checksum should be included in the JSON file (similar to PHP Composer Lockfiles) so that we can easily check for corrupt data before allowing imports, but this will add more complexity to the UI.

So, to replicate this code in Configuration (displaying a blue Tetris animation which speeds up over 3 seconds on the Power Cell): 2023-04-30_19-05

We would have JSON which looks something like this:

{
    "title": "1984 Counter-Clockwise Mode [GBFANS]",
    "checksum": "ca50190ac4a3943ba5c17e566a4febb2",
    "author": "GBFans.com",
    "description": "Classic 1984-style Pack Animations, featuring a Counter-Clockwise Cyclotron (as seen in certain scenes on the Venkman/Zeddemore Packs)",
    "modes": {
        "proton": {
            "powercell": {
                "color": {
                    "red": 0,
                    "green": 0,
                    "blue": 255
                },
                "states": {
                    "startup": {
                        "effect": "tetris",
                        "startSpeed": 25,
                        "endSpeed": 5,
                        "duration": 3000,
                        "reverse": false
                    }
                }
            }
        }
    }
}

Ideally we will want to parse each JSON State configuration into a class which can be more easily passed through the application. This would save us from having to perform lookup logic on nested values.

Initialization

On a blank device there will be no initial configuration files. Although it would be easy enough to upload them as each device is being flashed (before dispatch to customers), ideally this should be an automated process. Similar to the solution for the webserver files, we could automatically compile the preset JSON files into C headers and write them to the filesystem if LittleFS is empty. Or, we can check for a .initialized file on the filesystem, if not found then we write the JSON files out (along with a blank .initialized file). I wouldn't anticipate this additional check would add any significant delay to startup time, it's already quite fast reading from our demo config.json file.

prodestrian commented 1 year ago

Additional Configuration Notes

Further to the above, we will still require a separate config.json. At a minimum this will define the default Preset, and potentially any global customizations (such as the Order in which each TVG mode should be looped through, so potentially when "Slime Mode" is received from the Wand, it could instead switch to "Stasis Mode"). Depending on whether hardware can be instantiated dynamically, we could also use this to store hardware configuration (such as LED strip length if someone has more/less for their Cyclotron, or wants to use a single powerful LED for their N-Filter). We could also use this to store settings for individual spare pins, perhaps if someone wants to solder their Afterlife Gearbox switch to a spare pin and use it to toggle Modes between Classic/Afterlife.

We should be able to abstract the hardware out further too, so that our Lights class simply has 3 LED strips defined of varying lengths, each one is converted to an FX class. As we set States in the Lights class, we use Configuration to tell each FX instance what to do (animation, speed, etc). This should allow us to remove a significant amount of code.

We could (in theory) abstract our Light class even further, so that we don't require individual functions for each State.

prodestrian commented 1 year ago

Configuration Server Progress Update

I attempted to use WifiManager in this project however it causes weird flickering of the LEDs (the first LED in each strip flashes randomly). I have attempted to work around this but it might be an issue at the hardware level. So I switched to ESPAsyncWebServer, but this seems to have the same problem.

This suggests it's an issue with either our Lights or Control classes (either writing the LED data, or reading from the GPIO pins). After some trial and error it seems to be our lights.update(); call, if I comment this out the flickering disappears (even though the LED strips have been initialized).

So, I completely disabled the Cyclotron/NFilter LED strips and FX instances but the same issue still occurs (in case it was due to having multiple timers running). I then disabled the check from the ramp instance, still no luck. I removed the updateBrightness call so the FastLED write (FastLED[0].showLeds(255)) would be as simple as possible. Again, same flickering. I removed the FastLED writes entirely so it would just be checking for updates without writing them. This seems OK.

It must be a FastLED + Wifi conflict, which gives me something to look for: https://forum.makerforums.info/t/i-could-not-get-the-flicker-to-stop-on-a-esp8266-with-arduinoota-wifi/64610 https://github.com/FastLED/FastLED/issues/306

Interestingly it looks like FastLED 3.6.0 was released 2 weeks ago, I was still running 3.5.0. It says there have been improvements, so I will upgrade: https://github.com/FastLED/FastLED/blob/master/release_notes.md It still flickers.

Someone in one of the threads suggested removing the #define FASTLED_ALLOW_INTERRUPTS 0 which I added previously. This reduced the flickering but it's still there.

I then found #define FASTLED_INTERRUPT_RETRY_COUNT 0, adding this to Lights.h seems to have fixed the flickering.

I added the following web server endpoint:

    // Send a GET request to <IP>/get?message=<message>
    server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
        String message;
        if (request->hasParam(PARAM_MESSAGE)) {
            message = request->getParam(PARAM_MESSAGE)->value();
            if (message == "idle")
            {
                lights.setState(IDLE);
            } else if (message == "wifioff") {
                // Turn off Wifi
                WiFi.mode(WIFI_OFF);
            } else {
                lights.setState(INACTIVE);
            }
        } else {
            message = "No message sent";
        }
        request->send(200, "text/plain", "Hello, GET: " + message);
    });

When connecting to the device over Wifi this URL lets us change the pack state from IDLE to INACTIVE and back again. This is working well, the lights are updating without flicker, however there is a moderate amount of lag (more noticeable on the Powercell).

So I included a ?message=wifioff feature which should turn off the Wifi. If I leave the lights in IDLE (spinning) mode and hit this URL, I would expect it to just kill the Wifi connection but it also stops the lights, potentially because the server is still running. So I changed this to server.end(), the lag remained. I tried:

server.end();
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_OFF);

It lags at first, but after a few seconds it seems to improve.

So the next test is to have the Wifi switched off at startup and go immediately into an idle animation. Then, send a string over Serial to switch the Wifi on and start the webserver. Then use the webserver to toggle the idle animation on/off. Finally use the webserver to stop the Wifi connection. All of this should occur without introducing lag or restarts.

This seems to be working, I can turn Wifi on/off, and I can still access the webserver even if it is initialized late: 2023-06-11_11-39 2023-06-11_11-38

I experienced unusual behaviour, I switched the animation state over the webserver and it seemed to cause the device to restart. I have added a serial output to detect this and I don't believe this is actually occurring (more than likely it's my Chrome browser doing pre-fetching, so it hits the URL even though I haven't actually clicked "enter").

Starting Wifi causes the LED animations to pause briefly. If Wifi fails to connect it can be a 10 second pause.

I don't believe either of the above are problems.

However turning off Wifi after several attempts did seem to cause a fatal exception: 2023-06-11_11-47 This may be due to the serial strings I am using for debugging, so I will move onto the Wifi Activation feature now in case it's a false alarm.

prodestrian commented 1 year ago

Enabling On-Demand Configuration Mode

We are not able to use hardware (ie a separate switch) to enable WiFi/Configuration, and we don't want it enabled on startup unless requested as this introduces severe performance issues.

So we need a way to enable WiFi and the Configuration webserver on-demand using only signals from the Soundboard (and the Wand).

I believe a specific sequence of signals within a set interval would work.

The first requirement is having the ability to keep track of the last X signals received. I have now implemented this, it knows the last 5 signals/commands. If I switch the pack on/off 3 times I see the following result: 2023-06-11_12-32 (Startup => Powerdown => Startup => Powerdown => Off) 5 signals isn't enough of a history so I will increase this to 7:

Changed! - Old: 14, New: 0
History: 
    0: 0
    1: 14
    2: 1
    3: 14
    4: 1
    5: 14
    6: 1

Next we need a way to concatenate these into a string we can compare against (My C++ knowledge is lacking here, there's probably some more efficient way to do this comparison but I don't have time for that kind of deep dive right now). Here is what I came up with:

Changed! - Old: 1, New: 14
History: 
    0: 14
    1: 1
    2: 14
    3: 1
    4: 14
    5: 1
    6: 14
Changed! - Old: 14, New: 0
History: 
    0: 0
    1: 14
    2: 1
    3: 14
    4: 1
    5: 14
    6: 1
Sequence Matched!
Sequence Matched!
Sequence Matched!
Sequence Matched!

So in this case I was able to compare against a hard-coded sequence, and once received it can trigger additional code.

I have added debouncing so the sequence match is only returned once.

This means if I switch the pack on/off 3 times and wait for the shutdown to finish, it starts Wifi:

     Connected!
Changed! - Old: 0, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 0
Starting Web Server
IP Address: 192.168.1.19

So the next step is to have this action both start and stop the webserver, to rule out the fatal exception I was receiving earlier as being caused by Wifi deactivation.

This seems to be working, no exceptions that I can see:

Changed! - Old: 0, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 0
Starting Web Server
IP Address: 192.168.1.19
Changed! - Old: 0, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 1
Changed! - Old: 1, New: 14
Changed! - Old: 14, New: 0
Stopping Web Server

After starting/stopping the webserver and then booting the pack, it seems to be working smoothly (idle, firing, overheat, shutdown, etc). There's a tiny amount of lag on the powercell at times but I don't think it's a major issue right now.

prodestrian commented 1 year ago

Wifi Access Point

The above demo currently connects to my home WiFi (which occasionally fails to connect). We would prefer to have this as an access point instead.

So I will bring in some of the demo code we already have from #5 and get the webserver working as a Wifi hotspot. This seems to work:

#ifndef APSSID
#define APSSID "GBF-LK-" + WiFi.macAddress()
#define APPSK  "ghostbusters"
#endif

2023-06-11_13-48

However:

  1. I can't see the SSID on my phone
  2. I can't connect from my laptop (the password is rejected)

I'm certain we had this working previously.

So I uploaded the previous webserver demo code to see how this behaves, and it still refuses to connect from either device. So I tried the default ESP8266 WifiAccessPoint demo and had the same problem. Eventually via the Arduino IDE I ran a full filesystem flash (which includes Wifi configuration) and this seems to have fixed it, so I can connect from my laptop (still unable to connect via phone). It's possible my Wifi Configuration was corrupted from my WifiManager attempt earlier.

I uploaded our sketch and can see the SSID:

Starting Web Server
WiFi SSID: GBF-LK-48:55:19:14:DB:5D

2023-06-11_14-16

However I still couldn't connect from either device.

It looks like the DNSServer needs to be always running, regardless of whether WiFi is active.

I was able to trigger the WiFi and connect from my laptop, and access the demo page.

I disconnected the WiFi and tried to power up the pack. The signals were received but the lights did nothing. I restarted and still can't get the lights to switch on when I power up. My lights are completely not working right now, I can't see anything in the code which would cause this. I've tried demo Neopixel and FastLED sketches and they're not working either.

The default blink sketch works fine, the ESP8266 seems to work. I disconnected the Arduino Nano (which I'm emulating the soundboard from).

So there might be a hardware problem with the GBFans board. I will break out my multimeter and try to debug.

EDIT: It turned out to not be a hardware issue, it's due to the LittleFS being erased (and we don't have any fallback for JSON configuration files yet, so it fails).

I can continue from here now.

ajquick commented 1 year ago

Just a reminder with the hardware, make sure the output enable is active on the level shifter. The lights won't work without it and may be why it's not working with the generic demo code.

-AJ Quick

prodestrian commented 1 year ago

You're absolutely right, I thought I still had that in the demo code but I had reverted it for another project.

My Serial.begin() was sitting at the end of the setup() function, so it was starting Serial after loading configuration, which is why I didn't see the Opening config file failed. error (which would have saved me a lot of debugging). This was a good opportunity to overhaul/simplify the debug process, so I've defined two new methods: debug(x) debugln(x) And a new constant for debug mode: #define debug 0 (Set to 1 while debugging). This replaces all Serial.print() and Serial.println() calls.

With debug mode disabled, the LED animations are completely smooth now. There's some minor lag when WiFi has been activated, but as soon as WiFi is switched off again it runs fast. So if the lag only appears while the user is using WiFi then I don't think there's any big issues here.

I'm going to leave it running for an hour or so and see if there are any exceptions/crashes or memory leaks. EDIT: It's been running for nearly 2 hours with WiFi switched on and still working fine. Firing is instantaneous.

Then I can pick back up on the web UI portion of this task (which I started last night, so far it's going well).

prodestrian commented 1 year ago

Configuration Simplification and Refactor

To reduce the amount of code needed in each configuration option (and the number of options exposed via the UI) I have begun simplifying the ConfigManager, FX, and Light classes.

We no longer need separate RGB values for each lightstrip, we can use Hex Color Codes as supported by the CRGB class:

  "modes": {
    "proton": {
      "powercell": {
        "color": "0x0000FF"
      },
      "cyclotron": {
        "color": "0xFF0000"
      },
      "nfilter": {
        "color": "0xFFFFFF"
      }
    }
  }
}
Hex Color: 16711680
Config Color: 16711680

This sample output confirms that the modes.proton.cyclotron.color JSON value (0xFF000) is being converted to the same unsigned long as a hard-coded color. My Cyclotron is currently red as expected. If I change the color from 0xFF0000 (red) to 0x00FF00 (green) in the JSON file and upload it to the filesystem (without changing any code), my Cyclotron changes to green. So this part is working.

This is the first time we have confirmed that colours can actually change in 'realtime' on an existing FX instance, so I have modified the firing code to change colour from red to green and back again. This works fairly smoothly, which means MODE changes should work when I finish implementing them (proton, slime, stasis, meson).

My next requirement is to translate string 'effect' values into a usable ENUM value, ie "tetris" into TETRIS. I would prefer to keep the strings in the JSON files for readability but if necessary we can store integers here.

prodestrian commented 1 year ago

Light Preset & JSON Configuration Progress Update

After hours of issues with memory leaks and JSON parsing problems, I finally have working code to read values from JSON files and convert them into usable properties.

Example JSON config file:

{
    "modes": {
        "proton": {
            "color": {
                "powercell": "0x0000FF",
                "cyclotron": "0xFF0000",
                "nfilter": "0xFFFFFF"
            },
            "states": {
                "idle": {
                    "powercell": {
                        "effect": "SPINNING"
                    },
                    "cyclotron": {
                        "effect": "CYCLING"
                    },
                    "nfilter": {
                        "effect": "OFF"
                    }
                }
            }
        }
    }
}

In this case I was able to modify the powercell effect from SPINNING to CYCLING in the JSON file, upload it to the device, and the powercell changed as expected. There are helper classes which should greatly reduce code needed in each method.

I will need to add some sanity checks to ensure that invalid/missing values in the JSON files are handled gracefully, at the moment this causes a fatal exception (the device crashes and restarts itself constantly).

I have committed the changes I made so far as the application is in a semi-working state right now and I can build on this fairly easily.

Currently WiFi is not working though, I can't connect from my laptop or my phone. I rolled back to the WiFi configuration branch (which I'm 95% sure was working at the time I pushed the changes), and this isn't working either. I will reboot my laptop and see if this fixes it. If not, I'll need to invest more time going back and troubleshooting what might have caused it to suddenly stop working.

UPDATE Restarted my laptop and WiFi is working again. I switched back to the latest branch, recompiled/uploaded the code, switched on WiFi, and I was still able to connect. So I feel confident that nothing has been broken here.

ajquick commented 1 year ago

I just downloaded PlatformIO on VS Code. Is there a particular branch you want me to follow along with?

I tried one of the more recently updated branches and didn't get any Wi-Fi to show up or lights.

prodestrian commented 1 year ago

@ajquick feature/implement-light-presets is the very latest version and should work, however I haven't finished porting everything out of the filesystem so you will still need to upload the /data/config.json JSON file manually for now.

For debugging I recommend changing DEBUG 0 to DEBUG 1 in Settings.h before you build the firmware.

You can use the Arduino IDE to upload the files if that's easier, or via PlatformIO:

# Upload JSON file from `/data`
pio run --target uploadfs

# Build/Upload firmware
pio run --target upload

You can also access the Serial Monitor via:

pio device monitor --eol=LF

(You may not need the --eol=LF argument). This won't output anything unless you've compiled in debug mode (DEBUG 1).

Let me know if you have any issues.

ajquick commented 1 year ago

Here is what I'm seeing so far.

(I don't have a way to control the switches, so my lights are not being tested right now).

I have turned on the webserver and see the Wi-Fi, but I am unable to connect on either my phone or computer. It just immediately kicks me out and says unable to connect.

One thing that I will need to do is test the on-off-on-off-idle function with the actual sound board. There is a possibility it does the start up and then waits for start up to finish before going into shut down. I don't think it is an immediate on-off-on-off like you would do with the switches.

prodestrian commented 1 year ago

That's useful information, thanks! I think I'll need to revisit the WiFi, it works semi-reliably from my Ubuntu laptop but doesn't work at all from my Android phone. So I'll put together a simple test sketch and get that working smoothly on the hardware, then add the lights back in one piece at a time (making sure it works at each step). It might mean swapping out a dependency or two.

As far as the WiFi activation sequence it's pretty easy to adjust the code for that, so currently it's: Startup => Powerdown => Startup => Powerdown => Startup => Powerdown => Off (or 1 => 14 => 1 => 14 => 1 => 14 => 0)

This can be changed to: Startup => Idle => Powerdown => Startup => Idle => Powerdown => Off if that's the sequence we get from the soundboard.

It means changing this line here: https://github.com/gbfans/afterlife-lightkit/commit/a82e7e23a7985bdfec652c1b770b5f01c52fd466#diff-851554c4f7d4a1bd3cf5e7503033ed97ec6176282b987743c10ac08d99ee9303R142

if (controls.isSequenceMatch("1:14:1:14:1:14:0")) {

We can also add an "or" to this, in case someone tries to trigger WiFi while their pack is already idle (so the sequence might be slightly different), or if somehow the soundboard is sending different combinations of signals. And if needed we can increase the number of items available in the signal history from 7 to 10.

ajquick commented 1 year ago

I will definitely check the sound board and verify what we get exactly.

Is there any time frame it checks for the sequence or is it always just a sequence irrespective of time?

This is almost never going to happen during normal operation:

Startup => Powerdown => Startup => Powerdown => Startup => Powerdown => Off

But this might if someone turns on the pack without firing twice:

Startup => Idle => Powerdown => Startup => Idle => Powerdown => Off

prodestrian commented 1 year ago

At the moment there's no time limit, but because I'm not waiting for an "Idle" state between each cycle it means you have to trigger Powerdown while it's still in Startup. As you say, if the soundboard forces you to wait for Startup to finish (and then sends a quick Idle signal before going into Powerdown) it means what I have above won't work.

I've been at events where I've cycled my pack on/off multiple times without firing, but it's always with minutes/hours of Idle between those powercycles. That's why I was hoping that Powerdown during Startup would be a good way to do this, it seems like you'd have to do it intentionally. But we'll see what the soundboard does, even a single frame of "Idle" signal would stop this from working.

We'll need a timer to automatically deactivate WiFi so I'm more than happy to add another timer for the signals (ie "last signal received").

This would clear the history if more than (let's say 30 seconds) has elapsed since the last signal. Could probably even make it 10 seconds, honestly we're not using the history feature for anything else anytime soon anyway, unless we add a different sequence for quickly changing Presets (ie toggling the pack between Classic & Afterlife modes).

ajquick commented 1 year ago

One thing to consider is also standalone mode.

https://github.com/gbfans/afterlife-lightkit/issues/15#issuecomment-1181434990

Upon boot, if all pins are pulled low, it is assumed that the board is under the control of the sound board.

If all pins are pulled high (through pull up resistors) it is assumed the board is in standalone mode.

So Wi-Fi mode might be need to be turned on two different ways. I don't think standalone mode is active right now because the lights should turn on without anything connected.

prodestrian commented 1 year ago

We have the framework for supporting standalone mode but I haven't implemented it yet. I don't think it's a big job but there are other proirities I have to focus on first. I'm hoping to get another 8-12hrs of dev in this weekend to get some of those major features off the list (unfortunately probably not sorting out WiFi just yet).

We can turn on WiFi whichever way you think is best, as long as it's not on by default during startup because it blocks everything for several seconds (even when I use supposedly "asynchronous" code).

In the meantime, this weekend's priority is:

  1. Get presets working without relying on the filesystem (ie compile the JSON into the code and fallback to a default preset) - Approx 3-6hrs
  2. Actually implement the Afterlife preset (approx 1hrs)
  3. Implement TVG modes (I'll need to solder another button to my Arduino Nano to trigger the signal for this and emulate the soundboard signal) (approx 2-4hrs)
  4. (If I have time) get Classic/84 animation working (approx 1-2hrs)

As far as remaining tasks:

  1. Troubleshoot the WiFi issues (approx 1-8hrs)
  2. Get the WebServer and Configuration UI up and running (this is at least a whole-weekend job, approx 8-24hrs)
  3. Add OTA update Support including GitHub Actions compiled firmware and upgrade page (approx 8-12hrs)
  4. Add support for Standalone Mode (approx 2-4hrs)

I believe that would be enough to make this shippable (v1.0.0 release) but it's possible I'm missing things here. I don't know if we have full support for venting yet, there's some soundboard signals documented that I don't fully understand yet (automatic vs manual vent for example) so that may require some additional handling once you've connected it up to the soundboard and recorded how it's behaving.

prodestrian commented 1 year ago

Troubleshoot the WiFi issues (approx 1-8hrs)

I've just burned another ~2hrs~ ~3hrs~ 4hrs on this without any luck. My laptop connects, my Android phone doesn't. With debugging on, I can see my laptop connect and disconnect in the Serial Monitor. My phone tries to connect then immediately disconnects.

I've tried 5 different demo ESP8266WiFi sketches from various libraries. I've tried changing the channel. I've tried removing the password. I've tried powering the board from a high-current USB powerbank. I've tried changing the configuration in the Arduino IDE (eg CPU Frequency etc). I'm assuming the GBFans board is actually a "LOLIN (WEMOS) D1 Mini (Clone)"? (That's what I've always been setting the Arduino IDE to). I've also tried downgrading various packages to older versions. I've trawled through 30+ different support threads with zero success here.

I'm actually not convinced the WiFi access point has ever worked properly on my GBFans board. For context I received my board on August 11th, but all the demo work we did on the WiFi was well before that: https://github.com/gbfans/afterlife-lightkit/issues/5 There's a comment from you saying that it was working but I don't know if you were testing on the same/final hardware.

I'm going to put a pin in WiFi for now but this looks like it's going to be problematic if I can't even get a demo sketch connecting from my phone. I don't think I can fix this one on my own.

Edit: Here's the settings I'm using in the Arduino IDE for the GBFans board. I don't know if they're correct, but it's what I based the PlatformIO configuration on too: 2023-06-24_15-34 The only difference is PlatformIO is using dio instead of dout and a slightly different flash speed, but if I change these to match it doesn't fix anything.

Edit: I also tried my old Samsung phone, same issue. It finds the AP, it tries to connect, then immediately disconnects. Nothing appears in the debug logs.

Edit: I installed WLED to the device. The WLED-AP SSID appears. I still can't connect to it from my phone.

Edit: I downgraded to ESP8266 2.5.2, installed Python2, and uploaded the default WifiAccessPoint sketch. The SSID shows up. I can connect from my Ubuntu laptop and get the HTML page to appear, this part works perfectly. But I still can't connect from my phone at all so I've gone back to ESP8266 3.1.2.

Edit: One last thing, I tried installing AutoConnect. Again, works perfectly fine from my Ubuntu laptop, doesn't work from my phone. I tried several of their FAQ fixes (including changing the channel) but nothing worked.

I'm convinced there's a hardware issue going on here, but in the literal hundreds of forum posts I've read today I can't find anyone else who's had exactly this issue.