imported from "final" folder

This commit is contained in:
2025-11-28 12:12:50 +01:00
parent f9288986cf
commit ff8e725b35
1061 changed files with 225150 additions and 96 deletions

View File

@@ -0,0 +1,7 @@
#include "fl/sketch_macros.h"
#if !SKETCH_HAS_LOTS_OF_MEMORY
#include "platforms/sketch_fake.hpp"
#else
#include "NetTestReal.h"
#endif

View File

@@ -0,0 +1,403 @@
/// @file NetTest.ino
/// @brief Educational tutorial for FastLED WASM networking with explicit types
/// @author FastLED Community
///
/// This tutorial demonstrates network functionality in FastLED WASM builds, specifically
/// the fetch API for making HTTP requests. It shows two different approaches for handling
/// asynchronous operations with EXPLICIT TYPES for educational clarity.
///
/// EDUCATIONAL FOCUS: All types are explicitly declared instead of using 'auto'
/// to help you understand the FastLED type system and async patterns.
///
/// TWO ASYNC APPROACHES DEMONSTRATED:
///
/// APPROACH 1: Promise-based with .then() and .catch_() callbacks (JavaScript-like)
/// - Uses method chaining for async operations
/// - Callbacks handle success and error cases
/// - Non-blocking, event-driven pattern
///
/// APPROACH 2: fl::await_top_level() pattern for synchronous-style async code
/// - Uses explicit types: fl::promise<T>, fl::result<T>, fl::optional<T>
/// - Blocks until async operation completes (only safe in Arduino loop()!)
/// - More traditional imperative programming style
///
/// The example toggles between these approaches every 10 seconds to demonstrate
/// both patterns working with the same underlying fetch API.
///
/// FASTLED ASYNC TYPE SYSTEM TUTORIAL:
///
/// Key Types You'll Learn:
/// * fl::promise<T> - Represents a future value of type T
/// * fl::result<T> - Wraps either a successful T value or an Error
/// * fl::response - HTTP response with status, headers, and body
/// * fl::fetch_options - Configuration object for HTTP requests
/// * fl::optional<T> - May or may not contain a value of type T
/// * fl::Error - Error information with message and context
///
/// NEW FETCH API STRUCTURE:
/// * fetch_options is a pure data configuration object
/// * fetch_get() returns fl::promise<fl::response> (not auto!)
/// * Promises can be handled with .then()/.catch_() OR await_top_level()
/// * All async operations integrate with FastLED's engine automatically
///
/// EXPLICIT TYPE EXAMPLES:
///
/// Promise-based approach:
/// ```cpp
/// fl::promise<fl::response> promise = fl::fetch_get("http://example.com");
/// promise.then([](const fl::response& response) { /* handle success */ })
/// .catch_([](const fl::Error& error) { /* handle error */ });
/// ```
///
/// Await-based approach:
/// ```cpp
/// fl::promise<fl::response> promise = fl::fetch_get("http://example.com");
/// fl::result<fl::response> result = fl::await_top_level(promise);
/// if (result.ok()) {
/// const fl::response& response = result.value();
/// // Use response...
/// }
/// ```
///
/// TO RUN THIS TUTORIAL:
///
/// For WASM (recommended for networking):
/// 1. Install FastLED: `pip install fastled`
/// 2. cd into this examples directory
/// 3. Run: `fastled NetTest.ino`
/// 4. Open the web page and check browser console for detailed fetch results
///
/// For other platforms:
/// Uses mock responses for testing the API without network connectivity
#include "fl/fetch.h" // FastLED HTTP fetch API
#include "fl/warn.h" // FastLED logging system
#include "fl/async.h" // FastLED async utilities (await_top_level, etc.)
#include <FastLED.h> // FastLED core library
using namespace fl; // Use FastLED namespace for cleaner code
// LED CONFIGURATION
#define NUM_LEDS 10 // Number of LEDs in our strip
#define DATA_PIN 2 // Arduino pin connected to LED data line
CRGB leds[NUM_LEDS];
// Timing and approach control variables for tutorial cycling
static u32 last_request_time = 0; // Track last request time for 10-second intervals
static int approach_counter = 0; // Cycle through 4 different approaches
void setup() {
// Initialize LED strip
FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
// Set all LEDs to dark red initially (indicates waiting/starting state)
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 0)); // Dark red = starting up
FastLED.show();
// Tutorial introduction messages
FL_WARN("FastLED Networking Tutorial started - 10 LEDs set to dark red");
FL_WARN("Learning HTTP fetch API with TWO different async patterns:");
FL_WARN(" APPROACH 1: Promise-based (.then/.catch_) with explicit types");
FL_WARN(" APPROACH 2: fl::await_top_level pattern with explicit types");
FL_WARN("Toggles between approaches every 10 seconds for comparison...");
FL_WARN("LED colors indicate status: Red=Error, Green=Promise Success, Blue=Await Success");
}
// APPROACH 1: Promise-based async pattern (JavaScript-like)
// This approach uses method chaining and callbacks - very common in web development
void test_promise_approach() {
FL_WARN("APPROACH 1: Promise-based pattern with explicit types");
// TUTORIAL: fetch_get() returns fl::promise<fl::response> (not auto!)
// The promise represents a future HTTP response that may succeed or fail
// Chain .then() for success handling and the lambda receives a
// const fl::response& when the fetch succeeds. error_ will handle network device
// failures (no connection, DNS failure, etc, but not HTTP errors like 404, 500, etc.)
fl::fetch_get("http://fastled.io").then([](const fl::response& response) {
// TUTORIAL: Check if HTTP request was successful
if (response.ok()) {
FL_WARN("SUCCESS [Promise] HTTP fetch successful! Status: "
<< response.status() << " " << response.status_text());
// TUTORIAL: get_content_type() returns fl::optional<fl::string>
// Optional types may or may not contain a value - always check!
fl::optional<fl::string> content_type = response.get_content_type();
if (content_type.has_value()) {
FL_WARN("CONTENT [Promise] Content-Type: " << *content_type);
}
// TUTORIAL: response.text() returns fl::string with response body
const fl::string& response_body = response.text();
if (response_body.length() >= 100) {
FL_WARN("RESPONSE [Promise] First 100 characters: " << response_body.substr(0, 100));
} else {
FL_WARN("RESPONSE [Promise] Full response (" << response_body.length()
<< " chars): " << response_body);
}
// Visual feedback: Green LEDs indicate promise-based success
fill_solid(leds, NUM_LEDS, CRGB(0, 64, 0)); // Green for promise success
} else {
// HTTP error (like 404, 500, etc.) - still a valid response, just an error status
FL_WARN("ERROR [Promise] HTTP Error! Status: "
<< response.status() << " " << response.status_text());
FL_WARN("CONTENT [Promise] Error content: " << response.text());
// Visual feedback: Orange LEDs indicate HTTP error
fill_solid(leds, NUM_LEDS, CRGB(64, 32, 0)); // Orange for HTTP error
}
})
// TUTORIAL: Chain .catch_() for network/connection error handling
// The lambda receives a const fl::Error& when the fetch fails completely
.catch_([](const fl::Error& network_error) {
// Network error (no connection, DNS failure, etc.)
FL_WARN("ERROR [Promise] Network Error: " << network_error.message);
// Visual feedback: Red LEDs indicate network failure
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 0)); // Red for network error
});
}
// APPROACH 2: fl::await_top_level() pattern (synchronous-style async code)
// This approach blocks until completion - feels like traditional programming
void test_await_approach() {
FL_WARN("APPROACH 2: await_top_level pattern with explicit types");
// TUTORIAL: Create a fetch_options object to configure the HTTP request
// fetch_options is a data container - you can set timeout, headers, etc.
fl::fetch_options request_config(""); // Empty URL - will use the URL passed to fetch_get()
request_config.timeout(5000) // 5 second timeout
.header("User-Agent", "FastLED/NetTest-Tutorial"); // Custom user agent
// TUTORIAL: fetch_get() returns fl::promise<fl::response> (explicit type!)
// This promise represents the future HTTP response
fl::promise<fl::response> http_promise = fl::fetch_get("http://fastled.io", request_config);
// TUTORIAL: await_top_level() returns fl::result<fl::response>
// result wraps either a successful response OR an Error - never both!
// CRITICAL: await_top_level() blocks until completion - ONLY safe in Arduino loop()!
fl::result<fl::response> result = fl::await_top_level(http_promise);
// TUTORIAL: Check if the result contains a successful response
if (result.ok()) {
// TUTORIAL: Extract the response from the result
// result.value() returns const fl::response& - the actual HTTP response
const fl::response& http_response = result.value();
FL_WARN("SUCCESS [Await] HTTP fetch successful! Status: "
<< http_response.status() << " " << http_response.status_text());
// TUTORIAL: Check for optional Content-Type header
// get_content_type() returns fl::optional<fl::string> - may be empty!
fl::optional<fl::string> content_type = http_response.get_content_type();
if (content_type.has_value()) {
FL_WARN("CONTENT [Await] Content-Type: " << *content_type);
}
// TUTORIAL: Get the response body as fl::string
const fl::string& response_body = http_response.text();
if (response_body.length() >= 100) {
FL_WARN("RESPONSE [Await] First 100 characters: " << response_body.substr(0, 100));
} else {
FL_WARN("RESPONSE [Await] Full response (" << response_body.length()
<< " chars): " << response_body);
}
// Visual feedback: Blue LEDs indicate await-based success (different from promise)
fill_solid(leds, NUM_LEDS, CRGB(0, 0, 64)); // Blue for await success
} else {
// Either HTTP error OR network error - both end up here
// TUTORIAL: result.error_message() is a convenience method for getting error text
FL_WARN("ERROR [Await] Request failed: " << result.error_message());
// Visual feedback: Red LEDs for any await error
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 0)); // Red for any error
}
}
/// APPROACH 3: JSON Response Handling with FastLED's ideal JSON API
/// This demonstrates fetch responses with automatic JSON parsing
void test_json_response() {
FL_WARN("APPROACH 3: JSON Response handling with fl::Json integration");
// TUTORIAL: Fetch a JSON API endpoint (httpbin.org provides test JSON)
// This endpoint returns JSON with request information
fl::fetch_get("https://httpbin.org/json").then([](const fl::response& response) {
if (response.ok()) {
FL_WARN("SUCCESS [JSON Promise] HTTP fetch successful! Status: "
<< response.status() << " " << response.status_text());
// TUTORIAL: Check if response contains JSON content
// is_json() checks Content-Type header and body content
if (response.is_json()) {
FL_WARN("DETECTED [JSON Promise] Response contains JSON data");
// TUTORIAL: response.json() returns fl::Json with FastLED's ideal API
// Automatic parsing, caching, and safe access with defaults using operator|
fl::Json data = response.json();
// TUTORIAL: Safe JSON access with defaults - never crashes!
// Uses FastLED's proven pattern: json["path"]["to"]["key"] | default_value
fl::string slideshow_url = data["slideshow"]["author"] | fl::string("unknown");
fl::string slideshow_title = data["slideshow"]["title"] | fl::string("untitled");
int slide_count = data["slideshow"]["slides"].size();
FL_WARN("JSON [Promise] Slideshow Author: " << slideshow_url);
FL_WARN("JSON [Promise] Slideshow Title: " << slideshow_title);
FL_WARN("JSON [Promise] Slide Count: " << slide_count);
// TUTORIAL: Access nested arrays safely
if (data.contains("slideshow") && data["slideshow"].contains("slides")) {
fl::Json slides = data["slideshow"]["slides"];
if (slides.is_array() && slides.size() > 0) {
// Get first slide information with safe defaults
fl::string first_slide_title = slides[0]["title"] | fl::string("no title");
fl::string first_slide_type = slides[0]["type"] | fl::string("unknown");
FL_WARN("JSON [Promise] First slide: " << first_slide_title << " (" << first_slide_type << ")");
}
}
// Visual feedback: Blue LEDs for successful JSON parsing
fill_solid(leds, NUM_LEDS, CRGB(0, 0, 128)); // Blue for JSON success
} else {
FL_WARN("INFO [JSON Promise] Response is not JSON format");
// Visual feedback: Yellow for non-JSON response
fill_solid(leds, NUM_LEDS, CRGB(64, 64, 0)); // Yellow for non-JSON
}
} else {
FL_WARN("ERROR [JSON Promise] HTTP error: " << response.status()
<< " " << response.status_text());
// Visual feedback: Red LEDs for HTTP error
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 0)); // Red for HTTP error
}
}).catch_([](const fl::Error& error) {
FL_WARN("ERROR [JSON Promise] Network error: " << error.message);
// Visual feedback: Purple LEDs for network error
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 64)); // Purple for network error
});
FastLED.show();
}
/// APPROACH 4: JSON Response with await pattern
/// Same JSON handling but using await_top_level for synchronous-style code
void test_json_await() {
FL_WARN("APPROACH 4: JSON Response with await pattern");
// TUTORIAL: Using await pattern with JSON responses
// fl::fetch_get() returns fl::promise<fl::response>
fl::promise<fl::response> json_promise = fl::fetch_get("https://httpbin.org/get");
// TUTORIAL: await_top_level() converts promise to result
// fl::result<fl::response> contains either response or error
fl::result<fl::response> result = fl::await_top_level(json_promise);
if (result.ok()) {
// TUTORIAL: Extract the response from the result
const fl::response& http_response = result.value();
FL_WARN("SUCCESS [JSON Await] HTTP fetch successful! Status: "
<< http_response.status() << " " << http_response.status_text());
// TUTORIAL: Check for JSON content and parse if available
if (http_response.is_json()) {
FL_WARN("DETECTED [JSON Await] Response contains JSON data");
// TUTORIAL: Parse JSON with automatic caching
fl::Json data = http_response.json();
// TUTORIAL: httpbin.org/get returns information about the request
// Extract data with safe defaults using FastLED's ideal JSON API
fl::string origin_ip = data["origin"] | fl::string("unknown");
fl::string request_url = data["url"] | fl::string("unknown");
FL_WARN("JSON [Await] Request Origin IP: " << origin_ip);
FL_WARN("JSON [Await] Request URL: " << request_url);
// TUTORIAL: Access nested headers object safely
if (data.contains("headers")) {
fl::Json headers = data["headers"];
fl::string user_agent = headers["User-Agent"] | fl::string("unknown");
fl::string accept = headers["Accept"] | fl::string("unknown");
FL_WARN("JSON [Await] User-Agent: " << user_agent);
FL_WARN("JSON [Await] Accept: " << accept);
}
// TUTORIAL: Access query parameters (if any)
if (data.contains("args")) {
fl::Json args = data["args"];
if (args.size() > 0) {
FL_WARN("JSON [Await] Query parameters found: " << args.size());
} else {
FL_WARN("JSON [Await] No query parameters in request");
}
}
// Visual feedback: Cyan LEDs for successful await JSON processing
fill_solid(leds, NUM_LEDS, CRGB(0, 128, 128)); // Cyan for await JSON success
} else {
FL_WARN("INFO [JSON Await] Response is not JSON format");
// Visual feedback: Orange for non-JSON with await
fill_solid(leds, NUM_LEDS, CRGB(128, 32, 0)); // Orange for non-JSON await
}
} else {
// TUTORIAL: Handle request failures (network or HTTP errors)
FL_WARN("ERROR [JSON Await] Request failed: " << result.error_message());
// Visual feedback: Red LEDs for any await error
fill_solid(leds, NUM_LEDS, CRGB(128, 0, 0)); // Red for await error
}
FastLED.show();
}
void loop() {
// TUTORIAL: Cycle between different async approaches every 10 seconds
// This allows you to see both promise-based and await-based patterns in action
// The LEDs provide visual feedback about which approach succeeded
unsigned long current_time = millis();
// Switch approaches every 10 seconds
// 4 different approaches: Promise, Await, JSON Promise, JSON Await
if (current_time - last_request_time >= 10000) {
last_request_time = current_time;
// Cycle through 4 different approaches
if (approach_counter % 4 == 0) {
test_promise_approach();
FL_WARN("CYCLE: Demonstrated Promise-based pattern (Green LEDs on success)");
} else if (approach_counter % 4 == 1) {
test_await_approach();
FL_WARN("CYCLE: Demonstrated Await-based pattern (Blue LEDs on success)");
} else if (approach_counter % 4 == 2) {
test_json_response();
FL_WARN("CYCLE: Demonstrated JSON Promise pattern (Blue LEDs on success)");
} else if (approach_counter % 4 == 3) {
test_json_await();
FL_WARN("CYCLE: Demonstrated JSON Await pattern (Cyan LEDs on success)");
}
approach_counter++;
FL_WARN("NEXT: Will switch to next approach in 10 seconds...");
}
// TUTORIAL NOTE: Async operations are automatically managed!
// * On WASM: delay() pumps async tasks every 1ms automatically
// * On all platforms: FastLED.show() triggers async updates via engine events
// * No manual async updates needed - everything happens behind the scenes!
// TUTORIAL: This delay automatically pumps async tasks on WASM!
// The delay is broken into 1ms chunks with async processing between chunks.
// This isn't necessary when calling the await approach, but is critical
// the standard promise.then() approach.
// Note: In the future loop() may become a macro to inject auto pumping
// of the async tasks.
delay(10);
}